initial commit
This commit is contained in:
853
project/ble_peripheral/ble_app_vivaMayo/measurements.c
Normal file
853
project/ble_peripheral/ble_app_vivaMayo/measurements.c
Normal file
@@ -0,0 +1,853 @@
|
||||
/*******************************************************************************
|
||||
* @file measurements.c
|
||||
* @brief LED/PD measurement control module for NIRS system
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @version V2.0.0
|
||||
* @date 2025-12-31
|
||||
*
|
||||
* Copyright (c) 2025 Medithings Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This module provides LED and photodetector (PD) control functions for the
|
||||
* NIRS (Near-Infrared Spectroscopy) bladder monitoring system.
|
||||
*
|
||||
* ============================================================================
|
||||
* MODULE OVERVIEW
|
||||
* ============================================================================
|
||||
*
|
||||
* Hardware Components Controlled:
|
||||
* - 48 NIR LEDs (LED0-LED47) via CAT9532 I2C LED driver
|
||||
* - 4 Photodetectors (PD0, PD1, MOD, MOD2) via FSA5157P6X analog switch
|
||||
* - MCP4725 DAC for PD gain control (I2C, 12-bit)
|
||||
* - DS3930 EEPROM for LED power calibration storage
|
||||
*
|
||||
* LED-PD Mapping:
|
||||
* - LED 0-23 -> PD0 (photodetector 0)
|
||||
* - LED 24-47 -> PD1 (photodetector 1)
|
||||
* - MOD/MOD2 -> Lock-in amplifier modulation channels
|
||||
*
|
||||
* DAC Gain Range:
|
||||
* - MCP4725 output: 0.1V ~ 1.1V
|
||||
* - DAC value range: 125 ~ 1366 (12-bit)
|
||||
* - Default value: 1000 (mid-range)
|
||||
*
|
||||
* ============================================================================
|
||||
* MAIN FUNCTIONS
|
||||
* ============================================================================
|
||||
*
|
||||
* LED Control:
|
||||
* - led_on(index) : Turn on specific LED (0-47)
|
||||
* - led_off(index) : Turn off LED (99=all off, 98=clear)
|
||||
* - led_select(index) : Select LED via GPIO multiplexer
|
||||
*
|
||||
* PD Control:
|
||||
* - pd_on(index) : Turn on specific PD (0-3)
|
||||
* - pd_off(index) : Turn off PD (99=all off)
|
||||
* - pd_select(index) : Select PD channel
|
||||
*
|
||||
* Gain Control:
|
||||
* - pd_gain_set(led) : Set PD gain from led_pd_dac_v[] lookup table
|
||||
* - imm_gain_set(dac) : Set immediate DAC value
|
||||
* - led_pd_mod_set() : Set modulation gain for lock-in
|
||||
*
|
||||
* EEPROM Operations:
|
||||
* - led_power_read(index) : Read LED power from ROM
|
||||
* - led_power_save_mem(index) : Save LED power to ROM
|
||||
* - led_power_save_mem_48() : Save all 48 LED powers
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/*============================================================================*/
|
||||
/* Includes - Charles KWON */
|
||||
/*============================================================================*/
|
||||
#include "sdk_common.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "nrf.h"
|
||||
#include "nrf_drv_gpiote.h"
|
||||
#include "nrf_drv_spi.h"
|
||||
#include "nrf_drv_saadc.h"
|
||||
#include "nrf_drv_ppi.h"
|
||||
#include "nrf_drv_timer.h"
|
||||
#include "boards.h"
|
||||
#include "bsp.h"
|
||||
#include "app_error.h"
|
||||
#include "nrf_delay.h"
|
||||
#include "app_util_platform.h"
|
||||
#include "nrf_pwr_mgmt.h"
|
||||
#include "nrf_log.h"
|
||||
#include "ble_nus.h"
|
||||
#include "LED_Parse.h"
|
||||
#include "app_timer.h"
|
||||
#include "measurements.h"
|
||||
#include "main.h"
|
||||
//#include "ad5272_i2c.h"
|
||||
#include "mcp4725_i2c.h"
|
||||
#include <cmd_parse.h>
|
||||
#include "debug_print.h"
|
||||
#include "i2c_manager.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* Configuration Constants - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Total number of LEDs in the system
|
||||
* - Charles KWON
|
||||
*
|
||||
* Currently using 24 LEDs (LED0-LED23) for single PD configuration.
|
||||
* Can be extended to 48 for dual PD configuration.
|
||||
*/
|
||||
#define LED_NUM 24
|
||||
|
||||
/** @brief Timer interval for BLE data transmission (ms) */
|
||||
#define MEA_SEND_LOOP_INTERVAL 100
|
||||
|
||||
/*============================================================================*/
|
||||
/* State Variables - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Currently activated LED index
|
||||
* - Charles KWON
|
||||
*
|
||||
* Tracks which LED is currently turned on.
|
||||
* Value 99 indicates no LED is active (all off).
|
||||
*/
|
||||
uint8_t activated_led = 99;
|
||||
|
||||
/**
|
||||
* @brief Currently activated photodetector index
|
||||
* - Charles KWON
|
||||
*
|
||||
* Tracks which PD channel is currently selected.
|
||||
* Value 99 indicates no PD is active (all off).
|
||||
*/
|
||||
uint8_t activated_pd = 99;
|
||||
|
||||
/*============================================================================*/
|
||||
/* Timer Definitions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/** @brief Timer for sequenced BLE data transmission */
|
||||
APP_TIMER_DEF(m_mea_send_loop_timer_id);
|
||||
|
||||
/** @brief Counter for BLE transmission sequence */
|
||||
static uint8_t cnt = 0;
|
||||
|
||||
/*============================================================================*/
|
||||
/* Communication Buffers - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/** @brief TX buffer for BLE data transmission */
|
||||
char mea_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
|
||||
/** @brief External flag indicating command processing state */
|
||||
extern volatile bool processing;
|
||||
|
||||
/*============================================================================*/
|
||||
/* LED-PD Gain Calibration Table - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief DAC values for LED-PD gain matching
|
||||
* - Charles KWON
|
||||
*
|
||||
* Each LED has a corresponding DAC value that sets the optimal
|
||||
* PD amplifier gain for that LED-PD pair. Values are calibrated
|
||||
* during AGC (Automatic Gain Control) and stored in EEPROM.
|
||||
*
|
||||
* DAC Range:
|
||||
* - Minimum: 125 (0.1V output)
|
||||
* - Maximum: 1366 (1.1V output)
|
||||
* - Default: 1000 (mid-range)
|
||||
*
|
||||
* This array is populated from EEPROM at startup or after AGC.
|
||||
* Defined in device_config.c
|
||||
*/
|
||||
extern uint16_t led_pd_dac_v[];
|
||||
|
||||
/**
|
||||
* @brief DAC value when all LEDs are off
|
||||
* - Charles KWON
|
||||
*
|
||||
* Used as baseline/reference gain when no LED is active.
|
||||
*/
|
||||
uint16_t led_off_dac_v = 1360;
|
||||
|
||||
/**
|
||||
* @brief Default PD channel when LED is off
|
||||
* - Charles KWON
|
||||
*/
|
||||
uint8_t led_off_pd = 0;
|
||||
|
||||
/*============================================================================*/
|
||||
/* BLE Data Transmission Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Start LED-PD gain array printout via BLE
|
||||
* - Charles KWON
|
||||
*
|
||||
* Initiates sequenced transmission of the led_pd_dac_v[] array
|
||||
* over BLE. Data is sent in chunks to comply with BLE MTU limits.
|
||||
*/
|
||||
void led_pd_gain_array_printout(void)
|
||||
{
|
||||
cnt = 0;
|
||||
mea_send_timer_start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Timer callback for sending LED-PD gain data via BLE
|
||||
* - Charles KWON
|
||||
*
|
||||
* Called periodically to transmit gain calibration data.
|
||||
* Sends data in 3 chunks (cnt 0-2), then outputs debug info
|
||||
* and clears the processing flag.
|
||||
*
|
||||
* Output Format: "Tag-Currrnt [cnt], [dac0], [dac1], ..., [dac23]\r\n"
|
||||
*
|
||||
* @param[in] p_context Unused timer context
|
||||
*/
|
||||
void mea_send_loop(void *p_context)
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
DBG_PRINTF("\r\n1CNT =%d\r\n", cnt);
|
||||
mea_send_timer_stop();
|
||||
|
||||
if (cnt < 6) {
|
||||
uint8_t i = (int)(cnt);
|
||||
if (cnt < 3) {
|
||||
/* Build string dynamically for easy LED_NUM changes */
|
||||
int offset = sprintf(mea_tx_buffer, "Tag-Currrnt %d", i);
|
||||
for (uint8_t j = 0; j < LED_NUM; j++) {
|
||||
offset += sprintf(mea_tx_buffer + offset, ",\t%d", led_pd_dac_v[j]);
|
||||
}
|
||||
sprintf(mea_tx_buffer + offset, "\r\n");
|
||||
data_tx_handler(mea_tx_buffer);
|
||||
}
|
||||
DBG_PRINTF("\r\n2CNT =%d\r\n", cnt);
|
||||
mea_send_timer_start();
|
||||
} else {
|
||||
/* Final iteration: print debug summary */
|
||||
DBG_PRINTF("\r\nLED-PD Gain Array =\r\n");
|
||||
for (uint16_t j = 0; j < LED_NUM; j++) {
|
||||
DBG_PRINTF("%d,\t", led_pd_dac_v[j]);
|
||||
}
|
||||
DBG_PRINTF("\r\n");
|
||||
processing = false;
|
||||
}
|
||||
|
||||
cnt++;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* LED Control Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Turn on specified LED
|
||||
* - Charles KWON
|
||||
*
|
||||
* Activates the specified LED through the CAT9532 LED driver.
|
||||
* Also enables the analog switch trigger for signal routing.
|
||||
*
|
||||
* @param[in] led_index LED index (0-47 for specific LED, <=100 for valid range)
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on failure
|
||||
*
|
||||
* @note Sets activated_led to track current LED state
|
||||
* @note Calls trig_SW(true) to enable signal path
|
||||
*/
|
||||
ret_code_t led_on(uint8_t led_index)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
DBG_PRINTF("\tled_on, %d =====================\r\n", led_index);
|
||||
#endif
|
||||
|
||||
if (led_index <= 100) {
|
||||
activated_led = led_index;
|
||||
trig_SW(true); /* Enable analog switch for signal routing */
|
||||
|
||||
if (NRF_SUCCESS != led_select(led_index)) {
|
||||
DBG_PRINTF("ERR!!! LED Select\r\n");
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
} else {
|
||||
DBG_PRINTF("ERR!!! led_index Failed! %d\r\n", led_index);
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Turn off LED
|
||||
* - Charles KWON
|
||||
*
|
||||
* Deactivates LED and disables the analog switch trigger.
|
||||
*
|
||||
* @param[in] led_index 99 = all LEDs off, 98 = clear state
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on invalid index
|
||||
*
|
||||
* @note Calls trig_SW(false) to disable signal path
|
||||
*/
|
||||
ret_code_t led_off(uint8_t led_index)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
trig_SW(false); /* Disable analog switch */
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
DBG_PRINTF("led_off\r\n");
|
||||
#endif
|
||||
|
||||
if (led_index == 99) {
|
||||
LED_ALLOFF(); /* Turn off all LEDs */
|
||||
activated_led = 99;
|
||||
} else if (led_index == 98) {
|
||||
LED99(); /* Clear LED state */
|
||||
DBG_PRINTF("clear \r\n");
|
||||
activated_led = 98;
|
||||
} else {
|
||||
DBG_PRINTF("ERR!!! led_index Failed! %d\r\n", led_index);
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* Gain Control Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Set modulation gain via MCP4725 DAC
|
||||
* - Charles KWON
|
||||
*
|
||||
* Configures the DAC for lock-in amplifier modulation mode.
|
||||
* Used when measuring with the MOD photodetector channel.
|
||||
*
|
||||
* @param[in] mod_gain DAC value (0-2000)
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on failure
|
||||
*
|
||||
* @note Initializes SW I2C if not already done
|
||||
* @note Activates MOD channel (PD index 2)
|
||||
*/
|
||||
ret_code_t led_pd_mod_set(uint16_t mod_gain)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
|
||||
if (mod_gain <= 2000) {
|
||||
sw_i2c_init_once(); /* Ensure I2C is initialized */
|
||||
mcp4725_writeFastMode(mod_gain);
|
||||
|
||||
if (NRF_SUCCESS != pd_on(2)) { /* Enable MOD channel */
|
||||
DBG_PRINTF("ERR!!! MOD_on\r\n");
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
} else {
|
||||
DBG_PRINTF("ERR!!! lockin Test Failed!\r\n");
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set PD gain matching value for specified LED
|
||||
* - Charles KWON
|
||||
*
|
||||
* Configures the PD amplifier gain based on the LED index.
|
||||
* Automatically selects the appropriate PD channel:
|
||||
* - LED 0-23 -> PD0
|
||||
* - LED 24-47 -> PD1
|
||||
* - LED 99 -> Default PD (led_off_pd)
|
||||
*
|
||||
* @param[in] led_index LED index for gain lookup
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on failure
|
||||
*/
|
||||
ret_code_t led_pd_matching_value_set(uint8_t led_index)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
|
||||
if (led_index <= 100) {
|
||||
/* Set DAC gain from lookup table */
|
||||
if (NRF_SUCCESS != pd_gain_set(led_index)) {
|
||||
DBG_PRINTF("ERR!!! pd_gain_set\r\n");
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
/* Determine PD channel based on LED index */
|
||||
uint8_t pd_index;
|
||||
if (led_index < 24) {
|
||||
pd_index = 0; /* PD0 for LED 0-23 */
|
||||
} else if ((led_index < 48) && (led_index > 23)) {
|
||||
pd_index = 1; /* PD1 for LED 24-47 */
|
||||
} else if (led_index == 99) {
|
||||
pd_index = led_off_pd; /* Default PD when off */
|
||||
}
|
||||
|
||||
/* Activate the selected PD channel */
|
||||
if (NRF_SUCCESS != pd_on(pd_index)) {
|
||||
DBG_PRINTF("ERR!!! pd_on\r\n");
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
} else {
|
||||
DBG_PRINTF("ERR!!! led_indexFailed! %d\r\n", led_index);
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* Photodetector Control Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Turn on specified photodetector
|
||||
* - Charles KWON
|
||||
*
|
||||
* Activates one of the 4 PD channels via FSA5157P6X analog switch.
|
||||
*
|
||||
* PD Channels:
|
||||
* - 0: PD0 (for LED 0-23)
|
||||
* - 1: PD1 (for LED 24-47)
|
||||
* - 2: MOD (lock-in modulation)
|
||||
* - 3: MOD2 (secondary modulation)
|
||||
*
|
||||
* @param[in] pd_index Photodetector index (0-3)
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on failure
|
||||
*/
|
||||
ret_code_t pd_on(uint8_t pd_index)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
|
||||
#if FEATURE_FOR_SCOPE
|
||||
/* Debug pulse for oscilloscope timing */
|
||||
nrf_gpio_pin_set(PD_CLK_26);
|
||||
nrf_gpio_pin_clear(PD_CLK_26);
|
||||
#endif
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
DBG_PRINTF("pd_on, %d ===== \r\n", pd_index);
|
||||
#endif
|
||||
|
||||
if (pd_index <= 3) {
|
||||
activated_pd = pd_index;
|
||||
if (NRF_SUCCESS != pd_select(pd_index)) {
|
||||
DBG_PRINTF("ERR!!! pd_on Failed! %d\r\n", pd_index);
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
} else {
|
||||
DBG_PRINTF("ERR!!! pd_index Failed! %d\r\n", pd_index);
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Turn off photodetector
|
||||
* - Charles KWON
|
||||
*
|
||||
* Deactivates all PD channels. Only accepts index 99 for safety.
|
||||
*
|
||||
* @param[in] pd_index Must be 99 for all-off operation
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on invalid index
|
||||
*/
|
||||
ret_code_t pd_off(uint8_t pd_index)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
DBG_PRINTF("pd_off\r\n");
|
||||
#endif
|
||||
|
||||
if (pd_index == 99) {
|
||||
PD_ALLOFF();
|
||||
activated_pd = 99;
|
||||
} else {
|
||||
DBG_PRINTF("ERR!!! PD Off Failed! %d\r\n", pd_index);
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* LED Power EEPROM Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Read all 24 LED power values from ROM
|
||||
* - Charles KWON
|
||||
*
|
||||
* Reads LED power calibration values from DS3930 EEPROM.
|
||||
* Each read has 1ms delay for EEPROM access timing.
|
||||
*
|
||||
* @param[out] data Output buffer for 24 uint16_t values
|
||||
*/
|
||||
void led_power_read_48(uint16_t *data)
|
||||
{
|
||||
for (uint8_t i = 0; i < 24; i++) {
|
||||
data[i] = LED_READ_ROM(i);
|
||||
nrf_delay_ms(1); /* EEPROM access delay */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read single LED power value from ROM
|
||||
* - Charles KWON
|
||||
*
|
||||
* @param[in] led_index LED index (0-47)
|
||||
* @return LED power value, or NRF_ERROR_INTERNAL on invalid index
|
||||
*/
|
||||
int16_t led_power_read(uint8_t led_index)
|
||||
{
|
||||
int16_t led_power;
|
||||
|
||||
if (led_index <= 47) {
|
||||
led_power = LED_READ_ROM(led_index);
|
||||
} else {
|
||||
DBG_PRINTF("ERR!!! led_index Failed! %d\r\n", led_index);
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
return led_power;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Save LED power value to ROM
|
||||
* - Charles KWON
|
||||
*
|
||||
* Writes LED power calibration value to DS3930 EEPROM.
|
||||
*
|
||||
* @param[in] led_index LED index (0-47)
|
||||
* @param[in] led_power Power value (0-255)
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on failure
|
||||
*/
|
||||
ret_code_t led_power_save_mem(uint8_t led_index, int16_t led_power)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
|
||||
if ((led_index <= 47) || (led_power <= 255)) {
|
||||
if (NRF_SUCCESS != LED_WRITE_ROM(led_index, led_power)) {
|
||||
DBG_PRINTF("ERR!!! DS3930 1\r\n");
|
||||
err_code = NRF_ERROR_INTERNAL;
|
||||
}
|
||||
} else {
|
||||
DBG_PRINTF("ERR!!! led_index || pd_index Failed! %d, %d\r\n", led_index, led_power);
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Save same LED power value to all 48 LEDs
|
||||
* - Charles KWON
|
||||
*
|
||||
* Writes identical power value to all LED EEPROM locations.
|
||||
* Useful for factory reset or uniform calibration.
|
||||
*
|
||||
* @param[in] led_index Unused parameter
|
||||
* @param[in] led_power Power value to set for all LEDs
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on any write failure
|
||||
*
|
||||
* @note 10ms delay between writes for EEPROM programming time
|
||||
*/
|
||||
ret_code_t led_power_save_mem_6(uint8_t led_index, int16_t led_power)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
|
||||
for (uint8_t i = 0; i < 48; i++) {
|
||||
if (NRF_SUCCESS != LED_WRITE_ROM(i, led_power)) {
|
||||
DBG_PRINTF("ERR!!! DS3930 1\r\n");
|
||||
err_code = NRF_ERROR_INTERNAL;
|
||||
}
|
||||
nrf_delay_ms(10); /* EEPROM programming delay */
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Save array of LED power values to all 48 LEDs
|
||||
* - Charles KWON
|
||||
*
|
||||
* Writes individual power values for each LED from input array.
|
||||
*
|
||||
* @param[in] led_power Array of 48 power values (uint8_t)
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on any write failure
|
||||
*
|
||||
* @note 10ms delay between writes for EEPROM programming time
|
||||
*/
|
||||
ret_code_t led_power_save_mem_48(uint8_t *led_power)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
|
||||
for (uint8_t i = 0; i < 48; i++) {
|
||||
if (NRF_SUCCESS != LED_WRITE_ROM(i, (int16_t)(led_power[i]))) {
|
||||
DBG_PRINTF("ERR!!! DS3930 1\r\n");
|
||||
err_code = NRF_ERROR_INTERNAL;
|
||||
}
|
||||
nrf_delay_ms(10); /* EEPROM programming delay */
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set LED power (placeholder function)
|
||||
* - Charles KWON
|
||||
*
|
||||
* Currently not implemented. Reserved for future LED current control.
|
||||
*
|
||||
* @param[in] led_index LED index
|
||||
* @return NRF_SUCCESS always
|
||||
*/
|
||||
ret_code_t led_power_set(uint8_t led_index)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* DAC Gain Control Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Set PD gain via MCP4725 DAC based on LED-PD matching table
|
||||
* - Charles KWON
|
||||
*
|
||||
* Looks up the calibrated DAC value for the specified LED from
|
||||
* led_pd_dac_v[] array and programs the MCP4725 DAC.
|
||||
*
|
||||
* @param[in] activated_led LED index for gain lookup (0-47, 99=off)
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on invalid index
|
||||
*
|
||||
* @note Uses SW I2C for MCP4725 communication
|
||||
* @note LED 99 uses led_off_dac_v as default gain
|
||||
*/
|
||||
ret_code_t pd_gain_set(uint8_t activated_led)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
DBG_PRINTF("<<pd_gain_set L%d, P%d>>\r\n", activated_led, pd_index);
|
||||
#endif
|
||||
|
||||
if (activated_led <= 47) {
|
||||
sw_i2c_init_once(); /* Ensure I2C is ready */
|
||||
mcp4725_writeFastMode(led_pd_dac_v[activated_led]);
|
||||
} else if (activated_led == 99) {
|
||||
mcp4725_writeFastMode(led_off_dac_v); /* Use default off-state gain */
|
||||
} else {
|
||||
DBG_PRINTF("ERR!!! led_index Failed! %d\r\n", activated_led);
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set immediate DAC gain value
|
||||
* - Charles KWON
|
||||
*
|
||||
* Directly programs the MCP4725 DAC with specified value.
|
||||
* Used for manual gain adjustment and testing.
|
||||
*
|
||||
* @param[in] imm_dac DAC value (0-2000)
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_INTERNAL on out-of-range
|
||||
*/
|
||||
ret_code_t imm_gain_set(uint16_t imm_dac)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
|
||||
if (imm_dac <= 2000) {
|
||||
sw_i2c_init_once();
|
||||
mcp4725_writeFastMode(imm_dac);
|
||||
DBG_PRINTF("dac_v %d\r\n", imm_dac);
|
||||
} else {
|
||||
DBG_PRINTF("ERR!!! range Failed! %d\r\n", activated_led);
|
||||
return NRF_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* Hardware Selection Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Select and activate specific LED via GPIO multiplexer
|
||||
* - Charles KWON
|
||||
*
|
||||
* Programs the CAT9532 LED driver to activate the specified LED.
|
||||
* LED selection is done via I2C commands to set the appropriate
|
||||
* output pins on the LED driver IC.
|
||||
*
|
||||
* @param[in] led_index LED index (0-47 for specific LED, 99=clear/off)
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_NOT_FOUND on invalid index
|
||||
*
|
||||
* @note 1ms delay after selection for GPIO stabilization
|
||||
* @note LED macros (LED0, LED1, etc.) are defined in LED_Parse.h
|
||||
*/
|
||||
ret_code_t led_select(uint8_t led_index)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
|
||||
switch (led_index) {
|
||||
case 0: LED0(); break;
|
||||
case 1: LED1(); break;
|
||||
case 2: LED2(); break;
|
||||
case 3: LED3(); break;
|
||||
case 4: LED4(); break;
|
||||
case 5: LED5(); break;
|
||||
case 6: LED6(); break;
|
||||
case 7: LED7(); break;
|
||||
case 8: LED8(); break;
|
||||
case 9: LED9(); break;
|
||||
case 10: LED10(); break;
|
||||
case 11: LED11(); break;
|
||||
case 12: LED12(); break;
|
||||
case 13: LED13(); break;
|
||||
case 14: LED14(); break;
|
||||
case 15: LED15(); break;
|
||||
case 16: LED16(); break;
|
||||
case 17: LED17(); break;
|
||||
case 18: LED18(); break;
|
||||
case 19: LED19(); break;
|
||||
case 20: LED20(); break;
|
||||
case 21: LED21(); break;
|
||||
case 22: LED22(); break;
|
||||
case 23: LED23(); break;
|
||||
case 24: LED24(); break;
|
||||
case 25: LED25(); break;
|
||||
case 26: LED26(); break;
|
||||
case 27: LED27(); break;
|
||||
case 28: LED28(); break;
|
||||
case 29: LED29(); break;
|
||||
case 30: LED30(); break;
|
||||
case 31: LED31(); break;
|
||||
case 32: LED32(); break;
|
||||
case 33: LED33(); break;
|
||||
case 34: LED34(); break;
|
||||
case 35: LED35(); break;
|
||||
case 36: LED36(); break;
|
||||
case 37: LED37(); break;
|
||||
case 38: LED38(); break;
|
||||
case 39: LED39(); break;
|
||||
case 40: LED40(); break;
|
||||
case 41: LED41(); break;
|
||||
case 42: LED42(); break;
|
||||
case 43: LED43(); break;
|
||||
case 44: LED44(); break;
|
||||
case 45: LED45(); break;
|
||||
case 46: LED46(); break;
|
||||
case 47: LED47(); break;
|
||||
case 99: LED99(); break; /* All LEDs off / clear state */
|
||||
default:
|
||||
err_code = NRF_ERROR_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
|
||||
/* GPIO stabilization delay */
|
||||
nrf_delay_ms(1);
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Select photodetector channel
|
||||
* - Charles KWON
|
||||
*
|
||||
* Programs the FSA5157P6X analog switch to route the selected
|
||||
* PD output to the ADC input.
|
||||
*
|
||||
* PD Channel Mapping:
|
||||
* - 0: PD0 - Primary photodetector (LED 0-23)
|
||||
* - 1: PD1 - Secondary photodetector (LED 24-47)
|
||||
* - 2: MOD - Lock-in amplifier modulation channel
|
||||
* - 3: MOD2 - Secondary modulation channel
|
||||
*
|
||||
* @param[in] pd_index Photodetector index (0-3)
|
||||
* @return NRF_SUCCESS on success, NRF_ERROR_NOT_FOUND on invalid index
|
||||
*
|
||||
* @note All PD channels are turned off before selecting new one
|
||||
*/
|
||||
ret_code_t pd_select(uint8_t pd_index)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
|
||||
PD_ALLOFF(); /* Ensure clean state before switching */
|
||||
|
||||
switch (pd_index) {
|
||||
case 0: PD0(); break; /* Photodetector 0 */
|
||||
case 1: PD1(); break; /* Photodetector 1 */
|
||||
case 2: MOD(); break; /* Modulation channel */
|
||||
case 3: MOD2(); break; /* Secondary modulation */
|
||||
default:
|
||||
err_code = NRF_ERROR_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* Timer Control Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Start measurement send timer
|
||||
* - Charles KWON
|
||||
*
|
||||
* Starts the single-shot timer for sequenced BLE data transmission.
|
||||
*/
|
||||
void mea_send_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_mea_send_loop_timer_id,
|
||||
APP_TIMER_TICKS(MEA_SEND_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Stop measurement send timer
|
||||
* - Charles KWON
|
||||
*/
|
||||
void mea_send_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_mea_send_loop_timer_id));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize measurement send timer
|
||||
* - Charles KWON
|
||||
*
|
||||
* Creates the single-shot timer used for sequenced BLE transmission.
|
||||
* Must be called during system initialization.
|
||||
*/
|
||||
void mea_send_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_mea_send_loop_timer_id,
|
||||
APP_TIMER_MODE_SINGLE_SHOT, mea_send_loop));
|
||||
}
|
||||
Reference in New Issue
Block a user