Hi,
I'm trying to implement FreeRTOS on nRF52832 using the S132 SoftDevice and SDK version 17.1.0.
This is meant to read multiple sensors at a configured frequency - each thread is triggered from an ISR via a notification.
However, although all of my threads seem to start and yield OK, they don't seem to be waking on notifications or message queue events.
The scheduler seems to be running, and software timers work OK. I believe that I have configured the priorities correctly for Cortex-M4.
Can anyone please advise as to how I should go about diagnosing this? Please find code snippets below - is there anything obviously wrong?
Many thanks,
Adam
/**************************************************************
* @brief Main function, application entry point.
* @notes None.
* @param None.
**************************************************************/
int main(void) {
bool erase_bonds;
log_init();
NRF_LOG_INFO("\r\n************* FIRMWARE v%d.%d.%d *************", FIRM_VER_MAJOR, FIRM_VER_MINOR, FIRM_VER_PATCH);
NRF_LOG_INFO("DEVICE ID: %08X%08X", NRF_FICR->DEVICEID[0], NRF_FICR->DEVICEID[1]);
clock_init();
#if (USE_OS)
os_init();
#endif
set_system_state(INITIALISING);
#if USE_UART_OUTPUT
uart_init();
#endif
PRINT("\r\n\r\n************* FIRMWARE v%d.%d.%d *************\r\n", FIRM_VER_MAJOR, FIRM_VER_MINOR, FIRM_VER_PATCH);
PRINT("DEVICE ID: %08X%08X\r\n", NRF_FICR->DEVICEID[0], NRF_FICR->DEVICEID[1]);
board_init(&erase_bonds);
#if (USE_ADC)
saadc_init();
saadc_sampling_event_init();
#endif
#if (USE_I2C)
if (init_I2C() == NO_ERROR) {
NRF_LOG_INFO("I2C peripheral initialised successfully.");
}
else {
NRF_LOG_INFO("I2C peripheral initialisation failed!");
}
#endif
#if (TEST_SENSORS == true)
initialise_sensors();
test_sensors();
#endif
#if (USE_ADC == true)
saadc_sampling_event_enable();
delay_ms(ADC_READ_DURATION_MS);
#endif
ble_init();
#if (!USE_FREERTOS)
advertising_start((void *)&erase_bonds);
#endif
#if (USE_OS)
os_start(&erase_bonds);
#else
// Enter main loop.
for (;;) {
idle_state_handle();
}
#endif
}
/* ********************* GLOBAL DEFINES ********************* */
#ifndef DEBUG
#define DEBUG
#endif
#define FIRM_VER_MAJOR 1
#define FIRM_VER_MINOR 0
#define FIRM_VER_PATCH 0
#define USE_WDT false // Enable a watchdog timer.
#define USE_ADC false // Use the SAADC to read analogue channels.
#define USE_SENSOR_POWER_CTRL true // Use sensor power control during runtime (powered on by default).
#define USE_DEEP_SLEEP false // Use deep sleep mode
#ifndef USE_RTX5
#ifdef RTX5
#define USE_RTX5 true
#else
#define USE_RTX5 false
#endif
#endif
#ifndef USE_FREERTOS
#ifdef FREERTOS
#define USE_FREERTOS true
#else
#define USE_FREERTOS false
#endif
#endif
#define USE_OS (RTX5 || FREERTOS)
#define START_SENSOR_A_THREAD false
#define START_SENSOR_B_THREAD false
#define START_SENSOR_C_THREAD false
#define START_SENSOR_D_THREAD false
#define USE_BMA400 false
#define USE_BME680 false
#define USE_SHTC3 true
#define USE_MS5607 true
#define USE_TSL2X7X true
#define USE_MXC400X true
#define USE_I2C (USE_BMA400 || USE_BME680 || USE_SHTC3 || USE_MS5607 || USE_TSL2X7X || USE_MXC400X)
#ifndef DEBUG
#define USE_UART_OUTPUT false
#else
#define USE_UART_OUTPUT false
#endif
#define TEST_STATUS_DATA false
#define TEST_BMA400 false
#define TEST_BME680 false
#define TEST_SHTC3 false
#define TEST_MS5607 false
#define TEST_TSL2X7X false
#define TEST_MXC400X false
#define TEST_SENSORS (TEST_BMA400 || TEST_BME680 || TEST_SHTC3 || TEST_MS5607 || TEST_TSL2X7X || TEST_MXC400X)
#define INIT_DELAY_MS 500
#define TEST_DELAY_MS 2000
#define SENSOR_POWER_DELAY_MS 100
#if (((TEST_BMA400 == true) && (USE_BMA400 == false)) || ((TEST_BME680 == true) && (USE_BME680 == false)) || ((TEST_SHTC3 == true) && (USE_SHTC3 == false)) || ((TEST_MS5607 == true) && (USE_MS5607 == false)) || ((TEST_TSL2X7X == true) && (USE_TSL2X7X == false)) || ((TEST_MXC400X == true) && (USE_MXC400X == false)))
#error "Sensor testing requires the corresponding sensor to be set to active!"
#endif
#define BATTERY_MEAS_INT_MS 10000
#define POLL_PERIOD_DEFAULT_MS 10
#define ADC_FSR_ARRAY false
#define ADC_READ_DURATION_MS 50
#define ADC_SEND_DURATION_MS 1000
#define ADC_NUM_BANKS 1 /* Number of sensor banks */
#define ADC_NUM_CHANNELS 1 /* Number of ADC channels used per sensor bank */
#define ADC_NUM_SENSORS (ADC_NUM_BANKS * ADC_NUM_CHANNELS)
#define PIN_SENSOR_VDD_ENABLE NRF_GPIO_PIN_MAP(0, 9)
#define PIN_STATUS_LED_A NRF_GPIO_PIN_MAP(0, 10)
#define PIN_INTERRUPT_TSL2X7X NRF_GPIO_PIN_MAP(0, 29)
#define PIN_INTERRUPT_MXC400X NRF_GPIO_PIN_MAP(0, 28)
#define NUM_SENSORS 4
#define NUM_SENSOR_DATA_CHANNELS 4
#define LED_BEHAVIOUR_OFF BSP_INDICATE_IDLE
#define LED_BEHAVIOUR_CONNECTED BSP_INDICATE_CONNECTED
#define LED_BEHAVIOUR_ADV_FAST BSP_INDICATE_ADVERTISING
#define LED_BEHAVIOUR_ADV_SLOW BSP_INDICATE_ADVERTISING_SLOW
#define LED_BEHAVIOUR_NO_ALERT BSP_INDICATE_IDLE
#define LED_BEHAVIOUR_MILD_ALERT BSP_INDICATE_IDLE
#define LED_BEHAVIOUR_HIGH_ALERT BSP_INDICATE_IDLE
#define DEAD_BEEF 0xDEADBEEF /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */
#define UART_TX_BUF_SIZE 512 /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE 256 /**< UART RX buffer size. */
#if USE_UART_OUTPUT
# define PRINT printf
#else
# define PRINT fake_printf
#endif
/* ******************** ENUMERATED TYPES ******************** */
typedef enum {
UNKNOWN = 0,
POWERED_OFF = 1,
POWERED_ON = 2,
} etSensorStatus;
typedef enum {
INITIALISING = 0,
STANDBY = 1,
MEASURING = 2,
COMPLETE = 3,
SHUTTING_DOWN = 4,
} etSystemState;
/* ************************ STRUCTURES ************************ */
typedef struct {
bool enabled; // Sensor enable.
etSensorStatus status; // Sensor status.
bool mode; // If "true", sensor is polled; else, sensor is interrupt-driven (if supported).
uint32_t read_period; // Sensor read period (only used if "mode" is "true").
} sensorInformation_t;
/**************************************************************
* @brief Function for initializing the low-frequency and high-frequency clocks.
* @param None.
**************************************************************/
static void clock_init(void) {
NRF_LOG_INFO("Low frequency clock init!");
if (!nrf_drv_clock_lfclk_is_running()) {
APP_ERROR_CHECK(nrf_drv_clock_init());
nrf_drv_clock_lfclk_request(NULL);
while (!nrf_drv_clock_lfclk_is_running());
}
NRF_CLOCK->TASKS_HFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
}
/**************************************************************
* @brief Function for initializing GPIO.
* @param None.
**************************************************************/
void gpio_init(void) {
ret_code_t err_code;
NRF_LOG_INFO("GPIO init!");
// Initialise GPIOTE.
err_code = nrf_drv_gpiote_init();
APP_ERROR_CHECK(err_code);
// Initialise the sensor power control line.
nrf_drv_gpiote_out_config_t sensor_control_line_config = GPIOTE_CONFIG_OUT_SIMPLE(false);
err_code = nrf_drv_gpiote_out_init(PIN_SENSOR_VDD_ENABLE, &sensor_control_line_config);
APP_ERROR_CHECK(err_code);
// Initialise the LED control line.
nrf_drv_gpiote_out_config_t led_control_line_config = GPIOTE_CONFIG_OUT_SIMPLE(false);
err_code = nrf_drv_gpiote_out_init(PIN_STATUS_LED_A, &led_control_line_config);
APP_ERROR_CHECK(err_code);
// Initialise TSL2X7X interrupt line.
nrf_drv_gpiote_in_config_t tsl2x7x_interrupt_line_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
tsl2x7x_interrupt_line_config.pull = NRF_GPIO_PIN_PULLUP;
err_code = nrf_drv_gpiote_in_init(PIN_INTERRUPT_TSL2X7X, &tsl2x7x_interrupt_line_config, tsl2x7x_interrupt_handler);
APP_ERROR_CHECK(err_code);
// Initialise MXC400X interrupt line.
nrf_drv_gpiote_in_config_t mxc400x_interrupt_line_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
mxc400x_interrupt_line_config.pull = NRF_GPIO_PIN_PULLUP;
err_code = nrf_drv_gpiote_in_init(PIN_INTERRUPT_MXC400X, &mxc400x_interrupt_line_config, mxc400x_interrupt_handler);
APP_ERROR_CHECK(err_code);
}
/**************************************************************
* @brief Function for handling hard faults.
* @param None.
**************************************************************/
void HardFault_Handler(void) {
NVIC_SystemReset();
}
/**************************************************************
* @brief Assert callback function.
* @param[in] id Fault identifier. See @ref NRF_FAULT_IDS.
* @param[in] pc The program counter of the instruction that triggered the fault, or 0 if
* unavailable.
* @param[in] info Optional additional information regarding the fault. Refer to each fault
* identifier for details.
**************************************************************/
void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info) {
NVIC_SystemReset();
}
/**************************************************************
* @brief Function for assert macro callback.
* @notes This function will be called in case of an assert in the SoftDevice.
* @warning On assert from the SoftDevice, the system can only recover on reset.
* @param[in] line_num Line number of the failing ASSERT call.
* @param[in] p_file_name File name of the failing ASSERT call.
**************************************************************/
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name) {
app_error_handler(DEAD_BEEF, line_num, p_file_name);
}
/**************************************************************
* @brief WDT events handler.
* @details After two cycles of 32768[Hz] clock in WDT interrupt, reset occurs.
* @param None.
**************************************************************/
#if USE_WDT
void wdt_event_handler(void) {
// Do nothing.
}
#endif
/**************************************************************
* @brief Function configures the watchdog timer.
* @param None.
**************************************************************/
#if USE_WDT
void wdt_init(void) {
nrf_drv_wdt_config_t config = NRF_DRV_WDT_DEAFULT_CONFIG;
ret_code_t err_code;
NRF_LOG_INFO("WDT init!");
err_code = nrf_drv_wdt_init(&config, wdt_event_handler);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_wdt_channel_alloc(&wdt_channel_id);
APP_ERROR_CHECK(err_code);
}
#endif
/**************************************************************
* @brief Function enables the watchdog timer.
* @param None.
**************************************************************/
#if USE_WDT
void wdt_enable(void) {
nrf_drv_wdt_enable();
}
#endif
/**************************************************************
* @brief Function feeds the watchdog timer.
* @param None.
**************************************************************/
#if USE_WDT
void wdt_feed(void) {
nrf_drv_wdt_channel_feed(wdt_channel_id);
}
#endif
/**************************************************************
* @brief Function for measuring the battery level.
* @details None.
* @notes None.
* @param[in] None.
* @param[out] None.
**************************************************************/
void measure_battery_level(void) {
ret_code_t err_code;
NRF_LOG_INFO("Measuring battery level!");
saadc_init();
err_code = nrf_drv_saadc_sample();
APP_ERROR_CHECK(err_code);
}
/**************************************************************
* @brief Function for initializing the board.
* @notes None.
* @param None.
**************************************************************/
void board_init(bool *erase_bonds) {
NRF_LOG_INFO("Board init!");
#if (USE_DEEP_SLEEP)
deep_sleep_init();
#endif
gpio_init();
os_timers_init();
buttons_leds_init(erase_bonds);
power_management_init();
//flash_init();
#if (USE_WDT == true)
wdt_init();
wdt_enable();
#endif
}
/**************************************************************
* @brief Function for initializing deep sleep mode.
* @notes None.
* @param None.
**************************************************************/
void deep_sleep_init(void) {
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
}
/**************************************************************
* @brief Function for uninitializing the board.
* @notes None.
* @param None.
**************************************************************/
void board_deinit(void) {
NRF_LOG_INFO("Board deinit!");
nrf_drv_gpiote_uninit();
sensor_power_control(false);
led_A_power_control(false);
}
/**************************************************************
* @brief Function for putting the chip into sleep mode.
* @notes This function will not return.
* @param None.
**************************************************************/
void sleep_mode_enter(void) {
ret_code_t err_code;
NRF_LOG_INFO("Entering sleep mode!");
err_code = bsp_indication_set(BSP_INDICATE_IDLE);
APP_ERROR_CHECK(err_code);
// Prepare wakeup buttons.
err_code = bsp_btn_ble_sleep_mode_prepare();
APP_ERROR_CHECK(err_code);
// Go to system-off mode (this function will not return; wakeup will cause a reset).
err_code = sd_power_system_off();
APP_ERROR_CHECK(err_code);
while(1); // Catch unintended soft-resets when in debug mode (i.e. due to using debugger or RTT Viewer).
}
/**************************************************************
* @brief Function for initializing the UART module.
* @notes None.
* @param None.
**************************************************************/
void uart_init(void) {
ret_code_t err_code;
NRF_LOG_INFO("UART init!");
app_uart_comm_params_t const comm_params = {
.rx_pin_no = RX_PIN_NUMBER,
.tx_pin_no = TX_PIN_NUMBER,
.rts_pin_no = RTS_PIN_NUMBER,
.cts_pin_no = CTS_PIN_NUMBER,
.flow_control = APP_UART_FLOW_CONTROL_DISABLED,
.use_parity = false,
#if defined (UART_PRESENT)
.baud_rate = NRF_UART_BAUDRATE_115200
#else
.baud_rate = NRF_UARTE_BAUDRATE_115200
#endif
};
APP_UART_FIFO_INIT(&comm_params, UART_RX_BUF_SIZE, UART_TX_BUF_SIZE, uart_event_handle, APP_IRQ_PRIORITY_LOWEST, err_code);
APP_ERROR_CHECK(err_code);
}
/**************************************************************
* @brief Function for handling app_uart events.
* @notes This function will receive a single character from the app_uart module and append it to
* a string. The string will be be sent over BLE when the last character received was a
* 'new line' '\n' (hex 0x0A) or if the string has reached the maximum data length.
* @param None.
**************************************************************/
void uart_event_handle(app_uart_evt_t * p_event) {
uint8_t data_array[BLE_NUS_MAX_DATA_LEN];
uint8_t index = 0;
switch (p_event->evt_type) {
case APP_UART_DATA_READY:
UNUSED_VARIABLE(app_uart_get(&data_array[index]));
index++;
ble_send_nus_data(data_array, &index);
break;
case APP_UART_COMMUNICATION_ERROR:
APP_ERROR_HANDLER(p_event->data.error_communication);
break;
case APP_UART_FIFO_ERROR:
APP_ERROR_HANDLER(p_event->data.error_code);
break;
default:
break;
}
}
/**************************************************************
* @brief Function for initializing the nrf log module.
* @notes None.
* @param None.
**************************************************************/
void log_init(void) {
ret_code_t err_code;
err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
}
/**************************************************************
* @brief Function for initializing power management.
* @notes None.
* @param None.
**************************************************************/
void power_management_init(void) {
ret_code_t err_code;
NRF_LOG_INFO("Power management init!");
err_code = nrf_pwr_mgmt_init();
APP_ERROR_CHECK(err_code);
}
/**************************************************************
* @brief Function for handling the idle state (main loop).
* @notes If there is no pending log operation, then sleep until next the next event occurs.
* @param None.
**************************************************************/
void idle_state_handle(void) {
#if (USE_WDT == true)
wdt_feed();
#endif
if (NRF_LOG_PROCESS() == false) {
nrf_pwr_mgmt_run();
}
}
/**************************************************************
* @brief Function initialises Keil RTX5 operating system.
* @notes None.
* @param void
* @return void
**************************************************************/
void os_kernel_init(void) {
#if RTX5
osKernelInitialize();
NVIC_SetPriorityGrouping(3);
#endif
}
/**************************************************************
* @brief Function initialises OS mutexes.
* @notes None.
* @param void
* @return void
**************************************************************/
void os_mutex_init(void) {
#if USE_RTX5
i2c_Mutex = osMutexNew(NULL);
if (i2c_Mutex == NULL) {
app_error_fault_handler(NULL, NULL, NULL);
}
#elif USE_FREERTOS
#if FRERTOS_USE_STATIC_ALLOCATION
m_i2c_mutex = xSemaphoreCreateMutexStatic(&xMutexBuffer);
if (m_i2c_mutex == NULL) {
app_error_fault_handler(NULL, NULL, NULL);
}
#else
m_i2c_mutex = xSemaphoreCreateMutex();
if (m_i2c_mutex == NULL) {
app_error_fault_handler(NULL, NULL, NULL);
}
#endif
#endif
}
/**************************************************************
* @brief Function initialises OS message queues.
* @notes None.
* @param void
* @return void
**************************************************************/
void os_msg_queue_init(void) {
#if USE_RTX5
mediator_MsgQueue_id = osMessageQueueNew(MSGQUEUE_OBJECTS, sizeof(MSGQUEUE_OBJ_t), NULL);
sensorA_MsgQueue_id = osMessageQueueNew(MSGQUEUE_OBJECTS, sizeof(MSGQUEUE_OBJ_t), NULL);
sensorB_MsgQueue_id = osMessageQueueNew(MSGQUEUE_OBJECTS, sizeof(MSGQUEUE_OBJ_t), NULL);
sensorC_MsgQueue_id = osMessageQueueNew(MSGQUEUE_OBJECTS, sizeof(MSGQUEUE_OBJ_t), NULL);
sensorD_MsgQueue_id = osMessageQueueNew(MSGQUEUE_OBJECTS, sizeof(MSGQUEUE_OBJ_t), NULL);
if ((mediator_MsgQueue_id == NULL) || (sensorA_MsgQueue_id == NULL) || (sensorB_MsgQueue_id == NULL) || (sensorC_MsgQueue_id == NULL) || (sensorD_MsgQueue_id == NULL)) {
app_error_fault_handler(NULL, NULL, NULL);
}
#elif USE_FREERTOS
#if FRERTOS_USE_STATIC_ALLOCATION
m_mediator_queue = xQueueCreateStatic(MSGQUEUE_OBJECTS, sizeof(struct MSGQUEUE_OBJ_t *), xMediatorQueueStorage, &xMediatorQueueStructure);
m_sensorA_queue = xQueueCreateStatic(MSGQUEUE_OBJECTS, sizeof(struct MSGQUEUE_OBJ_t *), xSensorAQueueStorage, &xSensorAQueueStructure);
m_sensorB_queue = xQueueCreateStatic(MSGQUEUE_OBJECTS, sizeof(struct MSGQUEUE_OBJ_t *), xSensorBQueueStorage, &xSensorBQueueStructure);
m_sensorC_queue = xQueueCreateStatic(MSGQUEUE_OBJECTS, sizeof(struct MSGQUEUE_OBJ_t *), xSensorCQueueStorage, &xSensorCQueueStructure);
m_sensorD_queue = xQueueCreateStatic(MSGQUEUE_OBJECTS, sizeof(struct MSGQUEUE_OBJ_t *), xSensorDQueueStorage, &xSensorDQueueStructure);
if ((m_mediator_queue == NULL) || (m_sensorA_queue == NULL) || (m_sensorB_queue == NULL) || (m_sensorC_queue == NULL) || (m_sensorD_queue == NULL)) {
app_error_fault_handler(NULL, NULL, NULL);
}
#else
m_mediator_queue = xQueueCreate(MSGQUEUE_OBJECTS, sizeof(struct MSGQUEUE_OBJ_t *));
m_sensorA_queue = xQueueCreate(MSGQUEUE_OBJECTS, sizeof(struct MSGQUEUE_OBJ_t *));
m_sensorB_queue = xQueueCreate(MSGQUEUE_OBJECTS, sizeof(struct MSGQUEUE_OBJ_t *));
m_sensorC_queue = xQueueCreate(MSGQUEUE_OBJECTS, sizeof(struct MSGQUEUE_OBJ_t *));
m_sensorD_queue = xQueueCreate(MSGQUEUE_OBJECTS, sizeof(struct MSGQUEUE_OBJ_t *));
if ((m_mediator_queue == NULL) || (m_sensorA_queue == NULL) || (m_sensorB_queue == NULL) || (m_sensorC_queue == NULL) || (m_sensorD_queue == NULL)) {
app_error_fault_handler(NULL, NULL, NULL);
}
#endif
#endif
}
/**************************************************************
* @brief Function initialises OS threads.
* @notes None.
* @param void
* @return void
**************************************************************/
void os_thread_init(void) {
// Initialise logger thread.
#if USE_RTX5
const osThreadAttr_t logger_thread_attr = {
.name = "LOGGER",
.priority = osPriorityHigh,
};
logger_thread_id = osThreadNew(app_thread_logger, NULL, &logger_thread_attr);
if (logger_thread_id == NULL) {
NRF_LOG_INFO("Logger thread initialisation failed!");
app_error_fault_handler(NULL, NULL, NULL);
}
else {
NRF_LOG_INFO("Logger thread initialisation successful.");
}
#elif USE_FREERTOS
#if FRERTOS_USE_STATIC_ALLOCATION
m_logger_thread = xTaskCreateStatic(app_thread_logger, "LOGR", FRERTOS_THREAD_STACK_DEPTH, NULL, LOGGER_THREAD_PRIORITY, xLoggerTaskStack, &xLoggerTaskBuffer);
if (m_logger_thread == NULL) {
NRF_LOG_INFO("Logger thread initialisation failed!");
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
NRF_LOG_INFO("Logger thread initialisation successful.");
#else
if (xTaskCreate(app_thread_logger, "LOGR", FRERTOS_THREAD_STACK_DEPTH, NULL, FRERTOS_THREAD_DEFAULT_PRIORITY, &m_logger_thread) != pdPASS) {
NRF_LOG_INFO("Logger thread initialisation failed!");
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
NRF_LOG_INFO("Logger thread initialisation successful.");
#endif
#endif
// Initialise mediator thread.
#if USE_RTX5
const osThreadAttr_t mediator_thread_attr = {
.name = "MEDIATOR",
.priority = osPriorityHigh,
};
mediator_thread_id = osThreadNew(app_thread_mediator, NULL, &mediator_thread_attr);
if (mediator_thread_id == NULL) {
NRF_LOG_INFO("Mediator thread initialisation failed!");
app_error_fault_handler(NULL, NULL, NULL);
}
else {
NRF_LOG_INFO("Mediator thread initialisation successful.");
}
#elif USE_FREERTOS
#if FRERTOS_USE_STATIC_ALLOCATION
m_mediator_thread = xTaskCreateStatic(app_thread_mediator, "MEDR", FRERTOS_THREAD_STACK_DEPTH, NULL, MEDIATOR_THREAD_PRIORITY, xMediatorTaskStack, &xMediatorTaskBuffer);
if (m_mediator_thread == NULL) {
NRF_LOG_INFO("Mediator thread initialisation failed!");
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
NRF_LOG_INFO("Mediator thread initialisation successful.");
#else
if (xTaskCreate(app_thread_mediator, "MEDR", FRERTOS_THREAD_STACK_DEPTH, NULL, FRERTOS_THREAD_DEFAULT_PRIORITY, &m_mediator_thread) != pdPASS) {
NRF_LOG_INFO("Mediator thread initialisation failed!");
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
NRF_LOG_INFO("Mediator thread initialisation successful.");
#endif
#endif
// Initialise sensor threads.
#if USE_RTX5
const osThreadAttr_t sensorA_thread_attr = {
.name = "SENSOR A",
.priority = osPriorityAboveNormal,
};
sensorA_thread_id = osThreadNew(app_thread_sensorA, NULL, &sensorA_thread_attr);
const osThreadAttr_t sensorB_thread_attr = {
.name = "SENSOR B",
.priority = osPriorityAboveNormal,
};
sensorB_thread_id = osThreadNew(app_thread_sensorB, NULL, &sensorB_thread_attr);
const osThreadAttr_t sensorC_thread_attr = {
.name = "SENSOR C",
.priority = osPriorityAboveNormal,
};
sensorC_thread_id = osThreadNew(app_thread_sensorC, NULL, &sensorC_thread_attr);
const osThreadAttr_t sensorD_thread_attr = {
.name = "SENSOR D",
.priority = osPriorityAboveNormal,
};
sensorD_thread_id = osThreadNew(app_thread_sensorD, NULL, &sensorD_thread_attr);
if ((sensorA_thread_id == NULL) || (sensorB_thread_id == NULL) || (sensorC_thread_id == NULL) || (sensorD_thread_id == NULL)) {
NRF_LOG_INFO("Sensor thread initialisation failed!");
app_error_fault_handler(NULL, NULL, NULL);
}
else {
NRF_LOG_INFO("Sensor thread initialisation successful.");
}
#elif USE_FREERTOS
#if FRERTOS_USE_STATIC_ALLOCATION
#if START_SENSOR_A_THREAD
m_sensorA_thread = xTaskCreateStatic(app_thread_sensorA, "SR_A", FRERTOS_THREAD_STACK_DEPTH, NULL, SENSORA_THREAD_PRIORITY, xSensorATaskStack, &xSensorATaskBuffer);
if (m_sensorA_thread == NULL) {
NRF_LOG_INFO("Sensor A thread initialisation failed!");
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
NRF_LOG_INFO("Sensor A thread initialisation successful.");
#endif
#if START_SENSOR_B_THREAD
m_sensorB_thread = xTaskCreateStatic(app_thread_sensorB, "SR_B", FRERTOS_THREAD_STACK_DEPTH, NULL, SENSORB_THREAD_PRIORITY, xSensorBTaskStack, &xSensorBTaskBuffer);
if (m_sensorB_thread == NULL) {
NRF_LOG_INFO("Sensor B thread initialisation failed!");
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
NRF_LOG_INFO("Sensor B thread initialisation successful.");
#endif
#if START_SENSOR_C_THREAD
m_sensorC_thread = xTaskCreateStatic(app_thread_sensorC, "SR_C", FRERTOS_THREAD_STACK_DEPTH, NULL, SENSORC_THREAD_PRIORITY, xSensorCTaskStack, &xSensorCTaskBuffer);
if (m_sensorC_thread == NULL) {
NRF_LOG_INFO("Sensor C thread initialisation failed!");
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
NRF_LOG_INFO("Sensor C thread initialisation successful.");
#endif
#if START_SENSOR_D_THREAD
m_sensorD_thread = xTaskCreateStatic(app_thread_sensorD, "SR_D", FRERTOS_THREAD_STACK_DEPTH, NULL, SENSORD_THREAD_PRIORITY, xSensorDTaskStack, &xSensorDTaskBuffer);
if (m_sensorD_thread == NULL) {
NRF_LOG_INFO("Sensor D thread initialisation failed!");
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
NRF_LOG_INFO("Sensor D thread initialisation successful.");
#endif
#else
#if START_SENSOR_A_THREAD
if (xTaskCreate(app_thread_sensorA, "SR_A", FRERTOS_THREAD_STACK_DEPTH, NULL, FRERTOS_THREAD_DEFAULT_PRIORITY, &m_sensorA_thread) != pdPASS) {
NRF_LOG_INFO("Sensor A thread initialisation failed!");
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
NRF_LOG_INFO("Sensor A thread initialisation successful.");
#endif
#if START_SENSOR_B_THREAD
if (xTaskCreate(app_thread_sensorB, "SR_B", FRERTOS_THREAD_STACK_DEPTH, NULL, FRERTOS_THREAD_DEFAULT_PRIORITY, &m_sensorB_thread) != pdPASS) {
NRF_LOG_INFO("Sensor B thread initialisation failed!");
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
NRF_LOG_INFO("Sensor B thread initialisation successful.");
#endif
#if START_SENSOR_C_THREAD
if (xTaskCreate(app_thread_sensorC, "SR_C", FRERTOS_THREAD_STACK_DEPTH, NULL, FRERTOS_THREAD_DEFAULT_PRIORITY, &m_sensorC_thread) != pdPASS) {
NRF_LOG_INFO("Sensor C thread initialisation failed!");
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
NRF_LOG_INFO("Sensor C thread initialisation successful.");
#endif
#if START_SENSOR_D_THREAD
if (xTaskCreate(app_thread_sensorD, "SR_D", FRERTOS_THREAD_STACK_DEPTH, NULL, FRERTOS_THREAD_DEFAULT_PRIORITY, &m_sensorD_thread) != pdPASS) {
NRF_LOG_INFO("Sensor D thread initialisation failed!");
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
NRF_LOG_INFO("Sensor D thread initialisation successful.");
#endif
#endif
#endif
}
/**************************************************************
* @brief Function starts the RTX5 kernel.
* @notes None.
* @param void
* @return void
**************************************************************/
void os_start(bool *erase_bonds) {
#if USE_RTX5
osKernelStart();
#elif USE_FREERTOS
// Create a FreeRTOS task for the BLE stack - this will run advertising_start() before entering its loop.
nrf_sdh_freertos_init(advertising_start, erase_bonds);
vTaskStartScheduler();
#endif
for (;;) {
APP_ERROR_HANDLER(NRF_ERROR_FORBIDDEN); // Kernel start should not return unless RAM could not be allocated.
}
}
/**************************************************************
* @brief Function initialises RTX5 dependencies on system startup.
* @notes None.
* @param void
* @return void
**************************************************************/
void os_on_startup(void) {
#if USE_RTX5
SystemCoreClockUpdate();
#endif
}
/**************************************************************
* @brief Function initialises the OS.
* @notes None.
* @param void
* @return void
**************************************************************/
void os_init(void) {
os_on_startup();
os_kernel_init();
os_mutex_init();
os_msg_queue_init();
os_thread_init();
}
/**************************************************************
* @brief Function yields a thread.
* @notes None.
* @param void
* @return void
**************************************************************/
void os_thread_yield(void) {
#if USE_RTX5
osThreadYield();
#elif USE_FREERTOS
vTaskSuspend(NULL);
taskYIELD();
#endif
}
/**************************************************************
* @brief Function acquires the I2C mutex.
* @notes None.
* @param void
* @return void
**************************************************************/
bool os_acquire_i2c_mutex(void) {
#if USE_RTX5
osStatus_t status_OS;
#elif USE_FREERTOS
BaseType_t status_OS;
#endif
#if USE_RTX5
status_OS = osMutexAcquire(i2c_Mutex, osWaitForever);
if (status_OS == osOK) {
return true;
}
else {
return false;
}
#elif USE_FREERTOS
status_OS = xSemaphoreTake(m_i2c_mutex, portMAX_DELAY);
if (status_OS == pdTRUE) {
return true;
}
else {
return false;
}
#endif
}
/**************************************************************
* @brief Function releases the I2C mutex.
* @notes None.
* @param void
* @return void
**************************************************************/
bool os_release_i2c_mutex(void) {
#if USE_RTX5
osMutexRelease(i2c_Mutex);
#elif USE_FREERTOS
xSemaphoreGive(m_i2c_mutex);
#endif
return true;
}
/**************************************************************
* @brief Function gets a message queue handle.
* @notes None.
* @param void
* @return void
**************************************************************/
#if USE_RTX5
static osMessageQueueId_t os_get_queue_handle(etMessageQueues queue) {
#elif USE_FREERTOS
static QueueHandle_t os_get_queue_handle(etMessageQueues queue) {
#endif
if (queue == MEDIATOR_QUEUE) {
#if USE_RTX5
return mediator_MsgQueue_id;
#elif USE_FREERTOS
return m_mediator_queue;
#endif
}
else if (queue == SENSOR_A_QUEUE) {
#if USE_RTX5
return sensorA_MsgQueue_id;
#elif USE_FREERTOS
return m_sensorA_queue;
#endif
}
else if (queue == SENSOR_B_QUEUE) {
#if USE_RTX5
return sensorB_MsgQueue_id;
#elif USE_FREERTOS
return m_sensorB_queue;
#endif
}
else if (queue == SENSOR_C_QUEUE) {
#if USE_RTX5
return sensorC_MsgQueue_id;
#elif USE_FREERTOS
return m_sensorC_queue;
#endif
}
else if (queue == SENSOR_D_QUEUE) {
#if USE_RTX5
return sensorD_MsgQueue_id;
#elif USE_FREERTOS
return m_sensorD_queue;
#endif
}
else {
return NULL;
}
}
/**************************************************************
* @brief Function gets a task handle.
* @notes None.
* @param void
* @return void
**************************************************************/
static TaskHandle_t os_get_task_handle(etMessageQueues queue) {
if (queue == MEDIATOR_QUEUE) {
return m_mediator_thread;
}
else if (queue == SENSOR_A_QUEUE) {
return m_sensorA_thread;
}
else if (queue == SENSOR_B_QUEUE) {
return m_sensorB_thread;
}
else if (queue == SENSOR_C_QUEUE) {
return m_sensorC_thread;
}
else if (queue == SENSOR_D_QUEUE) {
return m_sensorD_thread;
}
else {
return NULL;
}
}
/**************************************************************
* @brief Function puts a message onto a message queue.
* @notes None.
* @param void
* @return void
**************************************************************/
bool os_message_queue_put(etMessageQueues queue, MSGQUEUE_OBJ_t *msg, bool fromISR) {
#if USE_RTX5
osStatus_t status_OS = osMessageQueuePut(os_get_queue_handle(queue), msg, osPriorityNone, osWaitForever);
if (status_OS == osOK) {
return true;
}
else {
return false;
}
#elif USE_FREERTOS
BaseType_t status_OS;
if (fromISR) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
status_OS = xQueueSendToBackFromISR(os_get_queue_handle(queue), msg, &xHigherPriorityTaskWoken);
if (status_OS == pdTRUE) {
vTaskNotifyGiveFromISR(os_get_task_handle(queue), &xHigherPriorityTaskWoken);
//status_OS = xTaskNotifyFromISR(os_get_task_handle(queue), MESSAGE_WAITING_NOTIFICATION, eSetValueWithOverwrite , &xHigherPriorityTaskWoken);
}
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
else {
status_OS = xQueueSendToBack(os_get_queue_handle(queue), msg, portMAX_DELAY);
if (status_OS == pdTRUE) {
status_OS = xTaskNotifyGive(os_get_task_handle(queue));
//status_OS = xTaskNotify(os_get_task_handle(queue), MESSAGE_WAITING_NOTIFICATION, eSetValueWithOverwrite);
}
portYIELD();
}
if (status_OS == pdTRUE) {
return true;
}
else {
return false;
}
#endif
}
/**************************************************************
* @brief Function puts a message onto the Sensor A message queue.
* @notes None.
* @param void
* @return void
**************************************************************/
bool os_message_queue_get(etMessageQueues queue, MSGQUEUE_OBJ_t *msg, bool fromISR) {
#if USE_RTX5
osStatus_t status_OS = osMessageQueueGet(os_get_queue_handle(queue), msg, NULL, MSG_QUEUE_BLOCK_MS);
if (status_OS == osOK) {
return true;
}
else {
return false;
}
#elif USE_FREERTOS
BaseType_t status_OS;
if (fromISR) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
status_OS = xQueueReceiveFromISR(os_get_queue_handle(queue), msg, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
else {
ulTaskNotifyTake(NOTIFICATION_DEFAULT_VALUE, portMAX_DELAY);
status_OS = xQueueReceive(os_get_queue_handle(queue), msg, MSG_QUEUE_BLOCK_MS);
}
if (status_OS == pdTRUE) {
return true;
}
else {
return false;
}
#endif
}
/**************************************************************
* @brief Function checks for stack overflow events.
* @notes None.
* @param void
* @return void
**************************************************************/
#if USE_FREERTOS
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) {
NRF_LOG_INFO("Stack overflow!");
app_error_fault_handler(NULL, NULL, NULL);
}
#endif
/**************************************************************
* @brief Application logging thread.
* @notes Thread handles log events.
* @param void
* @return void
**************************************************************/
void app_thread_logger(void *argument) {
UNUSED_PARAMETER(argument);
NRF_LOG_INFO("Logger thread start!");
while (1) {
NRF_LOG_INFO("Logger thread running!");
NRF_LOG_FLUSH();
NRF_LOG_INFO("Logger thread sleeping!");
os_thread_yield();
}
}
/**************************************************************
* @brief Application logging thread hook.
* @notes Thread handles log events.
* @param void
* @return void
**************************************************************/
#if (NRF_LOG_ENABLED && NRF_LOG_DEFERRED)
void log_pending_hook(void) {
BaseType_t result = pdFAIL;
if (__get_IPSR() != 0) {
BaseType_t higherPriorityTaskWoken = pdFALSE;
result = xTaskNotifyFromISR(m_logger_thread, 0, eSetValueWithoutOverwrite, &higherPriorityTaskWoken);
if (result != pdFAIL) {
portYIELD_FROM_ISR(higherPriorityTaskWoken);
}
}
else {
UNUSED_RETURN_VALUE(xTaskNotify(m_logger_thread, 0, eSetValueWithoutOverwrite));
}
}
#endif
/**************************************************************
* @brief Application mediator thread.
* @notes Thread handles sensor threads by waiting for interrupt-driven configuration events.
* It also handles all data storage (both configuration and sensor data) in both RAM and FLASH.
* @param void
* @return void
**************************************************************/
void app_thread_mediator(void *argument) {
UNUSED_PARAMETER(argument);
static MSGQUEUE_OBJ_t msg;
NRF_LOG_INFO("Mediator thread start!");
// Power on the sensors by default.
#if (USE_SENSOR_POWER_CTRL == false)
sensor_power_control(true);
#endif
NRF_LOG_INFO("Free Heap: %d", xPortGetFreeHeapSize());
// Send a startup message to the mediator thread.
msg.Idx = INIT;
os_message_queue_put(MEDIATOR_QUEUE, &msg, false);
while (1) {
NRF_LOG_INFO("Mediator thread running!");
// Wait from a message from the BLE characteristic ISR (start/stop/clear) or each sensor thread.
if (os_message_queue_get(MEDIATOR_QUEUE, &msg, false)) {
NRF_LOG_INFO("Message received (Mediator)!");
// The system has just started up.
if (msg.Idx == INIT) {
NRF_LOG_INFO("Initialising (Mediator)!");
// Update the current system state.
set_system_state(STANDBY);
// Power off the sensors in standby.
#if (USE_SENSOR_POWER_CTRL == true)
sensor_power_control(false);
#endif
}
// A start request has occurred.
else if (msg.Idx == START) {
NRF_LOG_INFO("Starting (Mediator)!");
// Start measurements.
if (get_system_state() != MEASURING) {
// Update the current system state.
set_system_state(MEASURING);
// Clear measurement data.
flash_erase_pages();
flash_clear_measurements();
// Implement the startup delay timer as required.
delay_ms(get_start_delay_ms());
// Power on the sensors during measurement.
#if (USE_SENSOR_POWER_CTRL == true)
sensor_power_control(true);
#endif
// Shutdown sensor thread A or setup sensor interrupts as required.
if (!shtc3_info.enabled) {
msg.Idx = STOP;
os_message_queue_put(SENSOR_A_QUEUE, &msg, false);
}
else {
if (shtc3_info.mode) {
msg.Idx = STOP_INTERRUPTS;
}
else {
msg.Idx = START_INTERRUPTS;
}
os_message_queue_put(SENSOR_A_QUEUE, &msg, false);
}
// Shutdown sensor thread B or setup sensor interrupts as required.
if (!ms5607_info.enabled) {
msg.Idx = STOP;
os_message_queue_put(SENSOR_B_QUEUE, &msg, false);
}
else {
if (ms5607_info.mode) {
msg.Idx = STOP_INTERRUPTS;
}
else {
msg.Idx = START_INTERRUPTS;
}
os_message_queue_put(SENSOR_B_QUEUE, &msg, false);
}
// Shutdown sensor thread C or setup sensor interrupts as required.
if (!tsl2x7x_info.enabled) {
msg.Idx = STOP;
os_message_queue_put(SENSOR_C_QUEUE, &msg, false);
}
else {
if (tsl2x7x_info.mode) {
msg.Idx = STOP_INTERRUPTS;
}
else {
msg.Idx = START_INTERRUPTS;
}
os_message_queue_put(SENSOR_C_QUEUE, &msg, false);
}
// Shutdown sensor thread D or setup sensor interrupts as required.
if (!mxc400x_info.enabled) {
msg.Idx = STOP;
os_message_queue_put(SENSOR_D_QUEUE, &msg, false);
}
else {
if (mxc400x_info.mode) {
msg.Idx = STOP_INTERRUPTS;
}
else {
msg.Idx = START_INTERRUPTS;
}
os_message_queue_put(SENSOR_D_QUEUE, &msg, false);
}
// Start the common poll timer, which will wake enabled sensor threads as required.
os_poll_timer_start(get_common_poll_period_ms(), false);
}
else {
NRF_LOG_INFO("System start request ignored!");
}
}
// A stop request has occurred.
else if (msg.Idx == STOP) {
NRF_LOG_INFO("Stopping (Mediator)!");
// Halt the system.
if (get_system_state() == MEASURING) {
// Update the current system state.
set_system_state(STANDBY);
// Stop the common poll timer.
os_poll_timer_stop(false);
// Shutdown all sensor threads.
msg.Idx = STOP;
os_message_queue_put(SENSOR_A_QUEUE, &msg, false);
os_message_queue_put(SENSOR_B_QUEUE, &msg, false);
os_message_queue_put(SENSOR_C_QUEUE, &msg, false);
os_message_queue_put(SENSOR_D_QUEUE, &msg, false);
// Wait for sensor threads to finish.
while((shtc3_info.status == POWERED_ON) || (ms5607_info.status == POWERED_ON) || (tsl2x7x_info.status == POWERED_ON) || (mxc400x_info.status == POWERED_ON));
// Power off the sensors in standby.
#if (USE_SENSOR_POWER_CTRL == true)
sensor_power_control(false);
#endif
}
else {
NRF_LOG_INFO("System stop request ignored!");
}
}
// A shutdown request has occurred.
else if (msg.Idx == SHUTDOWN) {
NRF_LOG_INFO("Shutting down (Mediator)!");
// Prepare the system for shutdown.
if (get_system_state() != MEASURING) {
// Update the current system state.
set_system_state(SHUTTING_DOWN);
// Wait for sensor threads to finish.
while((shtc3_info.status == POWERED_ON) || (ms5607_info.status == POWERED_ON) || (tsl2x7x_info.status == POWERED_ON) || (mxc400x_info.status == POWERED_ON));
// Power off the sensors during shutdown.
#if (USE_SENSOR_POWER_CTRL == true)
sensor_power_control(false);
#endif
flash_deinit();
ble_disconnect(false);
board_deinit();
sleep_mode_enter();
}
else {
NRF_LOG_INFO("System shutdown request ignored!");
}
}
// A clear data request has occurred.
else if (msg.Idx == CLEAR_DATA) {
NRF_LOG_INFO("Clearing data (Mediator)!");
// Clear all stored data.
if (get_system_state() != MEASURING) {
flash_clear_measurements();
}
else {
NRF_LOG_INFO("Clear data request ignored!");
}
}
// A send data request has occurred.
else if (msg.Idx == SEND_DATA) {
NRF_LOG_INFO("Sending data (Mediator)!");
while(!ble_send_label_data());
}
// Sensor data has been received, and needs to be stored.
else if ((msg.Idx == SENSOR_A_DATA) || (msg.Idx == SENSOR_B_DATA) || (msg.Idx == SENSOR_C_DATA) || (msg.Idx == SENSOR_D_DATA)) {
NRF_LOG_INFO("Storing data (Mediator)!");
store_measurement(msg.timestamp_unix, msg.timestamp_ms, msg.Idx, (int16_t*)msg.sensor_channel_data);
increment_num_measurements();
// Halt when measurement is complete.
if (is_meas_complete()) {
set_system_state(COMPLETE);
NRF_LOG_INFO("Measurement complete!");
os_poll_timer_stop(false);
// If disconnected, advertise.
if (getConnectionStatus() == BLE_DISCONNECTED) {
advertising_start(false);
}
}
}
}
NRF_LOG_INFO("Mediator thread sleeping!");
os_thread_yield();
}
}
/**************************************************************
* @brief Application sensor thread (sensor A - SHTC3).
* @notes Thread handles sensor configuration and communications as required, then relays data back to the mediator thread.
* @param void
* @return void
**************************************************************/
void app_thread_sensorA(void *argument) {
UNUSED_PARAMETER(argument);
MSGQUEUE_OBJ_t msg;
etError status_sensor;
float temp, humidity;
NRF_LOG_INFO("Sensor A thread start!");
while (1) {
// Wait from a message from the poll timer ISR (start) or the mediator (start/stop).
if (os_message_queue_get(SENSOR_A_QUEUE, &msg, false)) {
NRF_LOG_INFO("Message received (Sensor A)!");
// Read data from the sensor.
if ((msg.Idx == START) && (shtc3_info.enabled)) {
NRF_LOG_INFO("Reading data (Sensor A)!");
// Ensure that the sensor is powered on.
if (shtc3_info.status != POWERED_ON) {
if (os_acquire_i2c_mutex()) {
status_sensor = SHTC3_Wakeup();
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
NRF_LOG_INFO("SHTC3 sensor initialised successfully.");
shtc3_info.status = POWERED_ON;
}
else {
NRF_LOG_INFO("SHTC3 sensor initialisation failed!");
shtc3_info.status = UNKNOWN;
}
}
}
// Read sensor data as required.
if (shtc3_info.status == POWERED_ON) {
if (os_acquire_i2c_mutex()) {
status_sensor = (etError)read_SHTC3(&temp, &humidity);
os_release_i2c_mutex();
if (status_sensor) {
NRF_LOG_INFO("SHTC3 sensor read OK.");
NRF_LOG_INFO("Temperature = " NRF_LOG_FLOAT_MARKER " C", NRF_LOG_FLOAT(temp));
NRF_LOG_INFO("Humidity = " NRF_LOG_FLOAT_MARKER " %rH", NRF_LOG_FLOAT(humidity));
// Communicate data to the mediator thread for storage.
msg.Idx = SENSOR_A_DATA;
msg.timestamp_unix = get_system_time_unix();
msg.timestamp_ms = get_system_time_ms();
msg.sensor_channel_data[0] = (int16_t)(temp * 10);
msg.sensor_channel_data[1] = (int16_t)(humidity * 10);
os_message_queue_put(MEDIATOR_QUEUE, &msg, false);
}
else {
NRF_LOG_INFO("SHTC3 sensor read failed!");
}
}
}
}
// Configure the sensor for interrupt-driven operation, if supported.
else if (((msg.Idx == START_INTERRUPTS) || (msg.Idx == STOP_INTERRUPTS)) && (!shtc3_info.mode)) {
NRF_LOG_INFO("Configuring interrupts (Sensor A)!");
// Interrupts not supported - shut down the sensor.
NRF_LOG_INFO("SHTC3 does not support interrupts, shutting down!");
// Switch the sensor off.
if (shtc3_info.status != POWERED_OFF) {
if (os_acquire_i2c_mutex()) {
status_sensor = SHTC3_Sleep();
os_release_i2c_mutex();
if (status_sensor != NO_ERROR) {
NRF_LOG_INFO("SHTC3 sensor shutdown succesfully.");
shtc3_info.status = POWERED_OFF;
}
else {
NRF_LOG_INFO("SHTC3 sensor shutdown failed!");
shtc3_info.status = UNKNOWN;
}
}
}
}
// Shut down the sensor.
else if (msg.Idx == STOP) {
NRF_LOG_INFO("Shutting down (Sensor A)!");
// Switch the sensor off.
if (shtc3_info.status != POWERED_OFF) {
if (os_acquire_i2c_mutex()) {
status_sensor = SHTC3_Sleep();
os_release_i2c_mutex();
if (status_sensor != NO_ERROR) {
NRF_LOG_INFO("SHTC3 sensor shutdown succesfully.");
shtc3_info.status = POWERED_OFF;
}
else {
NRF_LOG_INFO("SHTC3 sensor shutdown failed!");
shtc3_info.status = UNKNOWN;
}
}
}
}
// Message not handled.
else {
NRF_LOG_INFO("Sensor A command ignored!");
}
}
os_thread_yield();
}
}
/**************************************************************
* @brief Application sensor thread (sensor B - MS5607).
* @notes Thread handles sensor configuration and communications as required, then relays data back to the mediator thread.
* @param void
* @return void
**************************************************************/
void app_thread_sensorB(void *argument) {
UNUSED_PARAMETER(argument);
MSGQUEUE_OBJ_t msg;
bool status_sensor;
float temperature, pressure, altitude;
NRF_LOG_INFO("Sensor B thread start!");
while (1) {
// Wait from a message from the poll timer ISR (start) or the mediator (start/stop).
if (os_message_queue_get(SENSOR_B_QUEUE, &msg, false)) {
NRF_LOG_INFO("Message received (Sensor B)!");
// Read data from the sensor.
if ((msg.Idx == START) && (ms5607_info.enabled)) {
NRF_LOG_INFO("Reading data (Sensor B)!");
// Ensure that the sensor is powered on.
if (ms5607_info.status != POWERED_ON) {
if (os_acquire_i2c_mutex()) {
status_sensor = MS5607_Initialise();
os_release_i2c_mutex();
if (status_sensor) {
NRF_LOG_INFO("MS5607 sensor initialised successfully.");
ms5607_info.status = POWERED_ON;
}
else {
NRF_LOG_INFO("MS5607 sensor initialisation failed!");
ms5607_info.status = UNKNOWN;
}
}
}
// Read sensor data as required.
if (ms5607_info.status == POWERED_ON) {
if (os_acquire_i2c_mutex()) {
status_sensor = read_MS5607(&temperature, &pressure, &altitude);
os_release_i2c_mutex();
if (status_sensor) {
NRF_LOG_INFO("MS5607 read OK.");
NRF_LOG_INFO("Temperature = " NRF_LOG_FLOAT_MARKER " C", NRF_LOG_FLOAT(temperature));
NRF_LOG_INFO("Pressure = " NRF_LOG_FLOAT_MARKER " mbar", NRF_LOG_FLOAT(pressure));
NRF_LOG_INFO("Altitude = " NRF_LOG_FLOAT_MARKER " m", NRF_LOG_FLOAT(altitude));
// Communicate data to the mediator thread for storage.
msg.Idx = SENSOR_B_DATA;
msg.timestamp_unix = get_system_time_unix();
msg.timestamp_ms = get_system_time_ms();
msg.sensor_channel_data[0] = (int16_t)(temperature * 10);
msg.sensor_channel_data[1] = (int16_t)(pressure * 10);
msg.sensor_channel_data[2] = (int16_t)(altitude * 10);
os_message_queue_put(MEDIATOR_QUEUE, &msg, false);
}
else {
NRF_LOG_INFO("MS5607 sensor read failed!");
}
}
}
}
// Configure the sensor for interrupt-driven operation, if supported.
else if ((msg.Idx == START_INTERRUPTS) && (!ms5607_info.mode)) {
NRF_LOG_INFO("Configuring interrupts (Sensor B)!");
// Interrupts not supported - shut down the sensor.
NRF_LOG_INFO("MS5607 sensor does not support interrupts, shutting down!");
// Switch the sensor off.
if (ms5607_info.status != POWERED_OFF) {
NRF_LOG_INFO("MS5607 sensor does not support shutdown!");
ms5607_info.status = POWERED_OFF;
}
}
// Switch off interrupts, if supported.
else if (msg.Idx == STOP_INTERRUPTS) {
NRF_LOG_INFO("Configuring interrupts (Sensor B)!");
// Interrupts not supported - shut down the sensor.
NRF_LOG_INFO("MS5607 sensor does not support interrupts, shutting down!");
// Switch the sensor off.
if (ms5607_info.status != POWERED_OFF) {
NRF_LOG_INFO("MS5607 sensor does not support shutdown!");
ms5607_info.status = POWERED_OFF;
}
}
// Shut down the sensor.
else if (msg.Idx == STOP) {
NRF_LOG_INFO("Shutting down (Sensor B)!");
// Switch the sensor off.
if (ms5607_info.status != POWERED_OFF) {
NRF_LOG_INFO("MS5607 sensor does not support shutdown!");
ms5607_info.status = POWERED_OFF;
}
}
// Message not handled.
else {
NRF_LOG_INFO("Sensor B command ignored!");
}
}
os_thread_yield();
}
}
/**************************************************************
* @brief Application sensor thread (sensor C - TSL2X7X).
* @notes Thread handles sensor configuration and communications as required, then relays data back to the mediator thread.
* @param void
* @return void
**************************************************************/
void app_thread_sensorC(void *argument) {
UNUSED_PARAMETER(argument);
MSGQUEUE_OBJ_t msg;
etError status_sensor;
bool luxOK = false, proxOK = false;
int lux_data, prox_data;
struct iio_chan_spec chan;
NRF_LOG_INFO("Sensor C thread start!");
while (1) {
// Wait from a message from the poll timer ISR (start) or the mediator (start/stop).
if (os_message_queue_get(SENSOR_C_QUEUE, &msg, false)) {
NRF_LOG_INFO("Message received (Sensor C)!");
// Read data from the sensor.
if ((msg.Idx == START) && (tsl2x7x_info.enabled)) {
NRF_LOG_INFO("Reading data (Sensor C)!");
// Ensure that the sensor is powered on.
if (tsl2x7x_info.status != POWERED_ON) {
if (os_acquire_i2c_mutex()) {
status_sensor = (etError)tsl2x7x_initialise(tsl2771, &tsl2X7X_chip_global);
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
NRF_LOG_INFO("TSL2X7X sensor initialised successfully.");
tsl2x7x_info.status = POWERED_ON;
}
else {
NRF_LOG_INFO("TSL2X7X sensor initialisation failed!");
tsl2x7x_info.status = UNKNOWN;
}
}
}
// Read sensor data as required.
if (tsl2x7x_info.status == POWERED_ON) {
if (os_acquire_i2c_mutex()) {
lux_data = tsl2x7x_get_lux(&tsl2X7X_chip_global);
os_release_i2c_mutex();
if (lux_data >= 0) {
luxOK = true;
}
}
if (os_acquire_i2c_mutex()) {
prox_data = tsl2x7x_get_prox(&tsl2X7X_chip_global);
os_release_i2c_mutex();
if (prox_data >= 0) {
proxOK = true;
}
}
}
if (luxOK && proxOK) {
NRF_LOG_INFO("TSL2771 read OK.");
NRF_LOG_INFO("Lux = %d", lux_data);
NRF_LOG_INFO("Proximity = %d", prox_data);
// Communicate data to the mediator thread for storage.
msg.Idx = SENSOR_C_DATA;
msg.timestamp_unix = get_system_time_unix();
msg.timestamp_ms = get_system_time_ms();
msg.sensor_channel_data[0] = (int16_t)(lux_data * 10);
msg.sensor_channel_data[1] = (int16_t)(prox_data * 10);
os_message_queue_put(MEDIATOR_QUEUE, &msg, false);
}
else {
NRF_LOG_INFO("TSL2771 read failed!");
}
}
// Configure the sensor for interrupt-driven operation, if supported.
else if ((msg.Idx == START_INTERRUPTS) && (!tsl2x7x_info.mode)) {
NRF_LOG_INFO("Configuring interrupts (Sensor C)!");
NRF_LOG_INFO("Setting up interrupts on TSL2771.");
#if (TSLX7X_DEF_INTERRUPTS_EN)
// Configure intensity thresholds as required.
#if (TSLX7X_DEF_INTERRUPTS_EN && 0x10)
// Set the intensity high threshold.
bool alsSettingsOK = false;
chan.type = IIO_INTENSITY;
if (os_acquire_i2c_mutex()) {
status_sensor = (etError)tsl2x7x_write_thresh(&tsl2X7X_chip_global, &chan, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING, TSLX7X_DEF_ALS_THRES_HI);
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
// Set the proximity high threshold.
chan.type = IIO_PROXIMITY;
if (os_acquire_i2c_mutex()) {
status_sensor = (etError)tsl2x7x_write_thresh(&tsl2X7X_chip_global, &chan, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING, TSLX7X_DEF_ALS_THRES_LOW);
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
if (os_acquire_i2c_mutex()) {
status_sensor = (etError)tsl2x7x_write_interrupt_config(&tsl2X7X_chip_global, &chan, true);
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
alsSettingsOK = true;
}
}
}
}
}
}
if (alsSettingsOK) {
NRF_LOG_INFO("TSL2X7X sensor intensity interrupt configuration set successfully.");
}
else {
NRF_LOG_INFO("TSL2X7X sensor intensity interrupt configuration failed!");
}
#endif
// Configure proximity thresholds as required.
#if (TSLX7X_DEF_INTERRUPTS_EN && 0x20)
// Set the proximity high threshold.
bool prxSettingsOK = false;
chan.type = IIO_PROXIMITY;
if (os_acquire_i2c_mutex()) {
status_sensor = (etError)tsl2x7x_write_thresh(&tsl2X7X_chip_global, &chan, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING, TSLX7X_DEF_PRX_THRES_HI);
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
// Set the proximity high threshold.
chan.type = IIO_PROXIMITY;
if (os_acquire_i2c_mutex()) {
status_sensor = (etError)tsl2x7x_write_thresh(&tsl2X7X_chip_global, &chan, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING, TSLX7X_DEF_PRX_THRES_LOW);
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
if (os_acquire_i2c_mutex()) {
status_sensor = (etError)tsl2x7x_write_interrupt_config(&tsl2X7X_chip_global, &chan, true);
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
prxSettingsOK = true;
}
}
}
}
}
}
if (prxSettingsOK) {
NRF_LOG_INFO("TSL2X7X sensor proximity interrupt configuration set successfully.");
}
else {
NRF_LOG_INFO("TSL2X7X sensor proximity interrupt configuration failed!");
}
#endif
// Set up interrupt handling on the MCU.
nrf_drv_gpiote_in_event_enable(PIN_INTERRUPT_TSL2X7X, true);
#else
// Interrupts not enabled - shut down the sensor.
NRF_LOG_INFO("TSL2X7X interrupts not enabled, shutting down!");
// Switch the sensor off.
if (tsl2x7x_info.status != POWERED_OFF) {
if (os_acquire_i2c_mutex()) {
status_sensor = (etError)tsl2x7x_chip_off(&tsl2X7X_chip_global);
os_release_i2c_mutex();
if (status_sensor != NO_ERROR) {
tsl2x7x_info.status = POWERED_OFF;
NRF_LOG_INFO("TSL2X7X sensor shutdown succesfully.");
}
else {
tsl2x7x_info.status = UNKNOWN;
NRF_LOG_INFO("TSL2X7X sensor shutdown failed!");
}
}
}
#endif
}
// Switch off interrupts, if supported.
else if (msg.Idx == STOP_INTERRUPTS) {
NRF_LOG_INFO("Configuring interrupts (Sensor C)!");
NRF_LOG_INFO("Halting interrupts on TSL2771.");
// Disable intensity interrupts.
bool alsSettingsOK = false;
chan.type = IIO_INTENSITY;
if (os_acquire_i2c_mutex()) {
status_sensor = (etError)tsl2x7x_write_interrupt_config(&tsl2X7X_chip_global, &chan, true);
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
alsSettingsOK = true;
}
}
// Disable proximity interrupts.
bool prxSettingsOK = false;
chan.type = IIO_PROXIMITY;
if (os_acquire_i2c_mutex()) {
status_sensor = (etError)tsl2x7x_write_interrupt_config(&tsl2X7X_chip_global, &chan, true);
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
prxSettingsOK = true;
}
}
if (prxSettingsOK && alsSettingsOK) {
NRF_LOG_INFO("TSL2X7X sensor interrupt configuration disabled successfully.");
}
else {
NRF_LOG_INFO("TSL2X7X sensor interrupt configuration failed!");
}
// Set up interrupt handling on the MCU.
nrf_drv_gpiote_in_event_disable(PIN_INTERRUPT_TSL2X7X);
}
// Shut down the sensor.
else if (msg.Idx == STOP) {
NRF_LOG_INFO("Shutting down (Sensor C)!");
// Switch the sensor off.
if (tsl2x7x_info.status != POWERED_OFF) {
if (os_acquire_i2c_mutex()) {
status_sensor = (etError)tsl2x7x_chip_off(&tsl2X7X_chip_global);
os_release_i2c_mutex();
if (status_sensor != NO_ERROR) {
tsl2x7x_info.status = POWERED_OFF;
NRF_LOG_INFO("TSL2X7X sensor shutdown succesfully.");
}
else {
tsl2x7x_info.status = UNKNOWN;
NRF_LOG_INFO("TSL2X7X sensor shutdown failed!");
}
}
}
}
// Message not handled.
else {
NRF_LOG_INFO("Sensor C command ignored!");
}
}
os_thread_yield();
}
}
/**************************************************************
* @brief Application sensor thread (sensor D - MXC400X).
* @notes Thread handles sensor configuration and communications as required, then relays data back to the mediator thread.
* @param void
* @return void
**************************************************************/
void app_thread_sensorD(void *argument) {
UNUSED_PARAMETER(argument);
MSGQUEUE_OBJ_t msg;
etError status_sensor;
bool accOK = false, tempOK = false;
NRF_LOG_INFO("Sensor D thread start!");
while (1) {
// Wait from a message from the poll timer ISR (start) or the mediator (start/stop).
if (os_message_queue_get(SENSOR_D_QUEUE, &msg, false)) {
NRF_LOG_INFO("Message received (Sensor D)!");
// Read data from the sensor.
if ((msg.Idx == START) && (mxc400x_info.enabled)) {
NRF_LOG_INFO("Reading data (Sensor D)!");
// Ensure that the sensor is powered on.
if (mxc400x_info.status != POWERED_ON) {
if (os_acquire_i2c_mutex()) {
status_sensor = mxc400x_chip_init(&mxc400x_data_global, MXC400X_CAL_OFFSET_X, MXC400X_CAL_OFFSET_Y, MXC400X_CAL_OFFSET_Z, false);
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
NRF_LOG_INFO("MXC400X sensor initialised successfully with range %d.", MXC400X_SEL_RANGE);
mxc400x_info.status = POWERED_ON;
}
else {
NRF_LOG_INFO("MXC400X sensor initialisation failed!");
mxc400x_info.status = UNKNOWN;
}
}
}
// Read sensor data as required.
if (mxc400x_info.status == POWERED_ON) {
if (os_acquire_i2c_mutex()) {
accOK = (mxc400x_read_xyz(&mxc400x_data_global) == NO_ERROR);
tempOK = (mxc400x_read_temp(&mxc400x_data_global) == NO_ERROR);
os_release_i2c_mutex();
if (accOK && tempOK) {
NRF_LOG_INFO("MXC400X read OK.");
NRF_LOG_INFO("X = " NRF_LOG_FLOAT_MARKER " g", NRF_LOG_FLOAT(mxc400x_data_global.chans_calc[MXC400X_AXIS_X]));
NRF_LOG_INFO("Y = " NRF_LOG_FLOAT_MARKER " g", NRF_LOG_FLOAT(mxc400x_data_global.chans_calc[MXC400X_AXIS_Y]));
NRF_LOG_INFO("Z = " NRF_LOG_FLOAT_MARKER " g", NRF_LOG_FLOAT(mxc400x_data_global.chans_calc[MXC400X_AXIS_Z]));
NRF_LOG_INFO("Temperature = " NRF_LOG_FLOAT_MARKER " C", NRF_LOG_FLOAT(mxc400x_data_global.temp));
// Communicate data to the mediator thread for storage.
msg.Idx = SENSOR_D_DATA;
msg.timestamp_unix = get_system_time_unix();
msg.timestamp_ms = get_system_time_ms();
msg.sensor_channel_data[0] = (int16_t)(mxc400x_data_global.chans_calc[MXC400X_AXIS_X] * 10);
msg.sensor_channel_data[1] = (int16_t)(mxc400x_data_global.chans_calc[MXC400X_AXIS_Y] * 10);
msg.sensor_channel_data[2] = (int16_t)(mxc400x_data_global.chans_calc[MXC400X_AXIS_Z] * 10);
msg.sensor_channel_data[3] = (int16_t)(mxc400x_data_global.temp * 10);
os_message_queue_put(MEDIATOR_QUEUE, &msg, false);
}
else {
NRF_LOG_INFO("MXC400X read failed!");
}
}
}
}
// Configure the sensor for interrupt-driven operation, if supported.
else if ((msg.Idx == START_INTERRUPTS) && (!mxc400x_info.mode)) {
NRF_LOG_INFO("Configuring interrupts (Sensor D)!");
NRF_LOG_INFO("Setting up interrupts on MXC400X.");
if (os_acquire_i2c_mutex()) {
status_sensor = mxc400x_chip_init(&mxc400x_data_global, MXC400X_CAL_OFFSET_X, MXC400X_CAL_OFFSET_Y, MXC400X_CAL_OFFSET_Z, true);
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
NRF_LOG_INFO("MXC400X sensor initialised successfully with range %d.", MXC400X_SEL_RANGE);
mxc400x_info.status = POWERED_ON;
}
else {
NRF_LOG_INFO("MXC400X sensor initialisation failed!");
mxc400x_info.status = UNKNOWN;
}
}
// Set up interrupt handling on the MCU.
nrf_drv_gpiote_in_event_enable(PIN_INTERRUPT_MXC400X, true);
}
// Switch off interrupts, if supported.
else if (msg.Idx == STOP_INTERRUPTS) {
NRF_LOG_INFO("Configuring interrupts (Sensor D)!");
NRF_LOG_INFO("Halting interrupts on MXC400X.");
if (os_acquire_i2c_mutex()) {
status_sensor = mxc400x_chip_init(&mxc400x_data_global, 0, 0, 0, false);
os_release_i2c_mutex();
if (status_sensor == NO_ERROR) {
NRF_LOG_INFO("MXC400X sensor initialised successfully with range %d.", MXC400X_SEL_RANGE);
mxc400x_info.status = POWERED_ON;
}
else {
NRF_LOG_INFO("MXC400X sensor initialisation failed!");
mxc400x_info.status = UNKNOWN;
}
}
// Set up interrupt handling on the MCU.
nrf_drv_gpiote_in_event_disable(PIN_INTERRUPT_MXC400X);
}
// Shut down the sensor.
else if (msg.Idx == STOP) {
NRF_LOG_INFO("Shutting down (Sensor D)!");
// Switch the sensor off.
if (mxc400x_info.status != POWERED_OFF) {
if (os_acquire_i2c_mutex()) {
status_sensor = mxc400x_set_power_mode(&mxc400x_data_global, false);
os_release_i2c_mutex();
if (status_sensor != NO_ERROR) {
NRF_LOG_INFO("MXC400X sensor shutdown succesfully.");
mxc400x_info.status = POWERED_OFF;
}
else {
NRF_LOG_INFO("MXC400X sensor shutdown failed!");
mxc400x_info.status = UNKNOWN;
}
}
}
}
// Message not handled.
else {
NRF_LOG_INFO("Sensor D command ignored!");
}
}
os_thread_yield();
}
}
/**************************************************************
* @brief Function handles a repeated timer timeout event.
* @param None.
**************************************************************/
void os_poll_timer_handler(void * p_context) {
static MSGQUEUE_OBJ_t msg;
static uint64_t time_offset_ms = 0;
UNUSED_PARAMETER(p_context);
// Update the real-time clock.
set_system_time_ms(get_system_time_ms() + get_common_poll_period_ms());
if ((get_system_time_ms() % 1000 == 0) || (get_system_time_ms() > 1000)) {
set_system_time_ms(0);
set_system_time_unix(get_system_time_unix() + 1);
// Update the BLE timestamp characteristic, if required.
#if ((USE_CUSTOM_SVC) && (!USE_CURRENT_TIME_SVC))
timestamp_data_update(get_system_time_unix());
#endif
}
// Update the time offset and poll counter.
time_offset_ms += get_common_poll_period_ms();
increment_num_polls();
// Start sensor threads as and when required.
msg.Idx = START;
if ((shtc3_info.enabled) && (shtc3_info.mode)) {
if ((time_offset_ms % shtc3_info.read_period) == 0) {
os_message_queue_put(SENSOR_A_QUEUE, &msg, true);
}
}
if ((ms5607_info.enabled) && (ms5607_info.mode)) {
if ((time_offset_ms % ms5607_info.read_period) == 0) {
os_message_queue_put(SENSOR_B_QUEUE, &msg, true);
}
}
if ((tsl2x7x_info.enabled) && (tsl2x7x_info.mode)) {
if ((time_offset_ms % tsl2x7x_info.read_period) == 0) {
os_message_queue_put(SENSOR_C_QUEUE, &msg, true);
}
}
if ((mxc400x_info.enabled) && (mxc400x_info.mode)) {
if ((time_offset_ms % mxc400x_info.read_period) == 0) {
os_message_queue_put(SENSOR_D_QUEUE, &msg, true);
}
}
}
/**************************************************************
* @brief Function for handling the battery measurement timer timeout.
* @details This function will be called each time the battery level measurement timer expires; this function will start the ADC.
* @notes None.
* @param[in] p_context Pointer used for passing some arbitrary information (context) from the app_start_timer() call to the timeout handler.
* @param[out] None.
**************************************************************/
void os_battery_timer_handler(void * p_context) {
UNUSED_PARAMETER(p_context);
measure_battery_level();
set_system_status();
}
/**************************************************************
* @brief Function for initialising software timers.
* @notes None.
* @param None.
**************************************************************/
void os_timers_init(void) {
#if !USE_FREERTOS
ret_code_t err_code;
#endif
NRF_LOG_INFO("Timers init!");
// Initialise the timer module.
#if !USE_FREERTOS
err_code = app_timer_init();
APP_ERROR_CHECK(err_code);
#endif
// Create a battery timer.
#if USE_FREERTOS
#if FRERTOS_USE_STATIC_ALLOCATION
m_battery_timer = xTimerCreateStatic("BATT", pdMS_TO_TICKS(BATTERY_MEAS_INT_MS), pdTRUE, 0, os_battery_timer_handler, &xBatteryTimerBuffer);
if (m_battery_timer == NULL) {
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
#else
m_battery_timer = xTimerCreate("BATT", pdMS_TO_TICKS(BATTERY_MEAS_INT_MS), pdTRUE, 0, os_battery_timer_handler);
if (m_battery_timer == NULL) {
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
#endif
#else
err_code = app_timer_create(&m_battery_timer_id, APP_TIMER_MODE_REPEATED, battery_timer_handler);
APP_ERROR_CHECK(err_code);
#endif
// Create a common poll timer.
#if USE_FREERTOS
#if FRERTOS_USE_STATIC_ALLOCATION
m_poll_timer = xTimerCreateStatic("POLL", pdMS_TO_TICKS(POLL_PERIOD_DEFAULT_MS), pdTRUE, 0, os_poll_timer_handler, &xPollTimerBuffer);
if (m_poll_timer == NULL) {
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
#else
m_poll_timer = xTimerCreate("POLL", pdMS_TO_TICKS(POLL_PERIOD_DEFAULT_MS), pdTRUE, 0, os_poll_timer_handler);
if (m_poll_timer == NULL) {
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
#endif
#else
err_code = app_timer_create(&m_poll_timer_id, APP_TIMER_MODE_REPEATED, poll_timer_handler);
APP_ERROR_CHECK(err_code);
#endif
}
/**************************************************************
* @brief Function starts the battery read timer.
* @notes None.
* @param None.
**************************************************************/
void os_battery_timer_start(uint32_t period_ms, bool fromISR) {
#if USE_FREERTOS
if (fromISR) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (xTimerChangePeriodFromISR(m_battery_timer, pdMS_TO_TICKS(period_ms), &xHigherPriorityTaskWoken) != pdPASS) { // Also starts the timer.
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
}
else {
if (xTimerChangePeriod(m_battery_timer, pdMS_TO_TICKS(period_ms), portMAX_DELAY) != pdPASS) { // Also starts the timer.
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
}
#else
static ret_code_t err_code = app_timer_start(m_battery_timer_id, APP_TIMER_TICKS(period_ms), NULL);
APP_ERROR_CHECK(err_code);
#endif
}
/**************************************************************
* @brief Function stops the battery timer.
* @notes None.
* @param None.
**************************************************************/
void os_battery_timer_stop(bool fromISR) {
#if USE_FREERTOS
if (m_battery_timer != NULL) {
if (fromISR) {
while(xTimerStop(m_battery_timer, portMAX_DELAY) != pdPASS);
}
else {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
while(xTimerStopFromISR(m_battery_timer, &xHigherPriorityTaskWoken) != pdPASS);
}
}
#else
static ret_code_t err_code = app_timer_stop(m_battery_timer_id);
APP_ERROR_CHECK(err_code);
#endif
}
/**************************************************************
* @brief Function starts the common poll timer.
* @notes None.
* @param None.
**************************************************************/
void os_poll_timer_start(uint32_t period_ms, bool fromISR) {
#if USE_FREERTOS
if (fromISR) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (xTimerChangePeriodFromISR(m_poll_timer, pdMS_TO_TICKS(period_ms), &xHigherPriorityTaskWoken) != pdPASS) { // Also starts the timer.
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
}
else {
if (xTimerChangePeriod(m_poll_timer, pdMS_TO_TICKS(period_ms), portMAX_DELAY) != pdPASS) { // Also starts the timer.
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
}
#else
static ret_code_t err_code = app_timer_start(m_poll_timer_id, APP_TIMER_TICKS(period_ms), NULL);
APP_ERROR_CHECK(err_code);
#endif
}
/**************************************************************
* @brief Function stops the common poll timer.
* @notes None.
* @param None.
**************************************************************/
void os_poll_timer_stop(bool fromISR) {
#if USE_FREERTOS
if (m_poll_timer != NULL) {
if (fromISR) {
while(xTimerStop(m_poll_timer, portMAX_DELAY) != pdPASS);
}
else {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
while(xTimerStopFromISR(m_poll_timer, &xHigherPriorityTaskWoken) != pdPASS);
}
}
#else
static ret_code_t err_code = app_timer_stop(m_poll_timer_id);
APP_ERROR_CHECK(err_code);
#endif
}