/******************************************************************************* * @file meas_pd_48.c * @brief M48 LED-PD measurement module for NIRS system * @author Charles KWON * @version V2.0.0 * @date 2025-12-31 * * Copyright (c) 2025 Medithings Inc. * All rights reserved. * * This module implements the M48 (24/48 LED) measurement sequence for the * NIRS bladder monitoring system. It handles sequential LED activation, * ADC sampling via PPI-triggered SAADC, and data transmission over BLE. * * ============================================================================ * MEASUREMENT FLOW (mcj? command example - ADC_PD_MODE 2) * ============================================================================ * * 1. Command Reception (parser.c - Cmd_mcj): * - BLE/UART receives "mcj?" command * - Sets ADC_PD_MODE = 2 (Half 0-23, single shot) * - Sets info4 = true (include batt, temp, IMU, pressure data) * - Calls m48_adc_start_init() to begin measurement * * 2. Initialization (m48_adc_start_init): * - Stops battery timer (to prevent ADC conflict) * - Copies LED_list_m48_1h[24] -> CURRENT_list_m48[24] * - Sets CURRENT_LED_NO = 24 * - Initializes I2C, SAADC, GPIOTE, PPI * - Configures first LED (LED0) via CAT9532 I2C * - Sets MCP4725 DAC value from led_pd_dac_v[0] (from EEPROM/AGC) * - Starts ADC sampling * * 3. ADC Sampling Loop (m48_voltage_handler - called 24 times): * For each LED[i] (i = 0 to 23): * a) PPI triggers 32 SAADC samples on ADA2200 SYNCO rising edge * b) When buffer full, m48_voltage_handler() interrupt fires: * - Averages 32 samples -> m48_led_pd_data[i] * - Turns off current LED * - If more LEDs remain: * * Increments m_pd_adc_cnt * * Configures next LED via CAT9532 * * Sets DAC value for next LED * * Calls m48_adc_start() for next measurement * - If last LED done: * * Calls m48_adc_end_final() * * 4. Finalization (m48_adc_end_final): * - Disables PPI, GPIOTE, SAADC * - Uninitializes peripherals * - If info4 == true, reads: * * Battery voltage (uint16_t) * * Temperature (uint16_t) * * IMU data (6 x uint16_t) * * Pressure P1, P2 (2 x uint16_t) * - Formats response with "rcj:" tag * - Sends via BLE: [batt, temp, imu[6], p1, p2, led_data[24]] * - Restarts battery timer * - Clears processing flag * * ============================================================================ * RESPONSE DATA FORMAT (rcj: tag - 72 bytes total) * ============================================================================ * * Byte[0-3]: "rcj:" tag (4 bytes) * Byte[4-5]: Battery voltage (uint16_t, big-endian) * Byte[6-7]: Temperature (uint16_t, big-endian) * Byte[8-19]: IMU data (6 x uint16_t = 12 bytes) * Byte[20-21]: Pressure P1 (uint16_t) * Byte[22-23]: Pressure P2 (uint16_t) * Byte[24-71]: LED measurement data (24 x uint16_t = 48 bytes) * * ============================================================================ * Measurement Modes (ADC_PD_MODE): * ============================================================================ * 0: Full 48 LED measurement (single shot) * 1: Full 48 LED measurement (continuous) * 2: Half measurement - LEDs 0-23 (single shot) - mcj? command * 3: Half measurement - LEDs 0-23 (continuous) * 4: Half measurement - LEDs 24-47 (single shot) * 5: Half measurement - LEDs 24-47 (continuous) * * Hardware Dependencies: * - ADA2200 Lock-in Amplifier (SYNCO pin P0.17 for PPI trigger) * - MCP4725 DAC for PD gain control (I2C) * - CAT9532 LED driver (I2C) * - SAADC for differential ADC measurement (AIN0/AIN1) * - EEPROM for storing AGC calibration values (addr 0x0480) ******************************************************************************/ #include "sdk_common.h" #include #include #include #include #include "nrf.h" #include "boards.h" #include "app_error.h" #include "nrf_drv_saadc.h" #include "nrfx_gpiote.h" #include "app_timer.h" #include "nrf_drv_timer.h" #include "nrf_delay.h" #include "nrf_drv_ppi.h" #include "ada2200_spi.h" #include "ble_nus.h" #include "measurements.h" #include "meas_pd_48.h" #include "mcp4725_i2c.h" //#include "ad5272_i2c.h" #include "main_timer.h" #include "battery_saadc.h" #include "tmp235_q1.h" #include "main.h" #include "app_raw_main.h" #include #include "debug_print.h" /*============================================================================*/ /* External Variables - Charles KWON */ /*============================================================================*/ /** @brief Flag to include additional sensor data (batt, temp, IMU, pressure) */ extern bool info4; /*============================================================================*/ /* Configuration Variables - Charles KWON */ /*============================================================================*/ /** * @brief Current measurement mode * - Charles KWON * * Values: * 0: Full 48 LED (single) 1: Full 48 LED (continuous) * 2: Half 0-23 (single) 3: Half 0-23 (continuous) * 4: Half 24-47 (single) 5: Half 24-47 (continuous) */ uint8_t ADC_PD_MODE = 0; /** @brief Number of ADC samples per LED measurement */ uint8_t m48_samples_in_buffer = 8; /** @brief Current number of LEDs to measure */ uint8_t CURRENT_LED_NO = 24; /** @brief Working LED index list for current measurement */ uint8_t CURRENT_list_m48[m48_LED_NO] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23}; /*============================================================================*/ /* LED Configuration Arrays - Charles KWON */ /*============================================================================*/ /** @brief Full LED list (0-23) */ static const uint8_t LED_list_m48[m48_LED_NO] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23}; /** @brief First half LED list (0-23) for modes 2,3 */ static const uint8_t LED_list_m48_1h[m48_LED_NO_H] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23}; /** @brief Second half LED list (24-47) for modes 4,5 */ static const uint8_t LED_list_m48_2h[m48_LED_NO_H] = {24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47}; /** @brief PD delay in microseconds */ extern uint16_t m_pd_delay_us; /*============================================================================*/ /* State Variables - Charles KWON */ /*============================================================================*/ /** @brief Command processing flag */ extern volatile bool processing; /** @brief BLE TX in progress flag */ extern volatile bool data_tx_in_progress; /** @brief BLE connection status */ extern volatile bool ble_connection_st; /** @brief Temperature measurement trigger */ extern bool go_temp; /** @brief Battery measurement trigger */ extern bool go_batt; /*============================================================================*/ /* Sensor Data Storage - Charles KWON */ /*============================================================================*/ /** @brief Battery level (mV) */ uint16_t volatile info_batt = 0; /** @brief Temperature reading */ uint16_t volatile info_temp = 0; /** @brief IMU data array [accX, accY, accZ, gyrX, gyrY, gyrZ] */ uint16_t volatile info_imu[6] = {0, 1, 2, 3, 4, 5}; /** @brief Pressure sensor 1 value */ uint16_t volatile info_p1 = 0; /** @brief Pressure sensor 2 value */ uint16_t volatile info_p2 = 0; /*============================================================================*/ /* Measurement State Machine Variables - Charles KWON */ /*============================================================================*/ /** @brief Measurement cycle counter */ uint32_t m48_cnt; /** @brief Current PD index (-1 = not started) */ static int8_t pd_no = -1; /** @brief Current LED index (-1 = not started) */ static int8_t led_no = -1; /** @brief Current buffer index */ static int8_t buf_no = 0; /** @brief Elapsed time in milliseconds */ static int32_t t_ms = 0; /** @brief Order counter for response sequencing */ uint8_t order_pd = 0; /*============================================================================*/ /* ADC Data Buffers - Charles KWON */ /*============================================================================*/ /** * @brief Raw ADC sample buffer for each LED * - Charles KWON * * Dimensions: [24 LEDs][32 samples max per LED] */ int16_t buff_m48_cycle[m48_LED_NO][m48_CYCLE_CNT] = {0}; /** @brief Summed ADC values for each LED (signed) */ int16_t m48_cycle_send_buff[m48_LED_NO] = {0}; /** @brief Summed ADC values for each LED (unsigned, for BLE TX) */ uint16_t single_bi_m48_cycle_send_buff[m48_LED_NO] = {0}; /** @brief ADC buffer size including delay samples */ #define SAMPLES_IN_BUFFER (4095 + 32) /** @brief Double-buffered ADC sample storage */ static nrf_saadc_value_t pd_m48_adc_buf[2][SAMPLES_IN_BUFFER]; /** * @brief Combined sensor + LED data buffer for BLE transmission * - Charles KWON * * Layout: [batt, temp, imu[6], p1, p2, led_data[24]] */ uint16_t single_info_m48_cycle_send_buff[m48_LED_NO] = {0}; /*============================================================================*/ /* Measurement State Flags - Charles KWON */ /*============================================================================*/ /** @brief M48 measurement start trigger */ bool pd_adc_m48_start = false; /** @brief M48 continuous measurement running */ bool pd_adc_m48_running = false; /** @brief M48 measurement in progress */ bool m48_testing = false; /** @brief New BLE data received flag */ extern bool ble_got_new_data; /** @brief IMU raw data enabled flag */ extern bool motion_raw_data_enabled; /*============================================================================*/ /* Communication Buffers - Charles KWON */ /*============================================================================*/ /** @brief BLE binary transmission buffer */ uint8_t m48_bin_buffer[BLE_NUS_MAX_DATA_LEN]; /** @brief Command source type */ extern which_cmd_t cmd_type_t; /*============================================================================*/ /* Timer Definitions - Charles KWON */ /*============================================================================*/ /** @brief Timer for measurement time tracking */ APP_TIMER_DEF(m_m48_check_loop_timer_id); /** @brief Send interval for delayed transmission */ #if FEATURE_DELAY #define m48_SEND_LOOP_INTERVAL 500 #else #define m48_SEND_LOOP_INTERVAL 100 #endif /** @brief Check loop interval (1ms) */ #define m48_CHECK_LOOP_INTERVAL 1 /** @brief PPI channel for SYNCO->SAADC trigger */ static nrf_ppi_channel_t m_ppi_channel; /*============================================================================*/ /* PPI Configuration - Charles KWON */ /*============================================================================*/ #if !FEATURE_PRINTF /** * @brief Initialize PPI for ADA2200 SYNCO triggered ADC sampling * - Charles KWON * * Connects ADA2200 SYNCO rising edge to SAADC sample task. */ void m48_ppi_init(void) { ret_code_t err_code; err_code = nrf_drv_ppi_init(); APP_ERROR_CHECK(err_code); uint32_t gpiote_event_addr = nrf_drv_gpiote_in_event_addr_get(ADA2200_SYNCO_PIN); uint32_t saadc_sample_task_addr = nrf_drv_saadc_sample_task_get(); err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel); APP_ERROR_CHECK(err_code); err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, gpiote_event_addr, saadc_sample_task_addr); APP_ERROR_CHECK(err_code); } /** * @brief Uninitialize PPI * - Charles KWON */ void m48_ppi_uninit(void) { ret_code_t err_code; err_code = nrf_drv_ppi_uninit(); APP_ERROR_CHECK(err_code); } /** * @brief Enable PPI channel for ADC sampling * - Charles KWON */ void m48_sampling_event_enable(void) { ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel); APP_ERROR_CHECK(err_code); } /** * @brief Disable PPI channel * - Charles KWON */ void m48_sampling_event_disable(void) { ret_code_t err_code = nrf_drv_ppi_channel_disable(m_ppi_channel); APP_ERROR_CHECK(err_code); } #endif /*============================================================================*/ /* ADC Interrupt Handler - Charles KWON */ /*============================================================================*/ /** * @brief SAADC event handler for M48 measurement * - Charles KWON * * Implements the LED-PD measurement state machine: * 1. On first call: activate first LED and set DAC gain * 2. Store ADC samples for current LED * 3. Advance to next LED or complete measurement * 4. On completion: sum samples, format response, send via BLE * * Response tags by mode: * - Mode 0: rsj: (Full with sensor data) * - Mode 2: rcj: (Half 0-23 with sensor data) * - Mode 3: rdj: (Half 0-23 continuous) * - Mode 4: rej: (Half 24-47 with sensor data) * - Mode 5: rfj: (Half 24-47 continuous) * * @param[in] p_event Pointer to SAADC event structure */ static void m48_voltage_handler(nrf_drv_saadc_evt_t const * p_event) { ret_code_t err_code; int16_t sum = 0; uint32_t m48_clk_delay = m_pd_delay_us / 16; /* Handle BLE disconnection during measurement */ if (ble_connection_st == 0) { DBG_PRINTF("m48 ADC STOP 1\r\n"); led_off(99); pd_off(99); pd_adc_m48_start = false; pd_adc_m48_running = false; m48_testing = false; info4 = false; DBG_PRINTF("LOST_AT48\r\n"); processing = false; go_batt = false; go_temp = false; m48_adc_end_final(); } else { if (p_event->type == NRF_DRV_SAADC_EVT_DONE) { err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, m48_samples_in_buffer + m48_clk_delay); APP_ERROR_CHECK(err_code); /*----------------------------------------------------------------*/ /* First call: Initialize LED/PD */ /*----------------------------------------------------------------*/ if (led_no == -1) { led_no = 0; pd_no = 0; led_on(CURRENT_list_m48[led_no]); led_pd_matching_value_set(CURRENT_list_m48[led_no]); } else { /*------------------------------------------------------------*/ /* Store ADC samples for current LED */ /*------------------------------------------------------------*/ for (uint16_t i = m48_clk_delay; i < m48_clk_delay + m48_samples_in_buffer; i++) { buff_m48_cycle[buf_no][i - m48_clk_delay] = p_event->data.done.p_buffer[i]; } #if FEATURE_PRINTF DBG_PRINTF("-----------------Read ADC // led_no = %d(%d), pd_no = %d(%d), buf_no = %d\r\n\r\n", led_no, LED_list_m48[led_no], pd_no, PD_list_m48[pd_no], buf_no); #endif buf_no++; /*------------------------------------------------------------*/ /* Advance to next LED or complete */ /*------------------------------------------------------------*/ if (pd_no >= -1) { if (led_no < CURRENT_LED_NO - 1) { /* Next LED */ led_no++; led_on(CURRENT_list_m48[led_no]); led_pd_matching_value_set(CURRENT_list_m48[led_no]); } else if (led_no >= CURRENT_LED_NO - 1) { /*----------------------------------------------------*/ /* All LEDs measured - process and send results */ /*----------------------------------------------------*/ pd_no = -1; led_no = -1; #if FEATURE_PRINTF DBG_PRINTF("\r\nEnded\r\n"); #endif /* Sum samples for each LED */ uint8_t k = 0; sum = 0; for (uint16_t i = 0; i < CURRENT_LED_NO; i++) { for (uint16_t j = 0; j < m48_samples_in_buffer; j++) { sum += buff_m48_cycle[i][j]; } m48_cycle_send_buff[k++] = sum; sum = 0; } buf_no = 0; if (ble_connection_st == 0) { DBG_PRINTF("m24 ADC STOP 1"); } else { pd_adc_m48_start = false; DBG_PRINTF("FINISH SEND\r\n"); m48_testing = false; if (cmd_type_t == CMD_UART) { /* UART response handled elsewhere */ } else if (cmd_type_t == CMD_BLE) { /* Convert to unsigned for BLE transmission */ for (uint16_t i = 0; i < CURRENT_LED_NO; i++) { DBG_PRINTF("%d,", m48_cycle_send_buff[i]); single_bi_m48_cycle_send_buff[i] = (uint16_t)(m48_cycle_send_buff[i]); } /*--------------------------------------------*/ /* Format response with sensor data */ /*--------------------------------------------*/ if (info4 == true) { single_info_m48_cycle_send_buff[0] = info_batt; single_info_m48_cycle_send_buff[1] = info_temp; for (uint16_t i = 0; i < 6; i++) { single_info_m48_cycle_send_buff[i + 2] = info_imu[i]; } single_info_m48_cycle_send_buff[8] = info_p1; single_info_m48_cycle_send_buff[9] = info_p2; for (uint16_t i = 0; i < CURRENT_LED_NO; i++) { single_info_m48_cycle_send_buff[i + 10] = single_bi_m48_cycle_send_buff[i]; } /* Send response based on mode */ if (ADC_PD_MODE == 0) { format_data(m48_bin_buffer, "rsj:", single_info_m48_cycle_send_buff, 56); binary_tx_handler(m48_bin_buffer, 58); } else if (ADC_PD_MODE == 2) { format_data(m48_bin_buffer, "rcj:", single_info_m48_cycle_send_buff, 34); binary_tx_handler(m48_bin_buffer, 36); } else if (ADC_PD_MODE == 3) { format_data(m48_bin_buffer, "rdj:", single_info_m48_cycle_send_buff, 34); binary_tx_handler(m48_bin_buffer, 36); } else if (ADC_PD_MODE == 4) { format_data(m48_bin_buffer, "rej:", single_info_m48_cycle_send_buff, 34); binary_tx_handler(m48_bin_buffer, 36); } else if (ADC_PD_MODE == 5) { format_data(m48_bin_buffer, "rfj:", single_info_m48_cycle_send_buff, 34); binary_tx_handler(m48_bin_buffer, 36); } } else { /* Send LED data only (no sensor data) */ char resp[4]; if (ADC_PD_MODE == 2) { format_data(m48_bin_buffer, "rdj:", single_bi_m48_cycle_send_buff, 24); binary_tx_handler(m48_bin_buffer, 26); } else if (ADC_PD_MODE == 4) { format_data(m48_bin_buffer, "rfj:", single_bi_m48_cycle_send_buff, 24); binary_tx_handler(m48_bin_buffer, 26); } else if (ADC_PD_MODE == 3) { sprintf(resp, "rd%01X:", order_pd); format_data(m48_bin_buffer, resp, single_bi_m48_cycle_send_buff, 24); binary_tx_handler(m48_bin_buffer, 26); } else if (ADC_PD_MODE == 5) { sprintf(resp, "rf%01X:", order_pd); format_data(m48_bin_buffer, resp, single_bi_m48_cycle_send_buff, 24); binary_tx_handler(m48_bin_buffer, 26); } } DBG_PRINTF("\r\n %d ms \r\n", t_ms); } /*------------------------------------------------*/ /* Handle continuous mode or cleanup */ /*------------------------------------------------*/ if (pd_adc_m48_running == true) { if (info4 == true) { m48_adc_end_final(); pd_adc_m48_start = true; go_batt = true; main_timer_start(); } else { t_ms = 0; m48_cnt = 0; m48_testing = true; m48_adc_start(); } } else { /* Single shot - cleanup */ if (info4 == true) { info4 = false; } led_off(99); pd_off(99); processing = false; m48_adc_end_final(); } } } } } } } } /*============================================================================*/ /* Timer Callback - Charles KWON */ /*============================================================================*/ /** * @brief Check timer callback for measurement timing * - Charles KWON * * Tracks elapsed time during measurement. * * @param[in] p_context Unused timer context */ void m48_check_loop(void * p_context) { UNUSED_PARAMETER(p_context); m48_check_timer_stop(); if (m48_testing == false) { DBG_PRINTF("%d ms \r\n", t_ms); } else { t_ms++; m48_check_timer_start(); } } /*============================================================================*/ /* Measurement Control Functions - Charles KWON */ /*============================================================================*/ /** * @brief Initialize and start M48 measurement based on mode * - Charles KWON * * Called from main_timer to begin measurement sequence. * Configures LED list based on ADC_PD_MODE setting. */ void m48_adc_start_init(void) { /* Handle new data received during continuous mode */ if (ble_got_new_data == true) { if (pd_adc_m48_running == true) { led_off(99); pd_off(99); pd_adc_m48_start = false; pd_adc_m48_running = false; DBG_PRINTF("FINISH NEWDATA\r\n"); processing = false; go_batt = false; go_temp = false; motion_raw_data_enabled = false; m48_testing = false; if (info4 == true) { info4 = false; } else { m48_adc_end_final(); } } } else if (pd_adc_m48_start == true) { /* Configure measurement based on mode */ switch (ADC_PD_MODE) { case 0: /* Full 48 LED (single) */ pd_adc_m48_running = false; CURRENT_LED_NO = m48_LED_NO; for (uint16_t i = 0; i < CURRENT_LED_NO; i++) { CURRENT_list_m48[i] = LED_list_m48[i]; } break; case 1: /* Full 48 LED (continuous) */ pd_adc_m48_running = true; CURRENT_LED_NO = m48_LED_NO; for (uint16_t i = 0; i < CURRENT_LED_NO; i++) { CURRENT_list_m48[i] = LED_list_m48[i]; } break; case 2: /* Half 0-23 (single) - mcj? */ pd_adc_m48_running = false; CURRENT_LED_NO = m48_LED_NO_H; for (uint16_t i = 0; i < CURRENT_LED_NO; i++) { CURRENT_list_m48[i] = LED_list_m48_1h[i]; } break; case 3: /* Half 0-23 (continuous) */ pd_adc_m48_running = true; CURRENT_LED_NO = m48_LED_NO_H; for (uint16_t i = 0; i < CURRENT_LED_NO; i++) { CURRENT_list_m48[i] = LED_list_m48_1h[i]; } break; case 4: /* Half 24-47 (single) */ pd_adc_m48_running = false; CURRENT_LED_NO = m48_LED_NO_H; for (uint16_t i = 0; i < CURRENT_LED_NO; i++) { CURRENT_list_m48[i] = LED_list_m48_2h[i]; DBG_PRINTF("%d,", CURRENT_list_m48[i]); } break; case 5: /* Half 24-47 (continuous) */ pd_adc_m48_running = true; CURRENT_LED_NO = m48_LED_NO_H; for (uint16_t i = 0; i < CURRENT_LED_NO; i++) { CURRENT_list_m48[i] = LED_list_m48_2h[i]; } break; default: pd_adc_m48_running = false; CURRENT_LED_NO = m48_LED_NO; for (uint16_t i = 0; i < CURRENT_LED_NO; i++) { CURRENT_list_m48[i] = LED_list_m48[i]; } break; } t_ms = 0; m48_cnt = 0; pd_adc_m48_start = false; m48_testing = true; m48_adc_start2(); m48_check_timer_start(); } } /** * @brief Initialize measurement buffers and state * - Charles KWON */ void m48_adc_start(void) { pd_no = -1; led_no = -1; buf_no = 0; /* Clear measurement buffers */ for (uint16_t i = 0; i < m48_LED_NO; i++) { for (uint16_t j = 0; j < m48_CYCLE_CNT; j++) { buff_m48_cycle[i][j] = 0; if (ble_got_new_data == true) { if (pd_adc_m48_running == true) { pd_adc_m48_start = false; pd_adc_m48_running = false; } } } } /* Update order counter for continuous mode */ if (order_pd >= 15) { order_pd = 0; } else { order_pd++; } } /** * @brief Start M48 measurement with full initialization * - Charles KWON */ void m48_adc_start2(void) { m48_adc_start(); m48_adc_init(); m48_irq_init(); m48_ppi_init(); m48_sampling_event_enable(); } /** * @brief Stop ADC sampling (partial cleanup) * - Charles KWON */ void m48_adc_end(void) { DBG_PRINTF("m48_adc_end\r\n"); m48_sampling_event_disable(); } /** * @brief Complete measurement cleanup * - Charles KWON * * Disables PPI, uninitializes SAADC, restarts battery timer. */ void m48_adc_end_final(void) { DBG_PRINTF("adc_end_final\r\n"); m48_sampling_event_disable(); m48_irq_uninit(); m48_ppi_uninit(); m48_adc_uninit(); battery_timer_start(); } /*============================================================================*/ /* SAADC Configuration - Charles KWON */ /*============================================================================*/ /** * @brief Initialize SAADC for M48 measurement * - Charles KWON * * Configures differential ADC on AIN0/AIN1 with 10-bit resolution. */ void m48_adc_init(void) { #if FEATURE_PRINTF DBG_PRINTF("m48_adc_init\r\n"); #endif static nrfx_saadc_config_t default_config; default_config.resolution = (nrf_saadc_resolution_t)NRFX_SAADC_CONFIG_RESOLUTION; default_config.oversample = (nrf_saadc_oversample_t)NRFX_SAADC_CONFIG_OVERSAMPLE; default_config.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY; default_config.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE; static nrf_saadc_channel_config_t config; config.resistor_p = NRF_SAADC_RESISTOR_DISABLED; config.resistor_n = NRF_SAADC_RESISTOR_DISABLED; config.gain = NRF_SAADC_GAIN1_6; config.reference = NRF_SAADC_REFERENCE_INTERNAL; config.acq_time = NRF_SAADC_ACQTIME_3US; config.mode = NRF_SAADC_MODE_DIFFERENTIAL; config.burst = NRF_SAADC_BURST_DISABLED; config.pin_p = (nrf_saadc_input_t)(NRF_SAADC_INPUT_AIN0); config.pin_n = (nrf_saadc_input_t)(NRF_SAADC_INPUT_AIN1); ret_code_t err_code = nrf_drv_saadc_init(&default_config, m48_voltage_handler); APP_ERROR_CHECK(err_code); err_code = nrf_drv_saadc_channel_init(0, &config); APP_ERROR_CHECK(err_code); err_code = nrf_drv_saadc_buffer_convert(pd_m48_adc_buf[0], m48_samples_in_buffer + m_pd_delay_us / 16); APP_ERROR_CHECK(err_code); } /** * @brief Uninitialize SAADC * - Charles KWON */ void m48_adc_uninit(void) { #if FEATURE_PRINTF DBG_PRINTF("pd_m48_adc_uninit\r\n"); #endif nrf_drv_saadc_uninit(); nrf_drv_saadc_channel_uninit(0); } /*============================================================================*/ /* GPIO Interrupt Configuration - Charles KWON */ /*============================================================================*/ #if !FEATURE_PRINTF /** * @brief Initialize GPIOTE for ADA2200 SYNCO pin * - Charles KWON * * Configures rising edge detection on SYNCO for PPI trigger. */ void m48_irq_init(void) { ret_code_t err_code; if (!nrfx_gpiote_is_init()) { err_code = nrfx_gpiote_init(); APP_ERROR_CHECK(err_code); } nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_LOTOHI(true); in_config.pull = NRF_GPIO_PIN_PULLDOWN; err_code = nrfx_gpiote_in_init(ADA2200_SYNCO_PIN, &in_config, NULL); APP_ERROR_CHECK(err_code); nrfx_gpiote_in_event_enable(ADA2200_SYNCO_PIN, true); } /** * @brief Uninitialize GPIOTE for SYNCO pin * - Charles KWON */ void m48_irq_uninit(void) { nrfx_gpiote_in_event_disable(ADA2200_SYNCO_PIN); nrfx_gpiote_in_uninit(ADA2200_SYNCO_PIN); } #endif /*============================================================================*/ /* Timer Control Functions - Charles KWON */ /*============================================================================*/ /** * @brief Start measurement check timer * - Charles KWON */ void m48_check_timer_start(void) { APP_ERROR_CHECK(app_timer_start(m_m48_check_loop_timer_id, APP_TIMER_TICKS(m48_CHECK_LOOP_INTERVAL), NULL)); } /** * @brief Stop measurement check timer * - Charles KWON */ void m48_check_timer_stop(void) { APP_ERROR_CHECK(app_timer_stop(m_m48_check_loop_timer_id)); } /** * @brief Initialize measurement check timer * - Charles KWON */ void m48_check_timer_init(void) { APP_ERROR_CHECK(app_timer_create(&m_m48_check_loop_timer_id, APP_TIMER_MODE_SINGLE_SHOT, m48_check_loop)); }