Skip to content

Commit

Permalink
Merge pull request #13 from msalinoh/ecg_drivers
Browse files Browse the repository at this point in the history
Conversion of ECG Drivers to RTOS Thread
  • Loading branch information
kgrewal26 authored Jul 21, 2023
2 parents 7c38efa + eb22956 commit 8f148e2
Show file tree
Hide file tree
Showing 15 changed files with 305 additions and 17 deletions.
15 changes: 14 additions & 1 deletion TagV3.0_U575VGT/Core/Inc/Lib Inc/threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
#include "app_threadx.h"
#include "Sensor Inc/audio.h"
#include "Sensor Inc/BNO08x.h"
#include "Sensor Inc/ECG.h"

//Enum for all threads so we can easily keep track of the list + total number of threads.
// If adding a new thread to the list, put it before the "NUM_THREADS" element, as it must always be the last element in the enum.
typedef enum __TX_THREAD_LIST {
AUDIO_THREAD,
IMU_THREAD,
NUM_THREADS
ECG_THREAD,
NUM_THREADS //DO NOT ADD THREAD ENUMS BELOW THIS
}Thread;

//TX Thread configuration members that can be defined at runtime (constants).
Expand Down Expand Up @@ -82,6 +84,17 @@ static Thread_ConfigTypeDef threadConfigList[NUM_THREADS] = {
.preempt_threshold = 5,
.timeslice = TX_NO_TIME_SLICE,
.start = TX_DONT_START
},
{
//ECG Thread
.thread_name = "ECG Thread",
.thread_entry_function = ecg_thread_entry,
.thread_input = 0x1234,
.thread_stack_size = 1024,
.priority = 4,
.preempt_threshold = 4,
.timeslice = TX_NO_TIME_SLICE,
.start = TX_DONT_START
}
};

Expand Down
3 changes: 3 additions & 0 deletions TagV3.0_U575VGT/Core/Inc/Sensor Inc/BNO08x.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
#define IMU_NEW_DATA_TIMEOUT_MS 2000
#define IMU_DUMMY_PACKET_TIMEOUT_MS 500

//ThreadX flag bit for when IMU data is ready
#define IMU_DATA_READY_FLAG 0x1

//IMU typedef definition for useful variables
typedef struct __IMU_Typedef{

Expand Down
97 changes: 96 additions & 1 deletion TagV3.0_U575VGT/Core/Inc/Sensor Inc/ECG.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,107 @@
* ECG.h
*
* Created on: Feb. 9, 2023
* Author: amjad
* Author: Kaveet
*/

#ifndef INC_SENSOR_INC_ECG_H_
#define INC_SENSOR_INC_ECG_H_

#include "main.h"
#include "tx_api.h"

#define ECG_ADC_I2C_ADDRESS 0b1000100

//Internal V_reference on the ADC
#define ECG_ADC_V_REF 2.048

//Programmable gain value
#define ECG_ADC_GAIN 1

//The full-scale range of the ADC
#define ECG_ADC_FS_RANGE (ECG_ADC_V_REF / ECG_ADC_GAIN)

//1 LSB is FS/2^23 by the datasheet.
#define ECG_ADC_LSB (ECG_ADC_FS_RANGE / 8388608)

//Registers
#define ECG_ADC_CONFIG_REGISTER 0
#define ECG_ADC_STATUS_REGISTER 1

//Commands to send to ADC
#define ECG_ADC_RESET 0b00000110
#define ECG_ADC_START 0b00001000
#define ECG_ADC_POWERDOWN 0b00000010
#define ECG_ADC_READ_DATA 0b00010000
#define ECG_ADC_READ_CONFIG_REG 0b00100000
#define ECG_ADC_READ_STATUS_REG 0b00100100
#define ECG_ADC_WRITE_CONFIG_REG 0b01000000

//Timeouts for polling data ready
#define ECG_ADC_DATA_TIMEOUT 2000

//ThreadX flag bit for when data is ready
#define ECG_DATA_READY_FLAG 0x1

//ECG configuration register has the following structure:
// Bit 7-5: MUX Electrode Selection
// Bit 4: Gain (0 = 1 or 1 = 4)
// Bit 3-2: Data Rate
// Bit 1: Conversion mode (0 = single, 1 = continuous)
// Bit 0: Vref (0 = internal/2.048V, 1 = external reference)
#define ECG_ADC_DEFAULT_CONFIG_REGISTER 0b00000010

typedef struct __ECG_TypeDef {

I2C_HandleTypeDef *i2c_handler;

GPIO_TypeDef* n_data_ready_port;
uint16_t n_data_ready_pin;

// Data buffers for I2C data
uint8_t raw_data[3];

float voltage;

} ECG_HandleTypeDef;

//Our ECG function that serves as the entry point for the thread. It manages the entire ECG.
void ecg_thread_entry(ULONG thread_input);

/*
* Initializes the ECG ADC so we can begin sampling data and retrieving it.
*
* Resets the chip and writes the appropriate registers to start continuous conversions.
*
* Parameters:
* -hi2c: an I2C handler for communication
* -ecg: an ECG handler to store data
*/
HAL_StatusTypeDef ecg_init(I2C_HandleTypeDef* hi2c, ECG_HandleTypeDef* ecg);

//Read the ADC for the newest data and stores it in the ECG struct
HAL_StatusTypeDef ecg_read_adc(ECG_HandleTypeDef* ecg);

//Writes to "data" to the ecg configuration register
HAL_StatusTypeDef ecg_write_configuration_register(ECG_HandleTypeDef* ecg, uint8_t data);

//Reads the configuration register and stores it in "data".
HAL_StatusTypeDef ecg_read_configuration_register(ECG_HandleTypeDef* ecg, uint8_t * data);

/*
* Configures the ADC MUX to toggle which electrodes to read from.
*
* Parameters:
* -ecg: the ecg handler
* -electrode_config: the bits of the 3-8 MUX to appropriately set the correct electrodes.
* The MUX only has 3 control bits, but this is an 8 bit number. Use the 3 least significant bits.
*/
HAL_StatusTypeDef ecg_configure_electrodes(ECG_HandleTypeDef* ecg, uint8_t electrode_config);

//Polls the ECG data ready pin for new data. Will timeout if no new data.
HAL_StatusTypeDef ecg_poll_data_ready(ECG_HandleTypeDef* ecg);

//Resets the ECG sensor (should be done before initialization)
HAL_StatusTypeDef ecg_reset_adc(ECG_HandleTypeDef* ecg);

#endif /* INC_SENSOR_INC_ECG_H_ */
4 changes: 4 additions & 0 deletions TagV3.0_U575VGT/Core/Inc/Sensor Inc/audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
#define AUDIO_CIRCULAR_BUFFER_SIZE_MAX (UINT16_MAX/2)
#define AUDIO_CIRCULAR_BUFFER_SIZE (32256)

//ThreadX flag bits to show when the buffer is half full or completely full
#define AUDIO_BUFFER_HALF_FULL_FLAG 0x1
#define AUDIO_BUFFER_FULL_FLAG 0x2

typedef enum {
AUDIO_BUF_STATE_EMPTY,
AUDIO_BUF_STATE_HALF_FULL,
Expand Down
1 change: 1 addition & 0 deletions TagV3.0_U575VGT/Core/Inc/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ void Error_Handler(void);
#define ECG_LOD_D11_GPIO_Port GPIOD
#define ECG_NDRDY_Pin GPIO_PIN_14
#define ECG_NDRDY_GPIO_Port GPIOD
#define ECG_NDRDY_EXTI_IRQn EXTI14_IRQn
#define ECG_NSDN_Pin GPIO_PIN_15
#define ECG_NSDN_GPIO_Port GPIOD

Expand Down
1 change: 1 addition & 0 deletions TagV3.0_U575VGT/Core/Inc/stm32u5xx_it.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ void BusFault_Handler(void);
void UsageFault_Handler(void);
void DebugMon_Handler(void);
void EXTI12_IRQHandler(void);
void EXTI14_IRQHandler(void);
void GPDMA1_Channel0_IRQHandler(void);
void TIM6_IRQHandler(void);
void SDMMC1_IRQHandler(void);
Expand Down
2 changes: 1 addition & 1 deletion TagV3.0_U575VGT/Core/Src/Lib Src/threads.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Thread_HandleTypeDef threads[NUM_THREADS];

void threadListInit(){

for (uint8_t index = 0; index < NUM_THREADS; index++){
for (Thread index = 0; index < NUM_THREADS; index++){

//Attach our config info (the ones statically defined in the header file)
threads[index].config = threadConfigList[index];
Expand Down
4 changes: 2 additions & 2 deletions TagV3.0_U575VGT/Core/Src/Sensor Src/BNO08x.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ void IMU_thread_entry(ULONG thread_input){

//Poll for data to be ready. Flag is set by our interrupt handler.
//Calling this function blocks and suspends the thread until the data becomes available.
tx_event_flags_get(&imu_event_flags_group, 0x1, TX_OR_CLEAR, &actual_events, TX_WAIT_FOREVER);
tx_event_flags_get(&imu_event_flags_group, IMU_DATA_READY_FLAG, TX_OR_CLEAR, &actual_events, TX_WAIT_FOREVER);

imu_running = true;
//Get the data and store in our handler
Expand Down Expand Up @@ -116,7 +116,7 @@ HAL_StatusTypeDef IMU_get_data(IMU_HandleTypeDef* imu){

//Read the header in to a buffer
HAL_GPIO_WritePin(imu->cs_port, imu->cs_pin, GPIO_PIN_RESET);
HAL_SPI_Receive(imu->hspi, receiveData, IMU_ROTATION_VECTOR_REPORT_LENGTH, HAL_MAX_DELAY);
HAL_SPI_Receive(imu->hspi, receiveData, IMU_ROTATION_VECTOR_REPORT_LENGTH, 500);
HAL_GPIO_WritePin(imu->cs_port, imu->cs_pin, GPIO_PIN_SET);

//Extract length from first 2 bytes
Expand Down
150 changes: 149 additions & 1 deletion TagV3.0_U575VGT/Core/Src/Sensor Src/ECG.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,155 @@
* ECG.c
*
* Created on: Feb. 9, 2023
* Author: amjad
* Author: Kaveet
*/

#include "Sensor Inc/ECG.h"
#include "main.h"
#include "stm32u5xx_hal_cortex.h"
#include <stdbool.h>

extern I2C_HandleTypeDef hi2c4;

TX_EVENT_FLAGS_GROUP ecg_event_flags_group;

bool ecg_running = 0;

void ecg_thread_entry(ULONG thread_input){

//Declare ecg handler and initialize chip
ECG_HandleTypeDef ecg;
ecg_init(&hi2c4, &ecg);

//Create our flag that indicates when data is ready
tx_event_flags_create(&ecg_event_flags_group, "ECG Event Flags");

//Enable our interrupt handler (signals when data is ready)
HAL_NVIC_EnableIRQ(EXTI14_IRQn);

while(1){

//holds event flags
ULONG actual_events;

//We've initialized the ECG to be in continuous conversion mode, and we have an interrupt line signalling when data is ready.
//Thus, wait for our flag to be set in the interrupt handler. This function call will block the entire task until we receive the data.
tx_event_flags_get(&ecg_event_flags_group, ECG_DATA_READY_FLAG, TX_OR_CLEAR, &actual_events, TX_WAIT_FOREVER);

ecg_running = true;

//New data is ready, retrieve it
ecg_read_adc(&ecg);

ecg_running = false;
}
}
HAL_StatusTypeDef ecg_init(I2C_HandleTypeDef* hi2c, ECG_HandleTypeDef* ecg){

ecg->i2c_handler = hi2c;
ecg->n_data_ready_port = ECG_NDRDY_GPIO_Port;
ecg->n_data_ready_pin = ECG_NDRDY_Pin;

//Reset the ADC
ecg_reset_adc(ecg);

//Configure the ecg adc
ecg_write_configuration_register(ecg, ECG_ADC_DEFAULT_CONFIG_REGISTER);

//Start the conversions
uint8_t start_command = ECG_ADC_START;
HAL_I2C_Master_Transmit(ecg->i2c_handler, (ECG_ADC_I2C_ADDRESS << 1), &start_command, 1, HAL_MAX_DELAY);

return HAL_OK;
}

HAL_StatusTypeDef ecg_read_adc(ECG_HandleTypeDef* ecg){

//Issue read command
uint8_t read_command = ECG_ADC_READ_DATA;
HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(ecg->i2c_handler, (ECG_ADC_I2C_ADDRESS << 1), &read_command, 1, HAL_MAX_DELAY);

//return if failed
if (ret != HAL_OK)
return ret;

//Read the data
ret = HAL_I2C_Master_Receive(ecg->i2c_handler, (ECG_ADC_I2C_ADDRESS << 1), ecg->raw_data, 3, HAL_MAX_DELAY);

//We have 24 bits of data. Turn it into a signed 32 bit integer. Data is shifted out of ADC with the MSB first.
//TODO: Do 2's complement conversion (once we can test values)
int32_t digitalReading = (ecg->raw_data[0] << 16) | (ecg->raw_data[1] << 8) | ecg->raw_data[0];
ecg->voltage = digitalReading * ECG_ADC_LSB;

return ret;
}

HAL_StatusTypeDef ecg_write_configuration_register(ECG_HandleTypeDef* ecg, uint8_t data){

uint8_t configure_command[2] = {0b01000000, data};

return HAL_I2C_Master_Transmit(ecg->i2c_handler, (ECG_ADC_I2C_ADDRESS << 1), configure_command, 2, HAL_MAX_DELAY);
}

HAL_StatusTypeDef ecg_read_configuration_register(ECG_HandleTypeDef* ecg, uint8_t * data){

//Send read command
uint8_t read_command = ECG_ADC_READ_CONFIG_REG;
HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(ecg->i2c_handler, (ECG_ADC_I2C_ADDRESS << 1), &read_command, 1, HAL_MAX_DELAY);

//return if failed
if (ret != HAL_OK)
return ret;

//Read the config register into the data buffer
ret = HAL_I2C_Master_Receive(ecg->i2c_handler, (ECG_ADC_I2C_ADDRESS << 1), data, 1, HAL_MAX_DELAY);

return ret;
}

HAL_StatusTypeDef ecg_configure_electrodes(ECG_HandleTypeDef* ecg, uint8_t electrode_config){

//The ADC has 8 possible configurations for the Positive and negative channel. See datasheet for a list of them.
//They are MUXED to allow for 3 bits to control the 8 combinations. These 3 bits are bits 7-5 in the config register.

//Read the config register
uint8_t config_register = 0;
ecg_read_configuration_register(ecg, &config_register);

//We want to only change the first 3 bits, so we should first unset them then set them appropriately.
//This prevents the other settings from being changed
//
//Unset the first 3 bits
config_register &= ~(111 << 5);

//Now set them with our passed in values
config_register |= (electrode_config << 5);

//Now, call the function to write to the config register with our new values
return ecg_write_configuration_register(ecg, config_register);
}

HAL_StatusTypeDef ecg_poll_data_ready(ECG_HandleTypeDef* ecg){

uint32_t startTime = HAL_GetTick();

//Poll for a falling edge (data ready is low)
while (HAL_GPIO_ReadPin(ecg->n_data_ready_port, ecg->n_data_ready_pin)){

//Track current time and check if we timeout
uint32_t currentTime = HAL_GetTick();

if ((currentTime - startTime) > ECG_ADC_DATA_TIMEOUT){
return HAL_TIMEOUT;
}
}

return HAL_OK;
}

HAL_StatusTypeDef ecg_reset_adc(ECG_HandleTypeDef* ecg){

uint8_t reset_command = ECG_ADC_RESET;

return HAL_I2C_Master_Transmit(ecg->i2c_handler, (ECG_ADC_I2C_ADDRESS << 1), &reset_command, 1, HAL_MAX_DELAY);
}
Loading

0 comments on commit 8f148e2

Please sign in to comment.