initial commit
This commit is contained in:
158
project/ble_peripheral/ble_app_vivaMayo/LED_Parse.c
Normal file
158
project/ble_peripheral/ble_app_vivaMayo/LED_Parse.c
Normal file
@@ -0,0 +1,158 @@
|
||||
#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 "nrf_log.h"
|
||||
|
||||
#include "LED_Parse.h"
|
||||
#include "app_timer.h"
|
||||
#include "measurements.h"
|
||||
#include "main.h"
|
||||
//#include "fstorage.h"
|
||||
#include "mcp4725_i2c.h" //VGA
|
||||
#include "ir_i2c.h" //IR
|
||||
#include "debug_print.h"
|
||||
//#define LED_S1 0x50 //VGA LED1~5
|
||||
//#define LED_S2 0x50
|
||||
//#define LED_S3 0x51
|
||||
//#define LED_S4 0x52
|
||||
//#define LED_S5 0x53
|
||||
//#define LED_S6 0x51 //VGA
|
||||
//#define LED_S7 0x54
|
||||
//#define LED_S8 0x55
|
||||
//#define LED_S9 0x56
|
||||
//#define LED_S10 0x57 //LED 46~50
|
||||
|
||||
#define LED_S0 0x50
|
||||
#define LED_S1 0x51
|
||||
#define LED_S2 0x52
|
||||
#define LED_S3 0x53
|
||||
#define LED_S4 0x54
|
||||
#define LED_S5 0x55
|
||||
#define LED_S6 0x56
|
||||
#define LED_S7 0x57
|
||||
|
||||
|
||||
uint8_t LED_READ_ROM(uint8_t led_index)
|
||||
{
|
||||
// uint32_t err_code = NRF_SUCCESS;
|
||||
uint8_t addr=0xf0;
|
||||
uint8_t r_index;
|
||||
r_index = (led_index)%6;
|
||||
uint8_t r_data[10];
|
||||
uint8_t r_value=0;
|
||||
|
||||
|
||||
switch(led_index) {
|
||||
|
||||
|
||||
|
||||
case 0: case 1: case 2: case 3: case 4: case 5:
|
||||
r_value = ir_command_read(LED_S0, addr + r_index, r_data);
|
||||
break;
|
||||
|
||||
case 6: case 7: case 8: case 9: case 10: case 11:
|
||||
r_value = ir_command_read(LED_S1, addr + r_index, r_data);
|
||||
break;
|
||||
|
||||
case 12: case 13: case 14: case 15: case 16: case 17:
|
||||
r_value = ir_command_read(LED_S2, addr + r_index, r_data);
|
||||
break;
|
||||
|
||||
case 18: case 19: case 20: case 21: case 22: case 23:
|
||||
r_value = ir_command_read(LED_S3, addr + r_index, r_data);
|
||||
break;
|
||||
|
||||
case 24: case 25: case 26: case 27: case 28: case 29:
|
||||
r_value = ir_command_read(LED_S4, addr + r_index, r_data);
|
||||
break;
|
||||
|
||||
case 30: case 31: case 32: case 33: case 34: case 35:
|
||||
r_value = ir_command_read(LED_S5, addr + r_index, r_data);
|
||||
break;
|
||||
|
||||
case 36: case 37: case 38: case 39: case 40: case 41:
|
||||
r_value = ir_command_read(LED_S6, addr + r_index, r_data);
|
||||
break;
|
||||
|
||||
case 42: case 43: case 44: case 45: case 46: case 47:
|
||||
r_value = ir_command_read(LED_S7, addr + r_index, r_data);
|
||||
break;
|
||||
default:
|
||||
DBG_PRINTF("Invalid LED index\r\n");
|
||||
// err_code = NRF_ERROR_NOT_FOUND;
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
return r_value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ret_code_t LED_WRITE_ROM(uint8_t led_index, int16_t led_power)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
uint8_t addr=0xf0;
|
||||
uint8_t r_index;
|
||||
r_index = (led_index)%6;
|
||||
|
||||
|
||||
|
||||
switch(led_index) {
|
||||
|
||||
|
||||
|
||||
case 0: case 1: case 2: case 3: case 4: case 5:
|
||||
ir_command_write(LED_S0, addr + r_index, led_power);
|
||||
break;
|
||||
|
||||
case 6: case 7: case 8: case 9: case 10: case 11:
|
||||
ir_command_write(LED_S1, addr + r_index, led_power);
|
||||
break;
|
||||
|
||||
case 12: case 13: case 14: case 15: case 16: case 17:
|
||||
ir_command_write(LED_S2, addr + r_index, led_power);
|
||||
break;
|
||||
|
||||
case 18: case 19: case 20: case 21: case 22: case 23:
|
||||
ir_command_write(LED_S3, addr + r_index, led_power);
|
||||
break;
|
||||
|
||||
case 24: case 25: case 26: case 27: case 28: case 29:
|
||||
ir_command_write(LED_S4, addr + r_index, led_power);
|
||||
break;
|
||||
|
||||
case 30: case 31: case 32: case 33: case 34: case 35:
|
||||
ir_command_write(LED_S5, addr + r_index, led_power);
|
||||
break;
|
||||
|
||||
case 36: case 37: case 38: case 39: case 40: case 41:
|
||||
ir_command_write(LED_S6, addr + r_index, led_power);
|
||||
break;
|
||||
|
||||
case 42: case 43: case 44: case 45: case 46: case 47:
|
||||
ir_command_write(LED_S7, addr + r_index, led_power);
|
||||
break;
|
||||
default:
|
||||
DBG_PRINTF("Invalid LED index\r\n");
|
||||
err_code = NRF_ERROR_NOT_FOUND;
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
DBG_PRINTF("write\r\n");
|
||||
return err_code;
|
||||
}
|
||||
|
||||
26
project/ble_peripheral/ble_app_vivaMayo/LED_Parse.h
Normal file
26
project/ble_peripheral/ble_app_vivaMayo/LED_Parse.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef _LED_Parse_H_
|
||||
#define _LED_Parse_H_
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "app_timer.h"
|
||||
#include "measurements.h"
|
||||
#include "main.h"
|
||||
//#include "fstorage.h"
|
||||
#include "mcp4725_i2c.h" //VGA
|
||||
#include "ir_i2c.h" //IR
|
||||
|
||||
|
||||
uint8_t LED_READ_ROM(uint8_t led_index);
|
||||
|
||||
ret_code_t LED_WRITE_ROM(uint8_t led_index, int16_t led_power);
|
||||
|
||||
#endif /* PARSE */
|
||||
|
||||
16
project/ble_peripheral/ble_app_vivaMayo/ad5272_i2c.h
Normal file
16
project/ble_peripheral/ble_app_vivaMayo/ad5272_i2c.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*******************************************************************************
|
||||
* @file ad5272_i2c.h
|
||||
* @brief AD5272 Digital Potentiometer Stub Header
|
||||
* @note Stub for MINIMAL_BOOT mode - functions not implemented
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef AD5272_I2C_H
|
||||
#define AD5272_I2C_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Stub functions - do nothing in MINIMAL mode */
|
||||
static inline void ad5272_i2c_init(void) { }
|
||||
static inline void ad5272_normal_mode(void) { }
|
||||
|
||||
#endif /* AD5272_I2C_H */
|
||||
113
project/ble_peripheral/ble_app_vivaMayo/ada2200_spi.c
Normal file
113
project/ble_peripheral/ble_app_vivaMayo/ada2200_spi.c
Normal file
@@ -0,0 +1,113 @@
|
||||
/*******************************************************************************
|
||||
* @file ada2200_spi.c
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.1
|
||||
* @date 2022-09-05
|
||||
* @brief ADA2200 Lock-in Amplifier SPI driver (uses shared SPI2 bus)
|
||||
******************************************************************************/
|
||||
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nrf.h"
|
||||
#include "nrf_gpio.h"
|
||||
#include "app_error.h"
|
||||
#include "boards.h"
|
||||
#include "nrf_delay.h"
|
||||
|
||||
#include "nrf_log.h"
|
||||
#include "ada2200_spi.h"
|
||||
#include "spi2_bus.h" /* Shared SPI2 bus */
|
||||
#include "debug_print.h"
|
||||
|
||||
/* Use CS pin from shared bus definition */
|
||||
#define ADA2200_CS_PIN SPI2_CS_ADA2200
|
||||
|
||||
//static uint8_t ada2200_startR[] ={ 0x00, 0x00, 0x81 }; /* {addr 16bit, data}, Reset for Defaults */
|
||||
|
||||
static uint8_t ada2200_start0[] ={ 0x00, 0x00, 0x18 }; /* {addr 16bit, data}, Set SDIO input only, Activate SDO */
|
||||
static uint8_t ada2200_start1[] ={ 0x00, 0x2B, 0x06 }; /* {addr 16bit, data}, Clock Configuration */
|
||||
static uint8_t ada2200_start2[] ={ 0x00, 0x2A, 0x18 }; /* {addr 16bit, data}, Enable Mixer, Select SDO output for Pin 13, OFF RCLK. */
|
||||
static uint8_t ada2200_start3[] ={ 0x00, 0x29, 0x23 }; /* 0x27 {addr 16bit, data}, Disable SYNCO output, Select SYNCO edge location (Sync timing adjustment) */
|
||||
static uint8_t ada2200_start4[] ={ 0x00, 0x2C, 0x01 }; /* {addr 16bit, data}, Enable RCLK output */
|
||||
|
||||
static uint8_t ada2200_stop0[] ={ 0x00, 0x00, 0x18 }; /* {addr 16bit, data}, Set SDIO input only, Activate SDO */
|
||||
static uint8_t ada2200_stop1[] ={ 0x00, 0x2B, 0x06 }; /* {addr 16bit, data}, Clock Configuration */
|
||||
static uint8_t ada2200_stop2[] ={ 0x00, 0x2A, 0x10 }; /* {addr 16bit, data}, Enable Mixer, Select SDO output for Pin 13, OFF RCLK. */
|
||||
static uint8_t ada2200_stop3[] ={ 0x00, 0x29, 0x01 }; /* 0x07 {addr 16bit, data}, Disable SYNCO output, Select SYNCO edge location (Sync timing adjustment) */
|
||||
static uint8_t ada2200_stop4[] ={ 0x00, 0x2C, 0x00 }; /* {addr 16bit, data}, Enable RCLK output */
|
||||
|
||||
static uint8_t m_tx_buf[3]; /**< TX buffer. */
|
||||
static uint8_t m_length = sizeof(m_tx_buf); /**< Transfer length. */
|
||||
|
||||
|
||||
void ada2200_spi_write(const void * data, size_t size)
|
||||
{
|
||||
/* Ensure bus is initialized */
|
||||
if (!spi2_bus_is_initialized()) {
|
||||
DBG_PRINTF("[ADA] auto-init SPI2\r\n");
|
||||
spi2_bus_init();
|
||||
}
|
||||
|
||||
memcpy(m_tx_buf, data, size);
|
||||
/* Use shared SPI2 bus with manual CS control */
|
||||
nrf_gpio_pin_clear(ADA2200_CS_PIN);
|
||||
spi2_bus_transfer(m_tx_buf, size, NULL, 0);
|
||||
nrf_gpio_pin_set(ADA2200_CS_PIN);
|
||||
}
|
||||
|
||||
|
||||
extern void ada2200_start(void)
|
||||
{
|
||||
//ada2200_spi_write(ada2200_startR, m_length);
|
||||
DBG_PRINTF("[ADA] start...\r\n");
|
||||
|
||||
ada2200_spi_write(ada2200_start0, m_length);
|
||||
DBG_PRINTF("[ADA] cmd0 OK\r\n");
|
||||
nrf_delay_us(2);
|
||||
ada2200_spi_write(ada2200_start1, m_length);
|
||||
nrf_delay_us(2);
|
||||
ada2200_spi_write(ada2200_start2, m_length);
|
||||
nrf_delay_us(2);
|
||||
ada2200_spi_write(ada2200_start3, m_length);
|
||||
nrf_delay_us(2);
|
||||
ada2200_spi_write(ada2200_start4, m_length);
|
||||
nrf_delay_us(2);
|
||||
}
|
||||
|
||||
|
||||
extern void ada2200_stop(void)
|
||||
{
|
||||
ada2200_spi_write(ada2200_stop0, m_length);
|
||||
nrf_delay_us(2);
|
||||
ada2200_spi_write(ada2200_stop1, m_length);
|
||||
nrf_delay_us(2);
|
||||
ada2200_spi_write(ada2200_stop2, m_length);
|
||||
nrf_delay_us(2);
|
||||
ada2200_spi_write(ada2200_stop3, m_length);
|
||||
nrf_delay_us(2);
|
||||
ada2200_spi_write(ada2200_stop4, m_length);
|
||||
nrf_delay_us(2);
|
||||
}
|
||||
|
||||
|
||||
extern void ada2200_init(void)
|
||||
{
|
||||
DBG_PRINTF("[ADA] init...\r\n");
|
||||
/* Initialize shared SPI2 bus (handles already-initialized case) */
|
||||
ret_code_t err = spi2_bus_init();
|
||||
if (err != NRF_SUCCESS) {
|
||||
APP_ERROR_CHECK(err);
|
||||
}
|
||||
DBG_PRINTF("[ADA] init OK\r\n");
|
||||
/* CS pin is configured by spi2_bus_init() */
|
||||
}
|
||||
|
||||
|
||||
extern void ada2200_uninit(void)
|
||||
{
|
||||
/* Don't uninit the shared bus - other devices may be using it */
|
||||
/* Just ensure CS is deasserted */
|
||||
nrf_gpio_pin_set(ADA2200_CS_PIN);
|
||||
}
|
||||
|
||||
25
project/ble_peripheral/ble_app_vivaMayo/ada2200_spi.h
Normal file
25
project/ble_peripheral/ble_app_vivaMayo/ada2200_spi.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*******************************************************************************
|
||||
* @file ada2200_spi.h
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _ADA2200_SPI_H_
|
||||
#define _ADA2200_SPI_H_
|
||||
|
||||
#define SPI_INSTANCE 2 /**< SPI instance index. */
|
||||
|
||||
#define SPI_MOSI_PIN 16
|
||||
#define SPI_MISO_PIN 15 /* Not Used */
|
||||
#define SPI_SCLK_PIN 14
|
||||
#define SPI_CS_PIN 13
|
||||
|
||||
extern void ada2200_start(void);
|
||||
extern void ada2200_stop(void);
|
||||
extern void ada2200_init(void);
|
||||
extern void ada2200_uninit(void);
|
||||
|
||||
#endif //_ADA2200_SPI_H_
|
||||
|
||||
317
project/ble_peripheral/ble_app_vivaMayo/battery_saadc.c
Normal file
317
project/ble_peripheral/ble_app_vivaMayo/battery_saadc.c
Normal file
@@ -0,0 +1,317 @@
|
||||
/*******************************************************************************
|
||||
* @file battery_saadc.c
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "nrf.h"
|
||||
#include "boards.h"
|
||||
#include "app_error.h"
|
||||
#include "nrf_drv_saadc.h"
|
||||
#include "nrf_drv_timer.h"
|
||||
#include "ble_nus.h"
|
||||
#include "nrf_log.h"
|
||||
#include "main.h"
|
||||
#include "app_timer.h"
|
||||
//#include "fstorage.h"
|
||||
#include "battery_saadc.h"
|
||||
#include "main_timer.h"
|
||||
#include "meas_pd_48.h"
|
||||
#include <cmd_parse.h>
|
||||
#include "debug_print.h"
|
||||
#define BATTERY_REF_VOLTAGE_IN_MILLIVOLTS 600 /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
|
||||
#define BATTERY_PRE_SCALING_COMPENSATION 6 /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
|
||||
#define BATTERY_ADC_RES_10BITS 1023 /**< Maximum digital value for 10-bit ADC conversion. */
|
||||
//#define PRESSURE_RESULT_IN_MILLI_VOLTS(adc) ((adc * 3600) / 1023)
|
||||
|
||||
#define PRESSURE_OFFSET_DEFAULT 0 // Pressure offset (adjust for calibration)
|
||||
#define MV_PER_ADC_STEP 805 // Approx 0.805mV per 1 LSB (nRF 12bit + scaling)
|
||||
/**@brief Macro to convert the result of ADC conversion in millivolts.
|
||||
*
|
||||
* @param[in] ADC_VALUE ADC result.
|
||||
*
|
||||
* @retval Result converted to millivolts.
|
||||
*/
|
||||
#define BATTERY_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
|
||||
((((ADC_VALUE) * BATTERY_REF_VOLTAGE_IN_MILLIVOLTS) / BATTERY_ADC_RES_10BITS) * BATTERY_PRE_SCALING_COMPENSATION)
|
||||
|
||||
static nrf_saadc_value_t adc_bufs[2];
|
||||
static int16_t pressure_adc_buf[2]; //cj add 25/11/19
|
||||
static uint16_t convert_adc_to_mV(int16_t raw_adc); //cj add 25/11/19
|
||||
|
||||
APP_TIMER_DEF(m_battery_loop_timer_id);
|
||||
#define BATTERY_LOOP_INTERVAL 5000
|
||||
|
||||
bool low_battery_check = false;
|
||||
extern bool info4; //cmd_parse
|
||||
|
||||
// cj add edit 25/11/24
|
||||
extern volatile uint16_t info_p1;
|
||||
extern volatile uint16_t info_p2;
|
||||
|
||||
|
||||
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
extern bool go_device_power_off;
|
||||
extern volatile bool processing;
|
||||
|
||||
extern which_cmd_t cmd_type_t;
|
||||
|
||||
extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] ;
|
||||
extern volatile uint16_t info_batt; //48_c
|
||||
extern bool go_temp; //
|
||||
extern bool go_batt; //cmd_parse
|
||||
|
||||
extern bool motion_raw_data_enabled ;
|
||||
extern bool ble_got_new_data;
|
||||
extern bool motion_data_once ;
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
static uint16_t convert_adc_to_mV(int16_t raw_adc)
|
||||
{
|
||||
if (raw_adc < 0)
|
||||
raw_adc = 0;
|
||||
|
||||
int32_t mv = (int32_t)raw_adc * MV_PER_ADC_STEP; // ?: 805 uV
|
||||
mv /= 1000;
|
||||
|
||||
/* Clamp output to valid range 0~3500mV */
|
||||
if (mv < 0)
|
||||
mv = 0;
|
||||
|
||||
if (mv > 3500)
|
||||
mv = 3500;
|
||||
|
||||
|
||||
return (uint16_t)mv;
|
||||
}
|
||||
void pressure_all_event_handler(nrf_drv_saadc_evt_t const * p_event)
|
||||
{
|
||||
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
|
||||
{
|
||||
int16_t p1_adc = p_event->data.done.p_buffer[0]; // AIN7
|
||||
int16_t p2_adc = p_event->data.done.p_buffer[1]; // AIN4
|
||||
|
||||
|
||||
uint16_t p1_mV = convert_adc_to_mV(p1_adc);
|
||||
uint16_t p2_mV = convert_adc_to_mV(p2_adc);
|
||||
|
||||
/* Update info_p1/info_p2 when in PD Full mode (info4=true) */
|
||||
if(info4 == true)
|
||||
{
|
||||
info_p1 = p1_mV;
|
||||
info_p2 = p2_mV;
|
||||
}
|
||||
// Re-buffer
|
||||
APP_ERROR_CHECK(nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, 2));
|
||||
|
||||
// uninit
|
||||
nrf_drv_saadc_uninit();
|
||||
nrf_drv_saadc_channel_uninit(0);
|
||||
nrf_drv_saadc_channel_uninit(1);
|
||||
|
||||
// UART send
|
||||
|
||||
if(cmd_type_t == CMD_UART)
|
||||
{
|
||||
DBG_PRINTF("P1:%d P2:%d\r\n", p1_mV, p2_mV);
|
||||
}
|
||||
else if(cmd_type_t == CMD_BLE && info4 == false)
|
||||
{
|
||||
DBG_PRINTF("P1:%d P2:%d\r\n", p1_mV, p2_mV);
|
||||
// uint16_t len = sprintf((char*)ble_bin_buffer,
|
||||
// "rpn:%04x,%04x", p1_mV, p2_mV);
|
||||
uint16_t result_data[2];
|
||||
result_data[0] = p1_mV;
|
||||
result_data[1] = p2_mV;
|
||||
format_data(ble_bin_buffer, "rpn:", result_data,2);
|
||||
binary_tx_handler(ble_bin_buffer,4);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
|
||||
{
|
||||
|
||||
static uint8_t low_battery_cnt = 0;
|
||||
|
||||
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
|
||||
{
|
||||
nrf_saadc_value_t register_val = 0;
|
||||
uint16_t batt_lvl_in_milli_volt_0 = 0;
|
||||
uint16_t batt_lvl_in_milli_volt_1 = 0;
|
||||
uint32_t err_code = 0;
|
||||
|
||||
register_val = p_event->data.done.p_buffer[0];
|
||||
|
||||
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, 1);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
nrf_drv_saadc_uninit();
|
||||
nrf_drv_saadc_channel_uninit(0);
|
||||
|
||||
batt_lvl_in_milli_volt_0 = BATTERY_RESULT_IN_MILLI_VOLTS(register_val);
|
||||
batt_lvl_in_milli_volt_1 = (batt_lvl_in_milli_volt_0) *1.42;
|
||||
if(low_battery_check == true) {
|
||||
|
||||
low_battery_check = false;
|
||||
|
||||
if(batt_lvl_in_milli_volt_1 <= LOW_BATTERY_VOLTAGE) {
|
||||
if(low_battery_cnt >= 10) {
|
||||
low_battery_cnt = 0;
|
||||
/*go to power off and fds save */
|
||||
DBG_PRINTF("Save FDS parameters and then Power OFF\r\n");
|
||||
go_device_power_off = true;
|
||||
main_timer_start();
|
||||
}else{
|
||||
low_battery_cnt++;
|
||||
DBG_PRINTF("WARNING!!! low_battery cnt = %d, Batt = %d(mV)\r\n", low_battery_cnt, batt_lvl_in_milli_volt_1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (info4 == true){
|
||||
|
||||
info_batt = batt_lvl_in_milli_volt_1;
|
||||
DBG_PRINTF("INFOTn%d\r\n\r\n", batt_lvl_in_milli_volt_1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
else {
|
||||
if(cmd_type_t == CMD_UART) {
|
||||
DBG_PRINTF("Tn%d\r\n\r\n", batt_lvl_in_milli_volt_1);
|
||||
} else if(cmd_type_t == CMD_BLE) {
|
||||
|
||||
single_format_data(ble_bin_buffer, "rsn:", batt_lvl_in_milli_volt_1);
|
||||
|
||||
binary_tx_handler(ble_bin_buffer,3);
|
||||
//data_tx_handler(ble_tx_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info4 == true){
|
||||
go_batt =false;
|
||||
go_temp = true;
|
||||
main_timer_start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for configuring ADC to do battery level conversion.
|
||||
*/
|
||||
static void battery_configure(void)
|
||||
{
|
||||
ret_code_t err_code = nrf_drv_saadc_init(NULL, battery_event_handler);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
nrf_saadc_channel_config_t config =
|
||||
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
|
||||
err_code = nrf_drv_saadc_channel_init(0, &config);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = nrf_drv_saadc_buffer_convert(&adc_bufs[0], 1);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = nrf_drv_saadc_buffer_convert(&adc_bufs[1], 1);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
void pressure_all_configure(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
err_code = nrf_drv_saadc_init(NULL, pressure_all_event_handler);
|
||||
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE) {
|
||||
DBG_PRINTF("SAADC init err=%d\r\n", err_code);
|
||||
return;
|
||||
}
|
||||
|
||||
nrf_saadc_channel_config_t ch0_cfg =
|
||||
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7);
|
||||
nrf_saadc_channel_config_t ch1_cfg =
|
||||
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN4);
|
||||
|
||||
err_code = nrf_drv_saadc_channel_init(0, &ch0_cfg);
|
||||
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE) {
|
||||
DBG_PRINTF("SAADC ch0 init err=%d\r\n", err_code);
|
||||
return;
|
||||
}
|
||||
|
||||
err_code = nrf_drv_saadc_channel_init(1, &ch1_cfg);
|
||||
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE) {
|
||||
DBG_PRINTF("SAADC ch1 init err=%d\r\n", err_code);
|
||||
return;
|
||||
}
|
||||
|
||||
err_code = nrf_drv_saadc_buffer_convert(pressure_adc_buf, 2);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
DBG_PRINTF("SAADC buf conv err=%d\r\n", err_code);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void battery_level_meas(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
battery_configure();
|
||||
err_code = nrf_drv_saadc_sample();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
|
||||
}
|
||||
void pressure_all_level_meas(void) //add cj add 25/11/19
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
pressure_all_configure(); /* Configure 2 ADC channels */
|
||||
|
||||
err_code = nrf_drv_saadc_sample();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
void battery_loop(void * p_context) /* For 1sec */
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
if(processing==true)
|
||||
{
|
||||
processing = false ; // add 20241218
|
||||
//low_battery_check = true;
|
||||
return;}
|
||||
else{
|
||||
low_battery_check = true;
|
||||
battery_level_meas();
|
||||
}
|
||||
}
|
||||
|
||||
void battery_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_battery_loop_timer_id, APP_TIMER_TICKS(BATTERY_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
void battery_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_battery_loop_timer_id));
|
||||
}
|
||||
|
||||
void battery_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_battery_loop_timer_id, APP_TIMER_MODE_REPEATED, battery_loop));
|
||||
}
|
||||
|
||||
|
||||
22
project/ble_peripheral/ble_app_vivaMayo/battery_saadc.h
Normal file
22
project/ble_peripheral/ble_app_vivaMayo/battery_saadc.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*******************************************************************************
|
||||
* @file battery_saadc.h
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _BATTERY_SAADC_H_
|
||||
#define _BATTERY_SAADC_H_
|
||||
|
||||
#define LOW_BATTERY_VOLTAGE 3100 /* Low Battery 임계값 */
|
||||
|
||||
|
||||
void battery_level_meas(void);
|
||||
void pressure_all_level_meas(void);
|
||||
void battery_timer_start(void);
|
||||
void battery_timer_stop(void);
|
||||
void battery_timer_init(void);
|
||||
|
||||
#endif //_BATTERY_SAADC_H_
|
||||
|
||||
615
project/ble_peripheral/ble_app_vivaMayo/ble/ble_core.c
Normal file
615
project/ble_peripheral/ble_app_vivaMayo/ble/ble_core.c
Normal file
@@ -0,0 +1,615 @@
|
||||
/*******************************************************************************
|
||||
* @file ble_core.c
|
||||
* @brief BLE Core Functions - Stack, GAP, GATT, Advertising
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-01-30
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details BLE stack initialization, GAP/GATT configuration, advertising.
|
||||
******************************************************************************/
|
||||
|
||||
#include "ble_core.h"
|
||||
#include "ble_data_tx.h"
|
||||
#include "power_ctrl.h"
|
||||
#include "device_config.h"
|
||||
|
||||
#include "nordic_common.h"
|
||||
#include "nrf.h"
|
||||
#include "ble_hci.h"
|
||||
#include "ble_advdata.h"
|
||||
#include "ble_advertising.h"
|
||||
#include "ble_srv_common.h"
|
||||
#include "ble_conn_params.h"
|
||||
#include "ble_nus.h"
|
||||
#include "nrf_sdh.h"
|
||||
#include "nrf_sdh_soc.h"
|
||||
#include "nrf_sdh_ble.h"
|
||||
#include "nrf_ble_gatt.h"
|
||||
#include "nrf_ble_qwr.h"
|
||||
#include "app_timer.h"
|
||||
#include "app_error.h"
|
||||
#include "bsp.h"
|
||||
#include "debug_print.h"
|
||||
#include "main_timer.h"
|
||||
#include "battery_saadc.h"
|
||||
#include "measurements.h"
|
||||
#include "power_control.h"
|
||||
#include "main.h"
|
||||
#include <cmd_parse.h>
|
||||
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
#include "peer_manager.h"
|
||||
#include "peer_manager_handler.h"
|
||||
#include "nrf_ble_lesc.h"
|
||||
#endif
|
||||
|
||||
#if BLE_DFU_ENABLED
|
||||
#include "ble_dfu.h"
|
||||
#include "nrf_pwr_mgmt.h"
|
||||
#include "nrf_power.h"
|
||||
#include "nrf_bootloader_info.h"
|
||||
#include "nrf_dfu_ble_svci_bond_sharing.h"
|
||||
#include "nrf_svci_async_function.h"
|
||||
#include "nrf_svci_async_handler.h"
|
||||
#include "ble_conn_state.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*==============================================================================
|
||||
* BLE SERVICE INSTANCES
|
||||
*============================================================================*/
|
||||
|
||||
BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT);
|
||||
NRF_BLE_GATT_DEF(m_gatt);
|
||||
NRF_BLE_QWR_DEF(m_qwr);
|
||||
BLE_ADVERTISING_DEF(m_advertising);
|
||||
|
||||
/*==============================================================================
|
||||
* GLOBAL VARIABLES
|
||||
*============================================================================*/
|
||||
|
||||
uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||
uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3;
|
||||
|
||||
static ble_uuid_t m_adv_uuids[] =
|
||||
{
|
||||
{BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}
|
||||
};
|
||||
|
||||
/*==============================================================================
|
||||
* FORWARD DECLARATIONS
|
||||
*============================================================================*/
|
||||
|
||||
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context);
|
||||
static void on_adv_evt(ble_adv_evt_t ble_adv_evt);
|
||||
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt);
|
||||
static void conn_params_error_handler(uint32_t nrf_error);
|
||||
static void nus_data_handler(ble_nus_evt_t * p_evt);
|
||||
static void nrf_qwr_error_handler(uint32_t nrf_error);
|
||||
void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt);
|
||||
|
||||
#if BLE_DFU_ENABLED
|
||||
static bool app_shutdown_handler(nrf_pwr_mgmt_evt_t event);
|
||||
static void buttonless_dfu_sdh_state_observer(nrf_sdh_state_evt_t state, void * p_context);
|
||||
static void advertising_config_get(ble_adv_modes_config_t * p_config);
|
||||
static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event);
|
||||
#endif
|
||||
|
||||
/*==============================================================================
|
||||
* BLE STACK INITIALIZATION
|
||||
*============================================================================*/
|
||||
|
||||
void ble_stack_init(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
err_code = nrf_sdh_enable_request();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
/* Configure BLE stack using default settings */
|
||||
uint32_t ram_start = 0;
|
||||
err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
/* Enable BLE stack */
|
||||
err_code = nrf_sdh_ble_enable(&ram_start);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
/* Register BLE event handler */
|
||||
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* GAP INITIALIZATION
|
||||
*============================================================================*/
|
||||
|
||||
void gap_params_init(void)
|
||||
{
|
||||
uint32_t err_code;
|
||||
ble_gap_conn_params_t gap_conn_params;
|
||||
ble_gap_conn_sec_mode_t sec_mode;
|
||||
#if FEATURE_STATIC_PASSKEY
|
||||
ble_opt_t ble_opt;
|
||||
#endif
|
||||
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
|
||||
|
||||
/* Set device name from EEPROM serial number */
|
||||
err_code = sd_ble_gap_device_name_set(&sec_mode,
|
||||
(const uint8_t *) SERIAL_NO,
|
||||
strlen(SERIAL_NO));
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
/* Configure preferred connection parameters */
|
||||
memset(&gap_conn_params, 0, sizeof(gap_conn_params));
|
||||
gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
|
||||
gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
|
||||
gap_conn_params.slave_latency = SLAVE_LATENCY;
|
||||
gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT;
|
||||
|
||||
err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
#if FEATURE_STATIC_PASSKEY
|
||||
/* Set static passkey for pairing */
|
||||
ble_opt.gap_opt.passkey.p_passkey = (const uint8_t *)m_static_passkey;
|
||||
err_code = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &ble_opt);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* GATT INITIALIZATION
|
||||
*============================================================================*/
|
||||
|
||||
void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt)
|
||||
{
|
||||
if ((m_conn_handle == p_evt->conn_handle) && (p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED))
|
||||
{
|
||||
m_ble_nus_max_data_len = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
void gatt_init(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* CONNECTION PARAMETERS
|
||||
*============================================================================*/
|
||||
|
||||
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
|
||||
{
|
||||
err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
}
|
||||
|
||||
static void conn_params_error_handler(uint32_t nrf_error)
|
||||
{
|
||||
APP_ERROR_HANDLER(nrf_error);
|
||||
}
|
||||
|
||||
void conn_params_init(void)
|
||||
{
|
||||
uint32_t err_code;
|
||||
ble_conn_params_init_t cp_init;
|
||||
|
||||
memset(&cp_init, 0, sizeof(cp_init));
|
||||
|
||||
cp_init.p_conn_params = NULL;
|
||||
cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
|
||||
cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY;
|
||||
cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT;
|
||||
cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID;
|
||||
cp_init.disconnect_on_fail = false;
|
||||
cp_init.evt_handler = on_conn_params_evt;
|
||||
cp_init.error_handler = conn_params_error_handler;
|
||||
|
||||
err_code = ble_conn_params_init(&cp_init);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* NUS DATA HANDLER
|
||||
*============================================================================*/
|
||||
|
||||
extern which_cmd_t cmd_type_t;
|
||||
|
||||
static void nus_data_handler(ble_nus_evt_t * p_evt)
|
||||
{
|
||||
if (p_evt->type == BLE_NUS_EVT_RX_DATA)
|
||||
{
|
||||
cmd_type_t = CMD_BLE;
|
||||
DBG_PRINTF("recv :%s \n", p_evt->params.rx_data.p_data);
|
||||
DBG_PRINTF("length %d \r\n", p_evt->params.rx_data.length);
|
||||
received_command_process(p_evt->params.rx_data.p_data, CMD_BLE, p_evt->params.rx_data.length);
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* QWR ERROR HANDLER
|
||||
*============================================================================*/
|
||||
|
||||
static void nrf_qwr_error_handler(uint32_t nrf_error)
|
||||
{
|
||||
APP_ERROR_HANDLER(nrf_error);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* DFU HANDLERS
|
||||
*============================================================================*/
|
||||
|
||||
#if BLE_DFU_ENABLED
|
||||
NRF_PWR_MGMT_HANDLER_REGISTER(app_shutdown_handler, 0);
|
||||
|
||||
NRF_SDH_STATE_OBSERVER(m_buttonless_dfu_state_obs, 0) =
|
||||
{
|
||||
.handler = buttonless_dfu_sdh_state_observer,
|
||||
};
|
||||
|
||||
static bool app_shutdown_handler(nrf_pwr_mgmt_evt_t event)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case NRF_PWR_MGMT_EVT_PREPARE_DFU:
|
||||
DBG_PRINTF("Power management wants to reset to DFU mode.\r\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
DBG_PRINTF("Power management allowed to reset to DFU mode.\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void buttonless_dfu_sdh_state_observer(nrf_sdh_state_evt_t state, void * p_context)
|
||||
{
|
||||
if (state == NRF_SDH_EVT_STATE_DISABLED)
|
||||
{
|
||||
nrf_power_gpregret2_set(BOOTLOADER_DFU_SKIP_CRC);
|
||||
nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);
|
||||
}
|
||||
}
|
||||
|
||||
static void advertising_config_get(ble_adv_modes_config_t * p_config)
|
||||
{
|
||||
memset(p_config, 0, sizeof(ble_adv_modes_config_t));
|
||||
|
||||
p_config->ble_adv_fast_enabled = true;
|
||||
p_config->ble_adv_fast_interval = APP_ADV_INTERVAL;
|
||||
p_config->ble_adv_fast_timeout = APP_ADV_DURATION;
|
||||
}
|
||||
|
||||
static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE:
|
||||
{
|
||||
DBG_PRINTF("Device is preparing to enter bootloader mode.\r\n");
|
||||
|
||||
ble_adv_modes_config_t config;
|
||||
advertising_config_get(&config);
|
||||
config.ble_adv_on_disconnect_disabled = true;
|
||||
ble_advertising_modes_config_set(&m_advertising, &config);
|
||||
|
||||
uint32_t conn_count = ble_conn_state_for_each_connected(disconnect, NULL);
|
||||
DBG_PRINTF("Disconnected %d links.\r\n", conn_count);
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_DFU_EVT_BOOTLOADER_ENTER:
|
||||
DBG_PRINTF("Device will enter bootloader mode.\r\n");
|
||||
break;
|
||||
|
||||
case BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED:
|
||||
DBG_PRINTF("Request to enter bootloader mode failed asynchroneously.\r\n");
|
||||
break;
|
||||
|
||||
case BLE_DFU_EVT_RESPONSE_SEND_ERROR:
|
||||
DBG_PRINTF("Request to send a response to client failed.\r\n");
|
||||
APP_ERROR_CHECK(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
DBG_PRINTF("Unknown event from ble_dfu_buttonless.\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dfu_init(void)
|
||||
{
|
||||
ret_code_t err_code = ble_dfu_buttonless_async_svci_init();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*==============================================================================
|
||||
* SERVICES INITIALIZATION
|
||||
*============================================================================*/
|
||||
|
||||
void services_init(void)
|
||||
{
|
||||
uint32_t err_code;
|
||||
ble_nus_init_t nus_init;
|
||||
nrf_ble_qwr_init_t qwr_init = {0};
|
||||
|
||||
/* Initialize Queued Write Module */
|
||||
qwr_init.error_handler = nrf_qwr_error_handler;
|
||||
err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
/* Initialize Nordic UART Service */
|
||||
memset(&nus_init, 0, sizeof(nus_init));
|
||||
nus_init.data_handler = nus_data_handler;
|
||||
err_code = ble_nus_init(&m_nus, &nus_init);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
#if BLE_DFU_ENABLED
|
||||
/* Initialize DFU service */
|
||||
ble_dfu_buttonless_init_t dfus_init = {0};
|
||||
dfus_init.evt_handler = ble_dfu_evt_handler;
|
||||
err_code = ble_dfu_buttonless_init(&dfus_init);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* WRAPPER FUNCTIONS FOR STATIC VARIABLES
|
||||
*============================================================================*/
|
||||
|
||||
uint32_t ble_nus_data_send_wrapper(uint8_t *p_data, uint16_t *p_length, uint16_t conn_handle)
|
||||
{
|
||||
return ble_nus_data_send(&m_nus, p_data, p_length, conn_handle);
|
||||
}
|
||||
|
||||
uint32_t ble_advertising_restart_without_whitelist_wrapper(void)
|
||||
{
|
||||
return ble_advertising_restart_without_whitelist(&m_advertising);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* ADVERTISING
|
||||
*============================================================================*/
|
||||
|
||||
static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
|
||||
{
|
||||
uint32_t err_code;
|
||||
|
||||
switch (ble_adv_evt)
|
||||
{
|
||||
case BLE_ADV_EVT_FAST:
|
||||
err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
break;
|
||||
|
||||
case BLE_ADV_EVT_IDLE:
|
||||
/* Advertising timeout - enter sleep mode */
|
||||
go_sleep_mode_enter = true;
|
||||
main_timer_start();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void advertising_init(void)
|
||||
{
|
||||
uint32_t err_code;
|
||||
ble_advertising_init_t init;
|
||||
|
||||
memset(&init, 0, sizeof(init));
|
||||
|
||||
init.advdata.name_type = BLE_ADVDATA_FULL_NAME;
|
||||
init.advdata.include_appearance = true;
|
||||
#if FEATURE_NO_SLEEP
|
||||
init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
|
||||
#else
|
||||
init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
|
||||
#endif
|
||||
init.srdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
|
||||
init.srdata.uuids_complete.p_uuids = m_adv_uuids;
|
||||
|
||||
init.config.ble_adv_fast_enabled = true;
|
||||
init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
|
||||
init.config.ble_adv_fast_timeout = APP_ADV_DURATION;
|
||||
init.evt_handler = on_adv_evt;
|
||||
|
||||
err_code = ble_advertising_init(&m_advertising, &init);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
|
||||
}
|
||||
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
void advertising_start(bool erase_bonds)
|
||||
{
|
||||
extern void delete_bonds(void);
|
||||
|
||||
DBG_PRINTF("adv_start(erase=%d)\r\n", erase_bonds);
|
||||
if (erase_bonds == true) {
|
||||
bond_data_delete = false;
|
||||
DBG_PRINTF("delete_bonds\r\n");
|
||||
delete_bonds();
|
||||
/* Advertising started by PM_EVT_PEERS_DELETE_SUCCEEDED event */
|
||||
} else {
|
||||
DBG_PRINTF("ble_adv_start...\r\n");
|
||||
ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
|
||||
DBG_PRINTF("err=%d\r\n", err_code);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
}
|
||||
#else
|
||||
void advertising_start(void)
|
||||
{
|
||||
uint32_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
|
||||
/* Ignore INVALID_STATE - already advertising or not ready */
|
||||
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE) {
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*==============================================================================
|
||||
* DISCONNECT
|
||||
*============================================================================*/
|
||||
|
||||
void disconnect(uint16_t conn_handle, void * p_context)
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
|
||||
ret_code_t err_code = sd_ble_gap_disconnect(conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
DBG_PRINTF("Failed to disconnect connection. Connection handle: %d Error: %d\r\n", conn_handle, err_code);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG_PRINTF("Disconnected connection handle %d\r\n", conn_handle);
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* BLE EVENT HANDLER
|
||||
*============================================================================*/
|
||||
|
||||
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
|
||||
{
|
||||
uint32_t err_code;
|
||||
extern void ble_data_tx_complete(void);
|
||||
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
pm_handler_secure_on_connection(p_ble_evt);
|
||||
#endif
|
||||
|
||||
switch (p_ble_evt->header.evt_id)
|
||||
{
|
||||
case BLE_GAP_EVT_DISCONNECTED:
|
||||
DBG_PRINTF("Disconnected\r\n");
|
||||
ble_connection_st = 0;
|
||||
m_conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||
|
||||
/* Put device to sleep on disconnect if active */
|
||||
if (device_status == true) {
|
||||
LED_ALLOFF();
|
||||
PD_ALLOFF();
|
||||
if (device_sleep_mode() == 0) {
|
||||
device_status = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restart advertising to allow reconnection */
|
||||
/* Delay to let PC complete disconnect processing */
|
||||
nrf_delay_ms(100);
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
advertising_start(false);
|
||||
#else
|
||||
advertising_start();
|
||||
#endif
|
||||
DBG_PRINTF("Advertising restarted\r\n");
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVT_CONNECTED:
|
||||
{
|
||||
DBG_PRINTF("Connected\r\n");
|
||||
ble_connection_st = 1;
|
||||
battery_timer_start();
|
||||
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
|
||||
err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
/* Set TX power to +8 dBm for connected state */
|
||||
sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, m_conn_handle, 8);
|
||||
|
||||
err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
|
||||
{
|
||||
DBG_PRINTF("PHY update request.\r\n");
|
||||
/* Use 1M PHY only for better PC compatibility */
|
||||
ble_gap_phys_t const phys =
|
||||
{
|
||||
.rx_phys = BLE_GAP_PHY_1MBPS,
|
||||
.tx_phys = BLE_GAP_PHY_1MBPS,
|
||||
};
|
||||
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
} break;
|
||||
|
||||
case BLE_GATTC_EVT_TIMEOUT:
|
||||
DBG_PRINTF("Client Timeout.\r\n");
|
||||
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
|
||||
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
break;
|
||||
|
||||
case BLE_GATTS_EVT_TIMEOUT:
|
||||
DBG_PRINTF("Server Timeout.\r\n");
|
||||
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
|
||||
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
|
||||
#if !FEATURE_SECURE_CONNECTION
|
||||
/* Reject pairing when security is disabled */
|
||||
err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
#endif
|
||||
break;
|
||||
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
case BLE_GAP_EVT_PASSKEY_DISPLAY:
|
||||
{
|
||||
/* Display passkey for user verification */
|
||||
char passkey[7];
|
||||
memcpy(passkey, p_ble_evt->evt.gap_evt.params.passkey_display.passkey, 6);
|
||||
passkey[6] = 0;
|
||||
DBG_PRINTF("Passkey: %s\r\n", passkey);
|
||||
} break;
|
||||
|
||||
case BLE_GAP_EVT_AUTH_KEY_REQUEST:
|
||||
#if FEATURE_STATIC_PASSKEY
|
||||
/* Provide static passkey for authentication */
|
||||
if (p_ble_evt->evt.gap_evt.params.auth_key_request.key_type == BLE_GAP_AUTH_KEY_TYPE_PASSKEY)
|
||||
{
|
||||
err_code = sd_ble_gap_auth_key_reply(p_ble_evt->evt.gap_evt.conn_handle,
|
||||
BLE_GAP_AUTH_KEY_TYPE_PASSKEY,
|
||||
(const uint8_t *)m_static_passkey);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVT_LESC_DHKEY_REQUEST:
|
||||
/* Handled by LESC module */
|
||||
break;
|
||||
#endif
|
||||
|
||||
case BLE_GATTS_EVT_HVN_TX_COMPLETE:
|
||||
/* TX complete - ready for next transmission */
|
||||
ble_data_tx_complete();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
123
project/ble_peripheral/ble_app_vivaMayo/ble/ble_core.h
Normal file
123
project/ble_peripheral/ble_app_vivaMayo/ble/ble_core.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*******************************************************************************
|
||||
* @file ble_core.h
|
||||
* @brief BLE Core Functions - Stack, GAP, GATT, Advertising
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-01-30
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details BLE stack initialization, GAP/GATT configuration, advertising.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef BLE_CORE_H
|
||||
#define BLE_CORE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "ble.h"
|
||||
#include "ble_advertising.h"
|
||||
#include "nrf_ble_gatt.h"
|
||||
|
||||
/*==============================================================================
|
||||
* CONSTANTS
|
||||
*============================================================================*/
|
||||
|
||||
#define APP_BLE_CONN_CFG_TAG 1
|
||||
#define NUS_SERVICE_UUID_TYPE BLE_UUID_TYPE_VENDOR_BEGIN
|
||||
#define APP_BLE_OBSERVER_PRIO 3
|
||||
#define APP_ADV_INTERVAL 64 /* 40 ms in 0.625 ms units */
|
||||
|
||||
#if FEATURE_NO_SLEEP
|
||||
#define APP_ADV_DURATION 0 /* Infinite advertising */
|
||||
#else
|
||||
#define APP_ADV_DURATION 18000 /* 180 seconds */
|
||||
#endif
|
||||
|
||||
/* Connection Parameters */
|
||||
#define MIN_CONN_INTERVAL MSEC_TO_UNITS(20, UNIT_1_25_MS)
|
||||
#define MAX_CONN_INTERVAL MSEC_TO_UNITS(75, UNIT_1_25_MS)
|
||||
#define SLAVE_LATENCY 0
|
||||
#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)
|
||||
#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000)
|
||||
#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000)
|
||||
#define MAX_CONN_PARAMS_UPDATE_COUNT 3
|
||||
|
||||
/* GATT Constants */
|
||||
#define OPCODE_LENGTH 1
|
||||
#define HANDLE_LENGTH 2
|
||||
|
||||
/*==============================================================================
|
||||
* EXTERNAL VARIABLES
|
||||
*============================================================================*/
|
||||
|
||||
extern uint16_t m_conn_handle;
|
||||
extern uint16_t m_ble_nus_max_data_len;
|
||||
|
||||
/*==============================================================================
|
||||
* FUNCTION PROTOTYPES
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Initialize BLE SoftDevice stack
|
||||
*/
|
||||
void ble_stack_init(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize GAP parameters
|
||||
*/
|
||||
void gap_params_init(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize GATT module
|
||||
*/
|
||||
void gatt_init(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize connection parameters module
|
||||
*/
|
||||
void conn_params_init(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize BLE advertising
|
||||
*/
|
||||
void advertising_init(void);
|
||||
|
||||
/**
|
||||
* @brief Start BLE advertising
|
||||
* @param erase_bonds If true, delete bonds before advertising
|
||||
*/
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
void advertising_start(bool erase_bonds);
|
||||
#else
|
||||
void advertising_start(void);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Disconnect a connection
|
||||
* @param conn_handle Connection handle to disconnect
|
||||
* @param p_context Unused context pointer
|
||||
*/
|
||||
void disconnect(uint16_t conn_handle, void *p_context);
|
||||
|
||||
/**
|
||||
* @brief Initialize BLE services (NUS, DFU)
|
||||
*/
|
||||
void services_init(void);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for ble_nus_data_send (accesses static m_nus)
|
||||
*/
|
||||
uint32_t ble_nus_data_send_wrapper(uint8_t *p_data, uint16_t *p_length, uint16_t conn_handle);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for ble_advertising_restart_without_whitelist (accesses static m_advertising)
|
||||
*/
|
||||
uint32_t ble_advertising_restart_without_whitelist_wrapper(void);
|
||||
|
||||
#if BLE_DFU_ENABLED
|
||||
/**
|
||||
* @brief Initialize DFU async SVCI
|
||||
*/
|
||||
void dfu_init(void);
|
||||
#endif
|
||||
|
||||
#endif /* BLE_CORE_H */
|
||||
223
project/ble_peripheral/ble_app_vivaMayo/ble/ble_data_tx.c
Normal file
223
project/ble_peripheral/ble_app_vivaMayo/ble/ble_data_tx.c
Normal file
@@ -0,0 +1,223 @@
|
||||
/*******************************************************************************
|
||||
* @file ble_data_tx.c
|
||||
* @brief BLE Data Transmission Functions
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-01-30
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details BLE NUS data transmission with CRC16 checksum.
|
||||
******************************************************************************/
|
||||
|
||||
#include "ble_data_tx.h"
|
||||
#include "ble_core.h"
|
||||
#include "ble_nus.h"
|
||||
#include "crc16.h"
|
||||
#include "nrf_delay.h"
|
||||
#include "app_error.h"
|
||||
#include "debug_print.h"
|
||||
#include <string.h>
|
||||
|
||||
/*==============================================================================
|
||||
* GLOBAL VARIABLES
|
||||
*============================================================================*/
|
||||
|
||||
volatile bool ble_connection_st = 0;
|
||||
volatile bool data_tx_in_progress = false;
|
||||
|
||||
uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
|
||||
/*==============================================================================
|
||||
* PRIVATE VARIABLES
|
||||
*============================================================================*/
|
||||
|
||||
static uint8_t m_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
static uint16_t m_tx_len = 0;
|
||||
static bool m_tx_in_progress = false;
|
||||
|
||||
/*==============================================================================
|
||||
* DATA TRANSMISSION FUNCTIONS
|
||||
*============================================================================*/
|
||||
|
||||
void data_tx_handler(char const *p_data_to_send)
|
||||
{
|
||||
if (m_tx_in_progress)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find string length by locating '\r' terminator */
|
||||
char const *p_end_char = strchr(p_data_to_send, '\r');
|
||||
if (p_end_char == NULL)
|
||||
{
|
||||
DBG_PRINTF("TX data has no '\\r' terminator. Aborting send.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t data_len = p_end_char - p_data_to_send;
|
||||
|
||||
/* Validate length (need space for 2-byte CRC) */
|
||||
if (data_len > (BLE_NUS_MAX_DATA_LEN - 2))
|
||||
{
|
||||
DBG_PRINTF("TX data is too long to fit CRC. Len: %d", data_len);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prepare buffer with data and CRC16 */
|
||||
memcpy(m_tx_buffer, p_data_to_send, data_len);
|
||||
uint16_t crc = crc16_compute(m_tx_buffer, data_len, NULL);
|
||||
m_tx_buffer[data_len] = (uint8_t)(crc & 0xFF);
|
||||
m_tx_buffer[data_len + 1] = (uint8_t)((crc >> 8) & 0xFF);
|
||||
|
||||
m_tx_len = data_len + 2;
|
||||
|
||||
/* Attempt to send */
|
||||
uint32_t err_code;
|
||||
|
||||
if (ble_connection_st == BLE_CONNECTED_ST)
|
||||
{
|
||||
m_tx_in_progress = true;
|
||||
|
||||
err_code = ble_nus_data_send_wrapper( m_tx_buffer, &m_tx_len, m_conn_handle);
|
||||
|
||||
if (err_code == NRF_SUCCESS)
|
||||
{
|
||||
DBG_PRINTF("NUS data sent to SoftDevice buffer.");
|
||||
}
|
||||
else if (err_code == NRF_ERROR_RESOURCES)
|
||||
{
|
||||
DBG_PRINTF("NUS buffer full. Will retry later.");
|
||||
}
|
||||
else if (err_code == NRF_ERROR_INVALID_STATE || err_code == NRF_ERROR_NOT_FOUND)
|
||||
{
|
||||
ble_connection_st = BLE_DISCONNECTED_ST;
|
||||
DBG_PRINTF("Failed to send NUS data, device not connected.");
|
||||
m_tx_in_progress = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tx_in_progress = false;
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void binary_tx_handler(uint8_t const *ble_bin_buff, uint16_t length)
|
||||
{
|
||||
uint32_t err_code;
|
||||
static uint8_t tx_buffer[BLE_NUS_MAX_DATA_LEN] = {0};
|
||||
|
||||
if (ble_connection_st == 0) {
|
||||
DBG_PRINTF("Lost!\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
data_tx_in_progress = true;
|
||||
|
||||
if (length * sizeof(uint16_t) > (BLE_NUS_MAX_DATA_LEN - 2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ble_connection_st == BLE_CONNECTED_ST) {
|
||||
memcpy(tx_buffer, ble_bin_buff, length * sizeof(uint16_t));
|
||||
uint16_t crc = crc16_compute(tx_buffer, length * sizeof(uint16_t), NULL);
|
||||
|
||||
tx_buffer[length * sizeof(uint16_t)] = (uint8_t)(crc & 0xFF);
|
||||
tx_buffer[length * sizeof(uint16_t) + 1] = (uint8_t)((crc >> 8) & 0xFF);
|
||||
|
||||
uint16_t total_len = length * sizeof(uint16_t) + 2;
|
||||
|
||||
do {
|
||||
err_code = ble_nus_data_send_wrapper( tx_buffer, &total_len, m_conn_handle);
|
||||
|
||||
if ((err_code != NRF_ERROR_INVALID_STATE) &&
|
||||
(err_code != NRF_ERROR_RESOURCES) &&
|
||||
(err_code != NRF_ERROR_NOT_FOUND))
|
||||
{
|
||||
nrf_delay_ms(10);
|
||||
}
|
||||
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
} while (err_code == NRF_ERROR_RESOURCES);
|
||||
|
||||
data_tx_in_progress = false;
|
||||
}
|
||||
}
|
||||
|
||||
void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length)
|
||||
{
|
||||
binary_tx_handler(ble_bin_buff, length);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* DATA FORMATTING FUNCTIONS
|
||||
*============================================================================*/
|
||||
|
||||
void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value)
|
||||
{
|
||||
uint16_t tag1 = ((uint16_t)tag[1] << 8) | (uint16_t)tag[0];
|
||||
uint16_t tag2 = ((uint16_t)tag[3] << 8) | (uint16_t)tag[2];
|
||||
|
||||
buffer[0] = (uint8_t)(tag1 & 0xFF);
|
||||
buffer[1] = (uint8_t)((tag1 >> 8) & 0xFF);
|
||||
buffer[2] = (uint8_t)(tag2 & 0xFF);
|
||||
buffer[3] = (uint8_t)((tag2 >> 8) & 0xFF);
|
||||
|
||||
buffer[5] = (uint8_t)(value & 0xFF);
|
||||
buffer[4] = (uint8_t)((value >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length)
|
||||
{
|
||||
uint16_t tag1 = ((uint16_t)tag[1] << 8) | (uint16_t)tag[0];
|
||||
uint16_t tag2 = ((uint16_t)tag[3] << 8) | (uint16_t)tag[2];
|
||||
|
||||
buffer[0] = (uint8_t)(tag1 & 0xFF);
|
||||
buffer[1] = (uint8_t)((tag1 >> 8) & 0xFF);
|
||||
buffer[2] = (uint8_t)(tag2 & 0xFF);
|
||||
buffer[3] = (uint8_t)((tag2 >> 8) & 0xFF);
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
buffer[4 + i * 2 + 1] = (uint8_t)(data_array[i] & 0xFF);
|
||||
buffer[4 + i * 2] = (uint8_t)((data_array[i] >> 8) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_array, size_t length)
|
||||
{
|
||||
uint16_t tag1 = ((uint16_t)tag[1] << 8) | (uint16_t)tag[0];
|
||||
uint16_t tag2 = ((uint16_t)tag[3] << 8) | (uint16_t)tag[2];
|
||||
|
||||
buffer[0] = (uint8_t)(tag1 & 0xFF);
|
||||
buffer[1] = (uint8_t)((tag1 >> 8) & 0xFF);
|
||||
buffer[2] = (uint8_t)(tag2 & 0xFF);
|
||||
buffer[3] = (uint8_t)((tag2 >> 8) & 0xFF);
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
buffer[4 + i] = (uint8_t)(data_array[i] & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length)
|
||||
{
|
||||
uint16_t tag1 = ((uint16_t)tag[1] << 8) | (uint16_t)tag[0];
|
||||
uint16_t tag2 = ((uint16_t)tag[3] << 8) | (uint16_t)tag[2];
|
||||
|
||||
buffer[0] = (uint8_t)(tag1 & 0xFF);
|
||||
buffer[1] = (uint8_t)((tag1 >> 8) & 0xFF);
|
||||
buffer[2] = (uint8_t)(tag2 & 0xFF);
|
||||
buffer[3] = (uint8_t)((tag2 >> 8) & 0xFF);
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
buffer[4 + i] = (uint8_t)(data_ascii[i] & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* TX COMPLETE HANDLER (called from ble_evt_handler)
|
||||
*============================================================================*/
|
||||
|
||||
void ble_data_tx_complete(void)
|
||||
{
|
||||
m_tx_in_progress = false;
|
||||
}
|
||||
82
project/ble_peripheral/ble_app_vivaMayo/ble/ble_data_tx.h
Normal file
82
project/ble_peripheral/ble_app_vivaMayo/ble/ble_data_tx.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*******************************************************************************
|
||||
* @file ble_data_tx.h
|
||||
* @brief BLE Data Transmission Functions
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-01-30
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details BLE NUS data transmission with CRC16 checksum.
|
||||
* - ASCII data transmission
|
||||
* - Binary data transmission
|
||||
* - Data formatting functions
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef BLE_DATA_TX_H
|
||||
#define BLE_DATA_TX_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "main.h"
|
||||
|
||||
/*==============================================================================
|
||||
* EXTERNAL VARIABLES
|
||||
*============================================================================*/
|
||||
|
||||
extern volatile bool ble_connection_st;
|
||||
extern volatile bool data_tx_in_progress;
|
||||
extern uint8_t ble_bin_buffer[];
|
||||
|
||||
/*==============================================================================
|
||||
* FUNCTION PROTOTYPES
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Send ASCII data over BLE NUS with CRC
|
||||
* @param p_data_to_send Null-terminated string ending with '\r'
|
||||
*/
|
||||
void data_tx_handler(char const *p_data_to_send);
|
||||
|
||||
/**
|
||||
* @brief Send binary data over BLE NUS with CRC
|
||||
* @param ble_bin_buff Binary data buffer
|
||||
* @param length Number of 16-bit words to send
|
||||
*/
|
||||
void binary_tx_handler(uint8_t const *ble_bin_buff, uint16_t length);
|
||||
|
||||
/**
|
||||
* @brief Format single 16-bit value with 4-character tag
|
||||
* @param buffer Output buffer (minimum 6 bytes)
|
||||
* @param tag 4-character tag string
|
||||
* @param value 16-bit value to format
|
||||
*/
|
||||
void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value);
|
||||
|
||||
/**
|
||||
* @brief Format 16-bit array with 4-character tag
|
||||
* @param buffer Output buffer
|
||||
* @param tag 4-character tag string
|
||||
* @param data_array Array of 16-bit values
|
||||
* @param length Number of elements in array
|
||||
*/
|
||||
void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Format 8-bit array with 4-character tag
|
||||
* @param buffer Output buffer
|
||||
* @param tag 4-character tag string
|
||||
* @param data_array Array of 8-bit values
|
||||
* @param length Number of bytes in array
|
||||
*/
|
||||
void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_array, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Format ASCII string with 4-character tag
|
||||
* @param buffer Output buffer
|
||||
* @param tag 4-character tag string
|
||||
* @param data_ascii ASCII string data
|
||||
* @param length String length
|
||||
*/
|
||||
void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length);
|
||||
|
||||
#endif /* BLE_DATA_TX_H */
|
||||
199
project/ble_peripheral/ble_app_vivaMayo/ble/ble_security.c
Normal file
199
project/ble_peripheral/ble_app_vivaMayo/ble/ble_security.c
Normal file
@@ -0,0 +1,199 @@
|
||||
/*******************************************************************************
|
||||
* @file ble_security.c
|
||||
* @brief BLE Security - Peer Manager, LESC, Bonding
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-01-30
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details Peer Manager initialization and security event handling.
|
||||
******************************************************************************/
|
||||
|
||||
#include "sdk_config.h"
|
||||
#include "ble_security.h"
|
||||
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
|
||||
#include "ble_core.h"
|
||||
#include "ble_data_tx.h"
|
||||
#include "power_ctrl.h"
|
||||
#include "device_config.h"
|
||||
|
||||
#include "peer_manager.h"
|
||||
#include "peer_manager_handler.h"
|
||||
#include "nrf_ble_lesc.h"
|
||||
#include "ble_conn_state.h"
|
||||
#include "app_error.h"
|
||||
#include "debug_print.h"
|
||||
#include "battery_saadc.h"
|
||||
#include "ble_quick_security.h"
|
||||
#include <string.h>
|
||||
|
||||
/*==============================================================================
|
||||
* GLOBAL VARIABLES
|
||||
*============================================================================*/
|
||||
|
||||
bool erase_bonds = false;
|
||||
|
||||
/*==============================================================================
|
||||
* PRIVATE VARIABLES
|
||||
*============================================================================*/
|
||||
|
||||
static pm_peer_id_t m_peer_to_be_deleted = PM_PEER_ID_INVALID;
|
||||
static uint8_t c_addr[6];
|
||||
|
||||
/*==============================================================================
|
||||
* PEER MANAGER EVENT HANDLER
|
||||
*============================================================================*/
|
||||
|
||||
static void pm_evt_handler(pm_evt_t const * p_evt)
|
||||
{
|
||||
pm_peer_data_bonding_t peer_bonding_data;
|
||||
uint32_t return_code;
|
||||
ret_code_t err_code;
|
||||
|
||||
/* Standard Peer Manager handlers */
|
||||
pm_handler_on_pm_evt(p_evt);
|
||||
pm_handler_disconnect_on_sec_failure(p_evt);
|
||||
pm_handler_flash_clean(p_evt);
|
||||
|
||||
/* Security module (automatic mode handling) */
|
||||
ble_security_quick_pm_handler(p_evt);
|
||||
|
||||
/* Application-specific event handling */
|
||||
switch (p_evt->evt_id)
|
||||
{
|
||||
case PM_EVT_CONN_SEC_SUCCEEDED:
|
||||
{
|
||||
pm_conn_sec_status_t conn_sec_status;
|
||||
err_code = pm_conn_sec_status_get(p_evt->conn_handle, &conn_sec_status);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
/* Accept if: MITM protected OR dev mode (no security) */
|
||||
if (conn_sec_status.mitm_protected || BLE_DEV_MODE)
|
||||
{
|
||||
DBG_PRINTF("Link secured. Role: %d, conn_handle: %d, Procedure: %d\r\n",
|
||||
ble_conn_state_role(p_evt->conn_handle),
|
||||
p_evt->conn_handle,
|
||||
p_evt->params.conn_sec_succeeded.procedure);
|
||||
|
||||
/* Start battery monitoring after secure connection */
|
||||
ble_connection_st = 1;
|
||||
battery_timer_start();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Security insufficient - disconnect (production mode) */
|
||||
DBG_PRINTF("Link security FAILED\r\n");
|
||||
err_code = pm_peer_id_get(m_conn_handle, &m_peer_to_be_deleted);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
err_code = sd_ble_gap_disconnect(m_conn_handle,
|
||||
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PM_EVT_CONN_SEC_FAILED:
|
||||
{
|
||||
DBG_PRINTF("Security failed: peer_id=%d, error=%d\r\n",
|
||||
p_evt->peer_id,
|
||||
p_evt->params.conn_sec_failed.error);
|
||||
|
||||
/* Auto-retry if key missing (supports rebonding) */
|
||||
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING)
|
||||
{
|
||||
err_code = pm_conn_secure(p_evt->conn_handle, true);
|
||||
if (err_code != NRF_ERROR_INVALID_STATE)
|
||||
{
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PM_EVT_PEERS_DELETE_SUCCEEDED:
|
||||
/* Restart advertising after bond deletion */
|
||||
advertising_start(false);
|
||||
break;
|
||||
|
||||
case PM_EVT_CONN_SEC_CONFIG_REQ:
|
||||
{
|
||||
/* Allow repairing for rebonding support */
|
||||
pm_conn_sec_config_t conn_sec_config = {
|
||||
.allow_repairing = true
|
||||
};
|
||||
pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
|
||||
}
|
||||
break;
|
||||
|
||||
case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
|
||||
{
|
||||
/* Store peer address for tracking */
|
||||
return_code = pm_peer_data_bonding_load(p_evt->peer_id, &peer_bonding_data);
|
||||
|
||||
if (return_code == NRF_SUCCESS)
|
||||
{
|
||||
DBG_PRINTF("Peer updated: %02x:%02x:%02x:%02x:%02x:%02x\r\n",
|
||||
peer_bonding_data.peer_ble_id.id_addr_info.addr[5],
|
||||
peer_bonding_data.peer_ble_id.id_addr_info.addr[4],
|
||||
peer_bonding_data.peer_ble_id.id_addr_info.addr[3],
|
||||
peer_bonding_data.peer_ble_id.id_addr_info.addr[2],
|
||||
peer_bonding_data.peer_ble_id.id_addr_info.addr[1],
|
||||
peer_bonding_data.peer_ble_id.id_addr_info.addr[0]);
|
||||
|
||||
memcpy(c_addr,
|
||||
peer_bonding_data.peer_ble_id.id_addr_info.addr,
|
||||
sizeof(c_addr));
|
||||
|
||||
DBG_PRINTF("Stored c_addr[3]: %02x\r\n", c_addr[3]);
|
||||
m_reset_status = 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG_PRINTF("Failed to load peer data: error=%d\r\n", return_code);
|
||||
m_reset_status = 10;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* PUBLIC FUNCTIONS
|
||||
*============================================================================*/
|
||||
|
||||
void peer_manager_init(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
/* Security mode auto-configuration */
|
||||
ble_security_quick_init(BLE_DEV_MODE);
|
||||
|
||||
/* Register PM event handler */
|
||||
err_code = pm_register(pm_evt_handler);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
DBG_PRINTF("BLE Security initialized (mode=%d)\r\n", BLE_DEV_MODE);
|
||||
}
|
||||
|
||||
void delete_bonds(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
DBG_PRINTF("Erase bonds!\r\n");
|
||||
|
||||
err_code = pm_peers_delete();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
void security_idle_state_handle(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
err_code = nrf_ble_lesc_request_handler();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
#endif /* FEATURE_SECURE_CONNECTION */
|
||||
69
project/ble_peripheral/ble_app_vivaMayo/ble/ble_security.h
Normal file
69
project/ble_peripheral/ble_app_vivaMayo/ble/ble_security.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*******************************************************************************
|
||||
* @file ble_security.h
|
||||
* @brief BLE Security - Peer Manager, LESC, Bonding
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-01-30
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details Peer Manager initialization and security event handling.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef BLE_SECURITY_H
|
||||
#define BLE_SECURITY_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
#include "ble_gap.h"
|
||||
|
||||
/*==============================================================================
|
||||
* CONSTANTS
|
||||
*============================================================================*/
|
||||
#define LESC_DEBUG_MODE 0
|
||||
#define SEC_PARAM_BOND 1
|
||||
#define SEC_PARAM_MITM 1
|
||||
#if FEATURE_STATIC_PASSKEY
|
||||
#define SEC_PARAM_LESC 0
|
||||
#else
|
||||
#define SEC_PARAM_LESC 1
|
||||
#endif
|
||||
#define SEC_PARAM_KEYPRESS 0
|
||||
#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_DISPLAY_ONLY
|
||||
#define SEC_PARAM_OOB 0
|
||||
#define SEC_PARAM_MIN_KEY_SIZE 7
|
||||
#define SEC_PARAM_MAX_KEY_SIZE 16
|
||||
#define PASSKEY_TXT_LENGTH 8
|
||||
#define PASSKEY_LENGTH 6
|
||||
|
||||
#define BLE_DEV_MODE 1 /* 1=Dev mode, 0=Production */
|
||||
#endif
|
||||
|
||||
/*==============================================================================
|
||||
* FUNCTION PROTOTYPES
|
||||
*============================================================================*/
|
||||
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
|
||||
/*==============================================================================
|
||||
* EXTERNAL VARIABLES (SECURE CONNECTION ONLY)
|
||||
*============================================================================*/
|
||||
|
||||
extern bool erase_bonds;
|
||||
/**
|
||||
* @brief Initialize Peer Manager with security configuration
|
||||
*/
|
||||
void peer_manager_init(void);
|
||||
|
||||
/**
|
||||
* @brief Delete all bond information from flash
|
||||
*/
|
||||
void delete_bonds(void);
|
||||
|
||||
/**
|
||||
* @brief Handle idle state for LESC
|
||||
*/
|
||||
void security_idle_state_handle(void);
|
||||
#endif
|
||||
|
||||
#endif /* BLE_SECURITY_H */
|
||||
19
project/ble_peripheral/ble_app_vivaMayo/ble/ble_services.c
Normal file
19
project/ble_peripheral/ble_app_vivaMayo/ble/ble_services.c
Normal file
@@ -0,0 +1,19 @@
|
||||
/*******************************************************************************
|
||||
* @file ble_services.c
|
||||
* @brief BLE Services - NUS and DFU (Stub file)
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-01-30
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details This is now a stub file. All service initialization code has been
|
||||
* moved to ble_core.c to avoid static variable access issues with
|
||||
* Nordic SDK macros (BLE_NUS_DEF, NRF_BLE_QWR_DEF, etc.).
|
||||
*
|
||||
* See ble_core.c for:
|
||||
* - services_init()
|
||||
* - dfu_init()
|
||||
* - NUS data handler
|
||||
* - DFU event handlers
|
||||
******************************************************************************/
|
||||
|
||||
/* Empty stub - all code moved to ble_core.c */
|
||||
33
project/ble_peripheral/ble_app_vivaMayo/ble/ble_services.h
Normal file
33
project/ble_peripheral/ble_app_vivaMayo/ble/ble_services.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*******************************************************************************
|
||||
* @file ble_services.h
|
||||
* @brief BLE Services - NUS and DFU
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-01-30
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details Nordic UART Service and DFU service initialization.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef BLE_SERVICES_H
|
||||
#define BLE_SERVICES_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/*==============================================================================
|
||||
* FUNCTION PROTOTYPES
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Initialize BLE services (NUS, DFU)
|
||||
*/
|
||||
void services_init(void);
|
||||
|
||||
#if BLE_DFU_ENABLED
|
||||
/**
|
||||
* @brief Initialize DFU async SVCI
|
||||
*/
|
||||
void dfu_init(void);
|
||||
#endif
|
||||
|
||||
#endif /* BLE_SERVICES_H */
|
||||
163
project/ble_peripheral/ble_app_vivaMayo/cat_i2c.c
Normal file
163
project/ble_peripheral/ble_app_vivaMayo/cat_i2c.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/*******************************************************************************
|
||||
* @file cat_i2c.c
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nrf.h"
|
||||
#include "app_error.h"
|
||||
#include "boards.h"
|
||||
#include "nrfx_gpiote.h"
|
||||
#include "nrfx_twi.h"
|
||||
#include "nrf_drv_twi.h"
|
||||
|
||||
#include "nrf_delay.h"
|
||||
#include "cat_i2c.h"
|
||||
|
||||
|
||||
|
||||
#define CAT_I2C_ADDR 0x50
|
||||
#define MAX_SERIAL_WRITE 16
|
||||
|
||||
//int16_t read_from_DS3930 = 0;
|
||||
//uint16_t data_160_to_write = 0;
|
||||
//static volatile bool m_xfer_done = false;
|
||||
|
||||
/* TWI instance. */
|
||||
//const nrfx_twi_t m_twi_ir = NRFX_TWI_INSTANCE(IR_I2C_INSTANCE);
|
||||
const nrf_drv_twi_t m_twi_cat = NRF_DRV_TWI_INSTANCE (CAT_I2C_INSTANCE);
|
||||
//void twi_handler(nrfx_twi_evt_t const * p_event, void * p_context)
|
||||
//{
|
||||
// m_xfer_done = true;
|
||||
//}
|
||||
//void ir_irq_init(void){
|
||||
// ret_code_t err_code;
|
||||
|
||||
// /* Initialize int pin */
|
||||
// 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);
|
||||
//}
|
||||
|
||||
|
||||
//void ir_irq_uninit(void){
|
||||
|
||||
// nrfx_gpiote_in_event_disable(ADA2200_SYNCO_PIN);
|
||||
// nrfx_gpiote_in_uninit(ADA2200_SYNCO_PIN);
|
||||
//}
|
||||
void cat_i2c_uninit(void){
|
||||
nrf_drv_twi_disable(&m_twi_cat);
|
||||
nrf_drv_twi_uninit(&m_twi_cat);
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
void cat_i2c_init(void){
|
||||
ret_code_t err_code;
|
||||
|
||||
const nrf_drv_twi_config_t twi_cat_config = {
|
||||
.scl = CAT_I2C_SCL_PIN,
|
||||
.sda = CAT_I2C_SDA_PIN,
|
||||
.frequency = NRF_DRV_TWI_FREQ_100K,
|
||||
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
|
||||
.clear_bus_init = false
|
||||
};
|
||||
|
||||
err_code = nrf_drv_twi_init(&m_twi_cat, &twi_cat_config, NULL, NULL);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
nrf_drv_twi_enable(&m_twi_cat);
|
||||
// ir_irq_init();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
uint8_t cat_command_read(uint8_t device_id, uint8_t address, uint8_t *data)
|
||||
{
|
||||
uint8_t read_data = 0;
|
||||
char adata[8];
|
||||
ret_code_t err_code;
|
||||
//address = 1|(address<<1);
|
||||
address = (address & 0xFF);
|
||||
err_code = nrf_drv_twi_tx(&m_twi_cat, device_id, &address, 1, true);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
// Handle error
|
||||
// return;
|
||||
}
|
||||
|
||||
err_code = nrf_drv_twi_rx(&m_twi_cat, device_id, data, 8);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
// Handle error
|
||||
return 0;
|
||||
}
|
||||
read_data = data[0];
|
||||
memcpy(adata,data,8);
|
||||
printf("Data %s . \r\n", adata);
|
||||
return read_data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cat_command_write(uint8_t device_id, uint8_t address, uint8_t *data)
|
||||
{
|
||||
//uint16_t data_to_write = 0;
|
||||
|
||||
uint8_t buffer[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
|
||||
|
||||
address = (address & 0xFF);
|
||||
|
||||
//buffer[0] = 0x00;
|
||||
buffer[0] = (address);
|
||||
// buffer[1] =(data & 0xFF);
|
||||
// buffer[2] = data1+1;
|
||||
// buffer[3] = data1+2;
|
||||
// buffer[4] = data1+3;
|
||||
// buffer[5] = data1+4;
|
||||
// buffer[6] = data1+5;
|
||||
memcpy(&buffer[1], data, 8 );
|
||||
|
||||
|
||||
ret_code_t err_code;
|
||||
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, 0x00, 1, false);
|
||||
|
||||
err_code = nrf_drv_twi_tx(&m_twi_cat, device_id, buffer, 9, false);
|
||||
|
||||
|
||||
printf("Data %x %x %x %x. \r\n", buffer[0], buffer[1], buffer[2], buffer[3]);
|
||||
printf("Data %s. \r\n", buffer);
|
||||
|
||||
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 6, false);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
|
||||
printf("TWI Error.\r\n");
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
50
project/ble_peripheral/ble_app_vivaMayo/cat_i2c.h
Normal file
50
project/ble_peripheral/ble_app_vivaMayo/cat_i2c.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*******************************************************************************
|
||||
* @file ir_i2c.h
|
||||
* @date 2024-07-17
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _CAT_I2C_H_
|
||||
#define _CAT_I2C_H_
|
||||
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nrf.h"
|
||||
#include "nrf_drv_gpiote.h"
|
||||
|
||||
|
||||
#define CAT_I2C_INSTANCE 0 /**< I2C instance index. */
|
||||
#define CAT_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15)
|
||||
#define CAT_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,14)
|
||||
//#define IR_I2C_SDA_PIN NRF_GPIO_PIN_MAP(0,25)
|
||||
//#define IR_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,0)
|
||||
/**
|
||||
* COMMAND CONSTANTS
|
||||
* Commands are 16-bit writes: bits 15:14 are 0s
|
||||
* Bits 13:10 are the command value below
|
||||
* Bits 9:0 are data for the command, but not all bits are used with all commands
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cat_i2c_uninit(void);
|
||||
void cat_i2c_init(void);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
uint8_t cat_command_read(uint8_t device_id, uint8_t address, uint8_t *data);
|
||||
void cat_command_write(uint8_t device_id, uint8_t address, uint8_t *data);
|
||||
|
||||
|
||||
|
||||
#endif /* !_ADA5272_I2C_H_ */
|
||||
|
||||
541
project/ble_peripheral/ble_app_vivaMayo/cat_interface.c
Normal file
541
project/ble_peripheral/ble_app_vivaMayo/cat_interface.c
Normal file
@@ -0,0 +1,541 @@
|
||||
/*******************************************************************************
|
||||
* @file cat_interface.c
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
/* board driver */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nrf.h"
|
||||
#include "app_error.h"
|
||||
#include "boards.h"
|
||||
#include "nrfx_gpiote.h"
|
||||
#include "nrfx_twi.h"
|
||||
#include "nrf_crypto.h"
|
||||
#include "nrf_crypto_aes.h"
|
||||
|
||||
#include "nrf_drv_twi.h"
|
||||
#include "system_interface.h"
|
||||
#include "nrf_delay.h"
|
||||
#include "cat_interface.h"
|
||||
#include "debug_print.h"
|
||||
/* I2C number and slave address for INV device */
|
||||
#define ICM_I2C_ADDR 0x68
|
||||
#define INV_MAX_SERIAL_WRITE 16
|
||||
|
||||
|
||||
#define EEPROM_I2C_ADDRESS 0x50
|
||||
#define EEPROM_PAGE_SIZE 64
|
||||
#define EEPROM_INSTANCE 0
|
||||
|
||||
|
||||
|
||||
#define AES_BLOCK_SIZE 16
|
||||
|
||||
static uint8_t aes_key[16] = {
|
||||
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
|
||||
0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81
|
||||
};
|
||||
|
||||
static uint8_t aes_iv[16] = { 0 }; // Fixed IV for simplicity
|
||||
|
||||
|
||||
|
||||
|
||||
const nrfx_twi_t m_eeprom = NRFX_TWI_INSTANCE(EEPROM_INSTANCE);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ret_code_t encrypt_data(const uint8_t *input, size_t length, uint8_t *output, size_t *output_len) {
|
||||
nrf_crypto_aes_context_t aes_ctx;
|
||||
size_t padded_len = ((length + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
|
||||
|
||||
uint8_t buffer[padded_len];
|
||||
memcpy(buffer, input, length);
|
||||
memset(&buffer[length], 0, padded_len - length); // Zero padding
|
||||
|
||||
ret_code_t err = nrf_crypto_aes_init(&aes_ctx, &g_nrf_crypto_aes_cbc_128_info, NRF_CRYPTO_ENCRYPT);
|
||||
VERIFY_SUCCESS(err);
|
||||
|
||||
err = nrf_crypto_aes_key_set(&aes_ctx, aes_key);
|
||||
VERIFY_SUCCESS(err);
|
||||
|
||||
err = nrf_crypto_aes_iv_set(&aes_ctx, aes_iv);
|
||||
VERIFY_SUCCESS(err);
|
||||
|
||||
*output_len = padded_len;
|
||||
err = nrf_crypto_aes_finalize(&aes_ctx, buffer, padded_len, output, output_len);
|
||||
return err;
|
||||
}
|
||||
|
||||
ret_code_t decrypt_data(const uint8_t *input, size_t length, uint8_t *output) {
|
||||
nrf_crypto_aes_context_t aes_ctx;
|
||||
uint8_t input_copy[length];
|
||||
memcpy(input_copy, input, length);
|
||||
|
||||
ret_code_t err = nrf_crypto_aes_init(&aes_ctx, &g_nrf_crypto_aes_cbc_128_info, NRF_CRYPTO_DECRYPT);
|
||||
VERIFY_SUCCESS(err);
|
||||
|
||||
err = nrf_crypto_aes_key_set(&aes_ctx, aes_key);
|
||||
VERIFY_SUCCESS(err);
|
||||
|
||||
err = nrf_crypto_aes_iv_set(&aes_ctx, aes_iv);
|
||||
VERIFY_SUCCESS(err);
|
||||
|
||||
size_t output_len = length;
|
||||
err = nrf_crypto_aes_finalize(&aes_ctx, input_copy, length, output, &output_len);
|
||||
return err;
|
||||
}
|
||||
|
||||
ret_code_t eeprom_write_encrypted(uint16_t mem_address, const uint8_t *plaintext, size_t length) {
|
||||
uint8_t encrypted_buf[256]; // Adjust if needed
|
||||
size_t encrypted_len = 0;
|
||||
|
||||
ret_code_t err = encrypt_data(plaintext, length, encrypted_buf, &encrypted_len);
|
||||
VERIFY_SUCCESS(err);
|
||||
|
||||
return eeprom_write_bytes(mem_address, encrypted_buf, encrypted_len);
|
||||
}
|
||||
|
||||
|
||||
ret_code_t eeprom_read_decrypted(uint16_t mem_address, uint8_t *plaintext, size_t original_length) {
|
||||
size_t encrypted_len = ((original_length + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
|
||||
uint8_t encrypted_buf[256]; // Adjust as needed
|
||||
|
||||
ret_code_t err = eeprom_read_bytes(mem_address, encrypted_buf, encrypted_len);
|
||||
VERIFY_SUCCESS(err);
|
||||
|
||||
return decrypt_data(encrypted_buf, encrypted_len, plaintext);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void eeprom_uninitialize(void){
|
||||
nrfx_twi_disable(&m_eeprom);
|
||||
nrfx_twi_uninit(&m_eeprom);
|
||||
}
|
||||
|
||||
void eeprom_initialize(void){
|
||||
ret_code_t err_code;
|
||||
|
||||
const nrfx_twi_config_t eeprom_config = {
|
||||
.scl = ICM42670_I2C_SCL_PIN,
|
||||
.sda = ICM42670_I2C_SDA_PIN,
|
||||
.frequency = NRF_TWI_FREQ_400K,
|
||||
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
|
||||
};
|
||||
|
||||
err_code = nrfx_twi_init(&m_eeprom, &eeprom_config, NULL, NULL);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
nrfx_twi_enable(&m_eeprom);
|
||||
}
|
||||
|
||||
ret_code_t eeprom_initialize_safe(void){
|
||||
ret_code_t err_code;
|
||||
|
||||
const nrfx_twi_config_t eeprom_config = {
|
||||
.scl = ICM42670_I2C_SCL_PIN,
|
||||
.sda = ICM42670_I2C_SDA_PIN,
|
||||
.frequency = NRF_TWI_FREQ_400K,
|
||||
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
|
||||
};
|
||||
|
||||
err_code = nrfx_twi_init(&m_eeprom, &eeprom_config, NULL, NULL);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
return err_code;
|
||||
}
|
||||
|
||||
nrfx_twi_enable(&m_eeprom);
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
ret_code_t eeprom_write_page(uint16_t mem_address, const uint8_t *data)
|
||||
{
|
||||
|
||||
uint8_t buffer[2 + EEPROM_PAGE_SIZE]; // 2 bytes for address + 64 bytes data
|
||||
ret_code_t ret;
|
||||
|
||||
buffer[0] = (uint8_t)(mem_address >> 8); // MSB
|
||||
buffer[1] = (uint8_t)(mem_address & 0xFF); // LSB
|
||||
memcpy(&buffer[2], data, EEPROM_PAGE_SIZE);
|
||||
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, buffer, 2 + EEPROM_PAGE_SIZE, false);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM write failed (code: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Wait for internal EEPROM write cycle (typically ~5ms)
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, NULL, 0, false);
|
||||
if (ret == NRF_SUCCESS)
|
||||
break;
|
||||
nrf_delay_us(100); // Wait 100us before retry
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
ret_code_t eeprom_write_uint16_array(uint16_t start_address, const uint16_t *data, size_t count)
|
||||
{
|
||||
|
||||
if (count != 48) {
|
||||
DBG_PRINTF("Error: This function is only for writing exactly 48 uint16_t values.\n");
|
||||
return NRF_ERROR_INVALID_PARAM;
|
||||
}
|
||||
if (start_address < 128) {
|
||||
DBG_PRINTF("Error: This function is only for after 192....\n");
|
||||
return NRF_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
uint8_t buffer[EEPROM_PAGE_SIZE];
|
||||
ret_code_t ret;
|
||||
|
||||
// Write first 32 uint16_t values (64 bytes)
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
buffer[i * 2] = (uint8_t)(data[i] >> 8); // MSB
|
||||
buffer[i * 2 + 1] = (uint8_t)(data[i] & 0xFF); // LSB
|
||||
// buffer[i * 2 +1] = (uint8_t)(data[i] >> 8); // MSB
|
||||
// buffer[i * 2 ] = (uint8_t)(data[i] & 0xFF); // LSB
|
||||
}
|
||||
|
||||
ret = eeprom_write_page(start_address, buffer);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Write remaining 16 uint16_t values (32 bytes)
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
buffer[i * 2] = (uint8_t)(data[i + 32] >> 8);
|
||||
buffer[i * 2 + 1] = (uint8_t)(data[i + 32] & 0xFF);
|
||||
}
|
||||
|
||||
ret = eeprom_write_page(start_address + EEPROM_PAGE_SIZE, buffer); // next page
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ret_code_t eeprom_read_uint16_array(uint16_t start_address, uint16_t *data, size_t count)
|
||||
{
|
||||
|
||||
|
||||
if (count != 48) {
|
||||
DBG_PRINTF("Error: This function is only for reading exactly 48 uint16_t values.\n");
|
||||
return NRF_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
uint8_t buffer[EEPROM_PAGE_SIZE];
|
||||
ret_code_t ret;
|
||||
|
||||
// Read first 64 bytes (32 uint16_t)
|
||||
ret = eeprom_read_bytes(start_address, buffer, EEPROM_PAGE_SIZE);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
data[i] = ((uint16_t)buffer[i * 2] << 8) | buffer[i * 2 + 1];
|
||||
}
|
||||
|
||||
// Read next 32 bytes (16 uint16_t)
|
||||
ret = eeprom_read_bytes(start_address + EEPROM_PAGE_SIZE, buffer, 32);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
data[i + 32] = ((uint16_t)buffer[i * 2] << 8) | buffer[i * 2 + 1];
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
ret_code_t eeprom_read_page(uint16_t mem_address, uint8_t *data)
|
||||
{
|
||||
|
||||
uint8_t addr_buf[2];
|
||||
ret_code_t ret;
|
||||
|
||||
addr_buf[0] = (uint8_t)(mem_address >> 8);
|
||||
addr_buf[1] = (uint8_t)(mem_address & 0xFF);
|
||||
|
||||
// Send memory address first
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, addr_buf, 2, true);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM set read address failed (code: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Read the page
|
||||
ret = icm42670_twi_rx(EEPROM_I2C_ADDRESS, data, EEPROM_PAGE_SIZE);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM read failed (code: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
DBG_PRINTF("EEPROM read \n");
|
||||
for (int i = 0; i < EEPROM_PAGE_SIZE; i++) {
|
||||
|
||||
DBG_PRINTF("%02X\n", data[i]);
|
||||
}
|
||||
DBG_PRINTF("\r\n");
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ret_code_t eeprom_write_byte(uint16_t mem_address, uint8_t data)
|
||||
{
|
||||
|
||||
uint8_t buffer[3]; // 2 bytes for address + 1 byte data
|
||||
ret_code_t ret;
|
||||
|
||||
buffer[0] = (uint8_t)(mem_address >> 8); // MSB of address
|
||||
buffer[1] = (uint8_t)(mem_address & 0xFF); // LSB of address
|
||||
buffer[2] = data;
|
||||
DBG_PRINTF("EEPROM write byte %02X,%02X,%02X\n", buffer[0], buffer[1], buffer[2]);
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, buffer, 3, false);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM write byte failed (code: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Wait for internal EEPROM write cycle (typically ~5ms)
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, NULL, 0, false);
|
||||
if (ret == NRF_SUCCESS)
|
||||
break;
|
||||
nrf_delay_us(100); // Wait 100us before retry
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
ret_code_t eeprom_read_byte(uint16_t mem_address, uint8_t *data)
|
||||
{
|
||||
|
||||
uint8_t addr_buf[2];
|
||||
ret_code_t ret;
|
||||
|
||||
addr_buf[0] = (uint8_t)(mem_address >> 8); // MSB of address
|
||||
addr_buf[1] = (uint8_t)(mem_address & 0xFF); // LSB of address
|
||||
|
||||
// Send memory address
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, addr_buf, 2, true);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM set address failed (code: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Read 1 byte
|
||||
ret = icm42670_twi_rx(EEPROM_I2C_ADDRESS, data, 1);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM read byte failed (code: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
ret_code_t eeprom_write_bytes(uint16_t mem_address, const uint8_t *data, size_t length)
|
||||
{
|
||||
|
||||
ret_code_t ret;
|
||||
size_t bytes_written = 0;
|
||||
|
||||
while (bytes_written < length) {
|
||||
size_t page_offset = mem_address % EEPROM_PAGE_SIZE;
|
||||
size_t bytes_to_write = EEPROM_PAGE_SIZE - page_offset;
|
||||
if (bytes_to_write > (length - bytes_written)) {
|
||||
bytes_to_write = length - bytes_written;
|
||||
}
|
||||
|
||||
uint8_t buffer[2 + EEPROM_PAGE_SIZE]; // 2-byte addr + up to 64 data bytes
|
||||
buffer[0] = (uint8_t)(mem_address >> 8);
|
||||
buffer[1] = (uint8_t)(mem_address & 0xFF);
|
||||
memcpy(&buffer[2], &data[bytes_written], bytes_to_write);
|
||||
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, buffer, bytes_to_write + 2, false);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM write error at addr 0x%04X\n", mem_address);
|
||||
return ret;
|
||||
}
|
||||
// Wait for internal EEPROM write cycle (typically ~5ms)
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, NULL, 0, false);
|
||||
if (ret == NRF_SUCCESS)
|
||||
break;
|
||||
nrf_delay_us(100); // Wait 100us before retry
|
||||
}
|
||||
|
||||
mem_address += bytes_to_write;
|
||||
bytes_written += bytes_to_write;
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
ret_code_t eeprom_read_bytes(uint16_t mem_address, uint8_t *data, size_t length)
|
||||
{
|
||||
|
||||
ret_code_t ret;
|
||||
uint8_t addr_buf[2];
|
||||
|
||||
addr_buf[0] = (uint8_t)(mem_address >> 8);
|
||||
addr_buf[1] = (uint8_t)(mem_address & 0xFF);
|
||||
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, addr_buf, 2, true); // send addr, no stop
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM read set address failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = icm42670_twi_rx(EEPROM_I2C_ADDRESS, data, length);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM read failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
ret_code_t eeprom_write_word(uint16_t mem_address, uint16_t data)
|
||||
{
|
||||
|
||||
uint8_t buffer[4]; // 2 bytes for address + 2 bytes for data
|
||||
ret_code_t ret;
|
||||
|
||||
buffer[0] = (uint8_t)(mem_address >> 8); // MSB of address
|
||||
buffer[1] = (uint8_t)(mem_address & 0xFF); // LSB of address
|
||||
buffer[2] = (uint8_t)(data & 0xFF); // LSB of data
|
||||
buffer[3] = (uint8_t)(data >> 8); // MSB of data
|
||||
|
||||
DBG_PRINTF("EEPROM write word %02X,%02X,%02X,%02X\n", buffer[0], buffer[1], buffer[2], buffer[3]);
|
||||
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, buffer, 4, false);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM write word failed (code: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Wait for internal write cycle (~5ms typical)
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, NULL, 0, false);
|
||||
if (ret == NRF_SUCCESS)
|
||||
break;
|
||||
nrf_delay_us(100); // 100us delay between polling
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
ret_code_t eeprom_read_word(uint16_t mem_address, uint16_t *data)
|
||||
{
|
||||
|
||||
uint8_t addr_buf[2];
|
||||
uint8_t read_buf[2];
|
||||
ret_code_t ret;
|
||||
|
||||
addr_buf[0] = (uint8_t)(mem_address >> 8);
|
||||
addr_buf[1] = (uint8_t)(mem_address & 0xFF);
|
||||
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, addr_buf, 2, true);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM set address for word read failed (code: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = icm42670_twi_rx(EEPROM_I2C_ADDRESS, read_buf, 2);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM read word failed (code: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*data = ((uint16_t)read_buf[1] << 8) | read_buf[0]; // Little-endian
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
ret_code_t eeprom_write_uint32(uint16_t mem_address, uint32_t data)
|
||||
{
|
||||
|
||||
uint8_t buffer[6]; // 2 bytes address + 4 bytes data
|
||||
ret_code_t ret;
|
||||
|
||||
buffer[0] = (uint8_t)(mem_address >> 8); // MSB of address
|
||||
buffer[1] = (uint8_t)(mem_address & 0xFF); // LSB of address
|
||||
buffer[2] = (uint8_t)(data >> 24);
|
||||
buffer[3] = (uint8_t)(data >> 16);
|
||||
buffer[4] = (uint8_t)(data >> 8);
|
||||
buffer[5] = (uint8_t)(data);
|
||||
|
||||
DBG_PRINTF("EEPROM write uint32: %02X %02X %02X %02X %02X %02X\n",
|
||||
buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
|
||||
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, buffer, 6, false);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM write uint32 failed (code: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Wait for internal EEPROM write cycle
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, NULL, 0, false);
|
||||
if (ret == NRF_SUCCESS)
|
||||
break;
|
||||
nrf_delay_us(100);
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ret_code_t eeprom_read_uint32(uint16_t mem_address, uint32_t *data)
|
||||
{
|
||||
uint8_t addr_buf[2];
|
||||
uint8_t data_buf[4];
|
||||
ret_code_t ret;
|
||||
|
||||
|
||||
addr_buf[0] = (uint8_t)(mem_address >> 8);
|
||||
addr_buf[1] = (uint8_t)(mem_address & 0xFF);
|
||||
|
||||
DBG_PRINTF("EEPROM address:%02X,%02X \n",addr_buf[0],addr_buf[1]);
|
||||
// Send memory address to read from
|
||||
ret = icm42670_twi_tx(EEPROM_I2C_ADDRESS, addr_buf, 2, true);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM read (addr phase) failed (code: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Read 4 bytes of data
|
||||
ret = icm42670_twi_rx(EEPROM_I2C_ADDRESS, data_buf, 4);
|
||||
if (ret != NRF_SUCCESS) {
|
||||
DBG_PRINTF("EEPROM read (data phase) failed (code: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*data = ((uint32_t)data_buf[0] << 24) |
|
||||
((uint32_t)data_buf[1] << 16) |
|
||||
((uint32_t)data_buf[2] << 8) |
|
||||
((uint32_t)data_buf[3]);
|
||||
|
||||
DBG_PRINTF("EEPROM read uint32: %02X %02X %02X %02X -> %08X\n",
|
||||
data_buf[0], data_buf[1], data_buf[2], data_buf[3], *data);
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
68
project/ble_peripheral/ble_app_vivaMayo/cat_interface.h
Normal file
68
project/ble_peripheral/ble_app_vivaMayo/cat_interface.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*******************************************************************************
|
||||
* @file app_raw_main.h
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _CAT_INTERFACE_H_
|
||||
#define _CAT_INTERFACE_H_
|
||||
#include "sdk_config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "nordic_common.h"
|
||||
#include "nrf.h"
|
||||
#include "sdk_errors.h"
|
||||
#define EEPROM_I2C_ADDRESS 0x50
|
||||
#define EEPROM_PAGE_SIZE 64
|
||||
#define SERIAL_ADDRESS 0x0030
|
||||
|
||||
|
||||
ret_code_t encrypt_data(const uint8_t *input, size_t length, uint8_t *output, size_t *output_len);
|
||||
|
||||
ret_code_t decrypt_data(const uint8_t *input, size_t length, uint8_t *output);
|
||||
|
||||
ret_code_t eeprom_write_encrypted(uint16_t mem_address, const uint8_t *plaintext, size_t length);
|
||||
|
||||
|
||||
ret_code_t eeprom_read_decrypted(uint16_t mem_address, uint8_t *plaintext, size_t original_length);
|
||||
|
||||
|
||||
|
||||
ret_code_t eeprom_read_page(uint16_t mem_address, uint8_t *data);
|
||||
|
||||
ret_code_t eeprom_write_page(uint16_t mem_address, const uint8_t *data);
|
||||
|
||||
ret_code_t eeprom_write_byte(uint16_t mem_address, uint8_t data);
|
||||
|
||||
ret_code_t eeprom_read_byte(uint16_t mem_address, uint8_t *data);
|
||||
|
||||
ret_code_t eeprom_write_bytes(uint16_t mem_address, const uint8_t *data, size_t length);
|
||||
|
||||
ret_code_t eeprom_read_bytes(uint16_t mem_address, uint8_t *data, size_t length);
|
||||
|
||||
ret_code_t eeprom_write_uint16_array(uint16_t start_address, const uint16_t *data, size_t count);
|
||||
|
||||
ret_code_t eeprom_read_uint16_array(uint16_t start_address, uint16_t *data, size_t count);
|
||||
|
||||
ret_code_t eeprom_write_word(uint16_t mem_address, uint16_t data);
|
||||
|
||||
ret_code_t eeprom_read_word(uint16_t mem_address, uint16_t *data);
|
||||
|
||||
|
||||
void eeprom_uninitialize(void);
|
||||
|
||||
void eeprom_initialize(void);
|
||||
|
||||
ret_code_t eeprom_initialize_safe(void);
|
||||
|
||||
|
||||
|
||||
ret_code_t eeprom_write_uint32(uint16_t mem_address, uint32_t data);
|
||||
|
||||
ret_code_t eeprom_read_uint32(uint16_t mem_address, uint32_t *data);
|
||||
|
||||
|
||||
#endif /* */
|
||||
|
||||
1189
project/ble_peripheral/ble_app_vivaMayo/cmd/cmd.c
Normal file
1189
project/ble_peripheral/ble_app_vivaMayo/cmd/cmd.c
Normal file
File diff suppressed because it is too large
Load Diff
42
project/ble_peripheral/ble_app_vivaMayo/cmd/cmd.h
Normal file
42
project/ble_peripheral/ble_app_vivaMayo/cmd/cmd.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*******************************************************************************
|
||||
* @file cmd.h
|
||||
* @brief VivaMayo Command Handler Interface
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-01-30
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details Firmware-specific command interface for VivaMayo.
|
||||
* Core types (ParsedCmd, CmdEntry) are defined in parser.h
|
||||
* Command table (g_cmd_table) is defined in cmd.c
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef CMD_H
|
||||
#define CMD_H
|
||||
|
||||
/* Include parser.h for ParsedCmd, CmdEntry, cmd_handler_t types */
|
||||
#include "parser.h"
|
||||
|
||||
/*==============================================================================
|
||||
* @section UTIL Utility Functions (for use by handlers)
|
||||
* @brief Convenience wrappers for parser utility functions
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Extract uint16 from ParsedCmd data (little endian)
|
||||
* @param cmd Parsed command
|
||||
* @param word_index Word index (0-based)
|
||||
* @param out Output value
|
||||
* @return true on success
|
||||
*/
|
||||
bool cmd_get_u16(const ParsedCmd *cmd, uint8_t word_index, uint16_t *out);
|
||||
|
||||
/**
|
||||
* @brief Extract ASCII string from ParsedCmd data
|
||||
* @param cmd Parsed command
|
||||
* @param offset Byte offset in data
|
||||
* @param out Output buffer
|
||||
* @param max_len Maximum length
|
||||
*/
|
||||
void cmd_get_ascii(const ParsedCmd *cmd, uint8_t offset, char *out, uint8_t max_len);
|
||||
|
||||
#endif /* CMD_H */
|
||||
318
project/ble_peripheral/ble_app_vivaMayo/cmd_parse.c
Normal file
318
project/ble_peripheral/ble_app_vivaMayo/cmd_parse.c
Normal file
@@ -0,0 +1,318 @@
|
||||
/*******************************************************************************
|
||||
* @file cmd_parse.c
|
||||
* @brief Command Parser Wrapper (Legacy Compatibility)
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-02-04
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details This file provides:
|
||||
* - Legacy compatibility wrapper (received_command_process)
|
||||
* - Utility functions (error handling, validation)
|
||||
* - EEPROM read functions
|
||||
*
|
||||
* Actual parsing is handled by mt_parser/parser.c
|
||||
* Command handlers are in cmd/cmd.c
|
||||
******************************************************************************/
|
||||
|
||||
#include "cmd_parse.h"
|
||||
#include "debug_print.h"
|
||||
#include "parser.h"
|
||||
#include "i2c_manager.h"
|
||||
#include "app_timer.h"
|
||||
#include "config/device_config.h"
|
||||
#include "ble/ble_data_tx.h"
|
||||
#include "ble_nus.h"
|
||||
#include "crc16.h"
|
||||
#include "cat_interface.h"
|
||||
#include "storage/dr_mem.h"
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
/*==============================================================================
|
||||
* @section DEFINES Constants
|
||||
*============================================================================*/
|
||||
|
||||
#define err_code1 65535 /* length error */
|
||||
#define err_code2 65534 /* activate error */
|
||||
#define err_code3 65533 /* param error */
|
||||
#define err_code4 65532 /* ? missing */
|
||||
#define err_code5 65531 /* CMD wrong */
|
||||
#define err_code6 65530 /* CRC wrong */
|
||||
|
||||
/*==============================================================================
|
||||
* @section EXTERN External Declarations
|
||||
*============================================================================*/
|
||||
|
||||
extern uint8_t m_reset_status;
|
||||
|
||||
/* Note: SERIAL_NO, bond_data_delete, m_static_passkey, m_pd_adc_cnt,
|
||||
m_pd_delay_us, m_life_cycle are declared in device_config.h */
|
||||
|
||||
/* Defined in measurements.h */
|
||||
extern uint16_t led_pd_dac_v[];
|
||||
|
||||
/* Defined in main.c */
|
||||
extern char ble_tx_buffer[];
|
||||
extern volatile bool processing;
|
||||
extern bool ble_got_new_data;
|
||||
|
||||
/* BLE buffer (defined in ble_data_tx.c) */
|
||||
extern uint8_t ble_bin_buffer[];
|
||||
|
||||
/* Parser globals (defined in parser.c) */
|
||||
extern dr_platform_if_t g_plat;
|
||||
extern bool g_log_enable;
|
||||
|
||||
/*==============================================================================
|
||||
* @section GLOBALS Global Variables (used by other modules)
|
||||
*============================================================================*/
|
||||
|
||||
uint8_t resetCount = 0; /**< Reset counter */
|
||||
bool info4 = false; /**< Additional info flag for PD Full mode */
|
||||
|
||||
/*==============================================================================
|
||||
* @section STATIC Static Variables
|
||||
*============================================================================*/
|
||||
|
||||
static uint32_t processing_start_tick = 0;
|
||||
|
||||
/*==============================================================================
|
||||
* @section UTIL Utility Functions
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Internal log function for parser
|
||||
*/
|
||||
static void log_printf(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* @section CRC CRC Functions
|
||||
*============================================================================*/
|
||||
|
||||
bool crc16_check(uint8_t const *p_data, uint32_t data_len, uint16_t expected_crc)
|
||||
{
|
||||
uint16_t computed_crc = crc16_compute(p_data, data_len, NULL);
|
||||
return (computed_crc == expected_crc);
|
||||
}
|
||||
|
||||
bool crc16_check_packet(uint8_t const *packet, uint32_t packet_len)
|
||||
{
|
||||
if (packet_len < 2) return false;
|
||||
|
||||
uint32_t data_len = packet_len - 2;
|
||||
uint16_t expected_crc = (packet[packet_len - 1] << 8) | packet[packet_len - 2];
|
||||
|
||||
return crc16_check(packet, data_len, expected_crc);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* @section ERROR Error Handling Functions
|
||||
*============================================================================*/
|
||||
|
||||
bool length_error(const char *cmd, uint8_t target_length, uint8_t length)
|
||||
{
|
||||
if (target_length == length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char resp_error[4];
|
||||
resp_error[0] = 'r';
|
||||
resp_error[1] = cmd[1];
|
||||
resp_error[2] = cmd[2];
|
||||
resp_error[3] = '!';
|
||||
|
||||
single_format_data(ble_bin_buffer, resp_error, err_code1);
|
||||
binary_tx_handler(ble_bin_buffer, 3);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool activate_error(const char *cmd, bool device_status)
|
||||
{
|
||||
if (device_status == true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char resp_error[4];
|
||||
resp_error[0] = 'r';
|
||||
resp_error[1] = cmd[1];
|
||||
resp_error[2] = cmd[2];
|
||||
resp_error[3] = '!';
|
||||
|
||||
single_format_data(ble_bin_buffer, resp_error, err_code2);
|
||||
binary_tx_handler(ble_bin_buffer, 3);
|
||||
return false;
|
||||
}
|
||||
|
||||
void param_error(const char *cmd)
|
||||
{
|
||||
char resp_error[4];
|
||||
resp_error[0] = 'r';
|
||||
resp_error[1] = cmd[1];
|
||||
resp_error[2] = cmd[2];
|
||||
resp_error[3] = '!';
|
||||
|
||||
single_format_data(ble_bin_buffer, resp_error, err_code3);
|
||||
binary_tx_handler(ble_bin_buffer, 3);
|
||||
}
|
||||
|
||||
void quest_error(const char *cmd)
|
||||
{
|
||||
char resp_error[4];
|
||||
const char pass_init[6] = "123456";
|
||||
|
||||
if ((cmd[0] == '*') && (cmd[1] == '*') && (cmd[2] == '*') && (cmd[3] == '*')) {
|
||||
if (dr_memWrite("passkey", (uint8_t *)pass_init, 6) != NRF_SUCCESS) {
|
||||
DBG_PRINTF("ERR!!! passkey write fail\r\n\r\n");
|
||||
}
|
||||
|
||||
resp_error[0] = '*';
|
||||
resp_error[1] = cmd[1];
|
||||
resp_error[2] = cmd[2];
|
||||
resp_error[3] = '*';
|
||||
single_format_data(ble_bin_buffer, resp_error, err_code4);
|
||||
binary_tx_handler(ble_bin_buffer, 3);
|
||||
} else {
|
||||
resp_error[0] = 'r';
|
||||
resp_error[1] = cmd[1];
|
||||
resp_error[2] = cmd[2];
|
||||
resp_error[3] = '!';
|
||||
|
||||
single_format_data(ble_bin_buffer, resp_error, err_code4);
|
||||
binary_tx_handler(ble_bin_buffer, 3);
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* @section INIT_READ Initial Values Read (via dr_mem API)
|
||||
*============================================================================*/
|
||||
|
||||
ret_code_t eeprom_init_values_read(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
/* FDS entries: Serial Number (12B, AES) */
|
||||
err_code = dr_memRead("serial_no", (uint8_t *)SERIAL_NO, 12);
|
||||
if (err_code != NRF_SUCCESS) return err_code;
|
||||
DBG_PRINTF("\r\n SN:%s \r\n", SERIAL_NO);
|
||||
|
||||
/* FDS entries: Passkey (6B, AES) */
|
||||
err_code = dr_memRead("passkey", (uint8_t *)m_static_passkey, 6);
|
||||
if (err_code != NRF_SUCCESS) return err_code;
|
||||
DBG_PRINTF("\r\n passkey-0 :%s \n", m_static_passkey);
|
||||
|
||||
/* FDS entries: Bond data delete (1B) */
|
||||
{
|
||||
uint8_t raw = 0;
|
||||
err_code = dr_memRead("bond_delete", &raw, 1);
|
||||
if (err_code != NRF_SUCCESS) return err_code;
|
||||
bond_data_delete = (raw != 0);
|
||||
}
|
||||
DBG_PRINTF("\r\n bond_data_delete :%d \n", bond_data_delete);
|
||||
|
||||
/* FDS entries: Reset status (1B) */
|
||||
err_code = dr_memRead("reset_status", &m_reset_status, 1);
|
||||
if (err_code != NRF_SUCCESS) return err_code;
|
||||
|
||||
/* FDS entries: PD ADC count (1B) */
|
||||
err_code = dr_memRead("pd_adc_cnt", &m_pd_adc_cnt, 1);
|
||||
if (err_code != NRF_SUCCESS) return err_code;
|
||||
if (m_pd_adc_cnt == 0 || m_pd_adc_cnt >= 255) {
|
||||
m_pd_adc_cnt = 8;
|
||||
}
|
||||
|
||||
/* FDS entries: PD delay (2B) */
|
||||
err_code = dr_memRead("pd_delay", &m_pd_delay_us, 2);
|
||||
if (err_code != NRF_SUCCESS) return err_code;
|
||||
if (m_pd_delay_us < 5000 || m_pd_delay_us > 30000) {
|
||||
m_pd_delay_us = 8000;
|
||||
}
|
||||
|
||||
/* W25Q32 entries: AGC Gain array (96B) */
|
||||
err_code = dr_memRead("agc_gain", led_pd_dac_v, 96);
|
||||
if (err_code != NRF_SUCCESS) return err_code;
|
||||
|
||||
/* W25Q32 entries: Life cycle (4B) */
|
||||
err_code = dr_memRead("life_cycle", &m_life_cycle, 4);
|
||||
if (err_code != NRF_SUCCESS) return err_code;
|
||||
|
||||
DBG_PRINTF("\r\n m_life_cycle:%u, m_pd_delay_us:%u, m_pd_adc_cnt:%u \r\n",
|
||||
m_life_cycle, m_pd_delay_us, m_pd_adc_cnt);
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* @section API Main Entry Point
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Process received command from UART or BLE
|
||||
* @details Initializes parser on first call, then delegates to dr_cmd_parser
|
||||
*/
|
||||
void received_command_process(uint8_t const *data_array, which_cmd_t cmd_t, uint8_t length)
|
||||
{
|
||||
uint8_t r_data[BLE_NUS_MAX_DATA_LEN] = {0};
|
||||
int parser_result;
|
||||
|
||||
(void)cmd_t; /* Currently not used by new parser */
|
||||
|
||||
ble_got_new_data = true;
|
||||
memset(ble_tx_buffer, 0, BLE_NUS_MAX_DATA_LEN);
|
||||
|
||||
/* Copy data to local buffer */
|
||||
for (uint16_t i = 0; i < length; i++) {
|
||||
r_data[i] = data_array[i];
|
||||
}
|
||||
|
||||
DBG_PRINTF("data : %s\r\n", r_data);
|
||||
DBG_PRINTF("Length : %d\r\n", length);
|
||||
|
||||
/* Initialize parser (once) */
|
||||
static bool parser_initialized = false;
|
||||
|
||||
if (!parser_initialized) {
|
||||
g_plat.log = log_printf;
|
||||
g_plat.tx_bin = binary_tx_handler;
|
||||
g_plat.crc_check = true; /* CRC enabled */
|
||||
g_log_enable = true;
|
||||
|
||||
parser_initialized = true;
|
||||
DBG_PRINTF(">>> Parser initialized (mt_parser)\r\n");
|
||||
}
|
||||
|
||||
/* Check if already processing */
|
||||
if (processing == true) {
|
||||
uint32_t now = app_timer_cnt_get();
|
||||
|
||||
if (processing_start_tick == 0) {
|
||||
processing_start_tick = now;
|
||||
}
|
||||
|
||||
/* Timeout check (5 seconds) */
|
||||
if (app_timer_cnt_diff_compute(now, processing_start_tick) > APP_TIMER_TICKS(5000)) {
|
||||
processing = false;
|
||||
processing_start_tick = 0;
|
||||
DBG_PRINTF("processing timeout -> force reset to false\r\n");
|
||||
} else {
|
||||
DBG_PRINTF("Busy - command ignored\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Call mt_parser */
|
||||
parser_result = dr_cmd_parser(r_data, length);
|
||||
|
||||
if (parser_result > 0) {
|
||||
DBG_PRINTF(">>> Handled by mt_parser (result=%d)\r\n", parser_result);
|
||||
} else if (parser_result == 9) {
|
||||
DBG_PRINTF(">>> CRC/Parse error\r\n");
|
||||
} else {
|
||||
DBG_PRINTF(">>> Unknown command\r\n");
|
||||
}
|
||||
}
|
||||
120
project/ble_peripheral/ble_app_vivaMayo/cmd_parse.h
Normal file
120
project/ble_peripheral/ble_app_vivaMayo/cmd_parse.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*******************************************************************************
|
||||
* @file cmd_parse.h
|
||||
* @brief Command Parser Interface (Legacy Compatibility Layer)
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-02-04
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details This header provides backward compatibility with existing code.
|
||||
* Actual parsing is handled by mt_parser.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _CMD_PARSE_H_
|
||||
#define _CMD_PARSE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "main.h"
|
||||
|
||||
/*==============================================================================
|
||||
* @section API Command Processing Entry Point
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Process received command from UART or BLE
|
||||
* @param data_array Raw command data
|
||||
* @param cmd_t Command source (CMD_UART or CMD_BLE)
|
||||
* @param length Data length
|
||||
*/
|
||||
void received_command_process(uint8_t const *data_array, which_cmd_t cmd_t, uint8_t length);
|
||||
|
||||
/*==============================================================================
|
||||
* @section UTIL Error Handling Functions
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Send parameter error response
|
||||
* @param cmd Command tag that caused the error
|
||||
*/
|
||||
void param_error(const char *cmd);
|
||||
|
||||
/**
|
||||
* @brief Send activation error response
|
||||
* @param cmd Command tag
|
||||
* @param device_status Current device status
|
||||
* @return true if error occurred
|
||||
*/
|
||||
bool activate_error(const char *cmd, bool device_status);
|
||||
|
||||
/**
|
||||
* @brief Send length error response
|
||||
* @param cmd Command tag
|
||||
* @param target_length Expected length
|
||||
* @param length Actual length
|
||||
* @return true if error occurred
|
||||
*/
|
||||
bool length_error(const char *cmd, uint8_t target_length, uint8_t length);
|
||||
|
||||
/**
|
||||
* @brief Send quest error response
|
||||
* @param cmd Command tag
|
||||
*/
|
||||
void quest_error(const char *cmd);
|
||||
|
||||
/*==============================================================================
|
||||
* @section VALID Validation Functions
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Validate serial number format
|
||||
* @param serial Serial number string
|
||||
* @return true if valid
|
||||
*/
|
||||
bool is_valid_serial_no(const char *serial);
|
||||
|
||||
/**
|
||||
* @brief Validate passkey format
|
||||
* @param passkey Passkey string
|
||||
* @return true if valid
|
||||
*/
|
||||
bool is_valid_passkey(const char *passkey);
|
||||
|
||||
/**
|
||||
* @brief Hash serial number to passkey
|
||||
* @param input Serial number string
|
||||
* @return Hashed passkey value
|
||||
*/
|
||||
uint32_t serial_to_passkey_hash(const char *input);
|
||||
|
||||
/*==============================================================================
|
||||
* @section CRC CRC Functions
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Check CRC16 against expected value
|
||||
* @param p_data Data buffer
|
||||
* @param data_len Data length
|
||||
* @param expected_crc Expected CRC value
|
||||
* @return true if match
|
||||
*/
|
||||
bool crc16_check(uint8_t const *p_data, uint32_t data_len, uint16_t expected_crc);
|
||||
|
||||
/**
|
||||
* @brief Check CRC16 in packet (last 2 bytes)
|
||||
* @param packet Complete packet
|
||||
* @param packet_len Packet length
|
||||
* @return true if valid
|
||||
*/
|
||||
bool crc16_check_packet(uint8_t const *packet, uint32_t packet_len);
|
||||
|
||||
/*==============================================================================
|
||||
* @section EEPROM EEPROM Functions
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Read initial values from EEPROM
|
||||
* @return NRF_SUCCESS on success
|
||||
*/
|
||||
ret_code_t eeprom_init_values_read(void);
|
||||
|
||||
#endif /* _CMD_PARSE_H_ */
|
||||
222
project/ble_peripheral/ble_app_vivaMayo/config/device_config.c
Normal file
222
project/ble_peripheral/ble_app_vivaMayo/config/device_config.c
Normal file
@@ -0,0 +1,222 @@
|
||||
/*******************************************************************************
|
||||
* @file device_config.c
|
||||
* @brief Device Configuration Management
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-01-30
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details EEPROM configuration loading and default values.
|
||||
******************************************************************************/
|
||||
|
||||
#include "device_config.h"
|
||||
#include "nrf_delay.h"
|
||||
#include "debug_print.h"
|
||||
#include "cat_interface.h"
|
||||
#include <cmd_parse.h>
|
||||
#include <string.h>
|
||||
#include "storage/dr_mem.h"
|
||||
#include "fstorage.h"
|
||||
|
||||
/*==============================================================================
|
||||
* GLOBAL VARIABLES
|
||||
*============================================================================*/
|
||||
|
||||
char m_static_passkey[7] = "123456";
|
||||
char SERIAL_NO[16] = "2025VIVAM00001";
|
||||
char HW_NO[12] = "HW00000001";
|
||||
bool bond_data_delete = true;
|
||||
uint8_t m_pd_adc_cnt = 8;
|
||||
uint16_t m_pd_delay_us = 8000;
|
||||
uint32_t m_life_cycle = 0;
|
||||
uint16_t led_pd_dac_v[48];
|
||||
|
||||
/*==============================================================================
|
||||
* VALIDATION FUNCTIONS
|
||||
*============================================================================*/
|
||||
|
||||
bool is_valid_serial_no(const char *serial)
|
||||
{
|
||||
if (serial == NULL) return false;
|
||||
|
||||
/* Check for reasonable serial number format */
|
||||
for (int i = 0; i < 12; i++) {
|
||||
char c = serial[i];
|
||||
if (c == 0) break;
|
||||
if (c == 0xFF) return false; /* Uninitialized EEPROM */
|
||||
if (c < 0x20 || c > 0x7E) return false; /* Non-printable */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_valid_passkey(const char *passkey)
|
||||
{
|
||||
if (passkey == NULL) return false;
|
||||
|
||||
/* Check for 6 digit passkey */
|
||||
for (int i = 0; i < 6; i++) {
|
||||
char c = passkey[i];
|
||||
if (c == 0xFF) return false; /* Uninitialized EEPROM */
|
||||
if (c < '0' || c > '9') return false; /* Not a digit */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* CONFIGURATION LOADING
|
||||
*============================================================================*/
|
||||
|
||||
void load_default_values(void)
|
||||
{
|
||||
/* Default Serial Number */
|
||||
memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
|
||||
memcpy(SERIAL_NO, "2025AAAAT001", 12);
|
||||
|
||||
/* Default Passkey */
|
||||
memset(m_static_passkey, 0, sizeof(m_static_passkey));
|
||||
memcpy(m_static_passkey, "123456", 6);
|
||||
|
||||
/* Default measurement parameters */
|
||||
m_pd_delay_us = 8000;
|
||||
m_pd_adc_cnt = 8;
|
||||
|
||||
/* Default state */
|
||||
bond_data_delete = 1;
|
||||
|
||||
/* Default AGC Gain values */
|
||||
for (int i = 0; i < 48; i++) {
|
||||
led_pd_dac_v[i] = 2048;
|
||||
}
|
||||
|
||||
DBG_PRINTF("[CFG] Using default values\r\n");
|
||||
}
|
||||
|
||||
static bool is_valid_hw_no(const char *hw)
|
||||
{
|
||||
if (hw == NULL) return false;
|
||||
/* All 0x00 or 0xFF = uninitialized */
|
||||
bool all_zero = true, all_ff = true;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if (hw[i] != 0x00) all_zero = false;
|
||||
if ((uint8_t)hw[i] != 0xFF) all_ff = false;
|
||||
}
|
||||
if (all_zero || all_ff) return false;
|
||||
/* Check printable ASCII */
|
||||
for (int i = 0; i < 12; i++) {
|
||||
char c = hw[i];
|
||||
if (c == 0) break;
|
||||
if (c < 0x20 || c > 0x7E) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void load_device_configuration(void)
|
||||
{
|
||||
extern uint8_t m_reset_status;
|
||||
ret_code_t err_code;
|
||||
|
||||
/* Start with RAM defaults, then override with FDS-saved values below.
|
||||
* dr_memSetDefaults() removed: it was clobbering FDS-loaded m_config,
|
||||
* wiping user-saved data (serial_no, passkey, etc.) on every boot
|
||||
* when hw_no was still all-zeros. */
|
||||
load_default_values();
|
||||
|
||||
/* Show m_config state right after config_load (before dr_memRead) */
|
||||
{
|
||||
char tmp[13] = {0};
|
||||
memcpy(tmp, m_config.serial_no, 12);
|
||||
DBG_PRINTF("[CFG] FDS serial_no='%s'\r\n", tmp);
|
||||
memcpy(tmp, m_config.hw_no, 12);
|
||||
DBG_PRINTF("[CFG] FDS hw_no='%s'\r\n", tmp);
|
||||
}
|
||||
|
||||
/* Read FDS entries (already loaded by config_load() into m_config) */
|
||||
err_code = dr_memRead("serial_no", (uint8_t *)SERIAL_NO, 12);
|
||||
DBG_PRINTF("[CFG] dr_memRead S/N rc=%u valid=%d\r\n", err_code, is_valid_serial_no(SERIAL_NO));
|
||||
DBG_PRINTF("[CFG] S/N hex: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
|
||||
(uint8_t)SERIAL_NO[0], (uint8_t)SERIAL_NO[1], (uint8_t)SERIAL_NO[2], (uint8_t)SERIAL_NO[3],
|
||||
(uint8_t)SERIAL_NO[4], (uint8_t)SERIAL_NO[5], (uint8_t)SERIAL_NO[6], (uint8_t)SERIAL_NO[7],
|
||||
(uint8_t)SERIAL_NO[8], (uint8_t)SERIAL_NO[9], (uint8_t)SERIAL_NO[10], (uint8_t)SERIAL_NO[11]);
|
||||
if (err_code != NRF_SUCCESS || !is_valid_serial_no(SERIAL_NO)) {
|
||||
DBG_PRINTF("[CFG] Invalid S/N - using default\r\n");
|
||||
memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
|
||||
memcpy(SERIAL_NO, "2025AAAAT001", 12);
|
||||
}
|
||||
|
||||
err_code = dr_memRead("passkey", (uint8_t *)m_static_passkey, 6);
|
||||
if (err_code != NRF_SUCCESS || !is_valid_passkey(m_static_passkey)) {
|
||||
DBG_PRINTF("[CFG] Invalid passkey - using default\r\n");
|
||||
memset(m_static_passkey, 0, sizeof(m_static_passkey));
|
||||
memcpy(m_static_passkey, "123456", 6);
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t raw = 0;
|
||||
dr_memRead("bond_delete", &raw, 1);
|
||||
bond_data_delete = (raw != 0);
|
||||
}
|
||||
|
||||
dr_memRead("reset_status", &m_reset_status, 1);
|
||||
if (m_reset_status > 2) {
|
||||
DBG_PRINTF("[CFG] Invalid reset_status (%u) - using 1\r\n", m_reset_status);
|
||||
m_reset_status = 1;
|
||||
}
|
||||
|
||||
dr_memRead("pd_adc_cnt", &m_pd_adc_cnt, 1);
|
||||
if (m_pd_adc_cnt == 0 || m_pd_adc_cnt >= 255) {
|
||||
DBG_PRINTF("[CFG] Invalid adc_cnt (%u) - using 8\r\n", m_pd_adc_cnt);
|
||||
m_pd_adc_cnt = 8;
|
||||
}
|
||||
|
||||
dr_memRead("pd_delay", &m_pd_delay_us, 2);
|
||||
if (m_pd_delay_us < 5000 || m_pd_delay_us > 30000) {
|
||||
DBG_PRINTF("[CFG] Invalid pd_delay (%u) - using 8000\r\n", m_pd_delay_us);
|
||||
m_pd_delay_us = 8000;
|
||||
}
|
||||
|
||||
/* W25Q32 entries - NOT read during boot
|
||||
* W25Q32 is not initialized at boot (SPIM + BSP event conflict).
|
||||
* agc_gain and life_cycle use RAM defaults from load_default_values().
|
||||
* Actual W25Q32 values are loaded later when W25Q32 is powered on
|
||||
* (via BLE command or measurement start). */
|
||||
|
||||
DBG_PRINTF("[CFG] Loaded: S/N=%s, delay=%u, cnt=%u, life=%u\r\n",
|
||||
SERIAL_NO, m_pd_delay_us, m_pd_adc_cnt, m_life_cycle);
|
||||
|
||||
/* 3. config_data_t dump (FDS raw) */
|
||||
{
|
||||
char tmp[13] = {0};
|
||||
#define CFG_DLY() nrf_delay_ms(10)
|
||||
|
||||
DBG_PRINTF("[CFG] === config_data_t (FDS) ===\r\n"); CFG_DLY();
|
||||
DBG_PRINTF("[CFG] magic : 0x%08X\r\n", m_config.magic_number); CFG_DLY();
|
||||
|
||||
memcpy(tmp, m_config.hw_no, 12); tmp[12] = 0;
|
||||
DBG_PRINTF("[CFG] hw_no : %s\r\n", tmp); CFG_DLY();
|
||||
|
||||
memcpy(tmp, m_config.serial_no, 12); tmp[12] = 0;
|
||||
DBG_PRINTF("[CFG] serial : %s\r\n", tmp); CFG_DLY();
|
||||
|
||||
DBG_PRINTF("[CFG] passkey : %c%c%c%c%c%c\r\n",
|
||||
m_config.static_passkey[0], m_config.static_passkey[1],
|
||||
m_config.static_passkey[2], m_config.static_passkey[3],
|
||||
m_config.static_passkey[4], m_config.static_passkey[5]); CFG_DLY();
|
||||
DBG_PRINTF("[CFG] bond_del: %u\r\n", m_config.bond_data_delete); CFG_DLY();
|
||||
DBG_PRINTF("[CFG] reset_st: %d\r\n", m_config.reset_status); CFG_DLY();
|
||||
DBG_PRINTF("[CFG] adc_cnt : %u\r\n", m_config.pd_adc_cnt); CFG_DLY();
|
||||
DBG_PRINTF("[CFG] pd_delay: %u\r\n", m_config.pd_delay_us); CFG_DLY();
|
||||
|
||||
/* W25Q32 entries */
|
||||
DBG_PRINTF("[CFG] agc_gain[48]:\r\n"); CFG_DLY();
|
||||
for (int i = 0; i < 48; i += 8) {
|
||||
DBG_PRINTF(" [%2d] %u %u %u %u %u %u %u %u\r\n", i,
|
||||
led_pd_dac_v[i], led_pd_dac_v[i+1],
|
||||
led_pd_dac_v[i+2], led_pd_dac_v[i+3],
|
||||
led_pd_dac_v[i+4], led_pd_dac_v[i+5],
|
||||
led_pd_dac_v[i+6], led_pd_dac_v[i+7]); CFG_DLY();
|
||||
}
|
||||
DBG_PRINTF("[CFG] life : %u\r\n", m_life_cycle); CFG_DLY();
|
||||
DBG_PRINTF("[CFG] ========================\r\n");
|
||||
|
||||
#undef CFG_DLY
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*******************************************************************************
|
||||
* @file device_config.h
|
||||
* @brief Device Configuration Management
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-01-30
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @details EEPROM configuration loading and default values.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef DEVICE_CONFIG_H
|
||||
#define DEVICE_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/*==============================================================================
|
||||
* CONSTANTS
|
||||
*============================================================================*/
|
||||
|
||||
#define LED_NUM 24 /* Number of LEDs in system */
|
||||
|
||||
/*==============================================================================
|
||||
* EXTERNAL VARIABLES
|
||||
*============================================================================*/
|
||||
|
||||
extern char m_static_passkey[7];
|
||||
extern char SERIAL_NO[16];
|
||||
extern bool bond_data_delete;
|
||||
extern uint8_t m_pd_adc_cnt;
|
||||
extern uint16_t m_pd_delay_us;
|
||||
extern uint32_t m_life_cycle;
|
||||
extern uint16_t led_pd_dac_v[];
|
||||
|
||||
/*==============================================================================
|
||||
* FUNCTION PROTOTYPES
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Load device configuration from EEPROM
|
||||
*/
|
||||
void load_device_configuration(void);
|
||||
|
||||
/**
|
||||
* @brief Load default configuration values
|
||||
*/
|
||||
void load_default_values(void);
|
||||
|
||||
/**
|
||||
* @brief Validate serial number string
|
||||
* @param serial Serial number string
|
||||
* @return true if valid
|
||||
*/
|
||||
bool is_valid_serial_no(const char *serial);
|
||||
|
||||
/**
|
||||
* @brief Validate passkey string
|
||||
* @param passkey Passkey string
|
||||
* @return true if valid
|
||||
*/
|
||||
bool is_valid_passkey(const char *passkey);
|
||||
|
||||
#endif /* DEVICE_CONFIG_H */
|
||||
46
project/ble_peripheral/ble_app_vivaMayo/debug_print.h
Normal file
46
project/ble_peripheral/ble_app_vivaMayo/debug_print.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// file: debug_print.h
|
||||
#ifndef DEBUG_PRINT_H
|
||||
#define DEBUG_PRINT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "nrf_delay.h"
|
||||
#include "SEGGER_RTT.h"
|
||||
|
||||
/* Output selection: 0=UART(printf), 1=RTT */
|
||||
#define USE_RTT_OUTPUT 1
|
||||
|
||||
#define ENABLE_PRINTF 1 // Set to 0 to disable globally (DBG_PRINTF off, LOG_PRINTF still works)
|
||||
#define LOG_FLUSH_MS 0 // UART flush delay (0 recommended due to blocking behavior)
|
||||
|
||||
#if USE_RTT_OUTPUT
|
||||
/* RTT output */
|
||||
#if ENABLE_PRINTF
|
||||
#define DBG_PRINTF(...) SEGGER_RTT_printf(0, __VA_ARGS__)
|
||||
#else
|
||||
#define DBG_PRINTF(...) // Do nothing
|
||||
#endif
|
||||
#define LOG_PRINTF(...) do { if (g_log_enable) SEGGER_RTT_printf(0, __VA_ARGS__); } while(0)
|
||||
#else
|
||||
/* UART output */
|
||||
#if ENABLE_PRINTF
|
||||
#define DBG_PRINTF(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DBG_PRINTF(...) // Do nothing
|
||||
#endif
|
||||
#if LOG_FLUSH_MS > 0
|
||||
#define LOG_PRINTF(...) do { \
|
||||
if (g_log_enable) { \
|
||||
printf(__VA_ARGS__); \
|
||||
nrf_delay_ms(LOG_FLUSH_MS); \
|
||||
} \
|
||||
} while(0)
|
||||
#else
|
||||
#define LOG_PRINTF(...) do { if (g_log_enable) printf(__VA_ARGS__); } while(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Log macro linked to g_log_enable flag (runtime controllable) */
|
||||
extern bool g_log_enable;
|
||||
|
||||
#endif // DEBUG_PRINT_H
|
||||
@@ -0,0 +1,603 @@
|
||||
# nRF52840 내장 Flash (FDS) 사용 가이드
|
||||
|
||||
## 1. 개요
|
||||
|
||||
### FDS (Flash Data Storage)란?
|
||||
|
||||
Nordic SDK에서 제공하는 내장 Flash 저장소 라이브러리.
|
||||
SoftDevice와 함께 동작하며, **자동 Wear Leveling**과 **Garbage Collection**을 지원한다.
|
||||
|
||||
본 프로젝트에서는 디바이스 설정(시리얼 번호, 패스키, 측정 파라미터 등)을 FDS에 저장하고,
|
||||
`dr_memRead` / `dr_memWrite` 추상화 API를 통해 BLE 명령어로 읽기/쓰기한다.
|
||||
|
||||
### 저장소 구조
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ BLE 명령어 │
|
||||
│ (mrh?, mrs?, mwh?, mws?, ...) │
|
||||
└──────────────┬───────────────────┬────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────────┐ ┌──────────────────────┐
|
||||
│ dr_memRead/Write │ │ dr_memRead/Write │
|
||||
│ (FDS backend) │ │ (W25Q32 backend) │
|
||||
└──────────┬───────────┘ └──────────┬───────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────────┐ ┌──────────────────────┐
|
||||
│ 내장 Flash (FDS) │ │ 외장 Flash (W25Q32) │
|
||||
│ - config_data_t │ │ - AGC Gain (96B) │
|
||||
│ - 41 bytes packed │ │ - Life Cycle (4B) │
|
||||
│ - Wear Leveling │ │ - SPI 인터페이스 │
|
||||
│ - RBP 보호 │ │ - 4MB │
|
||||
└──────────────────────┘ └──────────────────────┘
|
||||
```
|
||||
|
||||
### FDS vs 외장 W25Q32RV 비교
|
||||
|
||||
| 항목 | 내장 FDS | 외장 W25Q32RV |
|
||||
|------|----------|---------------|
|
||||
| 용량 | ~400KB (50 pages x 4KB x 2048 words) | 4MB |
|
||||
| 인터페이스 | SoftDevice API | SPI (SPIM) |
|
||||
| 자동 초기화 | `fds_init()` | 수동 (`w25q32_power_on()` + `w25q32_init()`) |
|
||||
| Wear Leveling | FDS 내장 (자동) | 없음 (직접 구현) |
|
||||
| 쓰기 단위 | 4 bytes (word) | 256 bytes (page) |
|
||||
| 지우기 단위 | 자동 GC | 4KB (sector erase) |
|
||||
| 부팅 시 사용 | 즉시 가능 | 불가 (SPIM + BSP 이벤트 충돌) |
|
||||
| 용도 | 디바이스 설정 (7개 필드) | 캘리브레이션 + 데이터 (2개 필드) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 데이터 구조
|
||||
|
||||
### config_data_t (fstorage.h)
|
||||
|
||||
FDS에 저장되는 디바이스 설정 구조체. `#pragma pack(1)`으로 패딩 없이 41 bytes.
|
||||
|
||||
```c
|
||||
#pragma pack(1)
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic_number; /* 4B - 0x20231226 (데이터 무결성 확인용) */
|
||||
char hw_no[12]; /* 12B - HW 번호 (공장 입력) */
|
||||
char serial_no[12]; /* 12B - 시리얼 번호 */
|
||||
uint8_t static_passkey[6]; /* 6B - BLE 페어링 패스키 */
|
||||
uint8_t bond_data_delete; /* 1B - 본딩 데이터 삭제 플래그 */
|
||||
int8_t reset_status; /* 1B - 리셋 상태 */
|
||||
uint8_t pd_adc_cnt; /* 1B - ADC 샘플링 횟수 */
|
||||
uint16_t pd_delay_us; /* 2B - PD 지연 시간 (us) */
|
||||
} config_data_t;
|
||||
```
|
||||
|
||||
### 메모리 레이아웃 (바이트 오프셋)
|
||||
|
||||
```
|
||||
Offset Size 필드 기본값 FDS에 저장
|
||||
────── ──── ────────────── ──────────────── ──────────
|
||||
0x00 4B magic_number 0x20231226 O
|
||||
0x04 12B hw_no (0x00 x 12) O
|
||||
0x10 12B serial_no "2025AAAAT001" O
|
||||
0x1C 6B static_passkey "123456" O
|
||||
0x22 1B bond_data_delete 1 O
|
||||
0x23 1B reset_status 99 O
|
||||
0x24 1B pd_adc_cnt 8 O
|
||||
0x25 2B pd_delay_us 8000 O
|
||||
────── ────
|
||||
Total: 39B (packed) → FDS word 정렬: (39 + 3) / 4 = 10 words (40B)
|
||||
```
|
||||
|
||||
### FDS 레코드 식별자
|
||||
|
||||
```c
|
||||
#define CONFIG_FILE (0x8010) /* File ID */
|
||||
#define CONFIG_REC_KEY (0x7010) /* Record Key */
|
||||
#define CONFIG_MAGIC_NUMBER_VALUE (0x20231226)
|
||||
```
|
||||
|
||||
전체 `config_data_t`를 **하나의 FDS 레코드**로 저장한다.
|
||||
읽기/쓰기 시 구조체 전체를 한 번에 처리한다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 부팅 시퀀스
|
||||
|
||||
### 초기화 순서 (main.c)
|
||||
|
||||
```
|
||||
[6] ble_stack_init() ← SoftDevice 활성화 (FDS 필수 전제)
|
||||
↓
|
||||
[6.5] fs_storage_init() ← FDS 이벤트 핸들러 등록 + fds_init()
|
||||
↓
|
||||
config_load() ← FDS에서 config_data_t 로드 → m_config 전역변수
|
||||
↓
|
||||
[6.6] load_device_configuration() ← m_config → 런타임 전역변수 복사
|
||||
↓
|
||||
[7] gap_params_init() ← SERIAL_NO로 BLE 디바이스 이름 설정
|
||||
↓
|
||||
[8] advertising_init() ← 광고 시작
|
||||
```
|
||||
|
||||
**핵심**: FDS는 반드시 `ble_stack_init()` 이후에 초기화해야 한다.
|
||||
SoftDevice가 활성화되어야 FDS 이벤트(SOC 이벤트)가 정상 발생한다.
|
||||
|
||||
### fs_storage_init() 동작
|
||||
|
||||
```c
|
||||
void fs_storage_init(void)
|
||||
{
|
||||
ret_code_t rc;
|
||||
|
||||
/* 1. 이벤트 핸들러 등록 (반드시 fds_init 이전) */
|
||||
rc = fds_register(fds_evt_handler);
|
||||
APP_ERROR_CHECK(rc);
|
||||
|
||||
/* 2. FDS 초기화 시작 (비동기) */
|
||||
rc = fds_init();
|
||||
APP_ERROR_CHECK(rc);
|
||||
|
||||
/* 3. FDS_EVT_INIT 이벤트 대기 (최대 3초) */
|
||||
wait_for_fds_ready();
|
||||
|
||||
/* 4. FDS 상태 확인 */
|
||||
fds_stat_t stat = { 0 };
|
||||
rc = fds_stat(&stat);
|
||||
APP_ERROR_CHECK(rc);
|
||||
}
|
||||
```
|
||||
|
||||
### config_load() 동작 흐름
|
||||
|
||||
```
|
||||
config_load() 시작
|
||||
│
|
||||
├─ fds_record_find() 호출
|
||||
│ │
|
||||
│ ├─ 실패 (rc != 0) ──→ 재시도 (최대 10회, 100ms 간격)
|
||||
│ │ │
|
||||
│ │ ├─ 10회 모두 실패 → 새 레코드 쓰기 (기본값)
|
||||
│ │ └─ 성공 → 아래 진행
|
||||
│ │
|
||||
│ └─ 성공 (rc == 0)
|
||||
│ │
|
||||
│ ├─ fds_record_open()
|
||||
│ │ ├─ 실패 → 레코드 삭제 + GC + 기본값 새로 쓰기
|
||||
│ │ └─ 성공 → memcpy → m_config
|
||||
│ │
|
||||
│ ├─ fds_record_close()
|
||||
│ │
|
||||
│ └─ magic_number 검증
|
||||
│ ├─ 불일치 → 레코드 삭제 + 기본값으로 덮어쓰기
|
||||
│ └─ 일치 → "Loaded OK" (정상 완료)
|
||||
│
|
||||
└─ 새 레코드 쓰기 (기본값)
|
||||
├─ fds_default_value_set()
|
||||
├─ fds_record_write() (비동기)
|
||||
├─ fds_flag_write == false 대기 (이벤트 핸들러에서 해제)
|
||||
└─ config_load() 재진입 (goto cfg_load_start)
|
||||
```
|
||||
|
||||
### 재시도 로직 (핵심!)
|
||||
|
||||
FDS가 완전히 준비되기 전에 `fds_record_find()`를 호출하면 실패할 수 있다 (rc=34313).
|
||||
이를 "레코드 없음"으로 처리하면 기존 저장 데이터를 기본값으로 덮어쓰게 된다.
|
||||
|
||||
```c
|
||||
uint8_t cfg_retry = 0;
|
||||
|
||||
cfg_load_start:
|
||||
memset((char *)&desc, 0, sizeof(desc));
|
||||
memset((char *)&tok, 0, sizeof(tok));
|
||||
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
|
||||
|
||||
/* FDS가 아직 완전히 준비되지 않았을 수 있음 - 기본값 쓰기 전 재시도 */
|
||||
if (rc != NRF_SUCCESS && cfg_retry < 10) {
|
||||
cfg_retry++;
|
||||
nrf_delay_ms(100);
|
||||
goto cfg_load_start;
|
||||
}
|
||||
```
|
||||
|
||||
**이 재시도 로직이 없으면**: 부팅할 때마다 저장된 설정이 기본값으로 덮어씌워질 수 있다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 데이터 저장 (config_save)
|
||||
|
||||
### 동작 방식
|
||||
|
||||
```c
|
||||
void config_save(void)
|
||||
{
|
||||
/* 1. 이전 FDS 작업 진행 중이면 스킵 (비차단) */
|
||||
if (fds_flag_write) return;
|
||||
|
||||
/* 2. magic_number 보정 */
|
||||
if (m_config.magic_number != CONFIG_MAGIC_NUMBER_VALUE)
|
||||
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
|
||||
|
||||
/* 3. 기존 레코드 찾기 */
|
||||
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
|
||||
|
||||
if (rc == NRF_SUCCESS) {
|
||||
/* 4a. 기존 레코드 업데이트 (비동기) */
|
||||
fds_flag_write = true;
|
||||
rc = fds_record_update(&desc, &m_dummy_record);
|
||||
|
||||
/* Flash 공간 부족 시 GC 후 재시도 */
|
||||
if (rc == FDS_ERR_NO_SPACE_IN_FLASH) {
|
||||
fds_gc();
|
||||
fds_record_update(&desc, &m_dummy_record);
|
||||
}
|
||||
} else {
|
||||
/* 4b. 새 레코드 쓰기 (비동기) */
|
||||
fds_flag_write = true;
|
||||
rc = fds_record_write(&desc, &m_dummy_record);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 비동기 처리 주의사항
|
||||
|
||||
`fds_record_update()` / `fds_record_write()`는 **비동기**로 동작한다.
|
||||
호출 즉시 리턴하고, 실제 Flash 쓰기는 SoftDevice 이벤트 루프에서 수행된다.
|
||||
|
||||
```
|
||||
config_save() 호출
|
||||
│
|
||||
├─ fds_flag_write = true
|
||||
├─ fds_record_update() → 즉시 리턴 (NRF_SUCCESS)
|
||||
│
|
||||
│ ... BLE 이벤트 루프에서 실제 Flash 쓰기 수행 ...
|
||||
│
|
||||
└─ fds_evt_handler(FDS_EVT_UPDATE) 콜백
|
||||
└─ fds_flag_write = false
|
||||
```
|
||||
|
||||
**BLE 콜백 컨텍스트에서 config_save()를 호출할 때**:
|
||||
`fds_flag_write`가 `false`가 될 때까지 대기하면 **교착 상태**가 발생한다.
|
||||
`config_save()`는 fire-and-forget 패턴으로 설계되어 있다.
|
||||
|
||||
### fds_evt_handler (이벤트 핸들러)
|
||||
|
||||
```c
|
||||
static void fds_evt_handler(fds_evt_t const *p_evt)
|
||||
{
|
||||
fds_last_evt = p_evt->id;
|
||||
|
||||
switch (p_evt->id)
|
||||
{
|
||||
case FDS_EVT_INIT:
|
||||
m_fds_initialized = true;
|
||||
break;
|
||||
|
||||
case FDS_EVT_WRITE:
|
||||
fds_flag_write = false;
|
||||
break;
|
||||
|
||||
case FDS_EVT_UPDATE:
|
||||
fds_flag_write = false;
|
||||
|
||||
/* 플래그에 따라 시스템 전원 제어 */
|
||||
if (go_device_power_off) device_power_off();
|
||||
if (go_sleep_mode_enter) sleep_mode_enter();
|
||||
if (go_NVIC_SystemReset) NVIC_SystemReset();
|
||||
break;
|
||||
|
||||
case FDS_EVT_DEL_RECORD:
|
||||
case FDS_EVT_DEL_FILE:
|
||||
case FDS_EVT_GC:
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**FDS_EVT_UPDATE에서 시스템 제어**: Flash 쓰기 완료 후 전원 OFF / 슬립 / 리셋을 수행한다.
|
||||
이를 통해 설정 저장이 확실히 완료된 후에만 시스템 상태가 변경된다.
|
||||
|
||||
---
|
||||
|
||||
## 5. dr_mem 추상화 계층
|
||||
|
||||
### 구조
|
||||
|
||||
`dr_memRead()` / `dr_memWrite()`는 이름(key) 기반 메모리 접근 API이다.
|
||||
내부적으로 룩업 테이블을 사용하여 FDS 또는 W25Q32 백엔드로 자동 라우팅한다.
|
||||
|
||||
```c
|
||||
/* 사용 예시 */
|
||||
dr_memWrite("serial_no", "2025VIVA0001", 12); /* → FDS에 저장 */
|
||||
dr_memRead("serial_no", buf, 12); /* → FDS에서 읽기 */
|
||||
dr_memWrite("agc_gain", gains, 96); /* → W25Q32에 저장 */
|
||||
```
|
||||
|
||||
### 룩업 테이블 (dr_mem.c)
|
||||
|
||||
| Key | Backend | Offset | Size | 기본값 |
|
||||
|-----|---------|--------|------|--------|
|
||||
| `hw_no` | FDS | offsetof(hw_no) = 4 | 12B | "123456789012" |
|
||||
| `serial_no` | FDS | offsetof(serial_no) = 16 | 12B | "2025AAAAT001" |
|
||||
| `passkey` | FDS | offsetof(static_passkey) = 28 | 6B | "123456" |
|
||||
| `bond_delete` | FDS | offsetof(bond_data_delete) = 34 | 1B | 1 |
|
||||
| `reset_status` | FDS | offsetof(reset_status) = 35 | 1B | 99 |
|
||||
| `pd_adc_cnt` | FDS | offsetof(pd_adc_cnt) = 36 | 1B | 8 |
|
||||
| `pd_delay` | FDS | offsetof(pd_delay_us) = 37 | 2B | 8000 |
|
||||
| `agc_gain` | W25Q32 | 0x000000 | 96B | 2048 x 48 |
|
||||
| `life_cycle` | W25Q32 | 0x000060 | 4B | 0 |
|
||||
|
||||
### FDS 백엔드 동작
|
||||
|
||||
**쓰기 (dr_memWrite → fds_backend_write)**:
|
||||
```
|
||||
1. 룩업 테이블에서 entry 찾기
|
||||
2. m_config의 해당 필드에 memcpy
|
||||
3. config_save() 호출 (비동기 FDS 기록)
|
||||
```
|
||||
|
||||
**읽기 (dr_memRead → fds_backend_read)**:
|
||||
```
|
||||
1. 룩업 테이블에서 entry 찾기
|
||||
2. m_config의 해당 필드에서 memcpy (RAM 읽기, Flash 접근 없음)
|
||||
```
|
||||
|
||||
**중요**: `dr_memRead()`는 항상 RAM(`m_config`)에서 읽는다.
|
||||
부팅 시 `config_load()`가 Flash → RAM 복사를 한 번 수행하므로,
|
||||
이후 `dr_memRead()`는 Flash에 직접 접근하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 6. BLE 명령어 인터페이스
|
||||
|
||||
### 읽기 명령어 (mr* → rr*)
|
||||
|
||||
| 명령어 | Key | 응답 태그 | 응답 형식 | BLE 패킷 크기 |
|
||||
|--------|-----|----------|-----------|--------------|
|
||||
| `mrh?` | hw_no | `rrh:` | ASCII 12B | 4+12 = 16B |
|
||||
| `mrs?` | serial_no | `rrs:` | ASCII 12B | 4+12 = 16B |
|
||||
| `mrp?` | passkey | `rrp:` | Byte 6B | 4+6 = 10B |
|
||||
| `mrb?` | bond_delete | `rrb:` | uint16 | 4+2 = 6B |
|
||||
| `mrr?` | reset_status | `rrr:` | uint16 | 4+2 = 6B |
|
||||
| `mrc?` | pd_adc_cnt | `rrc:` | uint16 | 4+2 = 6B |
|
||||
| `mrd?` | pd_delay | `rrd:` | uint16 | 4+2 = 6B |
|
||||
| `mrg?` | agc_gain | `rrg:` | uint16 x 48 | 4+96 = 100B |
|
||||
| `mrl?` | life_cycle | `rrl:` | uint16 x 2 (hi, lo) | 4+4 = 8B |
|
||||
|
||||
### 쓰기 명령어 (mw* → rw*)
|
||||
|
||||
| 명령어 | Key | 입력 형식 | 응답 태그 | 응답 의미 |
|
||||
|--------|-----|----------|----------|----------|
|
||||
| `mwh?DATA` | hw_no | ASCII 12B | `rwh:` | 0=성공, 1=실패 |
|
||||
| `mws?DATA` | serial_no | ASCII 12B | `rws:` | 0=성공, 1=실패 |
|
||||
| `mwp?DATA` | passkey | ASCII 6B | `rwp:` | 0=성공, 1=실패 |
|
||||
| `mwb?VAL` | bond_delete | uint16 | `rwb:` | 0=성공, 1=실패 |
|
||||
| `mwr?VAL` | reset_status | uint16 | `rwr:` | 0=성공, 1=실패 |
|
||||
| `mwc?VAL` | pd_adc_cnt | uint16 | `rwc:` | 0=성공, 1=실패 |
|
||||
| `mwd?VAL` | pd_delay | uint16 | `rwd:` | 0=성공, 1=실패 |
|
||||
| `mwg?V1,V2,...,V48` | agc_gain | uint16 x 48 | `rwg:` | 0=성공, 1=실패 |
|
||||
| `mwl?HI,LO` | life_cycle | uint16 x 2 | `rwl:` | 0=성공, 1=실패 |
|
||||
|
||||
### 명령어 처리 흐름 예시
|
||||
|
||||
```
|
||||
mws?2025VIVA0001 (BLE로 수신)
|
||||
│
|
||||
├─ Cmd_mws() 핸들러 호출
|
||||
│ ├─ cmd_get_ascii(cmd, 0, serial, 12) → "2025VIVA0001"
|
||||
│ ├─ dr_memWrite("serial_no", serial, 12)
|
||||
│ │ ├─ find_entry("serial_no") → FDS backend, offset=16, size=12
|
||||
│ │ ├─ memcpy(m_config + 16, "2025VIVA0001", 12)
|
||||
│ │ └─ config_save() → FDS에 비동기 기록
|
||||
│ └─ dr_ble_return_1("rws:", 0) → BLE로 성공 응답
|
||||
│
|
||||
└─ BLE 응답: "rws:" + 0x0000
|
||||
```
|
||||
|
||||
```
|
||||
mrs? (BLE로 수신)
|
||||
│
|
||||
├─ Cmd_mrs() 핸들러 호출
|
||||
│ ├─ dr_memRead("serial_no", buf, 12)
|
||||
│ │ ├─ find_entry("serial_no") → FDS backend, offset=16, size=12
|
||||
│ │ └─ memcpy(buf, m_config + 16, 12) → "2025VIVA0001"
|
||||
│ ├─ ascii_format_data(ble_bin_buffer, "rrs:", buf, 12)
|
||||
│ └─ binary_tx_handler(ble_bin_buffer, 8)
|
||||
│
|
||||
└─ BLE 응답: "rrs:" + "2025VIVA0001"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 런타임 전역변수 연동
|
||||
|
||||
### 부팅 시 데이터 흐름
|
||||
|
||||
```
|
||||
Flash (FDS Record)
|
||||
│
|
||||
├─ config_load()
|
||||
│ └─ m_config (RAM 구조체) ← FDS에서 읽은 원본
|
||||
│
|
||||
├─ load_device_configuration()
|
||||
│ ├─ dr_memRead("serial_no") → SERIAL_NO[] ← BLE 디바이스 이름
|
||||
│ ├─ dr_memRead("passkey") → m_static_passkey[] ← BLE 패스키
|
||||
│ ├─ dr_memRead("pd_delay") → m_pd_delay_us ← 측정 파라미터
|
||||
│ ├─ dr_memRead("pd_adc_cnt")→ m_pd_adc_cnt ← 측정 파라미터
|
||||
│ └─ ... (유효성 검증 + 기본값 폴백)
|
||||
│
|
||||
└─ gap_params_init()
|
||||
└─ sd_ble_gap_device_name_set(SERIAL_NO) ← BLE 광고 이름
|
||||
```
|
||||
|
||||
### 전역변수 목록
|
||||
|
||||
| 변수 | 타입 | 원본 (m_config) | 설명 |
|
||||
|------|------|----------------|------|
|
||||
| `SERIAL_NO[16]` | char[] | serial_no | BLE 디바이스 이름 |
|
||||
| `m_static_passkey[7]` | char[] | static_passkey | BLE 페어링 패스키 |
|
||||
| `m_pd_adc_cnt` | uint8_t | pd_adc_cnt | ADC 샘플링 횟수 |
|
||||
| `m_pd_delay_us` | uint16_t | pd_delay_us | PD 지연 시간 |
|
||||
| `bond_data_delete` | bool | bond_data_delete | 본딩 삭제 플래그 |
|
||||
| `m_life_cycle` | uint32_t | (W25Q32) | 사용 횟수 |
|
||||
| `led_pd_dac_v[48]` | uint16_t[] | (W25Q32) | AGC 게인 값 |
|
||||
|
||||
### 쓰기 시 RAM 동기화
|
||||
|
||||
`mw*` 명령으로 값을 변경할 때, 일부 명령어는 전역변수도 즉시 업데이트한다:
|
||||
|
||||
```c
|
||||
/* mwc? - pd_adc_cnt 쓰기 */
|
||||
dr_memWrite("pd_adc_cnt", &u8val, 1); // m_config + FDS 저장
|
||||
m_pd_adc_cnt = u8val; // 전역변수 즉시 반영
|
||||
|
||||
/* mwd? - pd_delay 쓰기 */
|
||||
dr_memWrite("pd_delay", &val, 2); // m_config + FDS 저장
|
||||
m_pd_delay_us = val; // 전역변수 즉시 반영
|
||||
```
|
||||
|
||||
**주의**: BLE 디바이스 이름(`SERIAL_NO`)은 부팅 시 한 번만 설정된다.
|
||||
`mws?`로 시리얼 번호를 변경해도 재부팅 전까지 BLE 광고 이름은 바뀌지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 8. sdk_config.h 설정
|
||||
|
||||
### FDS 관련 설정
|
||||
|
||||
```c
|
||||
/* FDS 활성화 */
|
||||
#define FDS_ENABLED 1
|
||||
|
||||
/* 가상 페이지 수 (각 페이지 = FDS_VIRTUAL_PAGE_SIZE * 4 bytes) */
|
||||
#define FDS_VIRTUAL_PAGES 50
|
||||
|
||||
/* 가상 페이지 크기 (words) - 기본 1024 words = 4KB */
|
||||
#define FDS_VIRTUAL_PAGE_SIZE 2048
|
||||
|
||||
/* CRC 검사 (현재 비활성화) */
|
||||
#define FDS_CRC_CHECK_ON_READ 0
|
||||
#define FDS_CRC_CHECK_ON_WRITE 0
|
||||
|
||||
/* 내부 Flash 저장소 활성화 */
|
||||
#define NRF_FSTORAGE_ENABLED 1
|
||||
|
||||
/* SOC 이벤트 핸들러 (FDS 이벤트 전달에 필수) */
|
||||
#define NRF_SDH_SOC_ENABLED 1
|
||||
```
|
||||
|
||||
### 저장 용량 계산
|
||||
|
||||
```
|
||||
총 FDS 영역 = 50 pages × 2048 words × 4 bytes = 400KB
|
||||
레코드 크기 = (39 + 3) / 4 = 10 words = 40 bytes
|
||||
오버헤드 = 레코드 헤더 3 words = 12 bytes
|
||||
|
||||
실제 1 레코드 = 52 bytes (데이터 40B + 헤더 12B)
|
||||
```
|
||||
|
||||
현재 프로젝트는 **1개의 레코드**만 사용하므로 용량은 충분하다.
|
||||
50 페이지 설정은 Wear Leveling 수명을 극대화하기 위한 것이다.
|
||||
|
||||
---
|
||||
|
||||
## 9. 에러 코드
|
||||
|
||||
| 이름 | 값 | 의미 | 대응 |
|
||||
|------|-----|------|------|
|
||||
| `NRF_SUCCESS` | 0 | 성공 | - |
|
||||
| `FDS_ERR_NOT_FOUND` | 34313 (0x8609) | 레코드 없음 | 새 레코드 쓰기 |
|
||||
| `FDS_ERR_NO_SPACE_IN_FLASH` | 34050 (0x8502) | Flash 공간 부족 | `fds_gc()` 후 재시도 |
|
||||
| `FDS_ERR_RECORD_TOO_LARGE` | 34051 (0x8503) | 레코드 크기 초과 | 구조체 크기 줄이기 |
|
||||
| `FDS_ERR_NOT_INITIALIZED` | 34052 (0x8504) | FDS 미초기화 | 부팅 순서 확인 |
|
||||
| `FDS_ERR_CRC_CHECK_FAILED` | - | CRC 불일치 | 레코드 삭제 + 재생성 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 트러블슈팅
|
||||
|
||||
### 문제 1: 부팅 시 저장된 값이 기본값으로 초기화됨
|
||||
|
||||
**증상**: BLE 명령으로 값을 저장한 후 재부팅하면 기본값으로 돌아감.
|
||||
BLE 읽기 명령(mr*)으로 읽으면 저장된 값이 정상 반환됨.
|
||||
|
||||
**원인**: `config_load()`에서 `fds_record_find()`가 첫 호출 시 실패 (rc=34313).
|
||||
FDS가 완전히 초기화되기 전에 호출되어 "레코드 없음"으로 판단,
|
||||
기존 데이터를 기본값으로 덮어씀.
|
||||
|
||||
**해결**: 재시도 로직 추가 (최대 10회, 100ms 간격).
|
||||
|
||||
```c
|
||||
if (rc != NRF_SUCCESS && cfg_retry < 10) {
|
||||
cfg_retry++;
|
||||
nrf_delay_ms(100);
|
||||
goto cfg_load_start;
|
||||
}
|
||||
```
|
||||
|
||||
### 문제 2: config_save() 호출 후 값이 저장되지 않음
|
||||
|
||||
**증상**: `dr_memWrite()` 호출 후 `config_save()` 리턴했지만 Flash에 기록 안 됨.
|
||||
|
||||
**원인**: `fds_flag_write`가 이미 `true`인 상태에서 `config_save()` 호출.
|
||||
이전 FDS 작업이 완료되지 않아 스킵됨.
|
||||
|
||||
**해결**: RTT 로그에서 `[CFG_SAVE] busy, skipped` 메시지 확인.
|
||||
연속 쓰기 시 적절한 간격을 두거나, 이전 쓰기 완료 후 재시도.
|
||||
|
||||
### 문제 3: wait_for_fds_ready()에서 타임아웃
|
||||
|
||||
**증상**: `[FDS] TIMEOUT!` 메시지 출력.
|
||||
|
||||
**원인**: `ble_stack_init()` 전에 `fs_storage_init()` 호출.
|
||||
SoftDevice가 비활성 상태이면 FDS 이벤트가 발생하지 않음.
|
||||
|
||||
**해결**: main.c에서 초기화 순서 확인:
|
||||
```
|
||||
ble_stack_init(); // 반드시 먼저!
|
||||
fs_storage_init(); // 그 다음
|
||||
config_load();
|
||||
```
|
||||
|
||||
### 문제 4: Flash 공간 부족 (FDS_ERR_NO_SPACE_IN_FLASH)
|
||||
|
||||
**증상**: `fds_record_update()` 실패, rc=34050.
|
||||
|
||||
**원인**: FDS는 업데이트 시 새 위치에 기록하고 기존을 무효화(invalidate).
|
||||
GC(Garbage Collection) 없이 반복 업데이트하면 유효 공간 소진.
|
||||
|
||||
**해결**: `fds_gc()` 호출 후 재시도 (config_save에 이미 구현됨).
|
||||
|
||||
```c
|
||||
if (rc == FDS_ERR_NO_SPACE_IN_FLASH) {
|
||||
fds_gc();
|
||||
rc = fds_record_update(&desc, &m_dummy_record);
|
||||
}
|
||||
```
|
||||
|
||||
### 문제 5: printf와 DBG_PRINTF 혼동
|
||||
|
||||
**증상**: RTT에서 FDS 관련 로그가 보이지 않음.
|
||||
|
||||
**원인**: `printf()`는 UART로 출력, `DBG_PRINTF()`는 RTT로 출력.
|
||||
config_load() 내부에서 printf를 사용하면 RTT에서 볼 수 없음.
|
||||
|
||||
**해결**: FDS 관련 함수에서는 `DBG_PRINTF()` 사용.
|
||||
|
||||
---
|
||||
|
||||
## 11. 관련 파일
|
||||
|
||||
| 파일 | 역할 |
|
||||
|------|------|
|
||||
| [fstorage.c](../fstorage.c) | FDS 초기화, config_load(), config_save() |
|
||||
| [fstorage.h](../fstorage.h) | config_data_t 구조체 정의 |
|
||||
| [storage/dr_mem.c](../storage/dr_mem.c) | dr_memRead/Write 구현, 룩업 테이블 |
|
||||
| [storage/dr_mem.h](../storage/dr_mem.h) | dr_mem API 선언 |
|
||||
| [config/device_config.c](../config/device_config.c) | 부팅 시 설정 로드 + 유효성 검증 |
|
||||
| [cmd/cmd.c](../cmd/cmd.c) | BLE 명령어 핸들러 (mr*/mw*) |
|
||||
| [main.c](../main.c) | 초기화 순서 |
|
||||
| [pca10056/s140/config/sdk_config.h](../pca10056/s140/config/sdk_config.h) | FDS SDK 설정 |
|
||||
|
||||
---
|
||||
|
||||
*문서 작성일: 2026-03-05*
|
||||
*마지막 업데이트: FDS 재시도 로직, dr_mem 추상화 계층, BLE 명령어 인터페이스 추가*
|
||||
@@ -0,0 +1,939 @@
|
||||
# FDS 내장 메모리 모듈 - 이식(Porting) 매뉴얼
|
||||
|
||||
> **대상**: Claude AI 또는 개발자가 이 문서를 읽고 새 프로젝트에 FDS 메모리 모듈을 이식할 수 있도록 작성됨.
|
||||
> 모든 단계를 순서대로 실행하면 빌드 가능한 상태까지 도달한다.
|
||||
|
||||
---
|
||||
|
||||
## 목차
|
||||
|
||||
1. [모듈 개요](#1-모듈-개요)
|
||||
2. [파일 목록 및 의존성](#2-파일-목록-및-의존성)
|
||||
3. [Step 1: 파일 복사](#step-1-파일-복사)
|
||||
4. [Step 2: config_data_t 커스터마이징](#step-2-config_data_t-커스터마이징)
|
||||
5. [Step 3: dr_mem 룩업 테이블 수정](#step-3-dr_mem-룩업-테이블-수정)
|
||||
6. [Step 4: device_config 전역변수 수정](#step-4-device_config-전역변수-수정)
|
||||
7. [Step 5: BLE 명령어 핸들러 추가](#step-5-ble-명령어-핸들러-추가)
|
||||
8. [Step 6: main.c 초기화 순서 통합](#step-6-mainc-초기화-순서-통합)
|
||||
9. [Step 7: sdk_config.h 설정](#step-7-sdk_configh-설정)
|
||||
10. [Step 8: Keil 프로젝트에 파일 추가](#step-8-keil-프로젝트에-파일-추가)
|
||||
11. [Step 9: 빌드 및 검증](#step-9-빌드-및-검증)
|
||||
12. [핵심 주의사항](#핵심-주의사항)
|
||||
13. [체크리스트](#체크리스트)
|
||||
|
||||
---
|
||||
|
||||
## 1. 모듈 개요
|
||||
|
||||
### 이 모듈이 하는 일
|
||||
|
||||
nRF52840 내장 Flash에 디바이스 설정을 저장하고, BLE 명령어로 읽기/쓰기하는 완전한 시스템.
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────┐
|
||||
│ BLE 명령어 │
|
||||
│ mrh? mrs? mwh? mws? ... │
|
||||
└───────────┬───────────────────────────┬───────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌───────────────────────┐ ┌───────────────────────────┐
|
||||
│ cmd/cmd.c │ │ cmd/cmd.c │
|
||||
│ Cmd_mrh(), Cmd_mrs() │ │ Cmd_mwh(), Cmd_mws() │
|
||||
│ (읽기 핸들러) │ │ (쓰기 핸들러) │
|
||||
└───────────┬───────────┘ └──────────┬────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ storage/dr_mem.c │
|
||||
│ dr_memRead() / dr_memWrite() │
|
||||
│ (이름 기반 메모리 접근 - 룩업 테이블 라우팅) │
|
||||
└──────────┬──────────────────────────┬────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌────────────────────┐ ┌───────────────────────┐
|
||||
│ FDS (내장 Flash) │ │ W25Q32 (외장 Flash) │
|
||||
│ config_data_t │ │ (선택사항) │
|
||||
│ fstorage.c │ │ w25q32 드라이버 │
|
||||
└────────────────────┘ └───────────────────────┘
|
||||
```
|
||||
|
||||
### 모듈 구성 요소
|
||||
|
||||
| 계층 | 역할 | 수정 필요? |
|
||||
|------|------|-----------|
|
||||
| **fstorage.c/h** | FDS 초기화, 레코드 읽기/쓰기 | 구조체만 수정 |
|
||||
| **storage/dr_mem.c/h** | 이름 기반 추상화 API | 룩업 테이블 수정 |
|
||||
| **config/device_config.c/h** | 부팅 시 전역변수 로드 | 전역변수 수정 |
|
||||
| **cmd/cmd.c** (mr*/mw* 부분) | BLE 명령어 핸들러 | 필드에 맞게 수정 |
|
||||
| **mt_parser** (외부 라이브러리) | 명령어 파싱/디스패치 | 수정 불필요 |
|
||||
| **ble/ble_data_tx.c/h** | BLE 데이터 전송 | 수정 불필요 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 파일 목록 및 의존성
|
||||
|
||||
### 복사해야 할 파일 (프로젝트 내부)
|
||||
|
||||
```
|
||||
프로젝트 루트/
|
||||
├── fstorage.c ← FDS 코어 (config_load, config_save)
|
||||
├── fstorage.h ← config_data_t 정의
|
||||
├── storage/
|
||||
│ ├── dr_mem.c ← 이름 기반 메모리 API
|
||||
│ └── dr_mem.h ← dr_memRead/Write 선언
|
||||
├── config/
|
||||
│ ├── device_config.c ← 부팅 시 설정 로드
|
||||
│ └── device_config.h ← 전역변수 선언
|
||||
└── cmd/
|
||||
├── cmd.c ← BLE 명령어 핸들러 (mr*/mw* 부분 추가)
|
||||
└── cmd.h ← cmd_get_u16, cmd_get_ascii 래퍼
|
||||
```
|
||||
|
||||
### 외부 의존 라이브러리 (프로젝트 외부 - 이미 존재해야 함)
|
||||
|
||||
```
|
||||
mt_parser/ ← 명령어 파싱 라이브러리 (공용)
|
||||
├── parser.c ParsedCmd, CmdEntry, dr_cmd_parser()
|
||||
├── parser.h 타입 정의
|
||||
└── dr_util/
|
||||
├── dr_util.c dr_ble_return_1(), dr_ble_return_2()
|
||||
└── dr_util.h 함수 선언
|
||||
```
|
||||
|
||||
### Nordic SDK 의존성 (sdk_config.h에서 활성화)
|
||||
|
||||
| 모듈 | 용도 | 필수? |
|
||||
|------|------|------|
|
||||
| `FDS` | Flash Data Storage | 필수 |
|
||||
| `NRF_FSTORAGE` | Flash Storage 하위 계층 | 필수 |
|
||||
| `NRF_SDH_SOC` | SoftDevice SOC 이벤트 | 필수 |
|
||||
| `CRC16` | 패킷 CRC 검증 | 필수 |
|
||||
| `NRF_PWR_MGMT` | 대기 시 전력 관리 | 필수 |
|
||||
|
||||
### 펌웨어 내부 의존 (이미 존재해야 함)
|
||||
|
||||
| 파일 | 제공하는 것 | 용도 |
|
||||
|------|-----------|------|
|
||||
| `main.h` | `which_cmd_t`, `BLE_CONNECTED_ST`, `binary_tx_handler()` 선언 | BLE 전송 |
|
||||
| `ble/ble_data_tx.c/h` | `format_data()`, `ascii_format_data()`, `ble_bin_buffer[]` | 데이터 포맷팅 |
|
||||
| `debug_print.h` | `DBG_PRINTF` 매크로 | 디버그 출력 |
|
||||
| `power/power_ctrl.c` | `device_power_off()`, `sleep_mode_enter()` | 전원 제어 |
|
||||
| `drivers/w25q32/w25q32.h` | `w25q32_read()`, `w25q32_write()` | 외장 Flash (선택) |
|
||||
|
||||
---
|
||||
|
||||
## Step 1: 파일 복사
|
||||
|
||||
### 1.1 디렉토리 생성
|
||||
|
||||
```bash
|
||||
# 새 프로젝트 루트에서
|
||||
mkdir -p storage
|
||||
mkdir -p config
|
||||
mkdir -p cmd # 이미 있으면 스킵
|
||||
```
|
||||
|
||||
### 1.2 파일 복사
|
||||
|
||||
**VivaMyo 프로젝트에서 복사할 파일:**
|
||||
|
||||
```bash
|
||||
# FDS 코어
|
||||
cp <VivaMayo>/fstorage.c <새 프로젝트>/fstorage.c
|
||||
cp <VivaMayo>/fstorage.h <새 프로젝트>/fstorage.h
|
||||
|
||||
# dr_mem 추상화
|
||||
cp <VivaMayo>/storage/dr_mem.c <새 프로젝트>/storage/dr_mem.c
|
||||
cp <VivaMayo>/storage/dr_mem.h <새 프로젝트>/storage/dr_mem.h
|
||||
|
||||
# device_config
|
||||
cp <VivaMayo>/config/device_config.c <새 프로젝트>/config/device_config.c
|
||||
cp <VivaMayo>/config/device_config.h <새 프로젝트>/config/device_config.h
|
||||
```
|
||||
|
||||
**cmd/cmd.c는 복사하지 않음** - 기존 cmd.c에 핸들러 코드를 추가한다 (Step 5 참조).
|
||||
|
||||
### 1.3 debug_print.h 확인
|
||||
|
||||
새 프로젝트에 `debug_print.h`가 없으면 복사:
|
||||
|
||||
```bash
|
||||
cp <VivaMayo>/debug_print.h <새 프로젝트>/debug_print.h
|
||||
```
|
||||
|
||||
내용 (RTT/UART 선택 가능):
|
||||
|
||||
```c
|
||||
#ifndef DEBUG_PRINT_H
|
||||
#define DEBUG_PRINT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "nrf_delay.h"
|
||||
#include "SEGGER_RTT.h"
|
||||
|
||||
#define USE_RTT_OUTPUT 1 // 0=UART(printf), 1=RTT
|
||||
#define ENABLE_PRINTF 1 // 0=DBG_PRINTF 비활성화
|
||||
|
||||
#if USE_RTT_OUTPUT
|
||||
#if ENABLE_PRINTF
|
||||
#define DBG_PRINTF(...) SEGGER_RTT_printf(0, __VA_ARGS__)
|
||||
#else
|
||||
#define DBG_PRINTF(...)
|
||||
#endif
|
||||
#define LOG_PRINTF(...) do { if (g_log_enable) SEGGER_RTT_printf(0, __VA_ARGS__); } while(0)
|
||||
#else
|
||||
#if ENABLE_PRINTF
|
||||
#define DBG_PRINTF(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DBG_PRINTF(...)
|
||||
#endif
|
||||
#define LOG_PRINTF(...) do { if (g_log_enable) printf(__VA_ARGS__); } while(0)
|
||||
#endif
|
||||
|
||||
extern bool g_log_enable;
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2: config_data_t 커스터마이징
|
||||
|
||||
### 2.1 fstorage.h 수정
|
||||
|
||||
새 프로젝트에 맞게 `config_data_t` 구조체를 수정한다.
|
||||
|
||||
**규칙:**
|
||||
- 반드시 `#pragma pack(1)` 사용 (패딩 없이 연속 저장)
|
||||
- 첫 번째 필드는 반드시 `uint32_t magic_number` (데이터 무결성 검증용)
|
||||
- 나머지 필드는 프로젝트 요구사항에 맞게 정의
|
||||
|
||||
**VivaMayo 원본:**
|
||||
|
||||
```c
|
||||
#pragma pack(1)
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic_number; /* 4B - 고정 (수정 금지) */
|
||||
char hw_no[12]; /* 12B - HW 번호 */
|
||||
char serial_no[12]; /* 12B - 시리얼 번호 */
|
||||
uint8_t static_passkey[6]; /* 6B - BLE 패스키 */
|
||||
uint8_t bond_data_delete; /* 1B - 본딩 삭제 플래그 */
|
||||
int8_t reset_status; /* 1B - 리셋 상태 */
|
||||
uint8_t pd_adc_cnt; /* 1B - ADC 샘플 수 */
|
||||
uint16_t pd_delay_us; /* 2B - PD 지연 시간 */
|
||||
} config_data_t; /* 39 bytes */
|
||||
```
|
||||
|
||||
**새 프로젝트용 예시 (커스터마이징):**
|
||||
|
||||
```c
|
||||
#pragma pack(1)
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic_number; /* 4B - 고정 (0x새날짜값) */
|
||||
char hw_no[12]; /* 12B - HW 번호 */
|
||||
char serial_no[12]; /* 12B - 시리얼 번호 */
|
||||
uint8_t static_passkey[6]; /* 6B - BLE 패스키 */
|
||||
uint8_t bond_data_delete; /* 1B - 본딩 삭제 */
|
||||
int8_t reset_status; /* 1B - 리셋 상태 */
|
||||
// ---- 여기서부터 프로젝트별 필드 추가/삭제 ----
|
||||
uint8_t my_custom_field; /* 1B */
|
||||
uint16_t my_custom_u16; /* 2B */
|
||||
} config_data_t;
|
||||
```
|
||||
|
||||
### 2.2 magic_number 값 변경
|
||||
|
||||
`fstorage.c`에서 magic_number를 새 날짜로 변경:
|
||||
|
||||
```c
|
||||
#define CONFIG_MAGIC_NUMBER_VALUE (0x20260305) /* 새 프로젝트 날짜 */
|
||||
```
|
||||
|
||||
**중요**: magic_number를 변경하면 기존 FDS 레코드는 무효화되고 기본값으로 초기화된다.
|
||||
이것은 의도된 동작이다 (새 프로젝트이므로).
|
||||
|
||||
### 2.3 fds_default_value_set() 수정
|
||||
|
||||
`fstorage.c`의 `fds_default_value_set()` 함수를 config_data_t 필드에 맞게 수정:
|
||||
|
||||
```c
|
||||
void fds_default_value_set(void)
|
||||
{
|
||||
memset(m_config.hw_no, 0, 12);
|
||||
memcpy(m_config.serial_no, "2025AAAAT001", 12); /* 프로젝트 기본 시리얼 */
|
||||
memcpy(m_config.static_passkey, "123456", 6); /* 기본 패스키 */
|
||||
m_config.bond_data_delete = 1;
|
||||
m_config.reset_status = 99;
|
||||
// ---- 프로젝트별 필드 기본값 ----
|
||||
m_config.my_custom_field = 0;
|
||||
m_config.my_custom_u16 = 1000;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: dr_mem 룩업 테이블 수정
|
||||
|
||||
### 3.1 storage/dr_mem.c 룩업 테이블 수정
|
||||
|
||||
`mem_table[]`을 config_data_t 필드에 맞게 수정한다.
|
||||
|
||||
**각 엔트리 형식:**
|
||||
|
||||
```c
|
||||
{ "키이름", 백엔드, offsetof(config_data_t, 필드명), 크기(bytes), false, &기본값 },
|
||||
```
|
||||
|
||||
**VivaMayo 원본:**
|
||||
|
||||
```c
|
||||
static const dr_mem_entry_t mem_table[] = {
|
||||
/* FDS entries */
|
||||
{ "hw_no", MEM_BACKEND_FDS, offsetof(config_data_t, hw_no), 12, false, dflt_hw_no },
|
||||
{ "serial_no", MEM_BACKEND_FDS, offsetof(config_data_t, serial_no), 12, false, dflt_serial },
|
||||
{ "passkey", MEM_BACKEND_FDS, offsetof(config_data_t, static_passkey), 6, false, dflt_passkey },
|
||||
{ "bond_delete", MEM_BACKEND_FDS, offsetof(config_data_t, bond_data_delete), 1, false, &dflt_bond_del },
|
||||
{ "reset_status", MEM_BACKEND_FDS, offsetof(config_data_t, reset_status), 1, false, &dflt_reset_st },
|
||||
{ "pd_adc_cnt", MEM_BACKEND_FDS, offsetof(config_data_t, pd_adc_cnt), 1, false, &dflt_adc_cnt },
|
||||
{ "pd_delay", MEM_BACKEND_FDS, offsetof(config_data_t, pd_delay_us), 2, false, &dflt_pd_delay },
|
||||
|
||||
/* W25Q32 entries (외장 Flash가 없으면 제거) */
|
||||
{ "agc_gain", MEM_BACKEND_W25Q32, 0x000000, 96, false, dflt_agc },
|
||||
{ "life_cycle", MEM_BACKEND_W25Q32, 0x000060, 4, false, &dflt_life_cycle },
|
||||
};
|
||||
```
|
||||
|
||||
**수정 방법:**
|
||||
|
||||
1. config_data_t에서 필드를 추가/삭제했으면 테이블도 동일하게 수정
|
||||
2. `offsetof(config_data_t, 필드명)` 은 반드시 정확해야 함
|
||||
3. 기본값 `static const` 변수도 파일 상단에 추가
|
||||
4. **W25Q32가 없으면** W25Q32 엔트리 전부 제거 + `#include "drivers/w25q32/w25q32.h"` 제거
|
||||
5. **W25Q32가 없으면** `w25q_backend_write()`, `w25q_backend_read()` 함수도 제거
|
||||
|
||||
### 3.2 W25Q32 없는 프로젝트 (FDS만 사용)
|
||||
|
||||
외장 Flash가 없는 프로젝트의 경우 dr_mem.c를 다음과 같이 단순화:
|
||||
|
||||
```c
|
||||
/* W25Q32 관련 코드 전부 제거 */
|
||||
|
||||
// #include "drivers/w25q32/w25q32.h" ← 삭제
|
||||
|
||||
// w25q_backend_write() 함수 ← 삭제
|
||||
// w25q_backend_read() 함수 ← 삭제
|
||||
|
||||
// mem_table에서 MEM_BACKEND_W25Q32 엔트리 ← 삭제
|
||||
|
||||
// dr_memWrite / dr_memRead의 switch문에서
|
||||
// case MEM_BACKEND_W25Q32: ← 삭제
|
||||
```
|
||||
|
||||
### 3.3 dr_mem.h는 수정 불필요
|
||||
|
||||
`dr_mem.h`는 범용 인터페이스이므로 그대로 사용한다.
|
||||
|
||||
---
|
||||
|
||||
## Step 4: device_config 전역변수 수정
|
||||
|
||||
### 4.1 config/device_config.h 수정
|
||||
|
||||
프로젝트에서 런타임에 사용하는 전역변수를 선언한다.
|
||||
이 변수들은 부팅 시 `m_config` (FDS)에서 복사된다.
|
||||
|
||||
```c
|
||||
#ifndef DEVICE_CONFIG_H
|
||||
#define DEVICE_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* ---- 프로젝트별 전역변수 extern 선언 ---- */
|
||||
extern char m_static_passkey[7]; /* BLE 패스키 (6자리 + null) */
|
||||
extern char SERIAL_NO[16]; /* 시리얼 번호 (BLE 디바이스 이름으로 사용) */
|
||||
extern bool bond_data_delete;
|
||||
// extern uint16_t my_custom_var; /* 추가 변수 */
|
||||
|
||||
void load_device_configuration(void);
|
||||
void load_default_values(void);
|
||||
bool is_valid_serial_no(const char *serial);
|
||||
bool is_valid_passkey(const char *passkey);
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
### 4.2 config/device_config.c 수정
|
||||
|
||||
**전역변수 정의부:**
|
||||
|
||||
```c
|
||||
char m_static_passkey[7] = "123456";
|
||||
char SERIAL_NO[16] = "2025XXXX00001";
|
||||
bool bond_data_delete = true;
|
||||
// uint16_t my_custom_var = 0;
|
||||
```
|
||||
|
||||
**load_default_values() 함수** - 각 전역변수의 기본값 설정:
|
||||
|
||||
```c
|
||||
void load_default_values(void)
|
||||
{
|
||||
memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
|
||||
memcpy(SERIAL_NO, "2025AAAAT001", 12);
|
||||
|
||||
memset(m_static_passkey, 0, sizeof(m_static_passkey));
|
||||
memcpy(m_static_passkey, "123456", 6);
|
||||
|
||||
bond_data_delete = 1;
|
||||
// my_custom_var = 0;
|
||||
}
|
||||
```
|
||||
|
||||
**load_device_configuration() 함수** - FDS(m_config)에서 전역변수로 복사:
|
||||
|
||||
```c
|
||||
void load_device_configuration(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
load_default_values();
|
||||
|
||||
/* FDS → 전역변수 복사 */
|
||||
err_code = dr_memRead("serial_no", (uint8_t *)SERIAL_NO, 12);
|
||||
if (err_code != NRF_SUCCESS || !is_valid_serial_no(SERIAL_NO)) {
|
||||
memcpy(SERIAL_NO, "2025AAAAT001", 12); /* 폴백 */
|
||||
}
|
||||
|
||||
err_code = dr_memRead("passkey", (uint8_t *)m_static_passkey, 6);
|
||||
if (err_code != NRF_SUCCESS || !is_valid_passkey(m_static_passkey)) {
|
||||
memcpy(m_static_passkey, "123456", 6);
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t raw = 0;
|
||||
dr_memRead("bond_delete", &raw, 1);
|
||||
bond_data_delete = (raw != 0);
|
||||
}
|
||||
|
||||
// dr_memRead("my_custom_key", &my_custom_var, 2);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: BLE 명령어 핸들러 추가
|
||||
|
||||
### 5.1 cmd/cmd.c에 include 추가
|
||||
|
||||
```c
|
||||
#include "../storage/dr_mem.h" /* dr_memRead, dr_memWrite */
|
||||
#include "../fstorage.h" /* m_config */
|
||||
#include "../debug_print.h"
|
||||
```
|
||||
|
||||
### 5.2 읽기 핸들러 패턴 (mr* → rr*)
|
||||
|
||||
config_data_t의 각 필드 타입에 따라 4가지 패턴을 사용한다.
|
||||
|
||||
**패턴 A: ASCII 문자열 (char[]) 필드**
|
||||
|
||||
```c
|
||||
static int Cmd_mrh(const ParsedCmd *cmd)
|
||||
{
|
||||
(void)cmd;
|
||||
char buf[12];
|
||||
dr_memRead("hw_no", buf, 12);
|
||||
ascii_format_data(ble_bin_buffer, "rrh:", buf, 12);
|
||||
binary_tx_handler(ble_bin_buffer, 8); /* (4+12)/2 = 8 words */
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
**패턴 B: uint8_t 바이트 배열 필드**
|
||||
|
||||
```c
|
||||
static int Cmd_mrp(const ParsedCmd *cmd)
|
||||
{
|
||||
(void)cmd;
|
||||
uint8_t buf[6];
|
||||
dr_memRead("passkey", buf, 6);
|
||||
format_data_byte(ble_bin_buffer, "rrp:", buf, 6);
|
||||
binary_tx_handler(ble_bin_buffer, 5); /* (4+6)/2 = 5 words */
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
**패턴 C: 단일 숫자 값 (1~2 byte)**
|
||||
|
||||
```c
|
||||
static int Cmd_mrb(const ParsedCmd *cmd)
|
||||
{
|
||||
(void)cmd;
|
||||
uint8_t val = 0;
|
||||
dr_memRead("bond_delete", &val, 1);
|
||||
dr_ble_return_1("rrb:", (uint16_t)val); /* tag + uint16 응답 */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Cmd_mrd(const ParsedCmd *cmd)
|
||||
{
|
||||
(void)cmd;
|
||||
uint16_t val = 0;
|
||||
dr_memRead("pd_delay", &val, 2);
|
||||
dr_ble_return_1("rrd:", val);
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
**패턴 D: uint32_t 값 (hi16 + lo16로 분할)**
|
||||
|
||||
```c
|
||||
static int Cmd_mrl(const ParsedCmd *cmd)
|
||||
{
|
||||
(void)cmd;
|
||||
uint32_t val = 0;
|
||||
dr_memRead("life_cycle", &val, 4);
|
||||
uint16_t hi = (uint16_t)(val >> 16);
|
||||
uint16_t lo = (uint16_t)(val & 0xFFFF);
|
||||
dr_ble_return_2("rrl:", hi, lo); /* tag + uint16 x 2 응답 */
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 쓰기 핸들러 패턴 (mw* → rw*)
|
||||
|
||||
**패턴 A: ASCII 문자열 쓰기**
|
||||
|
||||
```c
|
||||
static int Cmd_mwh(const ParsedCmd *cmd)
|
||||
{
|
||||
char hw[13];
|
||||
memset(hw, 0, sizeof(hw));
|
||||
cmd_get_ascii(cmd, 0, hw, 12);
|
||||
ret_code_t err = dr_memWrite("hw_no", hw, 12);
|
||||
dr_ble_return_1("rwh:", err == NRF_SUCCESS ? 0 : 1);
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
**패턴 B: uint8/uint16 값 쓰기**
|
||||
|
||||
```c
|
||||
static int Cmd_mwb(const ParsedCmd *cmd)
|
||||
{
|
||||
uint16_t val = 0;
|
||||
(void)cmd_get_u16(cmd, 0, &val);
|
||||
uint8_t u8val = (uint8_t)val;
|
||||
ret_code_t err = dr_memWrite("bond_delete", &u8val, 1);
|
||||
dr_ble_return_1("rwb:", err == NRF_SUCCESS ? 0 : 1);
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
**패턴 C: uint16 배열 쓰기 + RAM 동기화**
|
||||
|
||||
```c
|
||||
static int Cmd_mwg(const ParsedCmd *cmd)
|
||||
{
|
||||
uint16_t gains[48];
|
||||
uint8_t i;
|
||||
for (i = 0; i < 48; i++) {
|
||||
if (!cmd_get_u16(cmd, i, &gains[i])) gains[i] = 0;
|
||||
}
|
||||
ret_code_t err = dr_memWrite("agc_gain", gains, sizeof(gains));
|
||||
/* RAM 전역변수도 즉시 업데이트 */
|
||||
for (i = 0; i < 48; i++) led_pd_dac_v[i] = gains[i];
|
||||
dr_ble_return_1("rwg:", err == NRF_SUCCESS ? 0 : 1);
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
**패턴 D: uint32 쓰기 (hi16 + lo16 결합)**
|
||||
|
||||
```c
|
||||
static int Cmd_mwl(const ParsedCmd *cmd)
|
||||
{
|
||||
uint16_t hi = 0, lo = 0;
|
||||
(void)cmd_get_u16(cmd, 0, &hi);
|
||||
(void)cmd_get_u16(cmd, 1, &lo);
|
||||
uint32_t life = ((uint32_t)hi << 16) | (uint32_t)lo;
|
||||
ret_code_t err = dr_memWrite("life_cycle", &life, 4);
|
||||
m_life_cycle = life; /* RAM 전역변수도 즉시 업데이트 */
|
||||
dr_ble_return_1("rwl:", err == NRF_SUCCESS ? 0 : 1);
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 binary_tx_handler 호출 시 word 수 계산
|
||||
|
||||
```
|
||||
words = (TAG 4바이트 + DATA 바이트 수) / 2
|
||||
|
||||
예시:
|
||||
ASCII 12B 응답: (4 + 12) / 2 = 8 words
|
||||
Byte 6B 응답: (4 + 6) / 2 = 5 words
|
||||
uint16 1개 응답: dr_ble_return_1 사용 (내부에서 3 words)
|
||||
uint16 2개 응답: dr_ble_return_2 사용 (내부에서 4 words)
|
||||
```
|
||||
|
||||
### 5.5 명령어 테이블에 등록
|
||||
|
||||
`g_cmd_table[]`에 핸들러 엔트리 추가:
|
||||
|
||||
```c
|
||||
CmdEntry g_cmd_table[] = {
|
||||
/* ... 기존 명령어들 ... */
|
||||
|
||||
/* N. Memory Read (dr_mem) */
|
||||
{ "mrh?", true, Cmd_mrh }, /* hw_no → rrh: + 12B ASCII */
|
||||
{ "mrs?", true, Cmd_mrs }, /* serial_no → rrs: + 12B ASCII */
|
||||
{ "mrp?", true, Cmd_mrp }, /* passkey → rrp: + 6B */
|
||||
{ "mrb?", true, Cmd_mrb }, /* bond_delete → rrb: + uint16 */
|
||||
{ "mrr?", true, Cmd_mrr }, /* reset_status → rrr: + uint16 */
|
||||
/* ... 프로젝트별 추가 mr* 명령어 ... */
|
||||
|
||||
/* O. Memory Write (dr_mem) */
|
||||
{ "mwh?", true, Cmd_mwh }, /* hw_no ← 12B ASCII → rwh: */
|
||||
{ "mws?", true, Cmd_mws }, /* serial_no ← 12B ASCII → rws: */
|
||||
{ "mwp?", true, Cmd_mwp }, /* passkey ← 6B → rwp: */
|
||||
{ "mwb?", true, Cmd_mwb }, /* bond_delete ← uint16 → rwb: */
|
||||
{ "mwr?", true, Cmd_mwr }, /* reset_status ← uint16 → rwr: */
|
||||
/* ... 프로젝트별 추가 mw* 명령어 ... */
|
||||
};
|
||||
```
|
||||
|
||||
### 5.6 forward declaration 추가
|
||||
|
||||
`g_cmd_table[]` 앞에 핸들러 함수 선언:
|
||||
|
||||
```c
|
||||
/* Memory Read handlers */
|
||||
static int Cmd_mrh(const ParsedCmd *cmd);
|
||||
static int Cmd_mrs(const ParsedCmd *cmd);
|
||||
static int Cmd_mrp(const ParsedCmd *cmd);
|
||||
/* ... */
|
||||
|
||||
/* Memory Write handlers */
|
||||
static int Cmd_mwh(const ParsedCmd *cmd);
|
||||
static int Cmd_mws(const ParsedCmd *cmd);
|
||||
static int Cmd_mwp(const ParsedCmd *cmd);
|
||||
/* ... */
|
||||
```
|
||||
|
||||
### 5.7 명명 규칙
|
||||
|
||||
```
|
||||
읽기: mr + 필드 약자 + ? → 응답: rr + 필드 약자 + :
|
||||
쓰기: mw + 필드 약자 + ? → 응답: rw + 필드 약자 + :
|
||||
|
||||
약자 예시:
|
||||
h = hw_no
|
||||
s = serial_no
|
||||
p = passkey
|
||||
b = bond_delete
|
||||
r = reset_status
|
||||
c = pd_adc_cnt (count)
|
||||
d = pd_delay
|
||||
g = agc_gain
|
||||
l = life_cycle
|
||||
```
|
||||
|
||||
새 필드를 추가할 때는 기존 약자와 겹치지 않는 알파벳 사용.
|
||||
|
||||
---
|
||||
|
||||
## Step 6: main.c 초기화 순서 통합
|
||||
|
||||
### 6.1 필수 초기화 순서
|
||||
|
||||
```c
|
||||
int main(void)
|
||||
{
|
||||
/* ... GPIO, Timer 등 초기화 ... */
|
||||
|
||||
/* ① BLE 스택 초기화 (SoftDevice 활성화) - 반드시 FDS보다 먼저! */
|
||||
ble_stack_init();
|
||||
|
||||
/* ② FDS 초기화 */
|
||||
fs_storage_init(); /* fds_register + fds_init + wait_for_fds_ready */
|
||||
|
||||
/* ③ FDS에서 config_data_t 로드 (→ m_config 전역변수) */
|
||||
config_load(); /* = fs_set_value() 호출해도 동일 */
|
||||
|
||||
/* ④ m_config → 런타임 전역변수 복사 */
|
||||
load_device_configuration();
|
||||
|
||||
/* ⑤ GAP 파라미터 설정 (SERIAL_NO로 디바이스 이름 설정) */
|
||||
gap_params_init();
|
||||
gatt_init();
|
||||
services_init();
|
||||
advertising_init();
|
||||
|
||||
/* ... 나머지 초기화 ... */
|
||||
|
||||
for (;;) {
|
||||
idle_state_handle();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 순서를 반드시 지켜야 하는 이유
|
||||
|
||||
| 순서 | 이유 |
|
||||
|------|------|
|
||||
| ① → ② | SoftDevice가 FDS 이벤트를 전달해야 하므로 BLE 스택이 먼저 |
|
||||
| ② → ③ | FDS 초기화 완료 후에야 레코드 검색 가능 |
|
||||
| ③ → ④ | m_config에 FDS 데이터가 로드된 후에 전역변수로 복사 |
|
||||
| ④ → ⑤ | SERIAL_NO가 설정된 후에 GAP 이름 설정 |
|
||||
|
||||
### 6.3 필요한 extern/include
|
||||
|
||||
```c
|
||||
/* main.c에 추가할 include */
|
||||
#include "fstorage.h"
|
||||
#include "config/device_config.h"
|
||||
|
||||
/* main.c에 추가할 extern (전원 제어 플래그) */
|
||||
bool go_device_power_off = false;
|
||||
bool go_sleep_mode_enter = false;
|
||||
bool go_NVIC_SystemReset = false;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 7: sdk_config.h 설정
|
||||
|
||||
### 7.1 필수 설정
|
||||
|
||||
```c
|
||||
/* FDS 활성화 */
|
||||
#define FDS_ENABLED 1
|
||||
#define FDS_VIRTUAL_PAGES 50 /* 50 pages (Wear Leveling용) */
|
||||
#define FDS_VIRTUAL_PAGE_SIZE 2048 /* words (= 8KB per page) */
|
||||
#define FDS_CRC_CHECK_ON_READ 0 /* 0 권장 (성능) */
|
||||
#define FDS_CRC_CHECK_ON_WRITE 0 /* 0 권장 (성능) */
|
||||
#define FDS_BACKEND 2 /* 2 = nrf_fstorage_sd (SoftDevice 사용) */
|
||||
#define FDS_OP_QUEUE_SIZE 4
|
||||
|
||||
/* Flash Storage 활성화 */
|
||||
#define NRF_FSTORAGE_ENABLED 1
|
||||
|
||||
/* SOC 이벤트 핸들러 (FDS가 SoftDevice에서 이벤트 받기 위해 필수) */
|
||||
#define NRF_SDH_SOC_ENABLED 1
|
||||
#define NRF_SDH_SOC_OBSERVER_PRIO_LEVELS 2
|
||||
|
||||
/* 전력 관리 (wait_for_fds_ready에서 사용) */
|
||||
#define NRF_PWR_MGMT_ENABLED 1
|
||||
```
|
||||
|
||||
### 7.2 선택 설정
|
||||
|
||||
```c
|
||||
/* RTT 디버그 버퍼 (DBG_PRINTF 사용 시) */
|
||||
#define SEGGER_RTT_CONFIG_BUFFER_SIZE_UP 1024
|
||||
#define NRF_LOG_BACKEND_RTT_ENABLED 1
|
||||
```
|
||||
|
||||
### 7.3 Flash 메모리 맵 확인
|
||||
|
||||
FDS가 사용하는 Flash 영역은 링커 스크립트 또는 `nrf_dfu_types.h`에서 설정.
|
||||
기본적으로 **Application 영역 끝부분**에 배치된다.
|
||||
|
||||
FDS_VIRTUAL_PAGES를 늘리면 Application 코드 공간이 줄어드므로 주의.
|
||||
|
||||
---
|
||||
|
||||
## Step 8: Keil 프로젝트에 파일 추가
|
||||
|
||||
### 8.1 Source 파일 추가
|
||||
|
||||
Keil uVision → Project → Manage Project Items에서 다음 파일 추가:
|
||||
|
||||
| 그룹 | 파일 | 경로 |
|
||||
|------|------|------|
|
||||
| Application | `fstorage.c` | `../../fstorage.c` |
|
||||
| Application | `device_config.c` | `../../config/device_config.c` |
|
||||
| Application | `dr_mem.c` | `../../storage/dr_mem.c` |
|
||||
| mt_parser | `parser.c` | `mt_parser/parser.c` (이미 있으면 스킵) |
|
||||
| mt_parser | `dr_util.c` | `mt_parser/dr_util/dr_util.c` (이미 있으면 스킵) |
|
||||
| nRF_Libraries | `fds.c` | SDK `components/libraries/fds/fds.c` |
|
||||
| nRF_Libraries | `nrf_fstorage.c` | SDK `components/libraries/fstorage/nrf_fstorage.c` |
|
||||
| nRF_Libraries | `nrf_fstorage_sd.c` | SDK `components/libraries/fstorage/nrf_fstorage_sd.c` |
|
||||
| nRF_Libraries | `nrf_atflags.c` | SDK `components/libraries/atomic_flags/nrf_atflags.c` |
|
||||
|
||||
### 8.2 Include Path 추가
|
||||
|
||||
```
|
||||
Options → C/C++ → Include Paths에 추가:
|
||||
|
||||
../../storage
|
||||
../../config
|
||||
../../cmd
|
||||
<mt_parser 경로>
|
||||
<mt_parser>/dr_util
|
||||
```
|
||||
|
||||
### 8.3 Nordic SDK Include 확인
|
||||
|
||||
다음 SDK 경로들이 이미 Include Path에 있는지 확인:
|
||||
|
||||
```
|
||||
.../components/libraries/fds
|
||||
.../components/libraries/fstorage
|
||||
.../components/libraries/atomic_flags
|
||||
.../components/libraries/experimental_section_vars
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 9: 빌드 및 검증
|
||||
|
||||
### 9.1 빌드
|
||||
|
||||
Keil에서 Build (F7). 예상되는 에러와 해결:
|
||||
|
||||
| 에러 | 원인 | 해결 |
|
||||
|------|------|------|
|
||||
| `config_data_t` undeclared | fstorage.h include 누락 | `#include "fstorage.h"` 추가 |
|
||||
| `dr_memRead` undeclared | dr_mem.h include 누락 | `#include "storage/dr_mem.h"` 추가 |
|
||||
| `w25q32_read` undefined | W25Q32 드라이버 없음 | dr_mem.c에서 W25Q32 코드 제거 |
|
||||
| `device_power_off` undefined | power_ctrl 미구현 | 빈 함수 작성 또는 FDS_EVT_UPDATE에서 제거 |
|
||||
| Linker: FDS region overlap | Flash 영역 충돌 | FDS_VIRTUAL_PAGES 줄이기 (예: 10) |
|
||||
| `go_device_power_off` undeclared | main.c에 정의 안 됨 | main.c에 `bool go_device_power_off = false;` 추가 |
|
||||
|
||||
### 9.2 RTT 로그로 검증
|
||||
|
||||
부팅 후 RTT 로그에서 다음 확인:
|
||||
|
||||
```
|
||||
[FDS] OK ← FDS 초기화 성공
|
||||
[FDS] find rc=0 ← 레코드 발견 (두 번째 부팅부터)
|
||||
[FDS] Loaded OK ← 정상 로드
|
||||
[CFG] FDS serial_no='2025AAAAT001' ← 시리얼 번호 확인
|
||||
```
|
||||
|
||||
첫 부팅 시에는:
|
||||
|
||||
```
|
||||
[FDS] OK
|
||||
[FDS] find rc=34313 ← 레코드 없음 (정상)
|
||||
[FDS] retry 1/10 ← 재시도
|
||||
[FDS] New - writing defaults ← 기본값으로 새 레코드 생성
|
||||
[FDS] find rc=0
|
||||
[FDS] Loaded OK
|
||||
```
|
||||
|
||||
### 9.3 BLE 명령어 검증
|
||||
|
||||
1. BLE 연결
|
||||
2. `mrs?` 전송 → `rrs:2025AAAAT001` 응답 확인
|
||||
3. `mws?2025TEST0001` 전송 → `rws:0000` (성공) 응답 확인
|
||||
4. 재부팅
|
||||
5. `mrs?` 전송 → `rrs:2025TEST0001` 응답 확인 (값 유지됨)
|
||||
|
||||
---
|
||||
|
||||
## 핵심 주의사항
|
||||
|
||||
### 1. config_load()의 재시도 로직을 절대 제거하지 말 것
|
||||
|
||||
```c
|
||||
if (rc != NRF_SUCCESS && cfg_retry < 10) {
|
||||
cfg_retry++;
|
||||
nrf_delay_ms(100);
|
||||
goto cfg_load_start;
|
||||
}
|
||||
```
|
||||
|
||||
이 코드가 없으면 부팅 시 FDS가 아직 준비되지 않았을 때 저장된 값을 기본값으로 덮어쓴다.
|
||||
|
||||
### 2. config_save()에서 절대 대기하지 말 것
|
||||
|
||||
`config_save()`는 BLE 콜백 컨텍스트에서 호출될 수 있다.
|
||||
내부에서 `while(fds_flag_write)` 등으로 대기하면 **교착 상태** 발생.
|
||||
fire-and-forget 패턴을 유지해야 한다.
|
||||
|
||||
### 3. printf vs DBG_PRINTF 구분
|
||||
|
||||
| 함수 | 출력 경로 | RTT에서 보이나? |
|
||||
|------|----------|----------------|
|
||||
| `printf()` | UART | X |
|
||||
| `DBG_PRINTF()` | RTT (또는 UART, 설정에 따라) | O |
|
||||
|
||||
FDS 관련 디버그는 반드시 `DBG_PRINTF()` 사용.
|
||||
|
||||
### 4. BLE 디바이스 이름은 부팅 시 한 번만 설정됨
|
||||
|
||||
`gap_params_init()` → `sd_ble_gap_device_name_set(SERIAL_NO)`는 부팅 시 한 번만 호출.
|
||||
`mws?`로 시리얼 변경해도 재부팅 전까지 BLE 광고 이름은 바뀌지 않는다.
|
||||
|
||||
### 5. fds_record_update는 비동기
|
||||
|
||||
`fds_record_update()` 호출 → 즉시 리턴 → 실제 Flash 쓰기는 나중에 수행 →
|
||||
`FDS_EVT_UPDATE` 이벤트에서 `fds_flag_write = false`.
|
||||
|
||||
연속 쓰기 시 이전 쓰기가 완료될 때까지 다음 쓰기가 스킵될 수 있다 (`[CFG_SAVE] busy, skipped`).
|
||||
|
||||
### 6. #pragma pack(1) 필수
|
||||
|
||||
`config_data_t`에 `#pragma pack(1)`이 없으면 컴파일러가 패딩을 삽입한다.
|
||||
이 경우 `offsetof()` 값이 달라지고, FDS에서 읽은 데이터가 엉뚱한 필드에 매핑된다.
|
||||
|
||||
---
|
||||
|
||||
## 체크리스트
|
||||
|
||||
새 프로젝트에 이식할 때 이 체크리스트를 순서대로 확인:
|
||||
|
||||
```
|
||||
[ ] 1. 파일 복사 (fstorage.c/h, dr_mem.c/h, device_config.c/h)
|
||||
[ ] 2. config_data_t 구조체 수정 (fstorage.h)
|
||||
[ ] 3. CONFIG_MAGIC_NUMBER_VALUE 날짜 변경 (fstorage.c)
|
||||
[ ] 4. fds_default_value_set() 기본값 수정 (fstorage.c)
|
||||
[ ] 5. dr_mem.c 룩업 테이블 수정 (필드 추가/삭제)
|
||||
[ ] 6. dr_mem.c 기본값 static const 변수 수정
|
||||
[ ] 7. W25Q32 없으면 dr_mem.c에서 W25Q32 관련 코드 제거
|
||||
[ ] 8. device_config.h 전역변수 extern 수정
|
||||
[ ] 9. device_config.c 전역변수 정의 + load 함수 수정
|
||||
[ ] 10. cmd/cmd.c에 mr*/mw* 핸들러 추가
|
||||
[ ] 11. cmd/cmd.c의 g_cmd_table[]에 명령어 등록
|
||||
[ ] 12. main.c 초기화 순서 확인 (BLE → FDS → config_load → device_config → GAP)
|
||||
[ ] 13. main.c에 go_device_power_off 등 플래그 정의
|
||||
[ ] 14. sdk_config.h FDS 설정 확인
|
||||
[ ] 15. Keil 프로젝트에 .c 파일 추가
|
||||
[ ] 16. Keil Include Path 추가
|
||||
[ ] 17. 빌드 성공 확인
|
||||
[ ] 18. RTT 로그로 FDS 초기화 + 로드 확인
|
||||
[ ] 19. BLE mr*/mw* 명령어 읽기/쓰기 검증
|
||||
[ ] 20. 재부팅 후 값 유지 검증
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 빠른 참조: 새 필드 추가 시 수정 위치 요약
|
||||
|
||||
새 필드 `my_field`(uint16_t, 2B)를 추가할 때:
|
||||
|
||||
| 순서 | 파일 | 수정 내용 |
|
||||
|------|------|----------|
|
||||
| 1 | `fstorage.h` | `config_data_t`에 `uint16_t my_field;` 추가 |
|
||||
| 2 | `fstorage.c` | `fds_default_value_set()`에 `m_config.my_field = 1000;` |
|
||||
| 3 | `storage/dr_mem.c` | 기본값: `static const uint16_t dflt_my_field = 1000;` |
|
||||
| 4 | `storage/dr_mem.c` | 테이블: `{ "my_field", MEM_BACKEND_FDS, offsetof(config_data_t, my_field), 2, false, &dflt_my_field }` |
|
||||
| 5 | `config/device_config.h` | `extern uint16_t my_field_var;` |
|
||||
| 6 | `config/device_config.c` | `uint16_t my_field_var = 1000;` + `dr_memRead("my_field", ...)` |
|
||||
| 7 | `cmd/cmd.c` | `Cmd_mrf()` 읽기 핸들러 + `Cmd_mwf()` 쓰기 핸들러 |
|
||||
| 8 | `cmd/cmd.c` | `g_cmd_table[]`에 `{ "mrf?", true, Cmd_mrf }`, `{ "mwf?", true, Cmd_mwf }` |
|
||||
|
||||
**magic_number를 변경하면** 기존 디바이스의 저장 데이터가 초기화된다.
|
||||
구조체 크기만 바뀌고 기존 필드 순서가 유지되면 magic_number를 유지해도 되지만,
|
||||
필드 순서가 바뀌면 반드시 magic_number를 변경해야 한다.
|
||||
|
||||
---
|
||||
|
||||
*문서 작성일: 2026-03-05*
|
||||
*기준 프로젝트: VivaMyo (ble_app_vivaMayo)*
|
||||
@@ -0,0 +1,589 @@
|
||||
# VivaMyo BLE Application - 프로그램 아키텍처 문서
|
||||
|
||||
## 1. 프로젝트 개요
|
||||
|
||||
VivaMyo는 nRF52840 기반 BLE 의료 기기 펌웨어로, 방광 모니터링을 위한 NIRS(Near-Infrared Spectroscopy) 장치입니다.
|
||||
|
||||
### 1.1 주요 특징
|
||||
- **MCU**: Nordic nRF52840
|
||||
- **통신**: BLE (Nordic UART Service)
|
||||
- **보안**: LESC 페어링, Static Passkey
|
||||
- **센서**: LED 48개, Photodetector, IMU (ICM42670P), 온도센서, 압력센서
|
||||
- **저장**: EEPROM (설정값 암호화 저장)
|
||||
|
||||
---
|
||||
|
||||
## 2. 시스템 아키텍처
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ main.c │
|
||||
│ (Application Entry Point) │
|
||||
└──────────────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────┼──────────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌─────────────────┐ ┌─────────────────────┐
|
||||
│ BLE Stack │ │ Power Control │ │ Timer Management │
|
||||
│ (ble_core.c) │ │ (power_ctrl.c) │ │ (main_timer.c) │
|
||||
└───────┬───────┘ └─────────────────┘ └─────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ nus_data_handler() │
|
||||
│ (BLE Data Reception) │
|
||||
└──────────────────────────────┬────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ received_command_process() │
|
||||
│ (cmd_parse.c) │
|
||||
└──────────────────────────────┬────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────┴──────────────────────┐
|
||||
▼ ▼
|
||||
┌───────────────────────┐ ┌─────────────────────┐
|
||||
│ Mt_parser │ │ Legacy Parser │
|
||||
│ (dr_cmd_parser) │ │ (cmd_parse.c) │
|
||||
│ │ │ │
|
||||
│ parser.c + cmd.c │ │ 기존 명령어 처리 │
|
||||
└───────────┬───────────┘ └─────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ Command Handlers (cmd.c) │
|
||||
│ │
|
||||
│ Cmd_mta, Cmd_sta, Cmd_mcj, Cmd_msn, Cmd_mag ... │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Mt_parser 모듈 구조
|
||||
|
||||
Mt_parser는 명령어 파싱과 디스패치를 담당하는 독립 모듈입니다.
|
||||
|
||||
### 3.1 파일 구성
|
||||
|
||||
| 파일 | 경로 | 설명 |
|
||||
|------|------|------|
|
||||
| `parser.h` | `/mt_parser/parser.h` | 파서 인터페이스 정의 |
|
||||
| `parser.c` | `/mt_parser/parser.c` | CRC 검증, TAG 추출, 명령어 디스패치 |
|
||||
| `cmd.h` | `cmd/cmd.h` | 명령어 테이블 구조체 정의 |
|
||||
| `cmd.c` | `cmd/cmd.c` | 명령어 핸들러 구현 및 테이블 |
|
||||
| `dr_util.h/c` | `/mt_parser/dr_util/` | BLE 응답 유틸리티 함수 |
|
||||
|
||||
### 3.2 핵심 구조체
|
||||
|
||||
```c
|
||||
// parser.h - 플랫폼 인터페이스
|
||||
typedef struct {
|
||||
void (*log)(const char *fmt, ...); // 로그 출력 함수
|
||||
void (*tx_bin)(const uint8_t *buf, uint16_t len); // BLE 전송 함수
|
||||
bool crc_check; // CRC 검사 활성화 여부
|
||||
} dr_platform_if_t;
|
||||
|
||||
// cmd.h - 파싱된 명령어 구조체
|
||||
typedef struct {
|
||||
char tag[5]; // 4글자 명령어 + NULL ("sta?")
|
||||
uint8_t data[CMD_MAX_DATA]; // TAG 이후 데이터
|
||||
uint8_t data_len; // 데이터 길이
|
||||
} ParsedCmd;
|
||||
|
||||
// cmd.h - 명령어 테이블 엔트리
|
||||
typedef struct {
|
||||
char tag[5]; // 명령어 TAG ("sta?")
|
||||
bool enabled; // 활성화 여부
|
||||
cmd_handler_t handler; // 핸들러 함수 포인터
|
||||
} CmdEntry;
|
||||
```
|
||||
|
||||
### 3.3 전역 변수
|
||||
|
||||
```c
|
||||
extern dr_platform_if_t g_plat; // 플랫폼 인터페이스
|
||||
extern bool g_log_enable; // 로그 활성화 플래그
|
||||
extern CmdEntry g_cmd_table[]; // 명령어 테이블
|
||||
extern const uint16_t g_cmd_count; // 명령어 개수
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 명령어 처리 흐름
|
||||
|
||||
### 4.1 데이터 수신 경로
|
||||
|
||||
```
|
||||
[BLE Central]
|
||||
│
|
||||
▼ (NUS RX Characteristic Write)
|
||||
[ble_core.c:nus_data_handler()]
|
||||
│
|
||||
▼
|
||||
[cmd_parse.c:received_command_process(data, CMD_BLE, length)]
|
||||
│
|
||||
├─► [Mt_parser 초기화] (최초 1회)
|
||||
│ g_plat.log = log_printf
|
||||
│ g_plat.tx_bin = binary_tx_handler
|
||||
│ g_plat.crc_check = true
|
||||
│
|
||||
▼
|
||||
[parser.c:dr_cmd_parser(data, length)]
|
||||
│
|
||||
├─► [CRC16 검증] → 실패시 "crc!" 응답
|
||||
│
|
||||
├─► [TAG 추출] → 4글자 명령어 파싱
|
||||
│
|
||||
▼
|
||||
[dr_cmd_dispatch()] → g_cmd_table 검색
|
||||
│
|
||||
├─► [명령어 발견 + 활성화] → handler() 호출
|
||||
│
|
||||
├─► [명령어 발견 + 비활성화] → return 0
|
||||
│
|
||||
└─► [명령어 미발견] → return 0 (Legacy 파서로 fallback)
|
||||
```
|
||||
|
||||
### 4.2 CRC16 검증
|
||||
|
||||
```c
|
||||
// 패킷 구조: [TAG(4)] [DATA(N)] [CRC_LO] [CRC_HI]
|
||||
// CRC 계산: TAG + DATA 영역
|
||||
// CRC 위치: 패킷 마지막 2바이트 (Little Endian)
|
||||
|
||||
static bool dr_crc16_check_packet(const uint8_t *packet, uint32_t packet_len)
|
||||
{
|
||||
uint32_t data_len = packet_len - 2;
|
||||
uint16_t expected_crc = (uint16_t)packet[packet_len - 2]
|
||||
| ((uint16_t)packet[packet_len - 1] << 8);
|
||||
return dr_crc16_check(packet, data_len, expected_crc);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 명령어 테이블 (cmd.c)
|
||||
|
||||
### 5.1 명령어 카테고리
|
||||
|
||||
| 카테고리 | 명령어 | 설명 |
|
||||
|----------|--------|------|
|
||||
| **A. Device Status** | `mta?`, `sta?`, `str?`, `mqq?` | 디바이스 상태 제어/조회 |
|
||||
| **B. AGC/Gain** | `mag?`, `sag?`, `sar?` | Auto Gain Control |
|
||||
| **C. LED DP Value** | `ssa?`, `sab?`, `ssb?`, `srb?` | LED 밝기 설정/조회 |
|
||||
| **D. LED On/Off** | `ssc?`, `ssd?`, `sse?`, `ssf?`, `sif?`, `ssg?` | LED/Gain 제어 |
|
||||
| **E. Simple PD** | `ssh?` | 간단한 PD 측정 |
|
||||
| **F. M48 Full** | `mcj?`, `scj?`, `sdj?`, `sej?`, `sfj?`, `ssj?`, `szj?` | 48채널 PD 측정 |
|
||||
| **G. IMM ADC** | `saj?` | Immediate ADC 측정 |
|
||||
| **H. PD Settings** | `ssk?`, `srk?`, `ssl?`, `srl?` | ADC 횟수/지연 설정 |
|
||||
| **I. Sensors** | `msn?`, `ssn?`, `spn?`, `sso?`, `ssp?` | 배터리/압력/온도/IMU |
|
||||
| **J. System** | `ssq?`, `ssr?`, `sss?`, `sst?`, `ssv?` | 전원/리셋/버전 |
|
||||
| **K. Serial/Passkey** | `ssz?`, `spz?`, `sqz?`, `srz?` | 시리얼번호/패스키 |
|
||||
| **L. EEPROM Array** | `sez?`, `sfz?`, `sgz?` | EEPROM 배열 R/W |
|
||||
| **M. HW/Life Cycle** | `siz?`, `shz?`, `sxz?`, `syz?` | HW번호/사용횟수 |
|
||||
| **N. Debug** | `cmd?` | GPIO 테스트 |
|
||||
|
||||
### 5.2 명령어 상세 테이블
|
||||
|
||||
```c
|
||||
CmdEntry g_cmd_table[] = {
|
||||
/* Debug */
|
||||
{ "cmd?", true, Cmd_cmd }, // GPIO 테스트
|
||||
|
||||
/* A. Device Status */
|
||||
{ "mta?", true, Cmd_mta }, // Device Activate/Sleep (신규)
|
||||
{ "sta?", true, Cmd_sta }, // Device Activate/Sleep (호환)
|
||||
{ "str?", false, Cmd_str }, // Status Read
|
||||
{ "mqq?", true, Cmd_mqq }, // Quick Measurement
|
||||
|
||||
/* B. AGC / Gain Measurement */
|
||||
{ "mag?", true, Cmd_mag }, // Full AGC (신규)
|
||||
{ "sag?", true, Cmd_mag }, // Full AGC (호환)
|
||||
{ "sar?", false, Cmd_sar }, // Read LED-PD Gain Array
|
||||
|
||||
/* C. LED Power / DP Value */
|
||||
{ "ssa?", false, Cmd_ssa }, // Single LED Power Read
|
||||
{ "sab?", false, Cmd_sab }, // All LED AGC Data Read
|
||||
{ "ssb?", false, Cmd_ssb }, // Single LED Power Write
|
||||
{ "srb?", false, Cmd_srb }, // Read All 48 LED DP
|
||||
|
||||
/* D. LED On/Off & Gain Control */
|
||||
{ "ssc?", false, Cmd_ssc }, // LED On/Off (index 0-47, 99=off)
|
||||
{ "ssd?", false, Cmd_ssd }, // AGC Switch On/Off
|
||||
{ "sse?", false, Cmd_sse }, // Measure DAC Voltage
|
||||
{ "ssf?", false, Cmd_ssf }, // Set LED DAC Value
|
||||
{ "sif?", false, Cmd_sif }, // Immediate Gain Set
|
||||
{ "ssg?", false, Cmd_ssg }, // Set PD Channel
|
||||
|
||||
/* E. Simple PD Measurement */
|
||||
{ "ssh?", false, Cmd_ssh }, // Simple PD Measurement
|
||||
|
||||
/* F. PD-ADC M48 Full Measurement */
|
||||
{ "mcj?", true, Cmd_mcj }, // MODE=2 (Pressure + M48) - 신규
|
||||
{ "scj?", true, Cmd_mcj }, // MODE=2 (호환)
|
||||
{ "sdj?", false, Cmd_sdj }, // MODE=3 (M48 Only)
|
||||
{ "sej?", false, Cmd_sej }, // MODE=4 (M48 + Batt + IMU)
|
||||
{ "sfj?", false, Cmd_sfj }, // MODE=5 (M48 + Init)
|
||||
{ "ssj?", false, Cmd_ssj }, // MODE=0 (Combined)
|
||||
{ "szj?", false, Cmd_szj }, // FAST Mode Settings
|
||||
|
||||
/* G. IMM ADC */
|
||||
{ "saj?", false, Cmd_saj }, // 4-LED Immediate ADC
|
||||
|
||||
/* H. PD-ADC Count & Delay */
|
||||
{ "ssk?", false, Cmd_ssk }, // Set ADC Count (8/16/24/32)
|
||||
{ "srk?", false, Cmd_srk }, // Read ADC Count
|
||||
{ "ssl?", false, Cmd_ssl }, // Set PD Delay (us)
|
||||
{ "srl?", false, Cmd_srl }, // Read PD Delay
|
||||
|
||||
/* I. Sensor Measurements */
|
||||
{ "msn?", true, Cmd_msn }, // Battery Level (신규)
|
||||
{ "ssn?", true, Cmd_msn }, // Battery Level (호환)
|
||||
{ "spn?", false, Cmd_spn }, // Pressure Measurement
|
||||
{ "sso?", false, Cmd_sso }, // Temperature Measurement
|
||||
{ "ssp?", false, Cmd_ssp }, // IMU Raw Data
|
||||
|
||||
/* J. Power / Reset / Version */
|
||||
{ "ssq?", false, Cmd_ssq }, // Power Off
|
||||
{ "ssr?", false, Cmd_ssr }, // Bond Delete + Reset
|
||||
{ "sss?", false, Cmd_sss }, // Device Reset
|
||||
{ "sst?", false, Cmd_sst }, // Ready Response
|
||||
{ "ssv?", false, Cmd_ssv }, // Firmware Version
|
||||
|
||||
/* K. Serial / Passkey */
|
||||
{ "ssz?", false, Cmd_ssz }, // Write Serial Number
|
||||
{ "spz?", false, Cmd_spz }, // Write Passkey
|
||||
{ "sqz?", false, Cmd_sqz }, // Read Passkey
|
||||
{ "srz?", false, Cmd_srz }, // Read Serial Number
|
||||
|
||||
/* L. EEPROM Array */
|
||||
{ "sez?", false, Cmd_sez }, // Write 48*uint16 Array
|
||||
{ "sfz?", false, Cmd_sfz }, // Read 48*uint16 Array
|
||||
{ "sgz?", false, Cmd_sgz }, // Load DP Preset
|
||||
|
||||
/* M. Hardware No / Life Cycle */
|
||||
{ "siz?", false, Cmd_siz }, // Read HW Number
|
||||
{ "shz?", false, Cmd_shz }, // Write HW Number
|
||||
{ "sxz?", false, Cmd_sxz }, // Write Life Cycle
|
||||
{ "syz?", false, Cmd_syz }, // Read Life Cycle
|
||||
};
|
||||
```
|
||||
|
||||
### 5.3 활성화된 명령어 (enabled=true)
|
||||
|
||||
| 명령어 | 핸들러 | 기능 |
|
||||
|--------|--------|------|
|
||||
| `cmd?` | `Cmd_cmd` | GPIO 핀 제어 테스트 |
|
||||
| `mta?` | `Cmd_mta` | 디바이스 활성화/슬립 |
|
||||
| `sta?` | `Cmd_sta` | 디바이스 활성화/슬립 (호환) |
|
||||
| `mqq?` | `Cmd_mqq` | 빠른 측정 시작 |
|
||||
| `mag?` | `Cmd_mag` | Full AGC 측정 |
|
||||
| `sag?` | `Cmd_mag` | Full AGC 측정 (호환) |
|
||||
| `mcj?` | `Cmd_mcj` | M48 전체 측정 (MODE=2) |
|
||||
| `scj?` | `Cmd_mcj` | M48 전체 측정 (호환) |
|
||||
| `msn?` | `Cmd_msn` | 배터리 레벨 측정 |
|
||||
| `ssn?` | `Cmd_msn` | 배터리 레벨 측정 (호환) |
|
||||
|
||||
---
|
||||
|
||||
## 6. main.c와의 연결
|
||||
|
||||
### 6.1 초기화 순서
|
||||
|
||||
```c
|
||||
int main(void)
|
||||
{
|
||||
// 1. 기본 초기화
|
||||
uart_handler_init(); // UART 디버그
|
||||
log_init(); // NRF 로그
|
||||
gpio_init(); // GPIO 설정 (LED, PD, GAIN_SW)
|
||||
timers_init(); // 타이머 초기화
|
||||
|
||||
// 2. 설정 로드
|
||||
load_device_configuration(); // EEPROM에서 설정 읽기
|
||||
|
||||
// 3. BLE 초기화
|
||||
ble_stack_init(); // SoftDevice 활성화
|
||||
gap_params_init(); // GAP 파라미터 (디바이스명, Passkey)
|
||||
gatt_init(); // GATT 초기화
|
||||
services_init(); // NUS 서비스 등록
|
||||
advertising_init(); // 광고 설정
|
||||
conn_params_init(); // 연결 파라미터
|
||||
|
||||
// 4. 전원 버튼 타이머 시작
|
||||
power_ctrl_timers_start();
|
||||
|
||||
// 5. 메인 루프
|
||||
for (;;) {
|
||||
idle_state_handle(); // 전원 관리 + 보안 처리
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 BLE 데이터 수신 경로
|
||||
|
||||
```c
|
||||
// ble_core.c
|
||||
static void nus_data_handler(ble_nus_evt_t * p_evt)
|
||||
{
|
||||
if (p_evt->type == BLE_NUS_EVT_RX_DATA) {
|
||||
// cmd_parse.c의 received_command_process() 호출
|
||||
received_command_process(
|
||||
p_evt->params.rx_data.p_data,
|
||||
CMD_BLE,
|
||||
p_evt->params.rx_data.length
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 Mt_parser 초기화 (cmd_parse.c)
|
||||
|
||||
```c
|
||||
void received_command_process(uint8_t const *data_array, which_cmd_t cmd_t, uint8_t length)
|
||||
{
|
||||
// Mt_parser 초기화 (최초 1회)
|
||||
static bool parser_initialized = false;
|
||||
if (!parser_initialized) {
|
||||
g_plat.log = log_printf; // 로그 함수 연결
|
||||
g_plat.tx_bin = binary_tx_handler; // BLE TX 함수 연결
|
||||
g_plat.crc_check = true; // CRC 검사 활성화
|
||||
g_log_enable = true;
|
||||
parser_initialized = true;
|
||||
}
|
||||
|
||||
// Mt_parser 호출
|
||||
int result = dr_cmd_parser(r_data, length);
|
||||
|
||||
if (result > 0) {
|
||||
return; // Mt_parser에서 처리됨
|
||||
}
|
||||
|
||||
// Mt_parser에서 처리 안됨 → Legacy 파서로 처리
|
||||
// ... 기존 scmd 기반 처리 ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 소스코드 구조
|
||||
|
||||
### 7.1 디렉토리 구조
|
||||
|
||||
```
|
||||
ble_app_vivaMayo/
|
||||
├── main.c # 메인 엔트리 포인트
|
||||
├── main.h # 메인 헤더 (타입 정의, 함수 프로토타입)
|
||||
├── main_timer.c/h # 애플리케이션 타이머
|
||||
│
|
||||
├── cmd_parse.c/h # 명령어 파싱 (Legacy + Mt_parser 연결)
|
||||
│
|
||||
├── cmd/
|
||||
│ ├── cmd.c # 명령어 핸들러 구현 + 테이블
|
||||
│ └── cmd.h # 명령어 구조체 정의
|
||||
│
|
||||
├── ble/
|
||||
│ ├── ble_core.c/h # BLE 스택, GAP, GATT, 광고
|
||||
│ ├── ble_data_tx.c/h # BLE 데이터 전송, 포맷팅
|
||||
│ ├── ble_services.c/h # BLE 서비스
|
||||
│ └── ble_security.c/h # BLE 보안 (페어링)
|
||||
│
|
||||
├── power/
|
||||
│ └── power_ctrl.c/h # 전원 제어, 슬립 모드
|
||||
│
|
||||
├── peripheral/
|
||||
│ └── uart_handler.c/h # UART 디버그 출력
|
||||
│
|
||||
├── config/
|
||||
│ └── device_config.c/h # EEPROM 설정 관리
|
||||
│
|
||||
├── measurements.c/h # 측정 유틸리티
|
||||
├── meas_pd_*.c/h # PD ADC 측정 모듈들
|
||||
├── full_agc.c/h # AGC 자동 조절
|
||||
├── battery_saadc.c/h # 배터리 ADC
|
||||
│
|
||||
├── icm42670p/ # IMU 드라이버
|
||||
├── i2c_manager.c/h # I2C 관리
|
||||
├── ada2200_spi.c/h # SPI 디바이스
|
||||
├── mcp4725_i2c.c/h # DAC 제어
|
||||
├── tmp235_q1.c/h # 온도 센서
|
||||
├── cat_interface.c/h # CAT (압력센서) 인터페이스
|
||||
│
|
||||
└── docs/
|
||||
└── PROGRAM_ARCHITECTURE.md # 이 문서
|
||||
```
|
||||
|
||||
### 7.2 Mt_parser 디렉토리 (별도 위치)
|
||||
|
||||
```
|
||||
/mt_parser/
|
||||
├── parser.h # 파서 인터페이스
|
||||
├── parser.c # 파서 구현 (CRC, TAG, 디스패치)
|
||||
├── cmd.h # (참조용)
|
||||
│
|
||||
└── dr_util/
|
||||
├── dr_util.h # BLE 응답 유틸리티
|
||||
└── dr_util.c # dr_ble_return_1/2/3()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 명령어 프로토콜
|
||||
|
||||
### 8.1 패킷 구조
|
||||
|
||||
```
|
||||
┌─────────┬──────────────────┬─────────┐
|
||||
│ TAG │ DATA │ CRC │
|
||||
│ 4 bytes │ N bytes │ 2 bytes │
|
||||
└─────────┴──────────────────┴─────────┘
|
||||
|
||||
예시: sta? 명령어로 디바이스 활성화
|
||||
TX: [73 74 61 3F] [00 01] [CRC_LO] [CRC_HI]
|
||||
s t a ? value=1
|
||||
|
||||
응답: rta: value
|
||||
RX: [72 74 61 3A] [00 01] [CRC_LO] [CRC_HI]
|
||||
r t a : value=1
|
||||
```
|
||||
|
||||
### 8.2 데이터 형식
|
||||
|
||||
| 형식 | 설명 | 바이트 순서 |
|
||||
|------|------|-------------|
|
||||
| uint16 | 16비트 정수 | Big Endian (MSB first) |
|
||||
| ASCII | 문자열 | 순차적 |
|
||||
| CRC16 | 체크섬 | Little Endian (LSB first) |
|
||||
|
||||
### 8.3 응답 규칙
|
||||
|
||||
| 요청 TAG | 응답 TAG | 예시 |
|
||||
|----------|----------|------|
|
||||
| `sta?` | `rta:` | 상태 응답 |
|
||||
| `mcj?` | (측정 데이터) | 멀티 패킷 |
|
||||
| 오류 | `xxx!` | `crc!`, `err!` |
|
||||
|
||||
---
|
||||
|
||||
## 9. 주요 핸들러 구현 예시
|
||||
|
||||
### 9.1 Cmd_mta (디바이스 활성화)
|
||||
|
||||
```c
|
||||
static int Cmd_mta(const ParsedCmd *cmd)
|
||||
{
|
||||
uint16_t mode = 0;
|
||||
resetCount = 0;
|
||||
|
||||
// 데이터에서 mode 추출 (word index 0)
|
||||
(void)cmd_get_u16(cmd, 0, &mode);
|
||||
|
||||
if (mode == 1) {
|
||||
// 디바이스 활성화
|
||||
if (device_activated() == 0) {
|
||||
device_status = true;
|
||||
}
|
||||
}
|
||||
else if (mode == 0) {
|
||||
// 슬립 모드 진입
|
||||
if (device_status == true) {
|
||||
if (device_sleep_mode() == 0) {
|
||||
device_status = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BLE 응답 전송
|
||||
if (g_plat.tx_bin) {
|
||||
single_format_data(ble_bin_buffer, "rta:", mode);
|
||||
binary_tx_handler(ble_bin_buffer, 3);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 Cmd_mcj (M48 전체 측정)
|
||||
|
||||
```c
|
||||
static int Cmd_mcj(const ParsedCmd *cmd)
|
||||
{
|
||||
(void)cmd;
|
||||
|
||||
// 디바이스 활성화 확인
|
||||
if (device_status != true) {
|
||||
param_error("mcj?");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 측정 모드 설정
|
||||
ADC_PD_MODE = 2;
|
||||
info4 = true;
|
||||
ble_got_new_data = false;
|
||||
processing = true;
|
||||
|
||||
// 압력 측정
|
||||
pressure_all_level_meas();
|
||||
|
||||
// AGC 스위치 OFF
|
||||
AGC_GAIN_SW(false);
|
||||
|
||||
// M48 ADC 시작
|
||||
m48_samples_in_buffer = m_pd_adc_cnt;
|
||||
pd_adc_m48_start = true;
|
||||
|
||||
// 타이머 시작
|
||||
battery_timer_stop();
|
||||
go_batt = true;
|
||||
motion_data_once = true;
|
||||
main_timer_start();
|
||||
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 에러 코드
|
||||
|
||||
| 코드 | 값 | 설명 |
|
||||
|------|-----|------|
|
||||
| `err_code1` | 65535 | 길이 오류 |
|
||||
| `err_code2` | 65534 | 활성화 오류 |
|
||||
| `err_code3` | 65533 | 파라미터 오류 |
|
||||
| `err_code4` | 65532 | '?' 누락 |
|
||||
| `err_code5` | 65531 | 알 수 없는 명령어 |
|
||||
| `err_code6` | 65530 | CRC 오류 |
|
||||
|
||||
---
|
||||
|
||||
## 11. EEPROM 주소 맵
|
||||
|
||||
| 주소 | 크기 | 내용 |
|
||||
|------|------|------|
|
||||
| 0x0010 | 12 bytes | HW_NO (암호화) |
|
||||
| 0x0020 | 6 bytes | Passkey (암호화) |
|
||||
| 0x0030 | 12 bytes | SERIAL_NO (암호화) |
|
||||
| 0x0060 | 1 byte | bond_data_delete |
|
||||
| 0x0065 | 1 byte | reset_status |
|
||||
| 0x0070 | 1 byte | m_pd_adc_cnt |
|
||||
| 0x0080 | 2 bytes | m_pd_delay_us |
|
||||
| 0x0090 | 4 bytes | m_life_cycle |
|
||||
| 0x0480 | 96 bytes | led_pd_dac_v[48] (AGC Gain) |
|
||||
|
||||
---
|
||||
|
||||
## 12. 버전 정보
|
||||
|
||||
- **펌웨어 버전**: FW25LIT2B102
|
||||
- **디바이스명**: MEDIDEV_2004
|
||||
- **문서 작성일**: 2026-01-30
|
||||
- **작성자**: Claude Assistant
|
||||
|
||||
---
|
||||
|
||||
## 13. 관련 문서
|
||||
|
||||
- [W25Q32RV_FLASH_MEMORY.md](W25Q32RV_FLASH_MEMORY.md) - Flash 메모리 스펙
|
||||
- Nordic nRF52840 Datasheet
|
||||
- Nordic SDK 17.x Documentation
|
||||
@@ -0,0 +1,325 @@
|
||||
# W25Q32RV Serial NOR Flash Memory
|
||||
|
||||
## Overview
|
||||
|
||||
W25Q32RV는 Winbond에서 제조한 32M-bit (4MB) Serial NOR Flash Memory입니다. Dual/Quad SPI 및 QPI 인터페이스를 지원하며, 제한된 공간, 핀 수, 전력을 가진 시스템에 적합한 저장 솔루션을 제공합니다.
|
||||
|
||||
## 주요 사양
|
||||
|
||||
| 항목 | 사양 |
|
||||
|------|------|
|
||||
| 용량 | 32M-bit (4MB) |
|
||||
| 동작 전압 | 2.7V ~ 3.6V |
|
||||
| 최대 클럭 주파수 | 133MHz (SPI/QPI) |
|
||||
| Program/Erase 사이클 | 최소 100,000회 |
|
||||
| 데이터 보존 기간 | 20년 이상 |
|
||||
| 동작 온도 | -40°C ~ +105°C |
|
||||
| 대기 전류 | 10µA (typ), 28µA (max @85°C) |
|
||||
| Power-down 전류 | 0.1µA (typ) |
|
||||
|
||||
## 메모리 구조
|
||||
|
||||
### 메모리 배열
|
||||
|
||||
```
|
||||
총 용량: 4,194,304 bytes (4MB)
|
||||
├── 64 Blocks (64KB each)
|
||||
│ └── 16 Sectors per Block (4KB each)
|
||||
│ └── 16 Pages per Sector (256 bytes each)
|
||||
└── 총 16,384 Pages
|
||||
```
|
||||
|
||||
| 단위 | 크기 | 개수 | 주소 범위 |
|
||||
|------|------|------|----------|
|
||||
| Page | 256 bytes | 16,384 | - |
|
||||
| Sector | 4KB | 1,024 | 0x000000 - 0x3FFFFF |
|
||||
| Block (32KB) | 32KB | 128 | - |
|
||||
| Block (64KB) | 64KB | 64 | - |
|
||||
| Chip | 4MB | 1 | 0x000000 - 0x3FFFFF |
|
||||
|
||||
### 주소 맵핑
|
||||
|
||||
```
|
||||
Block 0 (64KB): 0x000000 - 0x00FFFF
|
||||
Block 1 (64KB): 0x010000 - 0x01FFFF
|
||||
...
|
||||
Block 63 (64KB): 0x3F0000 - 0x3FFFFF
|
||||
```
|
||||
|
||||
## 핀 구성 (8-pin SOP/WSON/XSON/USON)
|
||||
|
||||
| Pin | 이름 | I/O | 기능 |
|
||||
|-----|------|-----|------|
|
||||
| 1 | /CS | I | Chip Select (Active Low) |
|
||||
| 2 | DO (IO1) | I/O | Data Output / IO1 |
|
||||
| 3 | /WP (IO2) | I/O | Write Protect / IO2 |
|
||||
| 4 | VSS | - | Ground |
|
||||
| 5 | DI (IO0) | I/O | Data Input / IO0 |
|
||||
| 6 | CLK | I | Serial Clock |
|
||||
| 7 | /HOLD or /RESET (IO3) | I/O | Hold/Reset / IO3 |
|
||||
| 8 | VCC | - | Power Supply |
|
||||
|
||||
## SPI 모드
|
||||
|
||||
### 지원 모드
|
||||
|
||||
1. **Standard SPI**: CLK, /CS, DI, DO, /WP, /HOLD
|
||||
2. **Dual SPI**: CLK, /CS, IO0, IO1, /WP, /HOLD
|
||||
3. **Quad SPI**: CLK, /CS, IO0, IO1, IO2, IO3
|
||||
4. **QPI**: CLK, /CS, IO0, IO1, IO2, IO3 (4-bit 명령어)
|
||||
|
||||
### SPI 모드 0/3 지원
|
||||
|
||||
- **Mode 0** (CPOL=0, CPHA=0): CLK idle = LOW
|
||||
- **Mode 3** (CPOL=1, CPHA=1): CLK idle = HIGH
|
||||
|
||||
## 주요 명령어 (Instructions)
|
||||
|
||||
### Device ID 읽기
|
||||
|
||||
| 명령어 | 코드 | 설명 |
|
||||
|--------|------|------|
|
||||
| Read JEDEC ID | 9Fh | Manufacturer/Device ID 읽기 |
|
||||
| Read Manufacturer/Device ID | 90h | MFR ID (EFh) + Device ID (15h) |
|
||||
| Release Power-down/Device ID | ABh | Power-down 해제 및 ID 읽기 |
|
||||
|
||||
**Device ID 값:**
|
||||
- Manufacturer ID: `0xEF` (Winbond)
|
||||
- Device ID (8-bit): `0x15`
|
||||
- Device ID (16-bit): `0x4016`
|
||||
|
||||
### 읽기 명령어
|
||||
|
||||
| 명령어 | 코드 | 더미 클럭 | 최대 주파수 |
|
||||
|--------|------|-----------|-------------|
|
||||
| Read Data | 03h | 0 | 66MHz |
|
||||
| Fast Read | 0Bh | 8 | 133MHz |
|
||||
| Fast Read Dual Output | 3Bh | 8 | 133MHz |
|
||||
| Fast Read Quad Output | 6Bh | 8 | 133MHz |
|
||||
| Fast Read Dual I/O | BBh | 4 (Mode bits) | 133MHz |
|
||||
| Fast Read Quad I/O | EBh | 6 (Mode bits + Dummy) | 133MHz |
|
||||
|
||||
### 쓰기/프로그램 명령어
|
||||
|
||||
| 명령어 | 코드 | 설명 |
|
||||
|--------|------|------|
|
||||
| Write Enable | 06h | 쓰기 활성화 (WEL=1) |
|
||||
| Write Disable | 04h | 쓰기 비활성화 (WEL=0) |
|
||||
| Page Program | 02h | 페이지 프로그램 (최대 256 bytes) |
|
||||
| Quad Input Page Program | 32h | Quad 모드 페이지 프로그램 |
|
||||
|
||||
### 지우기 명령어
|
||||
|
||||
| 명령어 | 코드 | 크기 | 시간 (typ/max) |
|
||||
|--------|------|------|----------------|
|
||||
| Sector Erase | 20h | 4KB | 30ms / 240ms |
|
||||
| Block Erase (32KB) | 52h | 32KB | 80ms / 800ms |
|
||||
| Block Erase (64KB) | D8h | 64KB | 120ms / 1200ms |
|
||||
| Chip Erase | C7h/60h | 전체 | 6s / 40s |
|
||||
|
||||
### 상태 레지스터 명령어
|
||||
|
||||
| 명령어 | 코드 | 설명 |
|
||||
|--------|------|------|
|
||||
| Read Status Register-1 | 05h | SR1 읽기 |
|
||||
| Read Status Register-2 | 35h | SR2 읽기 |
|
||||
| Read Status Register-3 | 15h | SR3 읽기 |
|
||||
| Write Status Register-1 | 01h | SR1 쓰기 |
|
||||
| Write Status Register-2 | 31h | SR2 쓰기 |
|
||||
| Write Status Register-3 | 11h | SR3 쓰기 |
|
||||
|
||||
### 기타 명령어
|
||||
|
||||
| 명령어 | 코드 | 설명 |
|
||||
|--------|------|------|
|
||||
| Erase/Program Suspend | 75h | Erase/Program 일시 중단 |
|
||||
| Erase/Program Resume | 7Ah | Erase/Program 재개 |
|
||||
| Power-down | B9h | 저전력 모드 진입 |
|
||||
| Enable Reset | 66h | 리셋 활성화 |
|
||||
| Reset Device | 99h | 디바이스 리셋 |
|
||||
| Enter QPI Mode | 38h | QPI 모드 진입 |
|
||||
| Exit QPI Mode | FFh | QPI 모드 종료 |
|
||||
|
||||
## 상태 레지스터 (Status Registers)
|
||||
|
||||
### Status Register-1 (05h)
|
||||
|
||||
| Bit | 이름 | 설명 | R/W |
|
||||
|-----|------|------|-----|
|
||||
| S7 | SRP | Status Register Protect | R/W |
|
||||
| S6 | SEC | Sector Protect | R/W |
|
||||
| S5 | TB | Top/Bottom Block Protect | R/W |
|
||||
| S4 | BP2 | Block Protect Bit 2 | R/W |
|
||||
| S3 | BP1 | Block Protect Bit 1 | R/W |
|
||||
| S2 | BP0 | Block Protect Bit 0 | R/W |
|
||||
| S1 | WEL | Write Enable Latch | R |
|
||||
| S0 | BUSY | Erase/Write In Progress | R |
|
||||
|
||||
### Status Register-2 (35h)
|
||||
|
||||
| Bit | 이름 | 설명 | R/W |
|
||||
|-----|------|------|-----|
|
||||
| S15 | SUS | Suspend Status | R |
|
||||
| S14 | CMP | Complement Protect | R/W |
|
||||
| S13 | LB3 | Security Register Lock 3 | OTP |
|
||||
| S12 | LB2 | Security Register Lock 2 | OTP |
|
||||
| S11 | LB1 | Security Register Lock 1 | OTP |
|
||||
| S10 | LB0 | SFDP Lock | OTP |
|
||||
| S9 | QE | Quad Enable | R/W |
|
||||
| S8 | SRL | Status Register Lock | R/W |
|
||||
|
||||
### Status Register-3 (15h)
|
||||
|
||||
| Bit | 이름 | 설명 | R/W |
|
||||
|-----|------|------|-----|
|
||||
| S23 | HOLD/RST | /HOLD or /RESET 기능 | R/W |
|
||||
| S22 | DRV1 | Output Driver Strength 1 | R/W |
|
||||
| S21 | DRV0 | Output Driver Strength 0 | R/W |
|
||||
| S20-S16 | Reserved | 예약됨 | - |
|
||||
|
||||
### Output Driver Strength 설정
|
||||
|
||||
| DRV1 | DRV0 | 출력 임피던스 |
|
||||
|------|------|--------------|
|
||||
| 0 | 0 | 25Ω |
|
||||
| 0 | 1 | 33Ω |
|
||||
| 1 | 0 | 50Ω (기본값) |
|
||||
| 1 | 1 | 100Ω |
|
||||
|
||||
## 타이밍 특성
|
||||
|
||||
### AC 타이밍 파라미터
|
||||
|
||||
| 파라미터 | 심볼 | Min | Max | 단위 |
|
||||
|----------|------|-----|-----|------|
|
||||
| Clock Frequency (Fast Read) | fR | DC | 133 | MHz |
|
||||
| Clock Frequency (Read Data) | fR | DC | 66 | MHz |
|
||||
| /CS Setup Time | tSLCH | 5 | - | ns |
|
||||
| /CS Hold Time | tCHSL | 5 | - | ns |
|
||||
| Data In Setup Time | tDVCH | 2 | - | ns |
|
||||
| Data In Hold Time | tCHDX | 2.5 | - | ns |
|
||||
| Clock to Output Valid | tCLQV | - | 4.5 | ns |
|
||||
| Output Disable Time | tSHQZ | - | 7 | ns |
|
||||
| /CS Deselect Time (Read) | tSHSL1 | 10 | - | ns |
|
||||
| /CS Deselect Time (Write) | tSHSL2 | 50 | - | ns |
|
||||
|
||||
### 프로그램/지우기 타이밍
|
||||
|
||||
| 동작 | 심볼 | Typical | Maximum | 단위 |
|
||||
|------|------|---------|---------|------|
|
||||
| Page Program | tPP | 0.25 | 2 | ms |
|
||||
| Sector Erase (4KB) | tSE | 30 | 240 | ms |
|
||||
| Block Erase (32KB) | tBE1 | 80 | 800 | ms |
|
||||
| Block Erase (64KB) | tBE2 | 120 | 1200 | ms |
|
||||
| Chip Erase | tCE | 6 | 40 | s |
|
||||
| Write Status Register | tW | 1.5 | 15 | ms |
|
||||
|
||||
### 전원 타이밍
|
||||
|
||||
| 파라미터 | 심볼 | Min | Max | 단위 |
|
||||
|----------|------|-----|-----|------|
|
||||
| VCC(min) to /CS Low | tVSL | 20 | - | µs |
|
||||
| Power-up to Write Allowed | tPUW | 5 | - | ms |
|
||||
| Power-down Entry Time | tDP | - | 3 | µs |
|
||||
| Release from Power-down | tRES1 | - | 3 | µs |
|
||||
| Reset Time | tRST | - | 30 | µs |
|
||||
|
||||
## 전기적 특성
|
||||
|
||||
### DC 특성
|
||||
|
||||
| 파라미터 | 심볼 | Min | Typ | Max | 단위 |
|
||||
|----------|------|-----|-----|-----|------|
|
||||
| Input Low Voltage | VIL | -0.5 | - | 0.3×VCC | V |
|
||||
| Input High Voltage | VIH | 0.7×VCC | - | VCC+0.4 | V |
|
||||
| Output Low Voltage | VOL | - | - | 0.2 | V |
|
||||
| Output High Voltage | VOH | VCC-0.2 | - | - | V |
|
||||
| Standby Current | ICC1 | - | 10 | 28 | µA |
|
||||
| Power-down Current | ICC2 | - | 0.1 | 8 | µA |
|
||||
| Read Current (133MHz) | ICC3 | - | 11 | 20 | mA |
|
||||
| Program Current | ICC5 | - | 8 | 15 | mA |
|
||||
| Erase Current | ICC6 | - | 8 | 15 | mA |
|
||||
|
||||
## 쓰기 보호 (Write Protection)
|
||||
|
||||
### Block Protect 비트 조합 (CMP=0)
|
||||
|
||||
| SEC | TB | BP2 | BP1 | BP0 | 보호 영역 | 크기 |
|
||||
|-----|-----|-----|-----|-----|----------|------|
|
||||
| X | X | 0 | 0 | 0 | None | - |
|
||||
| 0 | 0 | 0 | 0 | 1 | Upper 1/64 | 64KB |
|
||||
| 0 | 0 | 0 | 1 | 0 | Upper 1/32 | 128KB |
|
||||
| 0 | 0 | 0 | 1 | 1 | Upper 1/16 | 256KB |
|
||||
| 0 | 0 | 1 | 0 | 0 | Upper 1/8 | 512KB |
|
||||
| 0 | 0 | 1 | 0 | 1 | Upper 1/4 | 1MB |
|
||||
| 0 | 0 | 1 | 1 | 0 | Upper 1/2 | 2MB |
|
||||
| X | X | 1 | 1 | 1 | ALL | 4MB |
|
||||
|
||||
## Security Registers
|
||||
|
||||
W25Q32RV는 3개의 256-byte Security Register를 제공합니다.
|
||||
|
||||
| Register | 주소 범위 |
|
||||
|----------|----------|
|
||||
| Security Register 1 | 0x001000 - 0x0010FF |
|
||||
| Security Register 2 | 0x002000 - 0x0020FF |
|
||||
| Security Register 3 | 0x003000 - 0x0030FF |
|
||||
|
||||
### Security Register 명령어
|
||||
|
||||
| 명령어 | 코드 | 설명 |
|
||||
|--------|------|------|
|
||||
| Erase Security Register | 44h | Security Register 지우기 |
|
||||
| Program Security Register | 42h | Security Register 프로그램 |
|
||||
| Read Security Register | 48h | Security Register 읽기 |
|
||||
|
||||
## QPI 모드
|
||||
|
||||
### QPI 모드 진입/종료
|
||||
|
||||
1. **QPI 진입 조건**: QE 비트가 1로 설정되어야 함
|
||||
2. **진입**: Enter QPI (38h) 명령어 실행
|
||||
3. **종료**: Exit QPI (FFh) 명령어 실행
|
||||
|
||||
### QPI 모드에서의 명령어 전송
|
||||
|
||||
- 모든 명령어, 주소, 데이터가 4비트씩 전송됨
|
||||
- 명령어 전송에 2클럭만 필요 (SPI 모드의 8클럭 대비)
|
||||
|
||||
## 소프트웨어 리셋
|
||||
|
||||
디바이스를 초기 상태로 리셋하려면 두 명령어를 순차적으로 실행:
|
||||
|
||||
1. Enable Reset (66h)
|
||||
2. Reset Device (99h)
|
||||
|
||||
**주의**: 리셋 중 진행 중인 Erase/Program 동작이 있으면 데이터 손상 가능
|
||||
|
||||
## 패키지 정보
|
||||
|
||||
| 패키지 | 코드 | 크기 |
|
||||
|--------|------|------|
|
||||
| SOP 150-mil | CN | 8-pin |
|
||||
| SOP 208-mil | CS | 8-pin |
|
||||
| WSON 6x5-mm | CP | 8-pad |
|
||||
| XSON 2x3-mm | XH | 8-pad |
|
||||
| USON 4x3-mm | UU | 8-pad |
|
||||
|
||||
## 주문 정보
|
||||
|
||||
**Part Number 형식**: W25Q32RV[Package][Temp][Option]
|
||||
|
||||
예시: `W25Q32RVCNJQ`
|
||||
- CN: 8-pin SOP 150-mil
|
||||
- J: Industrial Plus (-40°C ~ +105°C)
|
||||
- Q: QE=1 (고정)
|
||||
|
||||
## 참조 문서
|
||||
|
||||
- Winbond W25Q32RV Datasheet (Revision E, November 2025)
|
||||
- JEDEC JESD216 (SFDP Standard)
|
||||
|
||||
---
|
||||
|
||||
*문서 작성일: 2026-01-30*
|
||||
*소스: W25Q32RV Datasheet Rev.E*
|
||||
@@ -0,0 +1,317 @@
|
||||
# W25Q32RV 외장 Flash 초기화 가이드
|
||||
|
||||
## 개요
|
||||
|
||||
W25Q32RV는 4MB SPI NOR Flash입니다. nRF52 내장 FDS와 달리 **자동 초기화가 없으므로** 드라이버에서 직접 처리해야 합니다.
|
||||
|
||||
## 내장 FDS vs 외장 W25Q32RV 비교
|
||||
|
||||
| 항목 | 내장 FDS | 외장 W25Q32RV |
|
||||
|------|----------|---------------|
|
||||
| 용량 | ~8KB (설정에 따라) | 4MB |
|
||||
| 인터페이스 | SoftDevice API | SPI |
|
||||
| 자동 초기화 | ✅ `fds_init()` | ❌ 수동 |
|
||||
| Wear Leveling | ✅ FDS 내장 | ❌ 직접 구현 |
|
||||
| 포맷 필요 | 자동 처리 | Sector Erase 필수 |
|
||||
| 쓰기 단위 | 4 bytes (word) | 256 bytes (page) |
|
||||
| 지우기 단위 | 자동 GC | 4KB (sector) |
|
||||
|
||||
## 하드웨어 연결 (nRF52840)
|
||||
|
||||
```
|
||||
nRF52840 W25Q32RV
|
||||
───────── ────────
|
||||
P0.xx (SCK) ───► CLK (Pin 6)
|
||||
P0.xx (MOSI) ───► DI (Pin 5)
|
||||
P0.xx (MISO) ◄─── DO (Pin 2)
|
||||
P0.xx (CS) ───► /CS (Pin 1)
|
||||
3.3V ───► VCC (Pin 8)
|
||||
GND ───► VSS (Pin 4)
|
||||
3.3V ───► /WP (Pin 3) [또는 GPIO로 제어]
|
||||
3.3V ───► /HOLD (Pin 7) [또는 GPIO로 제어]
|
||||
```
|
||||
|
||||
## 초기화 순서
|
||||
|
||||
### 1단계: SPI 초기화
|
||||
|
||||
```c
|
||||
#include "nrf_drv_spi.h"
|
||||
|
||||
#define SPI_INSTANCE 0
|
||||
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);
|
||||
|
||||
void w25q32_spi_init(void)
|
||||
{
|
||||
nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
|
||||
spi_config.ss_pin = W25Q_CS_PIN;
|
||||
spi_config.miso_pin = W25Q_MISO_PIN;
|
||||
spi_config.mosi_pin = W25Q_MOSI_PIN;
|
||||
spi_config.sck_pin = W25Q_SCK_PIN;
|
||||
spi_config.frequency = NRF_DRV_SPI_FREQ_8M; // 최대 8MHz (nRF52 제한)
|
||||
spi_config.mode = NRF_DRV_SPI_MODE_0; // CPOL=0, CPHA=0
|
||||
|
||||
APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, NULL, NULL));
|
||||
}
|
||||
```
|
||||
|
||||
### 2단계: 칩 확인 (JEDEC ID 읽기)
|
||||
|
||||
```c
|
||||
#define W25Q_CMD_JEDEC_ID 0x9F
|
||||
#define W25Q_MANUFACTURER_ID 0xEF // Winbond
|
||||
#define W25Q_DEVICE_ID 0x4016 // W25Q32
|
||||
|
||||
bool w25q32_check_id(void)
|
||||
{
|
||||
uint8_t tx_buf[1] = { W25Q_CMD_JEDEC_ID };
|
||||
uint8_t rx_buf[4] = { 0 };
|
||||
|
||||
nrf_gpio_pin_clear(W25Q_CS_PIN);
|
||||
nrf_drv_spi_transfer(&spi, tx_buf, 1, rx_buf, 4);
|
||||
nrf_gpio_pin_set(W25Q_CS_PIN);
|
||||
|
||||
// rx_buf[1] = Manufacturer ID (0xEF)
|
||||
// rx_buf[2] = Memory Type (0x40)
|
||||
// rx_buf[3] = Capacity (0x16 = 32Mbit)
|
||||
|
||||
if (rx_buf[1] == 0xEF && rx_buf[2] == 0x40 && rx_buf[3] == 0x16) {
|
||||
DBG_PRINTF("W25Q32RV detected!\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
DBG_PRINTF("Flash ID mismatch: %02X %02X %02X\r\n",
|
||||
rx_buf[1], rx_buf[2], rx_buf[3]);
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
### 3단계: 상태 확인 (BUSY 체크)
|
||||
|
||||
```c
|
||||
#define W25Q_CMD_READ_STATUS1 0x05
|
||||
#define W25Q_STATUS_BUSY 0x01
|
||||
|
||||
bool w25q32_is_busy(void)
|
||||
{
|
||||
uint8_t tx_buf[1] = { W25Q_CMD_READ_STATUS1 };
|
||||
uint8_t rx_buf[2] = { 0 };
|
||||
|
||||
nrf_gpio_pin_clear(W25Q_CS_PIN);
|
||||
nrf_drv_spi_transfer(&spi, tx_buf, 1, rx_buf, 2);
|
||||
nrf_gpio_pin_set(W25Q_CS_PIN);
|
||||
|
||||
return (rx_buf[1] & W25Q_STATUS_BUSY) != 0;
|
||||
}
|
||||
|
||||
void w25q32_wait_busy(void)
|
||||
{
|
||||
while (w25q32_is_busy()) {
|
||||
nrf_delay_us(100);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4단계: Write Enable
|
||||
|
||||
```c
|
||||
#define W25Q_CMD_WRITE_ENABLE 0x06
|
||||
|
||||
void w25q32_write_enable(void)
|
||||
{
|
||||
uint8_t cmd = W25Q_CMD_WRITE_ENABLE;
|
||||
|
||||
nrf_gpio_pin_clear(W25Q_CS_PIN);
|
||||
nrf_drv_spi_transfer(&spi, &cmd, 1, NULL, 0);
|
||||
nrf_gpio_pin_set(W25Q_CS_PIN);
|
||||
}
|
||||
```
|
||||
|
||||
## 섹터 지우기 (Erase)
|
||||
|
||||
**중요**: Flash는 1→0만 가능합니다. 0→1로 바꾸려면 반드시 Erase가 필요합니다.
|
||||
|
||||
```c
|
||||
#define W25Q_CMD_SECTOR_ERASE 0x20 // 4KB 단위
|
||||
|
||||
void w25q32_sector_erase(uint32_t address)
|
||||
{
|
||||
uint8_t tx_buf[4];
|
||||
|
||||
// 4KB 경계로 정렬
|
||||
address &= 0xFFFFF000;
|
||||
|
||||
w25q32_write_enable();
|
||||
|
||||
tx_buf[0] = W25Q_CMD_SECTOR_ERASE;
|
||||
tx_buf[1] = (address >> 16) & 0xFF;
|
||||
tx_buf[2] = (address >> 8) & 0xFF;
|
||||
tx_buf[3] = address & 0xFF;
|
||||
|
||||
nrf_gpio_pin_clear(W25Q_CS_PIN);
|
||||
nrf_drv_spi_transfer(&spi, tx_buf, 4, NULL, 0);
|
||||
nrf_gpio_pin_set(W25Q_CS_PIN);
|
||||
|
||||
// Erase 완료 대기 (최대 240ms)
|
||||
w25q32_wait_busy();
|
||||
}
|
||||
```
|
||||
|
||||
### 지우기 시간
|
||||
|
||||
| 명령 | 크기 | 일반 시간 | 최대 시간 |
|
||||
|------|------|-----------|-----------|
|
||||
| Sector Erase (0x20) | 4KB | 30ms | 240ms |
|
||||
| Block Erase (0x52) | 32KB | 80ms | 800ms |
|
||||
| Block Erase (0xD8) | 64KB | 120ms | 1200ms |
|
||||
| Chip Erase (0xC7) | 4MB | 6s | 40s |
|
||||
|
||||
## 페이지 쓰기 (Page Program)
|
||||
|
||||
```c
|
||||
#define W25Q_CMD_PAGE_PROGRAM 0x02
|
||||
#define W25Q_PAGE_SIZE 256
|
||||
|
||||
void w25q32_page_write(uint32_t address, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
uint8_t tx_buf[4 + W25Q_PAGE_SIZE];
|
||||
|
||||
if (len > W25Q_PAGE_SIZE) {
|
||||
len = W25Q_PAGE_SIZE;
|
||||
}
|
||||
|
||||
w25q32_write_enable();
|
||||
|
||||
tx_buf[0] = W25Q_CMD_PAGE_PROGRAM;
|
||||
tx_buf[1] = (address >> 16) & 0xFF;
|
||||
tx_buf[2] = (address >> 8) & 0xFF;
|
||||
tx_buf[3] = address & 0xFF;
|
||||
memcpy(&tx_buf[4], data, len);
|
||||
|
||||
nrf_gpio_pin_clear(W25Q_CS_PIN);
|
||||
nrf_drv_spi_transfer(&spi, tx_buf, 4 + len, NULL, 0);
|
||||
nrf_gpio_pin_set(W25Q_CS_PIN);
|
||||
|
||||
// Write 완료 대기 (최대 2ms)
|
||||
w25q32_wait_busy();
|
||||
}
|
||||
```
|
||||
|
||||
## 데이터 읽기
|
||||
|
||||
```c
|
||||
#define W25Q_CMD_READ_DATA 0x03
|
||||
|
||||
void w25q32_read(uint32_t address, uint8_t *data, uint16_t len)
|
||||
{
|
||||
uint8_t tx_buf[4];
|
||||
|
||||
tx_buf[0] = W25Q_CMD_READ_DATA;
|
||||
tx_buf[1] = (address >> 16) & 0xFF;
|
||||
tx_buf[2] = (address >> 8) & 0xFF;
|
||||
tx_buf[3] = address & 0xFF;
|
||||
|
||||
nrf_gpio_pin_clear(W25Q_CS_PIN);
|
||||
nrf_drv_spi_transfer(&spi, tx_buf, 4, NULL, 0);
|
||||
nrf_drv_spi_transfer(&spi, NULL, 0, data, len);
|
||||
nrf_gpio_pin_set(W25Q_CS_PIN);
|
||||
}
|
||||
```
|
||||
|
||||
## 첫 사용 시 초기화 플로우
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 1. SPI 초기화 │
|
||||
│ w25q32_spi_init() │
|
||||
└────────────────┬────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 2. 칩 ID 확인 │
|
||||
│ w25q32_check_id() │
|
||||
│ → 실패 시 에러 처리 │
|
||||
└────────────────┬────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 3. Magic Number 확인 (주소 0x000000) │
|
||||
│ w25q32_read(0, &magic, 4) │
|
||||
└────────────────┬────────────────────────┘
|
||||
↓
|
||||
┌───────┴───────┐
|
||||
│ magic == 유효? │
|
||||
└───────┬───────┘
|
||||
│ │
|
||||
Yes No (첫 사용/손상)
|
||||
│ │
|
||||
↓ ↓
|
||||
┌──────────────┐ ┌─────────────────────────┐
|
||||
│ 정상 사용 │ │ 4. 첫 섹터 Erase │
|
||||
│ │ │ w25q32_sector_erase(0)│
|
||||
└──────────────┘ │ │
|
||||
│ 5. Magic Number 쓰기 │
|
||||
│ w25q32_page_write(0, │
|
||||
│ &magic, 4) │
|
||||
│ │
|
||||
│ 6. 기본값 저장 │
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
## 메모리 맵 예시
|
||||
|
||||
```
|
||||
주소 범위 용도 크기
|
||||
─────────────────────────────────────────────
|
||||
0x000000 - 0x000FFF Config 영역 4KB (Sector 0)
|
||||
0x001000 - 0x001FFF Calibration 1 4KB (Sector 1)
|
||||
0x002000 - 0x002FFF Calibration 2 4KB (Sector 2)
|
||||
0x003000 - 0x00FFFF Reserved 52KB
|
||||
0x010000 - 0x0FFFFF Data Log 1 960KB
|
||||
0x100000 - 0x1FFFFF Data Log 2 1MB
|
||||
0x200000 - 0x3FFFFF Data Log 3 2MB
|
||||
─────────────────────────────────────────────
|
||||
Total 4MB
|
||||
```
|
||||
|
||||
## Power-down 모드
|
||||
|
||||
저전력 모드가 필요한 경우:
|
||||
|
||||
```c
|
||||
#define W25Q_CMD_POWER_DOWN 0xB9
|
||||
#define W25Q_CMD_RELEASE_PD 0xAB
|
||||
|
||||
void w25q32_power_down(void)
|
||||
{
|
||||
uint8_t cmd = W25Q_CMD_POWER_DOWN;
|
||||
nrf_gpio_pin_clear(W25Q_CS_PIN);
|
||||
nrf_drv_spi_transfer(&spi, &cmd, 1, NULL, 0);
|
||||
nrf_gpio_pin_set(W25Q_CS_PIN);
|
||||
// 전류: 0.1µA (typ)
|
||||
}
|
||||
|
||||
void w25q32_wake_up(void)
|
||||
{
|
||||
uint8_t cmd = W25Q_CMD_RELEASE_PD;
|
||||
nrf_gpio_pin_clear(W25Q_CS_PIN);
|
||||
nrf_drv_spi_transfer(&spi, &cmd, 1, NULL, 0);
|
||||
nrf_gpio_pin_set(W25Q_CS_PIN);
|
||||
nrf_delay_us(3); // tRES1: Release from power-down
|
||||
}
|
||||
```
|
||||
|
||||
## 주의사항
|
||||
|
||||
1. **Erase 전 확인**: 쓰기 전 해당 섹터가 이미 Erase 되어 있는지 확인
|
||||
2. **Page 경계**: Page Program은 256바이트 경계를 넘지 않도록 주의
|
||||
3. **Wear Leveling**: 같은 섹터 반복 사용 시 수명 단축 (최소 100,000회)
|
||||
4. **전원 차단**: Erase/Program 중 전원 차단 시 데이터 손상 가능
|
||||
5. **SPI 속도**: nRF52840은 최대 8MHz, W25Q32RV는 133MHz까지 지원
|
||||
|
||||
## 관련 파일
|
||||
|
||||
- [W25Q32RV_FLASH_MEMORY.md](./W25Q32RV_FLASH_MEMORY.md) - 상세 스펙
|
||||
- [fstorage.c](../fstorage.c) - 내장 FDS 구현 참조
|
||||
|
||||
---
|
||||
|
||||
*문서 작성일: 2026-02-04*
|
||||
@@ -0,0 +1,214 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="900px" height="1200px" viewBox="-0.5 -0.5 900 1200" content="<mxfile host="app.diagrams.net"><diagram name="VivaMayo Architecture"><mxGraphModel dx="900" dy="1200" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="900" pageHeight="1200"><root><mxCell id="0"/><mxCell id="1" parent="0"/></root></mxGraphModel></diagram></mxfile>">
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
.title { font: bold 24px 'Segoe UI', Arial, sans-serif; fill: #1a1a1a; }
|
||||
.subtitle { font: 16px 'Segoe UI', Arial, sans-serif; fill: #666; }
|
||||
.box-main { fill: #4a90d9; stroke: #2d5a8a; stroke-width: 2; rx: 8; }
|
||||
.box-ble { fill: #5cb85c; stroke: #3d8b3d; stroke-width: 2; rx: 8; }
|
||||
.box-parser { fill: #f0ad4e; stroke: #c77c00; stroke-width: 2; rx: 8; }
|
||||
.box-cmd { fill: #d9534f; stroke: #a33b38; stroke-width: 2; rx: 8; }
|
||||
.box-handler { fill: #9b59b6; stroke: #6c3483; stroke-width: 2; rx: 8; }
|
||||
.box-peripheral { fill: #17a2b8; stroke: #0d6efd; stroke-width: 2; rx: 8; }
|
||||
.box-storage { fill: #6c757d; stroke: #495057; stroke-width: 2; rx: 8; }
|
||||
.box-label { font: bold 14px 'Segoe UI', Arial, sans-serif; fill: white; text-anchor: middle; }
|
||||
.box-sublabel { font: 11px 'Segoe UI', Arial, sans-serif; fill: rgba(255,255,255,0.8); text-anchor: middle; }
|
||||
.arrow { stroke: #333; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
|
||||
.arrow-dashed { stroke: #666; stroke-width: 2; fill: none; stroke-dasharray: 5,5; marker-end: url(#arrowhead); }
|
||||
.section-title { font: bold 16px 'Segoe UI', Arial, sans-serif; fill: #333; }
|
||||
.note { font: 12px 'Segoe UI', Arial, sans-serif; fill: #666; }
|
||||
.legend-box { stroke: #ccc; stroke-width: 1; fill: #fafafa; rx: 5; }
|
||||
</style>
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#333"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="900" height="1200" fill="#ffffff"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="40" class="title" text-anchor="middle">VivaMayo BLE Application Architecture</text>
|
||||
<text x="450" y="65" class="subtitle" text-anchor="middle">nRF52840 기반 NIRS 방광 모니터링 장치</text>
|
||||
|
||||
<!-- ==================== Section 1: Application Entry ==================== -->
|
||||
<text x="50" y="110" class="section-title">1. Application Entry</text>
|
||||
|
||||
<!-- main.c -->
|
||||
<rect x="350" y="130" width="200" height="60" class="box-main"/>
|
||||
<text x="450" y="158" class="box-label">main.c</text>
|
||||
<text x="450" y="175" class="box-sublabel">Application Entry Point</text>
|
||||
|
||||
<!-- ==================== Section 2: Core Modules ==================== -->
|
||||
<text x="50" y="230" class="section-title">2. Core Modules</text>
|
||||
|
||||
<!-- BLE Stack -->
|
||||
<rect x="100" y="250" width="160" height="50" class="box-ble"/>
|
||||
<text x="180" y="275" class="box-label">ble_core.c</text>
|
||||
<text x="180" y="290" class="box-sublabel">BLE Stack</text>
|
||||
|
||||
<!-- Power Control -->
|
||||
<rect x="370" y="250" width="160" height="50" class="box-peripheral"/>
|
||||
<text x="450" y="275" class="box-label">power_ctrl.c</text>
|
||||
<text x="450" y="290" class="box-sublabel">Power Management</text>
|
||||
|
||||
<!-- Timer -->
|
||||
<rect x="640" y="250" width="160" height="50" class="box-peripheral"/>
|
||||
<text x="720" y="275" class="box-label">main_timer.c</text>
|
||||
<text x="720" y="290" class="box-sublabel">Timer Management</text>
|
||||
|
||||
<!-- Arrows from main -->
|
||||
<path d="M 400 190 L 400 210 L 180 210 L 180 250" class="arrow"/>
|
||||
<path d="M 450 190 L 450 250" class="arrow"/>
|
||||
<path d="M 500 190 L 500 210 L 720 210 L 720 250" class="arrow"/>
|
||||
|
||||
<!-- ==================== Section 3: BLE Data Flow ==================== -->
|
||||
<text x="50" y="350" class="section-title">3. BLE Data Reception</text>
|
||||
|
||||
<!-- NUS Data Handler -->
|
||||
<rect x="100" y="370" width="200" height="50" class="box-ble"/>
|
||||
<text x="200" y="395" class="box-label">nus_data_handler()</text>
|
||||
<text x="200" y="410" class="box-sublabel">BLE NUS RX Event</text>
|
||||
|
||||
<!-- Arrow -->
|
||||
<path d="M 180 300 L 180 330 L 200 330 L 200 370" class="arrow"/>
|
||||
|
||||
<!-- ==================== Section 4: Command Processing ==================== -->
|
||||
<text x="50" y="470" class="section-title">4. Command Processing</text>
|
||||
|
||||
<!-- received_command_process -->
|
||||
<rect x="100" y="490" width="250" height="50" class="box-parser"/>
|
||||
<text x="225" y="515" class="box-label">received_command_process()</text>
|
||||
<text x="225" y="530" class="box-sublabel">cmd_parse.c</text>
|
||||
|
||||
<!-- Arrow -->
|
||||
<path d="M 200 420 L 200 450 L 225 450 L 225 490" class="arrow"/>
|
||||
|
||||
<!-- ==================== Section 5: Parser Layer ==================== -->
|
||||
<text x="50" y="590" class="section-title">5. Parser Layer (Mt_parser)</text>
|
||||
|
||||
<!-- dr_cmd_parser -->
|
||||
<rect x="100" y="610" width="200" height="60" class="box-parser"/>
|
||||
<text x="200" y="635" class="box-label">dr_cmd_parser()</text>
|
||||
<text x="200" y="655" class="box-sublabel">parser.c - CRC + TAG</text>
|
||||
|
||||
<!-- Legacy Parser -->
|
||||
<rect x="400" y="610" width="200" height="60" class="box-storage"/>
|
||||
<text x="500" y="635" class="box-label">Legacy Parser</text>
|
||||
<text x="500" y="655" class="box-sublabel">cmd_parse.c (fallback)</text>
|
||||
|
||||
<!-- Arrows -->
|
||||
<path d="M 225 540 L 225 570 L 200 570 L 200 610" class="arrow"/>
|
||||
<path d="M 300 640 L 400 640" class="arrow-dashed"/>
|
||||
<text x="350" y="630" class="note">fallback</text>
|
||||
|
||||
<!-- ==================== Section 6: Command Dispatch ==================== -->
|
||||
<text x="50" y="720" class="section-title">6. Command Dispatch</text>
|
||||
|
||||
<!-- g_cmd_table -->
|
||||
<rect x="100" y="740" width="200" height="50" class="box-cmd"/>
|
||||
<text x="200" y="765" class="box-label">g_cmd_table[]</text>
|
||||
<text x="200" y="780" class="box-sublabel">51 Commands</text>
|
||||
|
||||
<!-- Arrow -->
|
||||
<path d="M 200 670 L 200 740" class="arrow"/>
|
||||
|
||||
<!-- ==================== Section 7: Command Handlers ==================== -->
|
||||
<text x="50" y="840" class="section-title">7. Command Handlers (cmd.c)</text>
|
||||
|
||||
<!-- Handler boxes -->
|
||||
<rect x="50" y="860" width="120" height="45" class="box-handler"/>
|
||||
<text x="110" y="882" class="box-label">Cmd_mta</text>
|
||||
<text x="110" y="897" class="box-sublabel">Device Status</text>
|
||||
|
||||
<rect x="180" y="860" width="120" height="45" class="box-handler"/>
|
||||
<text x="240" y="882" class="box-label">Cmd_mcj</text>
|
||||
<text x="240" y="897" class="box-sublabel">M48 Measure</text>
|
||||
|
||||
<rect x="310" y="860" width="120" height="45" class="box-handler"/>
|
||||
<text x="370" y="882" class="box-label">Cmd_msn</text>
|
||||
<text x="370" y="897" class="box-sublabel">Battery</text>
|
||||
|
||||
<rect x="440" y="860" width="120" height="45" class="box-handler"/>
|
||||
<text x="500" y="882" class="box-label">Cmd_mag</text>
|
||||
<text x="500" y="897" class="box-sublabel">AGC</text>
|
||||
|
||||
<rect x="570" y="860" width="120" height="45" class="box-handler"/>
|
||||
<text x="630" y="882" class="box-label">Cmd_cmd</text>
|
||||
<text x="630" y="897" class="box-sublabel">GPIO Test</text>
|
||||
|
||||
<rect x="700" y="860" width="120" height="45" class="box-handler"/>
|
||||
<text x="760" y="882" class="box-label">...</text>
|
||||
<text x="760" y="897" class="box-sublabel">+45 more</text>
|
||||
|
||||
<!-- Arrows to handlers -->
|
||||
<path d="M 200 790 L 200 820 L 110 820 L 110 860" class="arrow"/>
|
||||
<path d="M 200 790 L 200 820 L 240 820 L 240 860" class="arrow"/>
|
||||
<path d="M 200 790 L 200 820 L 370 820 L 370 860" class="arrow"/>
|
||||
<path d="M 200 790 L 200 820 L 500 820 L 500 860" class="arrow"/>
|
||||
<path d="M 200 790 L 200 820 L 630 820 L 630 860" class="arrow"/>
|
||||
<path d="M 200 790 L 200 820 L 760 820 L 760 860" class="arrow"/>
|
||||
|
||||
<!-- ==================== Section 8: Hardware Layer ==================== -->
|
||||
<text x="50" y="960" class="section-title">8. Hardware / Peripherals</text>
|
||||
|
||||
<rect x="50" y="980" width="100" height="40" class="box-peripheral"/>
|
||||
<text x="100" y="1005" class="box-label">LED x48</text>
|
||||
|
||||
<rect x="160" y="980" width="100" height="40" class="box-peripheral"/>
|
||||
<text x="210" y="1005" class="box-label">PD ADC</text>
|
||||
|
||||
<rect x="270" y="980" width="100" height="40" class="box-peripheral"/>
|
||||
<text x="320" y="1005" class="box-label">IMU</text>
|
||||
|
||||
<rect x="380" y="980" width="100" height="40" class="box-peripheral"/>
|
||||
<text x="430" y="1005" class="box-label">Temp</text>
|
||||
|
||||
<rect x="490" y="980" width="100" height="40" class="box-peripheral"/>
|
||||
<text x="540" y="1005" class="box-label">Pressure</text>
|
||||
|
||||
<rect x="600" y="980" width="100" height="40" class="box-peripheral"/>
|
||||
<text x="650" y="1005" class="box-label">Battery</text>
|
||||
|
||||
<rect x="710" y="980" width="100" height="40" class="box-peripheral"/>
|
||||
<text x="760" y="1005" class="box-label">EEPROM</text>
|
||||
|
||||
<!-- ==================== Section 9: BLE Response ==================== -->
|
||||
<text x="50" y="1070" class="section-title">9. BLE Response</text>
|
||||
|
||||
<rect x="100" y="1090" width="200" height="50" class="box-ble"/>
|
||||
<text x="200" y="1115" class="box-label">binary_tx_handler()</text>
|
||||
<text x="200" y="1130" class="box-sublabel">ble_data_tx.c + CRC16</text>
|
||||
|
||||
<rect x="400" y="1090" width="200" height="50" class="box-ble"/>
|
||||
<text x="500" y="1115" class="box-label">BLE Central</text>
|
||||
<text x="500" y="1130" class="box-sublabel">Mobile App</text>
|
||||
|
||||
<path d="M 300 1115 L 400 1115" class="arrow"/>
|
||||
|
||||
<!-- ==================== Legend ==================== -->
|
||||
<rect x="650" y="1050" width="220" height="130" class="legend-box"/>
|
||||
<text x="760" y="1072" class="section-title" text-anchor="middle">Legend</text>
|
||||
|
||||
<rect x="665" y="1085" width="20" height="15" class="box-main"/>
|
||||
<text x="695" y="1097" class="note">Main Entry</text>
|
||||
|
||||
<rect x="665" y="1105" width="20" height="15" class="box-ble"/>
|
||||
<text x="695" y="1117" class="note">BLE Layer</text>
|
||||
|
||||
<rect x="665" y="1125" width="20" height="15" class="box-parser"/>
|
||||
<text x="695" y="1137" class="note">Parser Layer</text>
|
||||
|
||||
<rect x="780" y="1085" width="20" height="15" class="box-cmd"/>
|
||||
<text x="810" y="1097" class="note">Cmd Table</text>
|
||||
|
||||
<rect x="780" y="1105" width="20" height="15" class="box-handler"/>
|
||||
<text x="810" y="1117" class="note">Handlers</text>
|
||||
|
||||
<rect x="780" y="1125" width="20" height="15" class="box-peripheral"/>
|
||||
<text x="810" y="1137" class="note">Peripherals</text>
|
||||
|
||||
<!-- Version info -->
|
||||
<text x="450" y="1180" class="note" text-anchor="middle">VivaMayo Architecture v1.0 | Generated: 2026-01-30</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,177 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800px" height="980px" viewBox="-0.5 -0.5 800 980">
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
.title { font: bold 22px 'Segoe UI', Arial, sans-serif; fill: #1a1a1a; }
|
||||
.subtitle { font: 14px 'Segoe UI', Arial, sans-serif; fill: #666; }
|
||||
.process { fill: #e3f2fd; stroke: #1976d2; stroke-width: 2; rx: 8; }
|
||||
.decision { fill: #fff3e0; stroke: #f57c00; stroke-width: 2; }
|
||||
.terminal { fill: #e8f5e9; stroke: #388e3c; stroke-width: 2; rx: 20; }
|
||||
.error { fill: #ffebee; stroke: #d32f2f; stroke-width: 2; rx: 8; }
|
||||
.data { fill: #f3e5f5; stroke: #7b1fa2; stroke-width: 2; }
|
||||
.box-label { font: bold 12px 'Segoe UI', Arial, sans-serif; fill: #333; text-anchor: middle; }
|
||||
.box-sublabel { font: 10px 'Segoe UI', Arial, sans-serif; fill: #666; text-anchor: middle; }
|
||||
.arrow { stroke: #333; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
|
||||
.arrow-yes { stroke: #388e3c; stroke-width: 2; fill: none; marker-end: url(#arrowhead-green); }
|
||||
.arrow-no { stroke: #d32f2f; stroke-width: 2; fill: none; marker-end: url(#arrowhead-red); }
|
||||
.label { font: 11px 'Segoe UI', Arial, sans-serif; fill: #333; }
|
||||
.label-yes { font: bold 11px 'Segoe UI', Arial, sans-serif; fill: #388e3c; }
|
||||
.label-no { font: bold 11px 'Segoe UI', Arial, sans-serif; fill: #d32f2f; }
|
||||
.note { font: 10px 'Segoe UI', Arial, sans-serif; fill: #666; font-style: italic; }
|
||||
.packet { fill: #fafafa; stroke: #999; stroke-width: 1; }
|
||||
.packet-label { font: 10px 'Courier New', monospace; fill: #333; }
|
||||
</style>
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#333"/>
|
||||
</marker>
|
||||
<marker id="arrowhead-green" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#388e3c"/>
|
||||
</marker>
|
||||
<marker id="arrowhead-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#d32f2f"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="800" height="980" fill="#ffffff"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="400" y="35" class="title" text-anchor="middle">Command Processing Flow</text>
|
||||
<text x="400" y="55" class="subtitle" text-anchor="middle">Mt_parser 명령어 처리 흐름도</text>
|
||||
|
||||
<!-- ==================== Start ==================== -->
|
||||
<rect x="300" y="80" width="200" height="40" class="terminal"/>
|
||||
<text x="400" y="105" class="box-label">BLE Data Received</text>
|
||||
|
||||
<path d="M 400 120 L 400 150" class="arrow"/>
|
||||
|
||||
<!-- ==================== nus_data_handler ==================== -->
|
||||
<rect x="300" y="150" width="200" height="50" class="process"/>
|
||||
<text x="400" y="175" class="box-label">nus_data_handler()</text>
|
||||
<text x="400" y="190" class="box-sublabel">ble_core.c</text>
|
||||
|
||||
<path d="M 400 200 L 400 230" class="arrow"/>
|
||||
|
||||
<!-- ==================== received_command_process ==================== -->
|
||||
<rect x="275" y="230" width="250" height="50" class="process"/>
|
||||
<text x="400" y="255" class="box-label">received_command_process()</text>
|
||||
<text x="400" y="270" class="box-sublabel">cmd_parse.c</text>
|
||||
|
||||
<path d="M 400 280 L 400 310" class="arrow"/>
|
||||
|
||||
<!-- ==================== dr_cmd_parser ==================== -->
|
||||
<rect x="300" y="310" width="200" height="50" class="process"/>
|
||||
<text x="400" y="335" class="box-label">dr_cmd_parser()</text>
|
||||
<text x="400" y="350" class="box-sublabel">parser.c</text>
|
||||
|
||||
<path d="M 400 360 L 400 400" class="arrow"/>
|
||||
|
||||
<!-- ==================== CRC Check Decision ==================== -->
|
||||
<polygon points="400,400 480,440 400,480 320,440" class="decision"/>
|
||||
<text x="400" y="445" class="box-label">CRC OK?</text>
|
||||
|
||||
<!-- CRC Fail -->
|
||||
<path d="M 480 440 L 580 440 L 580 400" class="arrow-no"/>
|
||||
<text x="520" y="430" class="label-no">NO</text>
|
||||
|
||||
<rect x="530" y="360" width="120" height="40" class="error"/>
|
||||
<text x="590" y="385" class="box-label">"crc!" 응답</text>
|
||||
|
||||
<!-- CRC OK -->
|
||||
<path d="M 400 480 L 400 520" class="arrow-yes"/>
|
||||
<text x="415" y="500" class="label-yes">YES</text>
|
||||
|
||||
<!-- ==================== TAG Extract ==================== -->
|
||||
<rect x="300" y="520" width="200" height="50" class="process"/>
|
||||
<text x="400" y="540" class="box-label">TAG 추출</text>
|
||||
<text x="400" y="555" class="box-sublabel">"xxx?" 형식 (? 포함 4글자)</text>
|
||||
<text x="400" y="568" class="box-sublabel">dr_copy_tag()</text>
|
||||
|
||||
<path d="M 400 570 L 400 610" class="arrow"/>
|
||||
|
||||
<!-- ==================== Command Table Search Decision ==================== -->
|
||||
<polygon points="400,610 500,660 400,710 300,660" class="decision"/>
|
||||
<text x="400" y="655" class="box-label">g_cmd_table</text>
|
||||
<text x="400" y="670" class="box-label">에서 검색?</text>
|
||||
|
||||
<!-- Not Found -->
|
||||
<path d="M 500 660 L 600 660 L 600 620" class="arrow-no"/>
|
||||
<text x="540" y="650" class="label-no">NOT FOUND</text>
|
||||
|
||||
<rect x="530" y="580" width="140" height="40" class="process"/>
|
||||
<text x="600" y="605" class="box-label">Legacy Parser로</text>
|
||||
|
||||
<!-- Found -->
|
||||
<path d="M 400 710 L 400 750" class="arrow-yes"/>
|
||||
<text x="415" y="730" class="label-yes">FOUND</text>
|
||||
|
||||
<!-- ==================== Enabled Check ==================== -->
|
||||
<polygon points="400,750 470,785 400,820 330,785" class="decision"/>
|
||||
<text x="400" y="790" class="box-label">enabled?</text>
|
||||
|
||||
<!-- Disabled -->
|
||||
<path d="M 470 785 L 550 785" class="arrow-no"/>
|
||||
<text x="500" y="775" class="label-no">false</text>
|
||||
|
||||
<rect x="550" y="765" width="100" height="40" class="error"/>
|
||||
<text x="600" y="790" class="box-label">return 0</text>
|
||||
|
||||
<!-- Enabled -->
|
||||
<path d="M 400 820 L 400 860" class="arrow-yes"/>
|
||||
<text x="415" y="840" class="label-yes">true</text>
|
||||
|
||||
<!-- ==================== Handler Execute ==================== -->
|
||||
<rect x="300" y="860" width="200" height="50" class="terminal"/>
|
||||
<text x="400" y="885" class="box-label">handler() 실행</text>
|
||||
<text x="400" y="900" class="box-sublabel">Cmd_xxx()</text>
|
||||
|
||||
<!-- ==================== Packet Structure ==================== -->
|
||||
<text x="80" y="110" class="label" font-weight="bold">Packet Structure:</text>
|
||||
|
||||
<rect x="30" y="125" width="60" height="30" class="packet"/>
|
||||
<text x="60" y="145" class="packet-label">TAG</text>
|
||||
<text x="60" y="165" class="note">4 bytes</text>
|
||||
|
||||
<rect x="90" y="125" width="80" height="30" class="packet"/>
|
||||
<text x="130" y="145" class="packet-label">DATA</text>
|
||||
<text x="130" y="165" class="note">N bytes</text>
|
||||
|
||||
<rect x="170" y="125" width="60" height="30" class="packet"/>
|
||||
<text x="200" y="145" class="packet-label">CRC</text>
|
||||
<text x="200" y="165" class="note">2 bytes</text>
|
||||
|
||||
<!-- Example -->
|
||||
<text x="80" y="200" class="label" font-weight="bold">Example (sta? mode=1):</text>
|
||||
<!-- TAG box -->
|
||||
<rect x="30" y="215" width="80" height="50" class="packet"/>
|
||||
<text x="70" y="235" class="packet-label" text-anchor="middle">73 74 61 3F</text>
|
||||
<text x="70" y="255" class="note" text-anchor="middle">s t a ?</text>
|
||||
<!-- DATA box -->
|
||||
<rect x="110" y="215" width="60" height="50" class="packet"/>
|
||||
<text x="140" y="235" class="packet-label" text-anchor="middle">00 01</text>
|
||||
<text x="140" y="255" class="note" text-anchor="middle">val=1</text>
|
||||
<!-- CRC box -->
|
||||
<rect x="170" y="215" width="60" height="50" class="packet"/>
|
||||
<text x="200" y="235" class="packet-label" text-anchor="middle">XX XX</text>
|
||||
<text x="200" y="255" class="note" text-anchor="middle">CRC</text>
|
||||
|
||||
<!-- Legend -->
|
||||
<rect x="580" y="80" width="190" height="180" fill="#fafafa" stroke="#ddd" rx="5"/>
|
||||
<text x="675" y="105" class="label" font-weight="bold" text-anchor="middle">Legend</text>
|
||||
|
||||
<rect x="595" y="120" width="25" height="18" class="process"/>
|
||||
<text x="630" y="133" class="label">Process</text>
|
||||
|
||||
<polygon points="607,155 620,165 607,175 595,165" class="decision"/>
|
||||
<text x="630" y="168" class="label">Decision</text>
|
||||
|
||||
<rect x="595" y="185" width="25" height="18" class="terminal"/>
|
||||
<text x="630" y="198" class="label">Start/End</text>
|
||||
|
||||
<rect x="595" y="215" width="25" height="18" class="error"/>
|
||||
<text x="630" y="228" class="label">Error</text>
|
||||
|
||||
<!-- Version -->
|
||||
<text x="400" y="960" class="note" text-anchor="middle">Command Flow v1.0 | Generated: 2026-01-30</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
274
project/ble_peripheral/ble_app_vivaMayo/drivers/w25q32/w25q32.c
Normal file
274
project/ble_peripheral/ble_app_vivaMayo/drivers/w25q32/w25q32.c
Normal file
@@ -0,0 +1,274 @@
|
||||
/*******************************************************************************
|
||||
* @file w25q32.c
|
||||
* @brief W25Q32RV External SPI Flash Driver (Pure Bit-bang - bypass nrfx)
|
||||
******************************************************************************/
|
||||
|
||||
#include "w25q32.h"
|
||||
#include "../../debug_print.h"
|
||||
#include "../../spi2_bus.h"
|
||||
#include "nrf_gpio.h"
|
||||
#include "nrf_delay.h"
|
||||
|
||||
/*==============================================================================
|
||||
* COMMANDS
|
||||
*============================================================================*/
|
||||
|
||||
#define CMD_JEDEC_ID 0x9F
|
||||
#define CMD_READ 0x03
|
||||
#define CMD_WREN 0x06
|
||||
#define CMD_PP 0x02
|
||||
#define CMD_SECTOR_ERASE 0x20
|
||||
#define CMD_CHIP_ERASE 0xC7
|
||||
#define CMD_RDSR 0x05
|
||||
|
||||
#define SR_BUSY 0x01
|
||||
|
||||
/*==============================================================================
|
||||
* BIT-BANG SPI (bypass nrfx_spim completely)
|
||||
*============================================================================*/
|
||||
|
||||
static inline void cs_low(void) { nrf_gpio_pin_clear(W25Q_CS_PIN); }
|
||||
static inline void cs_high(void) { nrf_gpio_pin_set(W25Q_CS_PIN); }
|
||||
|
||||
/*==============================================================================
|
||||
* POWER CONTROL - P0.01 + P0.11 both HIGH = W25Q32 power ON
|
||||
*============================================================================*/
|
||||
|
||||
void w25q32_power_on(void)
|
||||
{
|
||||
nrf_gpio_cfg_output(W25Q_PWR_PIN1);
|
||||
nrf_gpio_cfg_output(W25Q_PWR_PIN2);
|
||||
nrf_gpio_pin_set(W25Q_PWR_PIN1); /* P0.01 HIGH */
|
||||
nrf_gpio_pin_set(W25Q_PWR_PIN2); /* P0.11 HIGH */
|
||||
nrf_delay_ms(10); /* Wait for power stabilization */
|
||||
DBG_PRINTF("[W25Q] Power ON (P0.%d=H, P0.%d=H)\r\n", W25Q_PWR_PIN1, W25Q_PWR_PIN2);
|
||||
}
|
||||
|
||||
void w25q32_power_off(void)
|
||||
{
|
||||
nrf_gpio_pin_clear(W25Q_PWR_PIN1);
|
||||
nrf_gpio_pin_clear(W25Q_PWR_PIN2);
|
||||
DBG_PRINTF("[W25Q] Power OFF\r\n");
|
||||
}
|
||||
|
||||
static void bb_spi_init(void)
|
||||
{
|
||||
/* Step 1: Power ON W25Q32 */
|
||||
w25q32_power_on();
|
||||
|
||||
/* Step 2: Release nrfx_spim ownership of pins */
|
||||
if (spi2_bus_is_initialized()) {
|
||||
DBG_PRINTF("[W25Q] Releasing SPI2 bus...\r\n");
|
||||
spi2_bus_uninit();
|
||||
nrf_delay_ms(1);
|
||||
}
|
||||
|
||||
/* Step 3: ADA2200 CS HIGH (deselect) */
|
||||
nrf_gpio_cfg_output(13); /* ADA2200 CS = P0.13 */
|
||||
nrf_gpio_pin_set(13); /* HIGH = deselected */
|
||||
DBG_PRINTF("[W25Q] ADA2200 CS(P0.13) = HIGH\r\n");
|
||||
|
||||
/* Step 4: Force pins back to GPIO mode */
|
||||
nrf_gpio_cfg_default(W25Q_SCK_PIN);
|
||||
nrf_gpio_cfg_default(W25Q_MOSI_PIN);
|
||||
nrf_gpio_cfg_default(W25Q_MISO_PIN);
|
||||
nrf_gpio_cfg_default(W25Q_CS_PIN);
|
||||
|
||||
/* Step 5: Configure bit-bang SPI with HIGH drive strength */
|
||||
nrf_gpio_cfg(W25Q_SCK_PIN,
|
||||
NRF_GPIO_PIN_DIR_OUTPUT,
|
||||
NRF_GPIO_PIN_INPUT_DISCONNECT,
|
||||
NRF_GPIO_PIN_NOPULL,
|
||||
NRF_GPIO_PIN_H0H1,
|
||||
NRF_GPIO_PIN_NOSENSE);
|
||||
|
||||
nrf_gpio_cfg(W25Q_MOSI_PIN,
|
||||
NRF_GPIO_PIN_DIR_OUTPUT,
|
||||
NRF_GPIO_PIN_INPUT_DISCONNECT,
|
||||
NRF_GPIO_PIN_NOPULL,
|
||||
NRF_GPIO_PIN_H0H1,
|
||||
NRF_GPIO_PIN_NOSENSE);
|
||||
|
||||
nrf_gpio_cfg_input(W25Q_MISO_PIN, NRF_GPIO_PIN_PULLUP);
|
||||
|
||||
nrf_gpio_cfg(W25Q_CS_PIN,
|
||||
NRF_GPIO_PIN_DIR_OUTPUT,
|
||||
NRF_GPIO_PIN_INPUT_DISCONNECT,
|
||||
NRF_GPIO_PIN_NOPULL,
|
||||
NRF_GPIO_PIN_H0H1,
|
||||
NRF_GPIO_PIN_NOSENSE);
|
||||
|
||||
nrf_gpio_pin_clear(W25Q_SCK_PIN); /* Clock idle low (Mode 0) */
|
||||
nrf_gpio_pin_set(W25Q_CS_PIN); /* CS high (deselected) */
|
||||
|
||||
DBG_PRINTF("[W25Q] GPIO: SCK=%d MOSI=%d MISO=%d(dedicated) CS=%d\r\n",
|
||||
W25Q_SCK_PIN, W25Q_MOSI_PIN, W25Q_MISO_PIN, W25Q_CS_PIN);
|
||||
|
||||
/* Step 6: Check MISO state */
|
||||
int miso_state = nrf_gpio_pin_read(W25Q_MISO_PIN);
|
||||
DBG_PRINTF("[W25Q] MISO(P0.%d) = %s\r\n", W25Q_MISO_PIN, miso_state ? "HIGH" : "LOW");
|
||||
}
|
||||
|
||||
static uint8_t bb_spi_transfer_byte(uint8_t tx)
|
||||
{
|
||||
uint8_t rx = 0;
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
/* Set MOSI */
|
||||
if (tx & (1 << i)) {
|
||||
nrf_gpio_pin_set(W25Q_MOSI_PIN);
|
||||
} else {
|
||||
nrf_gpio_pin_clear(W25Q_MOSI_PIN);
|
||||
}
|
||||
|
||||
/* Clock high - sample MISO */
|
||||
nrf_gpio_pin_set(W25Q_SCK_PIN);
|
||||
__NOP(); __NOP(); __NOP(); __NOP();
|
||||
|
||||
if (nrf_gpio_pin_read(W25Q_MISO_PIN)) {
|
||||
rx |= (1 << i);
|
||||
}
|
||||
|
||||
/* Clock low */
|
||||
nrf_gpio_pin_clear(W25Q_SCK_PIN);
|
||||
__NOP(); __NOP(); __NOP(); __NOP();
|
||||
}
|
||||
return rx;
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* PRIVATE HELPERS
|
||||
*============================================================================*/
|
||||
|
||||
static uint8_t read_status(void)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
cs_low();
|
||||
bb_spi_transfer_byte(CMD_RDSR);
|
||||
status = bb_spi_transfer_byte(0xFF);
|
||||
cs_high();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void wait_ready(void)
|
||||
{
|
||||
while (read_status() & SR_BUSY) {
|
||||
nrf_delay_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* PUBLIC FUNCTIONS
|
||||
*============================================================================*/
|
||||
|
||||
bool w25q32_init(void)
|
||||
{
|
||||
DBG_PRINTF("[W25Q] init (bit-bang)\r\n");
|
||||
|
||||
bb_spi_init();
|
||||
nrf_delay_ms(5);
|
||||
|
||||
return w25q32_check_jedec();
|
||||
}
|
||||
|
||||
bool w25q32_check_jedec(void)
|
||||
{
|
||||
uint8_t mfr, type, cap;
|
||||
|
||||
cs_low();
|
||||
bb_spi_transfer_byte(CMD_JEDEC_ID);
|
||||
mfr = bb_spi_transfer_byte(0xFF);
|
||||
type = bb_spi_transfer_byte(0xFF);
|
||||
cap = bb_spi_transfer_byte(0xFF);
|
||||
cs_high();
|
||||
|
||||
DBG_PRINTF("[W25Q] JEDEC = %02X %02X %02X\r\n", mfr, type, cap);
|
||||
|
||||
/* Winbond = 0xEF, W25Q32 = 0x40 0x16 */
|
||||
return (mfr == 0xEF);
|
||||
}
|
||||
|
||||
void w25q32_read(uint32_t addr, uint8_t *buf, uint32_t len)
|
||||
{
|
||||
cs_low();
|
||||
bb_spi_transfer_byte(CMD_READ);
|
||||
bb_spi_transfer_byte((uint8_t)(addr >> 16));
|
||||
bb_spi_transfer_byte((uint8_t)(addr >> 8));
|
||||
bb_spi_transfer_byte((uint8_t)(addr));
|
||||
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
buf[i] = bb_spi_transfer_byte(0xFF);
|
||||
}
|
||||
cs_high();
|
||||
}
|
||||
|
||||
void w25q32_write(uint32_t addr, const uint8_t *buf, uint32_t len)
|
||||
{
|
||||
while (len) {
|
||||
uint32_t page = 256 - (addr & 0xFF);
|
||||
if (page > len) page = len;
|
||||
|
||||
/* Write Enable */
|
||||
cs_low();
|
||||
bb_spi_transfer_byte(CMD_WREN);
|
||||
cs_high();
|
||||
|
||||
/* Page Program */
|
||||
cs_low();
|
||||
bb_spi_transfer_byte(CMD_PP);
|
||||
bb_spi_transfer_byte((uint8_t)(addr >> 16));
|
||||
bb_spi_transfer_byte((uint8_t)(addr >> 8));
|
||||
bb_spi_transfer_byte((uint8_t)(addr));
|
||||
|
||||
for (uint32_t i = 0; i < page; i++) {
|
||||
bb_spi_transfer_byte(buf[i]);
|
||||
}
|
||||
cs_high();
|
||||
|
||||
wait_ready();
|
||||
|
||||
addr += page;
|
||||
buf += page;
|
||||
len -= page;
|
||||
}
|
||||
}
|
||||
|
||||
void w25q32_sector_erase(uint32_t addr)
|
||||
{
|
||||
addr &= ~0xFFF; /* Align to 4KB sector */
|
||||
|
||||
/* Write Enable */
|
||||
cs_low();
|
||||
bb_spi_transfer_byte(CMD_WREN);
|
||||
cs_high();
|
||||
|
||||
/* Sector Erase */
|
||||
cs_low();
|
||||
bb_spi_transfer_byte(CMD_SECTOR_ERASE);
|
||||
bb_spi_transfer_byte((uint8_t)(addr >> 16));
|
||||
bb_spi_transfer_byte((uint8_t)(addr >> 8));
|
||||
bb_spi_transfer_byte((uint8_t)(addr));
|
||||
cs_high();
|
||||
|
||||
wait_ready();
|
||||
}
|
||||
|
||||
void w25q32_chip_erase(void)
|
||||
{
|
||||
DBG_PRINTF("[W25Q] Chip erase...\r\n");
|
||||
|
||||
/* Write Enable */
|
||||
cs_low();
|
||||
bb_spi_transfer_byte(CMD_WREN);
|
||||
cs_high();
|
||||
|
||||
/* Chip Erase */
|
||||
cs_low();
|
||||
bb_spi_transfer_byte(CMD_CHIP_ERASE);
|
||||
cs_high();
|
||||
|
||||
wait_ready();
|
||||
|
||||
DBG_PRINTF("[W25Q] Erase done\r\n");
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*******************************************************************************
|
||||
* @file w25q32.h
|
||||
* @brief W25Q32RV External SPI Flash Driver (Simplified)
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef W25Q32_H
|
||||
#define W25Q32_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/*==============================================================================
|
||||
* PIN CONFIGURATION - VivaMayo hardware
|
||||
*============================================================================*/
|
||||
|
||||
#define W25Q_CS_PIN 24 /* P0.24 - Chip Select */
|
||||
#define W25Q_SCK_PIN 14 /* P0.14 - Clock (shared with ADA2200) */
|
||||
#define W25Q_MOSI_PIN 16 /* P0.16 - Master Out (shared with ADA2200) */
|
||||
#define W25Q_MISO_PIN 19 /* P0.19 - Master In (dedicated, avoid ADA2200 conflict) */
|
||||
|
||||
/* Power control pins - both HIGH = W25Q32 power ON */
|
||||
#define W25Q_PWR_PIN1 1 /* P0.01 - W25Q32 dedicated */
|
||||
#define W25Q_PWR_PIN2 11 /* P0.11 - W25Q32 dedicated */
|
||||
|
||||
/*==============================================================================
|
||||
* API FUNCTIONS
|
||||
*============================================================================*/
|
||||
|
||||
void w25q32_power_on(void);
|
||||
void w25q32_power_off(void);
|
||||
bool w25q32_init(void);
|
||||
bool w25q32_check_jedec(void);
|
||||
#define w25q32_check_id w25q32_check_jedec /* Legacy alias */
|
||||
|
||||
void w25q32_read(uint32_t addr, uint8_t *buf, uint32_t len);
|
||||
void w25q32_write(uint32_t addr, const uint8_t *buf, uint32_t len);
|
||||
|
||||
void w25q32_sector_erase(uint32_t addr);
|
||||
void w25q32_chip_erase(void);
|
||||
|
||||
#endif /* W25Q32_H */
|
||||
401
project/ble_peripheral/ble_app_vivaMayo/fstorage.c
Normal file
401
project/ble_peripheral/ble_app_vivaMayo/fstorage.c
Normal file
@@ -0,0 +1,401 @@
|
||||
/*******************************************************************************
|
||||
TEST medi50 Dec 23
|
||||
******************************************************************************/
|
||||
|
||||
#include "sdk_config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "app_error.h"
|
||||
#include "boards.h"
|
||||
#include "nrf_fstorage.h"
|
||||
#include "nrf_soc.h"
|
||||
#include "nrf_strerror.h"
|
||||
#include "sdk_config.h"
|
||||
#include "nrf_fstorage_sd.h"
|
||||
#include "nrf_delay.h"
|
||||
#include "ble_gap.h"
|
||||
#include "fds.h"
|
||||
|
||||
#include "nrf_log.h"
|
||||
#include "nrf_log_ctrl.h"
|
||||
#include "nrf_log_default_backends.h"
|
||||
|
||||
#include "fstorage.h"
|
||||
#include "nrf_pwr_mgmt.h"
|
||||
#include "measurements.h"
|
||||
#include "main.h"
|
||||
#include "power/power_ctrl.h"
|
||||
#include "debug_print.h"
|
||||
|
||||
|
||||
/* File ID and Key used for the configuration record. */
|
||||
|
||||
#define CONFIG_FILE (0x8010)
|
||||
#define CONFIG_REC_KEY (0x7010)
|
||||
|
||||
#define CONFIG_MAGIC_NUMBER_VALUE (0x20231226)
|
||||
config_data_t m_config;
|
||||
|
||||
extern bool go_device_power_off;
|
||||
extern bool go_sleep_mode_enter;
|
||||
extern bool go_NVIC_SystemReset;
|
||||
|
||||
/* Flag to check fds initialization. */
|
||||
static bool volatile m_fds_initialized;
|
||||
|
||||
bool fds_flag_write = false;
|
||||
/* A record containing dummy configuration data. */
|
||||
static fds_record_t const m_dummy_record =
|
||||
{
|
||||
.file_id = CONFIG_FILE,
|
||||
.key = CONFIG_REC_KEY,
|
||||
.data.p_data = (void const *)&m_config,
|
||||
/* The length of a record is always expressed in 4-byte units (words). */
|
||||
.data.length_words = (sizeof(m_config) + 3) / sizeof(uint32_t),
|
||||
};
|
||||
|
||||
|
||||
/* 공장 입력 항목 1 */
|
||||
|
||||
|
||||
/* 공장 입력 항목 2 */
|
||||
//char serial_number_dflt[12] = "2025AAMAY0FF";
|
||||
//uint16_t pd_delay_us = 0;
|
||||
//int8_t reset_status=0;
|
||||
|
||||
//uint32_t pd_adc_calibration_dflt_PD0[M_LED_NUM] =
|
||||
// {1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000, /* PD01 */
|
||||
//};
|
||||
//uint32_t pd_adc_calibration_dflt_PD1[M_LED_NUM] =
|
||||
// {1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000, /* PD1 */
|
||||
//};
|
||||
//uint32_t pd_adc_calibration_dflt_PD2[M_LED_NUM] =
|
||||
// {1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000, /* MOD1 */
|
||||
//};
|
||||
|
||||
///* 공장 입력 항목 5 */
|
||||
//uint32_t dark_noise_for_pd_dflt[PD_NUM] = /*PD0, PD1, PD2, PD3, PD4, PD5, PD6, PD7, PD8, PD9, PD10, PD11, PD12, PD13, PD14, PD15, PD16, PD17, PD18, PD19, PD20, */
|
||||
// {1111, 1111};
|
||||
|
||||
|
||||
/* 공장 입력 항목 8 */
|
||||
//int8_t pd_adc_cnt_dflt = 8; /* 8, 16, 24, 32 */
|
||||
int8_t reset_status_dflt =99;
|
||||
/* 공장 입력 항목 9 */
|
||||
//uint16_t led_delay_us_dflt = 8000; /* 0 ~ 65535 */
|
||||
|
||||
/* 공장 입력 항목 10 */
|
||||
//uint16_t pd_delay_us_dflt = 8000; /* 0 ~ 65535 */
|
||||
|
||||
/* Static Passkey default */
|
||||
uint8_t static_passkey_dflt[6] = "123456";
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
//uint16_t led_power_dp_dflt[48] = {
|
||||
// //NULL, 42, 29, 41, 31, 28, 28, 42, 28, 39, 29, 27, 27, 42, 29, 40, 30, 28, 28, 40, 27, 38, 28, 25, 22
|
||||
// 25,25,25,18,19,24,24,25,25,18,19,25,23,25,25,18,19,24,24,25,25,19,20,25,24,25,25,25,18,19,24,24,25,25,18,19,25,23,25,25,18,19,24,24,25,25,19,20
|
||||
//};
|
||||
|
||||
void fds_default_value_set(void)
|
||||
{
|
||||
/* HW Number - empty (set via BLE command) */
|
||||
memset(m_config.hw_no, 0, 12);
|
||||
|
||||
/* Serial Number */
|
||||
memcpy(m_config.serial_no, "2025AAAAT001", 12);
|
||||
|
||||
/* Static Passkey */
|
||||
memcpy(m_config.static_passkey, static_passkey_dflt, 6);
|
||||
|
||||
/* Bond data delete */
|
||||
m_config.bond_data_delete = 1;
|
||||
|
||||
/* Reset status */
|
||||
m_config.reset_status = reset_status_dflt;
|
||||
|
||||
/* Measurement parameters */
|
||||
m_config.pd_adc_cnt = 8;
|
||||
m_config.pd_delay_us = 8000;
|
||||
}
|
||||
|
||||
|
||||
static volatile uint8_t fds_last_evt = 0xFF;
|
||||
|
||||
static void fds_evt_handler( fds_evt_t const *p_evt )
|
||||
{
|
||||
fds_last_evt = p_evt->id;
|
||||
|
||||
switch( p_evt->id )
|
||||
{
|
||||
case FDS_EVT_INIT:
|
||||
if( p_evt->result == NRF_SUCCESS )
|
||||
{
|
||||
m_fds_initialized = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case FDS_EVT_WRITE:
|
||||
{
|
||||
fds_flag_write = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case FDS_EVT_UPDATE:
|
||||
{
|
||||
fds_flag_write = false;
|
||||
|
||||
if(go_device_power_off == true) {
|
||||
/* After flash writing completed, System Power Off */
|
||||
device_power_off();
|
||||
}
|
||||
if(go_sleep_mode_enter == true) {
|
||||
/* After flash writing completed, System go to Sleep Mode */
|
||||
sleep_mode_enter();
|
||||
}
|
||||
if(go_NVIC_SystemReset == true) {
|
||||
/* After flash writing completed, System Reset */
|
||||
printf("Off FDS_ENVET\r\n");
|
||||
NVIC_SystemReset(); //0112
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FDS_EVT_DEL_RECORD:
|
||||
break;
|
||||
|
||||
case FDS_EVT_DEL_FILE:
|
||||
break;
|
||||
|
||||
case FDS_EVT_GC:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@brief Wait for fds to initialize. */
|
||||
static void wait_for_fds_ready( void )
|
||||
{
|
||||
uint32_t timeout = 0;
|
||||
while( !m_fds_initialized )
|
||||
{
|
||||
nrf_pwr_mgmt_run();
|
||||
nrf_delay_ms(1);
|
||||
timeout++;
|
||||
if (timeout > 3000) { /* 3 second timeout */
|
||||
printf("[FDS] TIMEOUT!\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void config_load( void )
|
||||
{
|
||||
ret_code_t rc;
|
||||
fds_record_desc_t desc = { 0 };
|
||||
fds_find_token_t tok = { 0 };
|
||||
uint8_t cfg_retry = 0;
|
||||
|
||||
cfg_load_start:
|
||||
memset((char *)&desc, 0, sizeof(desc));
|
||||
memset((char *)&tok, 0, sizeof(tok));
|
||||
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
|
||||
DBG_PRINTF("[FDS] find rc=%u\r\n", rc);
|
||||
|
||||
/* FDS may not be fully ready yet - retry before writing defaults */
|
||||
if (rc != NRF_SUCCESS && cfg_retry < 10) {
|
||||
cfg_retry++;
|
||||
DBG_PRINTF("[FDS] retry %u/10\r\n", cfg_retry);
|
||||
nrf_delay_ms(100);
|
||||
goto cfg_load_start;
|
||||
}
|
||||
|
||||
if( rc == NRF_SUCCESS )
|
||||
{
|
||||
/* A config file is in flash. Let's update it. */
|
||||
fds_flash_record_t config = { 0 };
|
||||
|
||||
/* Open the record and read its contents. */
|
||||
rc = fds_record_open(&desc, &config);
|
||||
if (rc != NRF_SUCCESS) {
|
||||
/* CRC error or corrupt record - delete and use defaults */
|
||||
DBG_PRINTF("[FDS] open ERR=%u, deleting\r\n", rc);
|
||||
(void)fds_record_delete(&desc);
|
||||
fds_gc();
|
||||
fds_default_value_set();
|
||||
goto cfg_load_write_new;
|
||||
}
|
||||
|
||||
/* Copy the configuration from flash into m_config. */
|
||||
memcpy(&m_config, config.p_data, sizeof(config_data_t));
|
||||
|
||||
/* Close the record when done reading. */
|
||||
rc = fds_record_close(&desc);
|
||||
APP_ERROR_CHECK(rc);
|
||||
|
||||
DBG_PRINTF("[FDS] magic=0x%08X (expect 0x%08X)\r\n",
|
||||
m_config.magic_number, CONFIG_MAGIC_NUMBER_VALUE);
|
||||
|
||||
if( m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE )
|
||||
{ // first init
|
||||
DBG_PRINTF("[FDS] FORMAT! overwriting with defaults\r\n");
|
||||
rc = fds_record_delete(&desc);
|
||||
APP_ERROR_CHECK(rc);
|
||||
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
|
||||
|
||||
// default....
|
||||
fds_default_value_set();
|
||||
|
||||
/* Write the updated record to flash. */
|
||||
rc = fds_record_update(&desc, &m_dummy_record);
|
||||
if( (rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH) )
|
||||
{
|
||||
rc = fds_gc();
|
||||
APP_ERROR_CHECK(rc);
|
||||
}
|
||||
else
|
||||
{
|
||||
APP_ERROR_CHECK(rc);
|
||||
}
|
||||
goto cfg_load_start;
|
||||
}
|
||||
DBG_PRINTF("[FDS] Loaded OK\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
cfg_load_write_new:
|
||||
DBG_PRINTF("[FDS] New - writing defaults\r\n");
|
||||
/* System config not found (or corrupt); write a new one. */
|
||||
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
|
||||
|
||||
// default....
|
||||
fds_default_value_set();
|
||||
|
||||
fds_flag_write = true;
|
||||
rc = fds_record_write(&desc, &m_dummy_record);
|
||||
if (rc != NRF_SUCCESS) {
|
||||
DBG_PRINTF("[FDS] Write ERR=%u\r\n", rc);
|
||||
fds_flag_write = false;
|
||||
}
|
||||
while( fds_flag_write )
|
||||
{
|
||||
nrf_pwr_mgmt_run();
|
||||
}
|
||||
if( (rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH) )
|
||||
{
|
||||
rc = fds_gc();
|
||||
APP_ERROR_CHECK(rc);
|
||||
}
|
||||
else
|
||||
{
|
||||
APP_ERROR_CHECK(rc);
|
||||
}
|
||||
NRF_LOG_FLUSH();
|
||||
goto cfg_load_start;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void config_save( void )
|
||||
{
|
||||
ret_code_t rc;
|
||||
fds_record_desc_t desc = { 0 };
|
||||
fds_find_token_t tok = { 0 };
|
||||
|
||||
DBG_PRINTF("[CFG_SAVE] start\r\n");
|
||||
|
||||
/* Skip if a previous FDS operation is still in progress (non-blocking) */
|
||||
if (fds_flag_write) {
|
||||
DBG_PRINTF("[CFG_SAVE] busy, skipped\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if( m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE )
|
||||
{
|
||||
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
|
||||
}
|
||||
|
||||
memset((char *)&desc, 0, sizeof(desc));
|
||||
memset((char *)&tok, 0, sizeof(tok));
|
||||
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
|
||||
DBG_PRINTF("[CFG_SAVE] find rc=%u\r\n", rc);
|
||||
|
||||
/* Debug: show what we're about to save */
|
||||
{
|
||||
char tmp[13] = {0};
|
||||
memcpy(tmp, m_config.hw_no, 12);
|
||||
DBG_PRINTF("[CFG_SAVE] hw='%s'\r\n", tmp);
|
||||
memcpy(tmp, m_config.serial_no, 12);
|
||||
DBG_PRINTF("[CFG_SAVE] sn='%s'\r\n", tmp);
|
||||
}
|
||||
|
||||
if( rc == NRF_SUCCESS )
|
||||
{
|
||||
fds_flag_write = true;
|
||||
rc = fds_record_update(&desc, &m_dummy_record);
|
||||
DBG_PRINTF("[CFG_SAVE] update rc=%u\r\n", rc);
|
||||
if( rc == FDS_ERR_NO_SPACE_IN_FLASH )
|
||||
{
|
||||
fds_flag_write = false;
|
||||
rc = fds_gc();
|
||||
DBG_PRINTF("[CFG_SAVE] gc rc=%u, retry\r\n", rc);
|
||||
fds_flag_write = true;
|
||||
rc = fds_record_update(&desc, &m_dummy_record);
|
||||
DBG_PRINTF("[CFG_SAVE] retry rc=%u\r\n", rc);
|
||||
}
|
||||
if( rc != NRF_SUCCESS )
|
||||
{
|
||||
DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc);
|
||||
fds_flag_write = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG_PRINTF("[CFG_SAVE] not found, writing new\r\n");
|
||||
fds_flag_write = true;
|
||||
rc = fds_record_write(&desc, &m_dummy_record);
|
||||
DBG_PRINTF("[CFG_SAVE] write rc=%u\r\n", rc);
|
||||
if( rc != NRF_SUCCESS )
|
||||
{
|
||||
DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc);
|
||||
fds_flag_write = false;
|
||||
}
|
||||
}
|
||||
|
||||
DBG_PRINTF("[CFG_SAVE] done\r\n");
|
||||
}
|
||||
|
||||
|
||||
void fs_set_value(void)
|
||||
{
|
||||
config_load();
|
||||
}
|
||||
|
||||
void fs_storage_init(void)
|
||||
{
|
||||
ret_code_t rc;
|
||||
|
||||
/* Register first to receive an event when initialization is complete. */
|
||||
rc = fds_register(fds_evt_handler);
|
||||
APP_ERROR_CHECK(rc);
|
||||
|
||||
rc = fds_init();
|
||||
APP_ERROR_CHECK(rc);
|
||||
|
||||
/* Wait for fds to initialize. */
|
||||
wait_for_fds_ready();
|
||||
|
||||
fds_stat_t stat = { 0 };
|
||||
rc = fds_stat(&stat);
|
||||
APP_ERROR_CHECK(rc);
|
||||
|
||||
printf("[FDS] OK\r\n");
|
||||
}
|
||||
|
||||
40
project/ble_peripheral/ble_app_vivaMayo/fstorage.h
Normal file
40
project/ble_peripheral/ble_app_vivaMayo/fstorage.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*******************************************************************************
|
||||
* @file fstorage.h
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef IHP_FSTORAGE_H_
|
||||
#define IHP_FSTORAGE_H_
|
||||
|
||||
#include "sdk_config.h"
|
||||
|
||||
#include "nordic_common.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma pack(1)
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic_number; /* 4B - 0x20231226 */
|
||||
char hw_no[12]; /* 12B - HW Number (AES encrypted) */
|
||||
char serial_no[12]; /* 12B - Serial Number (AES encrypted) */
|
||||
uint8_t static_passkey[6]; /* 6B - BLE Passkey (AES encrypted) */
|
||||
uint8_t bond_data_delete; /* 1B - Bond delete flag */
|
||||
int8_t reset_status; /* 1B - Reset status */
|
||||
uint8_t pd_adc_cnt; /* 1B - ADC sample count */
|
||||
uint16_t pd_delay_us; /* 2B - PD delay (us) */
|
||||
} config_data_t; /* Total: 41 bytes - FDS에 저장하는 디바이스 설정 */
|
||||
|
||||
extern config_data_t m_config;
|
||||
|
||||
void fds_default_value_set(void);
|
||||
void config_load( void );
|
||||
void config_save( void );
|
||||
|
||||
void fs_set_value(void);
|
||||
void fs_storage_init(void);
|
||||
|
||||
#endif /* IHP_FSTORAGE_H_ */
|
||||
|
||||
1082
project/ble_peripheral/ble_app_vivaMayo/full_agc.c
Normal file
1082
project/ble_peripheral/ble_app_vivaMayo/full_agc.c
Normal file
File diff suppressed because it is too large
Load Diff
172
project/ble_peripheral/ble_app_vivaMayo/full_agc.h
Normal file
172
project/ble_peripheral/ble_app_vivaMayo/full_agc.h
Normal file
@@ -0,0 +1,172 @@
|
||||
/*******************************************************************************
|
||||
* @file full_agc.h
|
||||
* @brief Automatic Gain Control (AGC) header for NIRS measurement 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 header defines the interface for the AGC module which automatically
|
||||
* calibrates photodetector gain for each LED-PD pair in the NIRS system.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _FULL_AGC_H__
|
||||
#define _FULL_AGC_H__
|
||||
|
||||
/*============================================================================*/
|
||||
/* Includes - Charles KWON */
|
||||
/*============================================================================*/
|
||||
#include "sdk_common.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* Configuration Constants - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Number of photodetectors in the system
|
||||
* - Charles KWON
|
||||
*
|
||||
* Used to determine iteration count for AGC scanning
|
||||
*/
|
||||
#define PD_NO 1
|
||||
|
||||
/*============================================================================*/
|
||||
/* AGC Control Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Initialize and start AGC sequence for current phase
|
||||
* - Charles KWON
|
||||
*
|
||||
* Configures LED/PD lists based on which phase is active (A or B).
|
||||
* Must be called after setting full_agc_a_start or full_agc_b_start.
|
||||
*/
|
||||
void full_agc_start(void);
|
||||
|
||||
/**
|
||||
* @brief Terminate AGC sequence and perform cleanup
|
||||
* - Charles KWON
|
||||
*
|
||||
* Stops all AGC timers, uninitializes SAADC, restores HW I2C mode,
|
||||
* turns off all LEDs/PDs, and clears the processing flag.
|
||||
*/
|
||||
void full_agc_end(void);
|
||||
|
||||
/*============================================================================*/
|
||||
/* SAADC Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Initialize SAADC for AGC voltage measurement
|
||||
* - Charles KWON
|
||||
*
|
||||
* Configures single-ended ADC on AIN6 (FSA5157P6X analog switch output)
|
||||
* with double-buffering for continuous sampling during AGC.
|
||||
*/
|
||||
void full_agc_adc_init(void);
|
||||
|
||||
/**
|
||||
* @brief Uninitialize SAADC after AGC completion
|
||||
* - Charles KWON
|
||||
*
|
||||
* Releases SAADC resources for use by other modules.
|
||||
*/
|
||||
void full_agc_uninit(void);
|
||||
|
||||
/*============================================================================*/
|
||||
/* AGC Loop Timer Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief AGC measurement loop timer callback
|
||||
* - Charles KWON
|
||||
*
|
||||
* Periodically called to drive the AGC state machine.
|
||||
* Each call advances the measurement sequence by one step.
|
||||
*
|
||||
* @param[in] p_context Unused timer context parameter
|
||||
*/
|
||||
void full_agc_loop(void * p_context);
|
||||
|
||||
/**
|
||||
* @brief Start AGC measurement loop timer
|
||||
* - Charles KWON
|
||||
*
|
||||
* Begins periodic timer that drives the AGC state machine.
|
||||
*/
|
||||
void full_agc_timer_start(void);
|
||||
|
||||
/**
|
||||
* @brief Stop AGC measurement loop timer
|
||||
* - Charles KWON
|
||||
*/
|
||||
void full_agc_timer_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize AGC loop timer
|
||||
* - Charles KWON
|
||||
*
|
||||
* Creates the repeating timer used for AGC measurement sequencing.
|
||||
* Must be called during system initialization before starting AGC.
|
||||
*/
|
||||
void full_agc_timer_init(void);
|
||||
|
||||
/*============================================================================*/
|
||||
/* AGC Entry Point - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Start full AGC measurement sequence
|
||||
* - Charles KWON
|
||||
*
|
||||
* Main entry point for AGC calibration. This function:
|
||||
* 1. Initializes AGC state variables
|
||||
* 2. Stops battery monitoring timer
|
||||
* 3. Configures Part A LED/PD lists
|
||||
* 4. Initializes SAADC
|
||||
* 5. Starts AGC loop timer
|
||||
*
|
||||
* AGC automatically transitions from Part A to Part B and
|
||||
* stores results in EEPROM upon completion.
|
||||
*/
|
||||
void full_agc_mesurement_start(void);
|
||||
|
||||
/*============================================================================*/
|
||||
/* AGC Data Transmission Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief AGC data transmission timer callback
|
||||
* - Charles KWON
|
||||
*
|
||||
* Handles sequenced transmission of AGC calibration results over BLE.
|
||||
* Data is sent in chunks to comply with BLE packet size limits.
|
||||
*
|
||||
* @param[in] p_context Unused timer context parameter
|
||||
*/
|
||||
void full_agc_send_loop(void * p_context);
|
||||
|
||||
/**
|
||||
* @brief Start AGC data transmission timer
|
||||
* - Charles KWON
|
||||
*/
|
||||
void full_agc_send_timer_start(void);
|
||||
|
||||
/**
|
||||
* @brief Stop AGC data transmission timer
|
||||
* - Charles KWON
|
||||
*/
|
||||
void full_agc_send_timer_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize AGC send timer
|
||||
* - Charles KWON
|
||||
*
|
||||
* Creates the single-shot timer used for sequenced BLE transmission.
|
||||
* Must be called during system initialization.
|
||||
*/
|
||||
void full_agc_send_timer_init(void);
|
||||
|
||||
#endif /* _FULL_AGC_H__ */
|
||||
31
project/ble_peripheral/ble_app_vivaMayo/hardfault_handler.c
Normal file
31
project/ble_peripheral/ble_app_vivaMayo/hardfault_handler.c
Normal file
@@ -0,0 +1,31 @@
|
||||
/*******************************************************************************
|
||||
* @file hardfault_handler.c
|
||||
* @brief HardFault Handler for debugging system crashes
|
||||
******************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "debug_print.h"
|
||||
|
||||
void HardFault_Handler(void)
|
||||
{
|
||||
volatile uint32_t *stack;
|
||||
__asm volatile ("mrs %0, msp" : "=r" (stack));
|
||||
|
||||
DBG_PRINTF("\r\n!!! HARDFAULT !!!\r\n");
|
||||
DBG_PRINTF("R0 = %08lX\r\n", stack[0]);
|
||||
DBG_PRINTF("R1 = %08lX\r\n", stack[1]);
|
||||
DBG_PRINTF("R2 = %08lX\r\n", stack[2]);
|
||||
DBG_PRINTF("R3 = %08lX\r\n", stack[3]);
|
||||
DBG_PRINTF("R12 = %08lX\r\n", stack[4]);
|
||||
DBG_PRINTF("LR = %08lX\r\n", stack[5]);
|
||||
DBG_PRINTF("PC = %08lX\r\n", stack[6]);
|
||||
DBG_PRINTF("PSR = %08lX\r\n", stack[7]);
|
||||
|
||||
while (1) {
|
||||
/* Blink LED to indicate fault */
|
||||
for (volatile uint32_t i = 0; i < 500000; i++);
|
||||
NRF_P0->OUTSET = (1 << 12);
|
||||
for (volatile uint32_t i = 0; i < 500000; i++);
|
||||
NRF_P0->OUTCLR = (1 << 12);
|
||||
}
|
||||
}
|
||||
94
project/ble_peripheral/ble_app_vivaMayo/i2c_manager.c
Normal file
94
project/ble_peripheral/ble_app_vivaMayo/i2c_manager.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/*******************************************************************************
|
||||
* @file i2c_manager.c
|
||||
* @brief Reliable HW↔SW I2C Switching Logic (with Mode Set Logging)
|
||||
******************************************************************************/
|
||||
|
||||
#include "i2c_manager.h"
|
||||
#include "debug_print.h"
|
||||
#include "nrf_delay.h"
|
||||
|
||||
#include "mcp4725_i2c.h"
|
||||
#include "nrf_drv_twi.h"
|
||||
#include "cat_interface.h"
|
||||
bool HW_I2C_FRQ = true;
|
||||
bool SW_I2C_FRQ = false;
|
||||
|
||||
/* 외부 EEPROM TWI 인스턴스 */
|
||||
extern const nrf_drv_twi_t m_eeprom;
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* HW (TWI) 초기화 */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
void hw_i2c_init_once(void)
|
||||
{
|
||||
// SW 모드일 경우 강제 해제 후 HW 전환
|
||||
if (SW_I2C_FRQ)
|
||||
{
|
||||
//DBG_PRINTF("[I2C]SW→HW\r\n");
|
||||
|
||||
// SW 리소스 해제 (필요 시 추가)
|
||||
SW_I2C_FRQ = false;
|
||||
nrf_delay_ms(2);
|
||||
}
|
||||
|
||||
// 이미 HW면 스킵
|
||||
if (HW_I2C_FRQ)
|
||||
{
|
||||
// DBG_PRINTF("[I2C] HW I2C set\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// 실제 HW 초기화
|
||||
// eeprom_initialize(); /* vivaMayo: No EEPROM */
|
||||
nrf_delay_ms(2);
|
||||
|
||||
HW_I2C_FRQ = true;
|
||||
SW_I2C_FRQ = false;
|
||||
|
||||
// DBG_PRINTF("[I2C] HW I2C Mode set!\r\n");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* SW (Port Bang-Bang) 초기화 */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
void sw_i2c_init_once(void)
|
||||
{
|
||||
// HW 모드일 경우 강제 해제 후 SW 전환
|
||||
if (HW_I2C_FRQ)
|
||||
{
|
||||
//DBG_PRINTF("[I2C]HW→SW\r\n");
|
||||
|
||||
nrf_drv_twi_disable(&m_eeprom);
|
||||
nrf_drv_twi_uninit(&m_eeprom);
|
||||
nrf_delay_ms(2);
|
||||
|
||||
HW_I2C_FRQ = false;
|
||||
}
|
||||
|
||||
// 이미 SW 모드면 재실행 금지
|
||||
if (SW_I2C_FRQ)
|
||||
{
|
||||
// DBG_PRINTF("[I2C] SWI2C already initialized\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// 실제 SW 초기화
|
||||
// eeprom_uninitialize(); // TWI 라인 해제 /* vivaMayo: No EEPROM */
|
||||
nrf_delay_ms(1);
|
||||
mcp4725_init(); // Port BangBang 방식 DAC init
|
||||
|
||||
SW_I2C_FRQ = true;
|
||||
HW_I2C_FRQ = false;
|
||||
|
||||
// DBG_PRINTF("[I2C] SW I2C Mode set!\r\n");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 전체 리셋 */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
void i2c_reset_state(void)
|
||||
{
|
||||
HW_I2C_FRQ = false;
|
||||
SW_I2C_FRQ = false;
|
||||
DBG_PRINTF("Flags reset\r\n");
|
||||
}
|
||||
18
project/ble_peripheral/ble_app_vivaMayo/i2c_manager.h
Normal file
18
project/ble_peripheral/ble_app_vivaMayo/i2c_manager.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*******************************************************************************
|
||||
* @file i2c_manager.h
|
||||
* @brief Common header for HW/SW I2C mutex control
|
||||
******************************************************************************/
|
||||
#ifndef __I2C_MANAGER_H__
|
||||
#define __I2C_MANAGER_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "app_error.h"
|
||||
|
||||
extern bool HW_I2C_FRQ;
|
||||
extern bool SW_I2C_FRQ;
|
||||
|
||||
void hw_i2c_init_once(void);
|
||||
void sw_i2c_init_once(void);
|
||||
void i2c_reset_state(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively “Software”) is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
#include "DataConverter.h"
|
||||
|
||||
uint8_t * inv_dc_int32_to_little8(int32_t x, uint8_t * little8)
|
||||
{
|
||||
little8[3] = (uint8_t)((x >> 24) & 0xff);
|
||||
little8[2] = (uint8_t)((x >> 16) & 0xff);
|
||||
little8[1] = (uint8_t)((x >> 8) & 0xff);
|
||||
little8[0] = (uint8_t)(x & 0xff);
|
||||
|
||||
return little8;
|
||||
}
|
||||
|
||||
uint8_t * inv_dc_int16_to_little8(int16_t x, uint8_t * little8)
|
||||
{
|
||||
little8[0] = (uint8_t)(x & 0xff);
|
||||
little8[1] = (uint8_t)((x >> 8) & 0xff);
|
||||
|
||||
return little8;
|
||||
}
|
||||
|
||||
uint8_t * inv_dc_int32_to_big8(int32_t x, uint8_t * big8)
|
||||
{
|
||||
big8[0] = (uint8_t)((x >> 24) & 0xff);
|
||||
big8[1] = (uint8_t)((x >> 16) & 0xff);
|
||||
big8[2] = (uint8_t)((x >> 8) & 0xff);
|
||||
big8[3] = (uint8_t)(x & 0xff);
|
||||
|
||||
return big8;
|
||||
}
|
||||
|
||||
int32_t inv_dc_little8_to_int32(const uint8_t * little8)
|
||||
{
|
||||
int32_t x = 0;
|
||||
|
||||
x |= ((int32_t)little8[3] << 24);
|
||||
x |= ((int32_t)little8[2] << 16);
|
||||
x |= ((int32_t)little8[1] << 8);
|
||||
x |= ((int32_t)little8[0]);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
int16_t inv_dc_big16_to_int16(uint8_t * data)
|
||||
{
|
||||
int16_t result;
|
||||
|
||||
result = (*data << 8);
|
||||
data++;
|
||||
result |= *data;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int16_t inv_dc_le_to_int16(const uint8_t * little8)
|
||||
{
|
||||
uint16_t x = 0;
|
||||
|
||||
x |= ((uint16_t)little8[0]);
|
||||
x |= ((uint16_t)little8[1] << 8);
|
||||
|
||||
return (int16_t)x;
|
||||
}
|
||||
|
||||
void inv_dc_sfix32_to_float(const int32_t * in, uint32_t len, uint8_t qx, float * out)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for(i = 0; i < len; ++i) {
|
||||
out[i] = (float)in[i] / (1 << qx);
|
||||
}
|
||||
}
|
||||
|
||||
void inv_dc_float_to_sfix32(const float * in, uint32_t len, uint8_t qx, int32_t * out)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for(i = 0; i < len; ++i) {
|
||||
out[i] = (int32_t)((in[i] * (1 << qx)) + ((in[i] >= 0) - 0.5f));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively “Software”) is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
/** @defgroup DataConverter Data Converter
|
||||
* @brief Helper functions to convert integer
|
||||
* @ingroup EmbUtils
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _INV_DATA_CONVERTER_H_
|
||||
#define _INV_DATA_CONVERTER_H_
|
||||
|
||||
#include "InvExport.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** @brief Converts a 32-bit long to a little endian byte stream
|
||||
*/
|
||||
uint8_t INV_EXPORT * inv_dc_int32_to_little8(int32_t x, uint8_t * little8);
|
||||
|
||||
/** @brief Converts a 16-bit integer to a little endian byte stream
|
||||
*/
|
||||
uint8_t INV_EXPORT * inv_dc_int16_to_little8(int16_t x, uint8_t * little8);
|
||||
|
||||
/** @brief Converts a 32-bit long to a big endian byte stream
|
||||
*/
|
||||
uint8_t INV_EXPORT * inv_dc_int32_to_big8(int32_t x, uint8_t *big8);
|
||||
|
||||
/** @brief Converts a little endian byte stream into a 32-bit integer
|
||||
*/
|
||||
int32_t INV_EXPORT inv_dc_little8_to_int32(const uint8_t * little8);
|
||||
|
||||
/** @brief Converts a little endian byte stream into a 16-bit integer
|
||||
*/
|
||||
int16_t INV_EXPORT inv_dc_le_to_int16(const uint8_t * little8);
|
||||
|
||||
/** @brief Converts big endian on 16 bits into an unsigned short
|
||||
*/
|
||||
int16_t INV_EXPORT inv_dc_big16_to_int16(uint8_t * data);
|
||||
|
||||
/** @brief Converts an array of 32-bit signed fixed-point integers to an array of floats
|
||||
* @param[in] in Pointer to the first element of the array of 32-bit signed fixed-point integers
|
||||
* @param[in] len Length of the array
|
||||
* @param[in] qx Number of bits used to represent the decimal part of the fixed-point integers
|
||||
* @param[out] out Pointer to the memory area where the output will be stored
|
||||
*/
|
||||
void INV_EXPORT inv_dc_sfix32_to_float(const int32_t * in, uint32_t len, uint8_t qx, float * out);
|
||||
|
||||
/** @brief Converts an array of floats to an array of 32-bit signed fixed-point integers
|
||||
* @param[in] in Pointer to the first element of the array of floats
|
||||
* @param[in] len Length of the array
|
||||
* @param[in] qx Number of bits used to represent the decimal part of the fixed-point integers
|
||||
* @param[out] out Pointer to the memory area where the output will be stored
|
||||
*/
|
||||
void INV_EXPORT inv_dc_float_to_sfix32(const float * in, uint32_t len, uint8_t qx, int32_t * out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INV_DATA_CONVERTER_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively “Software”) is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
#include "ErrorHelper.h"
|
||||
|
||||
const char * inv_error_str(int error)
|
||||
{
|
||||
switch(error) {
|
||||
case INV_ERROR_SUCCESS: return "Success";
|
||||
case INV_ERROR: return "Unspecified error";
|
||||
case INV_ERROR_NIMPL: return "Not implemented";
|
||||
case INV_ERROR_TRANSPORT: return "Transport error";
|
||||
case INV_ERROR_TIMEOUT: return "Timeout, action did not complete in time";
|
||||
case INV_ERROR_SIZE: return "Wrong size error";
|
||||
case INV_ERROR_OS: return "Operating system failure";
|
||||
case INV_ERROR_IO: return "Input/Output error";
|
||||
case INV_ERROR_MEM: return "Bad allocation";
|
||||
case INV_ERROR_HW: return "Hardware error";
|
||||
case INV_ERROR_BAD_ARG: return "Invalid arguments";
|
||||
case INV_ERROR_UNEXPECTED: return "Unexpected error";
|
||||
case INV_ERROR_FILE: return "Invalid file format";
|
||||
case INV_ERROR_PATH: return "Invalid file path";
|
||||
case INV_ERROR_IMAGE_TYPE: return "Unknown image type";
|
||||
case INV_ERROR_WATCHDOG: return "Watchdog error";
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively “Software”) is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
/** @defgroup ErrorHelper Error Helper
|
||||
* @brief Helper functions related to error code
|
||||
* @ingroup EmbUtils
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _INV_ERROR_HELPER_H_
|
||||
#define _INV_ERROR_HELPER_H_
|
||||
|
||||
#include "InvExport.h"
|
||||
#include "InvError.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief Returns string describing error number
|
||||
* @sa enum inv_error
|
||||
*/
|
||||
const char INV_EXPORT * inv_error_str(int error);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INV_ERROR_HELPER_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright (c) 2014-2015 InvenSense Inc. Portions Copyright (c) 2014-2015 Movea. All rights reserved.
|
||||
|
||||
This software, related documentation and any modifications thereto (collectively "Software") is subject
|
||||
to InvenSense and its licensors' intellectual property rights under U.S. and international copyright and
|
||||
other intellectual property rights laws.
|
||||
|
||||
InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
and any use, reproduction, disclosure or distribution of the Software without an express license
|
||||
agreement from InvenSense is strictly prohibited.
|
||||
*/
|
||||
|
||||
#include "InvBasicMath.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
unsigned int InvBasicMath_log2u(unsigned int val)
|
||||
{
|
||||
unsigned int ret = UINT_MAX;
|
||||
|
||||
while (val != 0) {
|
||||
val >>= 1;
|
||||
ret++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int InvBasicMath_isAnOrthonormalMatrix(const float matrix[9])
|
||||
{
|
||||
// Check if matrix is orthogonal
|
||||
// Matrix is orthogonal if transpose(Matrix) x Matrix = Identity
|
||||
|
||||
float transpose[9];
|
||||
float mult[9];
|
||||
int i, j;
|
||||
|
||||
// Compute Transpose(matrix)
|
||||
for (i = 0; i < 3; i++) {
|
||||
for(j = 0; j < 3; j++) {
|
||||
transpose[i*3+j] = matrix[i+j*3];
|
||||
}
|
||||
}
|
||||
|
||||
// Multiply transpose x matrix
|
||||
mult[0] = transpose[0]*matrix[0] + transpose[1]*matrix[3] + transpose[2]*matrix[6];
|
||||
mult[1] = transpose[0]*matrix[1] + transpose[1]*matrix[4] + transpose[2]*matrix[7];
|
||||
mult[2] = transpose[0]*matrix[2] + transpose[1]*matrix[5] + transpose[2]*matrix[8];
|
||||
|
||||
mult[3] = transpose[3]*matrix[0] + transpose[4]*matrix[3] + transpose[5]*matrix[6];
|
||||
mult[4] = transpose[3]*matrix[1] + transpose[4]*matrix[4] + transpose[5]*matrix[7];
|
||||
mult[5] = transpose[3]*matrix[2] + transpose[4]*matrix[5] + transpose[5]*matrix[8];
|
||||
|
||||
mult[6] = transpose[6]*matrix[0] + transpose[7]*matrix[3] + transpose[8]*matrix[6];
|
||||
mult[7] = transpose[6]*matrix[1] + transpose[7]*matrix[4] + transpose[8]*matrix[7];
|
||||
mult[8] = transpose[6]*matrix[2] + transpose[7]*matrix[5] + transpose[8]*matrix[8];
|
||||
|
||||
// Check that mult is identity
|
||||
for (i = 0; i < 3; i++) {
|
||||
for(j = 0; j < 3; j++) {
|
||||
if (i == j) {
|
||||
if (mult[i+j*3] != 1)
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
if (mult[i+j*3] != 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
float InvBasicMath_computeMatrixDeterminant(const float matrix[9])
|
||||
{
|
||||
return matrix[0] * (matrix[4]*matrix[8] - matrix[7]*matrix[5])
|
||||
-matrix[1] * (matrix[3]*matrix[8] - matrix[6]*matrix[5])
|
||||
+matrix[2] * (matrix[3]*matrix[7] - matrix[4]*matrix[6]);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
Copyright (c) 2014-2015 InvenSense Inc. Portions Copyright (c) 2014-2015 Movea. All rights reserved.
|
||||
|
||||
This software, related documentation and any modifications thereto (collectively "Software") is subject
|
||||
to InvenSense and its licensors' intellectual property rights under U.S. and international copyright and
|
||||
other intellectual property rights laws.
|
||||
|
||||
InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
and any use, reproduction, disclosure or distribution of the Software without an express license
|
||||
agreement from InvenSense is strictly prohibited.
|
||||
*/
|
||||
|
||||
/** @defgroup InvBasicMath InvBasicMath
|
||||
@brief This file contains basic (overloadable) math functions and macros
|
||||
@ingroup EmbUtils
|
||||
@{
|
||||
*/
|
||||
|
||||
#ifndef _INV_BASIC_MATH_H_
|
||||
#define _INV_BASIC_MATH_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief Return absolute value of argument
|
||||
*/
|
||||
#ifndef INV_ABS
|
||||
# define INV_ABS(a) ((a) < 0 ? -(a) : (a))
|
||||
#endif
|
||||
|
||||
/** @brief Return minimum of two arguments
|
||||
*/
|
||||
#ifndef INV_MIN
|
||||
# define INV_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/** @brief Return maximum of two arguments
|
||||
*/
|
||||
#ifndef INV_MAX
|
||||
# define INV_MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/** @brief Define value for pi
|
||||
*/
|
||||
#ifndef INV_PI
|
||||
# define INV_PI 3.14159265358979
|
||||
#endif
|
||||
#ifndef M_PI
|
||||
# define M_PI INV_PI
|
||||
#endif
|
||||
|
||||
/** @brief Return saturated integer
|
||||
*/
|
||||
#ifndef INV_SATURATE
|
||||
static inline long InvBasicMath_saturatel(long in, long min, long max)
|
||||
{
|
||||
if (in > max)
|
||||
return max;
|
||||
else if (in < min)
|
||||
return min;
|
||||
else
|
||||
return in;
|
||||
}
|
||||
# define INV_SATURATE(a, min, max) InvBasicMath_saturatel(a, min, max)
|
||||
#endif
|
||||
|
||||
/** @brief Compute log2 from integer
|
||||
*/
|
||||
#ifndef INV_LOG2
|
||||
unsigned int InvBasicMath_log2u(unsigned int val);
|
||||
# define INV_LOG2(a) InvBasicMath_log2u(a)
|
||||
#endif
|
||||
|
||||
/** @brief Check if matrix is orthonormal
|
||||
* @param [in] matrix 3x3 Matrix to be checked
|
||||
* @return 1 if it is an orthonormal matrix, 0 otherwise
|
||||
*/
|
||||
int InvBasicMath_isAnOrthonormalMatrix(const float matrix[9]);
|
||||
|
||||
/** @brief Compute the determinant of the matrix
|
||||
* @param [in] matrix 3x3 Matrix to be checked
|
||||
* @return the determinant value
|
||||
*/
|
||||
float InvBasicMath_computeMatrixDeterminant(const float matrix[9]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INV_BASIC_MATH_H_ */
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
Copyright (c) 2014-2015 InvenSense Inc. Portions Copyright (c) 2014-2015 Movea. All rights reserved.
|
||||
|
||||
This software, related documentation and any modifications thereto (collectively "Software") is subject
|
||||
to InvenSense and its licensors' intellectual property rights under U.S. and international copyright and
|
||||
other intellectual property rights laws.
|
||||
|
||||
InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
and any use, reproduction, disclosure or distribution of the Software without an express license
|
||||
agreement from InvenSense is strictly prohibited.
|
||||
*/
|
||||
|
||||
/** \defgroup RingBuffer RingBuffer
|
||||
\brief Macros to manage static circular buffer of any data type
|
||||
\ingroup EmbUtils
|
||||
\{
|
||||
*/
|
||||
|
||||
#ifndef _RING_BUFFER_H_
|
||||
#define _RING_BUFFER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/** \brief Macro to declare a ring buffer
|
||||
\param[in] type type of item contained in the ring buffer
|
||||
\param[in] size number of items that can contain the ring buffer
|
||||
To improve speed, size should be a power of 2
|
||||
*/
|
||||
#define RINGBUFFER_DECLARE(type, size) \
|
||||
struct { \
|
||||
uint16_t read, write; \
|
||||
type buffer[size]; \
|
||||
}
|
||||
|
||||
/** \brief Macro to declare a volatile ring buffer, i.e. modified within an interrupt context
|
||||
\param[in] type type of item contained in the ring buffer
|
||||
\param[in] size number of items that can contain the ring buffer
|
||||
To improve speed, size should be a power of 2
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_DECLARE(type, size) \
|
||||
struct { \
|
||||
volatile uint16_t read, write; \
|
||||
volatile type buffer[size]; \
|
||||
}
|
||||
|
||||
/** \brief Macro to declare a ring buffer
|
||||
\param[in] name name of the circular buffer
|
||||
\param[in] size number of items that can contain the ring buffer
|
||||
To improve speed, size should be a power of 2
|
||||
\param[in] type type of item contained in the ring buffer
|
||||
*/
|
||||
#define RINGBUFFER(name, size, type) RINGBUFFER_DECLARE(type, size) name
|
||||
|
||||
/** \brief Macro to declare a volatile ring buffer, i.e. modified within an interrupt context
|
||||
\param[in] name name of the circular buffer
|
||||
\param[in] size number of items that can contain the ring buffer
|
||||
To improve speed, size should be a power of 2
|
||||
\param[in] type type of item contained in the ring buffer
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE(name, size, type) RINGBUFFER_VOLATILE_DECLARE(type, size) name
|
||||
|
||||
/** \brief Macro to get maximum size of a ring buffer
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\return maximum number of items that can contain the ringbuffer
|
||||
*/
|
||||
#define RINGBUFFER_MAXSIZE(rb) (sizeof((rb)->buffer)/sizeof((rb)->buffer[0]))
|
||||
|
||||
/** \brief Macro to get maximum size of a volatile ring buffer, i.e. modified within an interrupt context
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\return maximum number of items that can contain the ringbuffer
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_MAXSIZE(rb) RINGBUFFER_MAXSIZE(rb)
|
||||
|
||||
/** \brief Macro to get current size of a ring buffer
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\return current number of items hold in the ringbuffer
|
||||
*/
|
||||
#define RINGBUFFER_SIZE(rb) ((uint16_t)((rb)->write - (rb)->read))
|
||||
|
||||
static inline uint16_t get_ringbuffer_volatile_size(void * rb)
|
||||
{
|
||||
struct { uint16_t read, write; } rb_var;
|
||||
memcpy(&rb_var, rb, sizeof(rb_var));
|
||||
return (rb_var.write - rb_var.read);
|
||||
}
|
||||
|
||||
/** \brief Macro to get current size of a volatile ring buffer, i.e. modified within an interrupt context
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\return current number of items hold in the ringbuffer
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_SIZE(rb) get_ringbuffer_volatile_size(rb)
|
||||
|
||||
/** \brief Macro to check if a ring buffer is full
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\return 1 if there is no slot left in the ring buffer, 0 otherwise
|
||||
*/
|
||||
#define RINGBUFFER_FULL(rb) (RINGBUFFER_SIZE(rb) == RINGBUFFER_MAXSIZE(rb))
|
||||
|
||||
/** \brief Macro to check if a volatile ring buffer, i.e. modified within an interrupt context, is full
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\return 1 if there is no slot left in the ring buffer, 0 otherwise
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_FULL(rb) (RINGBUFFER_VOLATILE_SIZE(rb) == RINGBUFFER_VOLATILE_MAXSIZE(rb))
|
||||
|
||||
/** \brief Macro to check if a ring buffer is empty
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\return 1 if there is no item in the ring buffer, 0 otherwise
|
||||
*/
|
||||
#define RINGBUFFER_EMPTY(rb) (RINGBUFFER_SIZE(rb) == 0)
|
||||
|
||||
/** \brief Macro to check if a volatile ring buffer, i.e. modified within an interrupt context, is empty
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\return 1 if there is no item in the ring buffer, 0 otherwise
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_EMPTY(rb) (RINGBUFFER_VOLATILE_SIZE(rb) == 0)
|
||||
|
||||
/** \brief Macro to get number of available slot in a ring buffer
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\return number of empty slot in the ring buffer
|
||||
*/
|
||||
#define RINGBUFFER_AVAILABLE(rb) (RINGBUFFER_MAXSIZE(rb) - RINGBUFFER_SIZE(rb))
|
||||
|
||||
/** \brief Macro to get number of available slot in a volatile ring buffer, i.e. modified within an interrupt context
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\return number of empty slot in the ring buffer
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_AVAILABLE(rb) (RINGBUFFER_VOLATILE_MAXSIZE(rb) - RINGBUFFER_VOLATILE_SIZE(rb))
|
||||
|
||||
/** \brief Macro to clear a ring buffer
|
||||
\param[in] rb pointer to the ring buffer
|
||||
*/
|
||||
#define RINGBUFFER_CLEAR(rb) \
|
||||
do { \
|
||||
(rb)->read = 0, (rb)->write = 0; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Macro to clear a volatile ring buffer, i.e. modified within an interrupt context
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_CLEAR(rb) RINGBUFFER_CLEAR(rb)
|
||||
|
||||
/** \brief Push item by reference
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[out] refData to available item slot
|
||||
\warning There is no error checking done.
|
||||
*/
|
||||
#define RINGBUFFER_PUSHREF(rb, refData) \
|
||||
do { \
|
||||
refData = &(rb)->buffer[(rb)->write % RINGBUFFER_MAXSIZE(rb)]; \
|
||||
++(rb)->write; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Push item by reference
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[out] refData to available item slot
|
||||
\warning There is no error checking done.
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_PUSHREF(rb, refData) \
|
||||
do { \
|
||||
uint16_t wr_ptr = (rb)->write; \
|
||||
refData = &(rb)->buffer[wr_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
|
||||
++(rb)->write; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Return reference to next available slot
|
||||
No push is performed
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[out] refData to available item slot
|
||||
\warning There is no error checking done.
|
||||
*/
|
||||
#define RINGBUFFER_GETREFNEXT(rb, refData) \
|
||||
do { \
|
||||
refData = &(rb)->buffer[(rb)->write % RINGBUFFER_MAXSIZE(rb)]; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Return reference to next available slot
|
||||
No push is performed
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[out] refData to available item slot
|
||||
\warning There is no error checking done.
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_GETREFNEXT(rb, refData) \
|
||||
do { \
|
||||
uint16_t wr_ptr = (rb)->write; \
|
||||
refData = &(rb)->buffer[wr_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Increment write counter
|
||||
Actually performed a push (assuming data were already copied)
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\warning There is no error checking done.
|
||||
*/
|
||||
#define RINGBUFFER_INCREMENT(rb, refData) \
|
||||
do { \
|
||||
++(rb)->write; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Increment write counter
|
||||
Actually performed a push (assuming data were already copied)
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\warning There is no error checking done.
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_INCREMENT(rb, refData) \
|
||||
do { \
|
||||
++(rb)->write; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Return reference to youngest item
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[out] refData reference to youngest item
|
||||
\warning There is no error checking done.
|
||||
*/
|
||||
#define RINGBUFFER_BACK(rb, refData) \
|
||||
do { \
|
||||
refData = &(rb)->buffer[((rb)->write-1) % RINGBUFFER_MAXSIZE(rb)]; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Return reference to youngest item
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[out] refData reference to youngest item
|
||||
\warning There is no error checking done.
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_BACK(rb, refData) \
|
||||
do { \
|
||||
uint16_t wr_ptr = (rb)->write; \
|
||||
refData = &(rb)->buffer[(wr_ptr-1) % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Macro to push an item to a ring buffer
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[in] ptrData pointer to the item to push.
|
||||
\warning There is no error checking done.
|
||||
You must check for fullness before pushing data
|
||||
*/
|
||||
#define RINGBUFFER_PUSH(rb, ptrData) \
|
||||
do { \
|
||||
(rb)->buffer[(rb)->write % RINGBUFFER_MAXSIZE(rb)] = *ptrData; \
|
||||
++(rb)->write; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Macro to push an item to a volatile ring buffer
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[in] ptrData pointer to the item to push.
|
||||
\warning There is no error checking done.
|
||||
You must check for fullness before pushing data
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_PUSH(rb, ptrData) \
|
||||
do { \
|
||||
uint16_t wr_ptr = (rb)->write; \
|
||||
(rb)->buffer[wr_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)] = *ptrData; \
|
||||
++(rb)->write; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Macro to unpush an item to a ring buffer
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[in] ptrData pointer to placeholder to hold unpushed item
|
||||
\warning There is no error checking done.
|
||||
You must check for emptiness before pushing data
|
||||
*/
|
||||
#define RINGBUFFER_UNPUSH(rb, ptrData) \
|
||||
do { \
|
||||
--(rb)->write; \
|
||||
*ptrData = (rb)->buffer[(rb)->write % RINGBUFFER_MAXSIZE(rb)]; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Macro to unpush an item to a volatile ring buffer
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[in] ptrData pointer to placeholder to hold unpushed item
|
||||
\warning There is no error checking done.
|
||||
You must check for emptiness before pushing data
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_UNPUSH(rb, ptrData) \
|
||||
do { \
|
||||
--(rb)->write; \
|
||||
uint16_t wr_ptr = (rb)->write; \
|
||||
*ptrData = (rb)->buffer[wr_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Return reference to oldest item
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[out] refData reference to oldest item
|
||||
\warning There is no error checking done.
|
||||
*/
|
||||
#define RINGBUFFER_FRONT(rb, refData) \
|
||||
do { \
|
||||
refData = &(rb)->buffer[(rb)->read % RINGBUFFER_MAXSIZE(rb)]; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Return reference to oldest item
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[out] refData reference to oldest item
|
||||
\warning There is no error checking done.
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_FRONT(rb, refData) \
|
||||
do { \
|
||||
uint16_t rd_ptr = (rb)->read; \
|
||||
refData = &(rb)->buffer[rd_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Macro to pop an item from a ring buffer
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[out] ptrData pointer to placeholder to hold popped item
|
||||
\warning There is no error checking done.
|
||||
You must check for emptiness before popping data
|
||||
*/
|
||||
#define RINGBUFFER_POP(rb, ptrData) \
|
||||
do { \
|
||||
*ptrData = (rb)->buffer[(rb)->read % RINGBUFFER_MAXSIZE(rb)]; \
|
||||
++(rb)->read; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Macro to pop an item from a volatile ring buffer
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[out] ptrData pointer to placeholder to hold popped item
|
||||
\warning There is no error checking done.
|
||||
You must check for emptiness before popping data
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_POP(rb, ptrData) \
|
||||
do { \
|
||||
uint16_t rd_ptr = (rb)->read; \
|
||||
*ptrData = (rb)->buffer[rd_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
|
||||
++(rb)->read; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Macro to pop an item from a ring buffer (data is not copied)
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\warning There is no error checking done.
|
||||
You must check for emptiness before popping data
|
||||
*/
|
||||
#define RINGBUFFER_POPNLOSE(rb) \
|
||||
do { \
|
||||
++(rb)->read; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Macro to pop an item from a volatile ring buffer (data is not copied)
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\warning There is no error checking done.
|
||||
You must check for emptiness before popping data
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_POPNLOSE(rb) \
|
||||
do { \
|
||||
++(rb)->read; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Macro to unpop an item to a ring buffer
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[out] ptrData pointer to to the item to unpop.
|
||||
\warning There is no error checking done.
|
||||
You must check for fullness before unpopping data
|
||||
*/
|
||||
#define RINGBUFFER_UNPOP(rb, ptrData) \
|
||||
do { \
|
||||
--(rb)->read; \
|
||||
(rb)->buffer[(rb)->read % RINGBUFFER_MAXSIZE(rb)] = *ptrData; \
|
||||
} while(0)
|
||||
|
||||
/** \brief Macro to unpop an item to a volatile ring buffer
|
||||
\param[in] rb pointer to the ring buffer
|
||||
\param[out] ptrData pointer to to the item to unpop.
|
||||
\warning There is no error checking done.
|
||||
You must check for fullness before unpopping data
|
||||
\warning it is advised to put this in a critical section
|
||||
*/
|
||||
#define RINGBUFFER_VOLATILE_UNPOP(rb, ptrData) \
|
||||
do { \
|
||||
--(rb)->read; \
|
||||
uint16_t rd_ptr = (rb)->read; \
|
||||
(rb)->buffer[rd_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)] = *ptrData; \
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively “Software”) is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
/** @brief Custom definition for boolean type to avoid compiler discrepancies
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _INV_BOOL_H_
|
||||
#define _INV_BOOL_H_
|
||||
|
||||
typedef int inv_bool_t;
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#ifndef true
|
||||
#define true 1
|
||||
#endif
|
||||
|
||||
#ifndef false
|
||||
#define false 0
|
||||
#endif
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* _INV_BOOL_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively “Software”) is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
/** @defgroup InvError Error code
|
||||
* @brief Common error code
|
||||
*
|
||||
* @ingroup EmbUtils
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _INV_ERROR_H_
|
||||
#define _INV_ERROR_H_
|
||||
|
||||
/** @brief Common error code definition
|
||||
*/
|
||||
enum inv_error
|
||||
{
|
||||
INV_ERROR_SUCCESS = 0, /**< no error */
|
||||
INV_ERROR = -1, /**< unspecified error */
|
||||
INV_ERROR_NIMPL = -2, /**< function not implemented for given
|
||||
arguments */
|
||||
INV_ERROR_TRANSPORT = -3, /**< error occurred at transport level */
|
||||
INV_ERROR_TIMEOUT = -4, /**< action did not complete in the expected
|
||||
time window */
|
||||
INV_ERROR_SIZE = -5, /**< size/length of given arguments is not
|
||||
suitable to complete requested action */
|
||||
INV_ERROR_OS = -6, /**< error related to OS */
|
||||
INV_ERROR_IO = -7, /**< error related to IO operation */
|
||||
INV_ERROR_MEM = -9, /**< not enough memory to complete requested
|
||||
action */
|
||||
INV_ERROR_HW = -10, /**< error at HW level */
|
||||
INV_ERROR_BAD_ARG = -11, /**< provided arguments are not good to
|
||||
perform requested action */
|
||||
INV_ERROR_UNEXPECTED = -12, /**< something unexpected happened */
|
||||
INV_ERROR_FILE = -13, /**< cannot access file or unexpected format */
|
||||
INV_ERROR_PATH = -14, /**< invalid file path */
|
||||
INV_ERROR_IMAGE_TYPE = -15, /**< error when image type is not managed */
|
||||
INV_ERROR_WATCHDOG = -16, /**< error when device doesn't respond
|
||||
to ping */
|
||||
};
|
||||
|
||||
#endif /* _INV_ERROR_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively “Software”) is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
#ifndef _INV_IDD_EXPORT_H_
|
||||
#define _INV_IDD_EXPORT_H_
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if !defined(INV_EXPORT) && defined(INV_DO_DLL_EXPORT)
|
||||
#define INV_EXPORT __declspec(dllexport)
|
||||
#elif !defined(INV_EXPORT) && defined(INV_DO_DLL_IMPORT)
|
||||
#define INV_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(INV_EXPORT)
|
||||
#define INV_EXPORT
|
||||
#endif
|
||||
|
||||
#endif /* _INV_IDD_EXPORT_H_ */
|
||||
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2017 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively "Software") is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
#include "inv_imu_defs.h"
|
||||
#include "inv_imu_extfunc.h"
|
||||
#include "inv_imu_driver.h"
|
||||
#include "inv_imu_apex.h"
|
||||
|
||||
int inv_imu_apex_enable_ff(struct inv_imu_device *s)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t value;
|
||||
|
||||
status |= inv_imu_start_dmp(s);
|
||||
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
|
||||
value &= ~APEX_CONFIG1_FF_ENABLE_MASK;
|
||||
value |= (uint8_t)APEX_CONFIG1_FF_ENABLE_EN;
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_disable_ff(struct inv_imu_device *s)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t value;
|
||||
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
|
||||
value &= ~APEX_CONFIG1_FF_ENABLE_MASK;
|
||||
value |= (uint8_t)APEX_CONFIG1_FF_ENABLE_DIS;
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_enable_smd(struct inv_imu_device *s)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t value;
|
||||
|
||||
status |= inv_imu_start_dmp(s);
|
||||
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
|
||||
value &= ~APEX_CONFIG1_SMD_ENABLE_MASK;
|
||||
value |= (uint8_t)APEX_CONFIG1_SMD_ENABLE_EN;
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_disable_smd(struct inv_imu_device *s)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t value;
|
||||
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
|
||||
value &= ~APEX_CONFIG1_SMD_ENABLE_MASK;
|
||||
value |= (uint8_t)APEX_CONFIG1_SMD_ENABLE_DIS;
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_init_parameters_struct(struct inv_imu_device *s, inv_imu_apex_parameters_t *apex_inputs)
|
||||
{
|
||||
int status = 0;
|
||||
(void)s;
|
||||
|
||||
/* Default parameters at POR */
|
||||
apex_inputs->pedo_amp_th = APEX_CONFIG3_PEDO_AMP_TH_62_MG;
|
||||
apex_inputs->pedo_step_cnt_th = 0x5;
|
||||
apex_inputs->pedo_step_det_th = 0x2;
|
||||
apex_inputs->pedo_sb_timer_th = APEX_CONFIG4_PEDO_SB_TIMER_TH_150_SAMPLES;
|
||||
apex_inputs->pedo_hi_enrgy_th = APEX_CONFIG4_PEDO_HI_ENRGY_TH_104_MG;
|
||||
apex_inputs->tilt_wait_time = APEX_CONFIG5_TILT_WAIT_TIME_4_S;
|
||||
apex_inputs->power_save_time = APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_8_S;
|
||||
apex_inputs->power_save = APEX_CONFIG0_DMP_POWER_SAVE_EN;
|
||||
apex_inputs->sensitivity_mode = APEX_CONFIG9_SENSITIVITY_MODE_NORMAL;
|
||||
apex_inputs->low_energy_amp_th = APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_80_MG;
|
||||
apex_inputs->smd_sensitivity = APEX_CONFIG9_SMD_SENSITIVITY_0;
|
||||
apex_inputs->ff_debounce_duration = APEX_CONFIG9_FF_DEBOUNCE_DURATION_2000_MS;
|
||||
apex_inputs->ff_max_duration_cm = APEX_CONFIG12_FF_MAX_DURATION_204_CM;
|
||||
apex_inputs->ff_min_duration_cm = APEX_CONFIG12_FF_MIN_DURATION_10_CM;
|
||||
apex_inputs->lowg_peak_th = APEX_CONFIG10_LOWG_PEAK_TH_563_MG;
|
||||
apex_inputs->lowg_peak_hyst = APEX_CONFIG5_LOWG_PEAK_TH_HYST_156_MG;
|
||||
apex_inputs->lowg_samples_th = APEX_CONFIG10_LOWG_TIME_TH_1_SAMPLE;
|
||||
apex_inputs->highg_peak_th = APEX_CONFIG11_HIGHG_PEAK_TH_2500_MG;
|
||||
apex_inputs->highg_peak_hyst = APEX_CONFIG5_HIGHG_PEAK_TH_HYST_156_MG;
|
||||
apex_inputs->highg_samples_th = APEX_CONFIG11_HIGHG_TIME_TH_1_SAMPLE;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_configure_parameters(struct inv_imu_device *s, const inv_imu_apex_parameters_t *apex_inputs)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t data;
|
||||
uint8_t apexConfig[7];
|
||||
APEX_CONFIG1_PED_ENABLE_t pedo_state;
|
||||
APEX_CONFIG1_TILT_ENABLE_t tilt_state;
|
||||
APEX_CONFIG1_FF_ENABLE_t ff_state;
|
||||
APEX_CONFIG1_SMD_ENABLE_t smd_state;
|
||||
|
||||
/* DMP cannot be configured if it is running, hence make sure all APEX algorithms are off */
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &data);
|
||||
pedo_state = (APEX_CONFIG1_PED_ENABLE_t)(data & APEX_CONFIG1_PED_ENABLE_MASK);
|
||||
tilt_state = (APEX_CONFIG1_TILT_ENABLE_t)(data & APEX_CONFIG1_TILT_ENABLE_MASK);
|
||||
ff_state = (APEX_CONFIG1_FF_ENABLE_t)(data & APEX_CONFIG1_FF_ENABLE_MASK);
|
||||
smd_state = (APEX_CONFIG1_SMD_ENABLE_t)(data & APEX_CONFIG1_SMD_ENABLE_MASK);
|
||||
if (pedo_state == APEX_CONFIG1_PED_ENABLE_EN)
|
||||
return INV_ERROR;
|
||||
if (tilt_state == APEX_CONFIG1_TILT_ENABLE_EN)
|
||||
return INV_ERROR;
|
||||
if (ff_state == APEX_CONFIG1_FF_ENABLE_EN)
|
||||
return INV_ERROR;
|
||||
if (smd_state == APEX_CONFIG1_SMD_ENABLE_EN)
|
||||
return INV_ERROR;
|
||||
|
||||
|
||||
status |= inv_imu_switch_on_mclk(s);
|
||||
|
||||
/* Power Save mode and low energy amplitude threshold (for Pedometer in Slow Walk mode) */
|
||||
/* APEX_CONFIG2_MREG1 */
|
||||
apexConfig[0] = (uint8_t)apex_inputs->power_save_time
|
||||
| (uint8_t)apex_inputs->low_energy_amp_th;
|
||||
|
||||
/* Pedometer parameters */
|
||||
/* APEX_CONFIG3_MREG1 */
|
||||
apexConfig[1] = (uint8_t)apex_inputs->pedo_amp_th
|
||||
| (apex_inputs->pedo_step_cnt_th & APEX_CONFIG3_PED_STEP_CNT_TH_SEL_MASK);
|
||||
|
||||
/* APEX_CONFIG4_MREG1 */
|
||||
apexConfig[2] = ((apex_inputs->pedo_step_det_th << APEX_CONFIG4_PED_STEP_DET_TH_SEL_POS)
|
||||
& APEX_CONFIG4_PED_STEP_DET_TH_SEL_MASK)
|
||||
| (uint8_t)apex_inputs->pedo_sb_timer_th
|
||||
| (uint8_t)apex_inputs->pedo_hi_enrgy_th;
|
||||
|
||||
/* Tilt, Lowg and highg parameters */
|
||||
/* APEX_CONFIG5_MREG1 */
|
||||
apexConfig[3] = (uint8_t)apex_inputs->tilt_wait_time
|
||||
| (uint8_t)apex_inputs->lowg_peak_hyst
|
||||
| (uint8_t)apex_inputs->highg_peak_hyst;
|
||||
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG2_MREG1, 4, &apexConfig[0]);
|
||||
|
||||
|
||||
/* APEX_CONFIG0 */
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG0, 1, &apexConfig[0]);
|
||||
apexConfig[0] &= ~APEX_CONFIG0_DMP_POWER_SAVE_EN_MASK;
|
||||
apexConfig[0] |= apex_inputs->power_save;
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG0, 1, &apexConfig[0]);
|
||||
|
||||
/* free fall parameter, SMD parameter and parameters for Pedometer in Slow Walk mode */
|
||||
/* APEX_CONFIG9_MREG1 */
|
||||
apexConfig[0] = (uint8_t)apex_inputs->ff_debounce_duration
|
||||
| (uint8_t)apex_inputs->smd_sensitivity
|
||||
| (uint8_t)apex_inputs->sensitivity_mode;
|
||||
|
||||
/* Lowg and highg parameters and free fall parameters */
|
||||
/* APEX_CONFIG10_MREG1 */
|
||||
apexConfig[1] = (uint8_t)apex_inputs->lowg_peak_th
|
||||
| (uint8_t)apex_inputs->lowg_samples_th;
|
||||
|
||||
/* APEX_CONFIG11_MREG1 */
|
||||
apexConfig[2] = (uint8_t)apex_inputs->highg_peak_th
|
||||
| (uint8_t)apex_inputs->highg_samples_th;
|
||||
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG9_MREG1, 3, &apexConfig[0]);
|
||||
|
||||
|
||||
/* APEX_CONFIG12_MREG1 */
|
||||
apexConfig[0] = (uint8_t)apex_inputs->ff_max_duration_cm
|
||||
| (uint8_t)apex_inputs->ff_min_duration_cm;
|
||||
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG12_MREG1, 1, &apexConfig[0]);
|
||||
|
||||
status |= inv_imu_switch_off_mclk(s);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_get_parameters(struct inv_imu_device *s, inv_imu_apex_parameters_t *apex_params)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t data[7];
|
||||
uint8_t value;
|
||||
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG0, 1, &value);
|
||||
apex_params->power_save = (APEX_CONFIG0_DMP_POWER_SAVE_t)(value & APEX_CONFIG0_DMP_POWER_SAVE_EN_MASK);
|
||||
|
||||
/* Access continuous config registers (CONFIG2-CONFIG11) */
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG2_MREG1, sizeof(data), &data[0]);
|
||||
|
||||
/* Get params from apex_config2 : dmp_power_save_time and low_energy_amp_th */
|
||||
apex_params->power_save_time = (APEX_CONFIG2_DMP_POWER_SAVE_TIME_t)
|
||||
(data[0] & APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_MASK);
|
||||
apex_params->low_energy_amp_th = (APEX_CONFIG2_LOW_ENERGY_AMP_TH_t)
|
||||
(data[0] & APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_MASK);
|
||||
|
||||
/* Get params from apex_config3 : pedo_amp_th and pedo_step_cnt_th */
|
||||
apex_params->pedo_amp_th = (APEX_CONFIG3_PEDO_AMP_TH_t)
|
||||
(data[1] & APEX_CONFIG3_PED_AMP_TH_SEL_MASK);
|
||||
apex_params->pedo_step_cnt_th = (data[1] & APEX_CONFIG3_PED_STEP_CNT_TH_SEL_MASK)
|
||||
>> APEX_CONFIG3_PED_STEP_CNT_TH_SEL_POS;
|
||||
|
||||
/* Get params from apex_config4 : pedo_step_det_th, pedo_sb_timer_th and pedo_hi_enrgy_th */
|
||||
apex_params->pedo_step_det_th = (data[2] & APEX_CONFIG4_PED_STEP_DET_TH_SEL_MASK)
|
||||
>> APEX_CONFIG4_PED_STEP_DET_TH_SEL_POS;
|
||||
apex_params->pedo_sb_timer_th = (APEX_CONFIG4_PEDO_SB_TIMER_TH_t)
|
||||
(data[2] & APEX_CONFIG4_PED_SB_TIMER_TH_SEL_MASK);
|
||||
apex_params->pedo_hi_enrgy_th = (APEX_CONFIG4_PEDO_HI_ENRGY_TH_t)
|
||||
(data[2] & APEX_CONFIG4_PED_HI_EN_TH_SEL_MASK);
|
||||
|
||||
/* Get params from apex_config5 : tilt_wait_time, lowg_peak_hyst and highg_peak_hyst */
|
||||
apex_params->tilt_wait_time = (APEX_CONFIG5_TILT_WAIT_TIME_t)
|
||||
(data[3] & APEX_CONFIG5_TILT_WAIT_TIME_SEL_MASK);
|
||||
apex_params->lowg_peak_hyst = (APEX_CONFIG5_LOWG_PEAK_TH_HYST_t)
|
||||
(data[3] & APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_MASK);
|
||||
apex_params->highg_peak_hyst = (APEX_CONFIG5_HIGHG_PEAK_TH_HYST_t)
|
||||
(data[3] & APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_MASK);
|
||||
|
||||
/* Get params from apex_config9 : ff_debounce_duration, smd_sensitivity and sensitivity_mode */
|
||||
apex_params->ff_debounce_duration = (APEX_CONFIG9_FF_DEBOUNCE_DURATION_t)
|
||||
(data[4] & APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_MASK);
|
||||
apex_params->smd_sensitivity = (APEX_CONFIG9_SMD_SENSITIVITY_t)
|
||||
(data[4] & APEX_CONFIG9_SMD_SENSITIVITY_SEL_MASK);
|
||||
apex_params->sensitivity_mode = (APEX_CONFIG9_SENSITIVITY_MODE_t)
|
||||
(data[4] & APEX_CONFIG9_SENSITIVITY_MODE_MASK);
|
||||
|
||||
/* Get params from apex_config10 : lowg_peak_th and lowg_samples_th */
|
||||
apex_params->lowg_peak_th = (APEX_CONFIG10_LOWG_PEAK_TH_t)
|
||||
(data[5] & APEX_CONFIG10_LOWG_PEAK_TH_SEL_MASK);
|
||||
apex_params->lowg_samples_th = (APEX_CONFIG10_LOWG_TIME_TH_SAMPLES_t)
|
||||
(data[5] & APEX_CONFIG10_LOWG_TIME_TH_SEL_MASK);
|
||||
|
||||
/* Get params from apex_config11 : highg_peak_th and highg_samples_th */
|
||||
apex_params->highg_peak_th = (APEX_CONFIG11_HIGHG_PEAK_TH_t)
|
||||
(data[6] & APEX_CONFIG11_HIGHG_PEAK_TH_SEL_MASK);
|
||||
apex_params->highg_samples_th = (APEX_CONFIG11_HIGHG_TIME_TH_SAMPLES_t)
|
||||
(data[6] & APEX_CONFIG11_HIGHG_TIME_TH_SEL_MASK);
|
||||
|
||||
/* Access apex reg 12 */
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG12_MREG1, 1, &data[0]);
|
||||
|
||||
/* Get params from apex_config12 : ff_max_duration_cm and ff_min_duration_cm */
|
||||
apex_params->ff_max_duration_cm = (APEX_CONFIG12_FF_MAX_DURATION_t)
|
||||
(data[0] & APEX_CONFIG12_FF_MAX_DURATION_SEL_MASK);
|
||||
apex_params->ff_min_duration_cm = (APEX_CONFIG12_FF_MIN_DURATION_t)
|
||||
(data[0] & APEX_CONFIG12_FF_MIN_DURATION_SEL_MASK);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_set_frequency(struct inv_imu_device *s, const APEX_CONFIG1_DMP_ODR_t frequency)
|
||||
{
|
||||
uint8_t value;
|
||||
int status = 0;
|
||||
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
|
||||
|
||||
value &= ~APEX_CONFIG1_DMP_ODR_MASK;
|
||||
value |= frequency;
|
||||
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_enable_pedometer(struct inv_imu_device *s)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t value;
|
||||
|
||||
status |= inv_imu_start_dmp(s);
|
||||
|
||||
/* Enable Pedometer */
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
|
||||
value &= ~APEX_CONFIG1_PED_ENABLE_MASK;
|
||||
value |= (uint8_t)APEX_CONFIG1_PED_ENABLE_EN;
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_disable_pedometer(struct inv_imu_device *s)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t value;
|
||||
|
||||
/* Disable Pedometer */
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
|
||||
value &= ~APEX_CONFIG1_PED_ENABLE_MASK;
|
||||
value |= (uint8_t)APEX_CONFIG1_PED_ENABLE_DIS;
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_enable_tilt(struct inv_imu_device *s)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t value;
|
||||
|
||||
status |= inv_imu_start_dmp(s);
|
||||
|
||||
/* Enable Tilt */
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
|
||||
value &= ~APEX_CONFIG1_TILT_ENABLE_MASK;
|
||||
value |= (uint8_t)APEX_CONFIG1_TILT_ENABLE_EN;
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_disable_tilt(struct inv_imu_device *s)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t value;
|
||||
|
||||
/* Disable Tilt */
|
||||
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
|
||||
value &= ~APEX_CONFIG1_TILT_ENABLE_MASK;
|
||||
value |= (uint8_t)APEX_CONFIG1_TILT_ENABLE_DIS;
|
||||
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_get_data_activity(struct inv_imu_device *s, inv_imu_apex_step_activity_t *apex_activity)
|
||||
{
|
||||
uint8_t data[4];
|
||||
int status = inv_imu_read_reg(s, APEX_DATA0, 4, data);
|
||||
|
||||
apex_activity->step_cnt = data[1] << 8 | data[0];
|
||||
apex_activity->step_cadence = data[2];
|
||||
apex_activity->activity_class = data[3] & APEX_DATA3_ACTIVITY_CLASS_MASK;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_apex_get_data_free_fall(struct inv_imu_device *s, uint16_t *freefall_duration)
|
||||
{
|
||||
uint8_t data[2];
|
||||
int status = inv_imu_read_reg(s, APEX_DATA4, 2, &data[0]);
|
||||
|
||||
*freefall_duration = (data[1] << 8) | data[0];
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2017 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively "Software") is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
/** @defgroup DriverApex IMU driver high level functions related to APEX and the DMP
|
||||
* @brief High-level function to setup an IMU device
|
||||
* @ingroup Driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @file inv_imu_apex.h
|
||||
* High-level function to setup an IMU device
|
||||
*/
|
||||
|
||||
#ifndef _INV_IMU_APEX_H_
|
||||
#define _INV_IMU_APEX_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "inv_imu_defs.h"
|
||||
|
||||
#include "InvError.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Forward declarations */
|
||||
struct inv_imu_device;
|
||||
|
||||
/** @brief IMU APEX inputs parameters definition
|
||||
*/
|
||||
typedef struct {
|
||||
APEX_CONFIG3_PEDO_AMP_TH_t pedo_amp_th;
|
||||
uint8_t pedo_step_cnt_th;
|
||||
uint8_t pedo_step_det_th;
|
||||
APEX_CONFIG4_PEDO_SB_TIMER_TH_t pedo_sb_timer_th;
|
||||
APEX_CONFIG4_PEDO_HI_ENRGY_TH_t pedo_hi_enrgy_th;
|
||||
APEX_CONFIG5_TILT_WAIT_TIME_t tilt_wait_time;
|
||||
APEX_CONFIG2_DMP_POWER_SAVE_TIME_t power_save_time;
|
||||
APEX_CONFIG0_DMP_POWER_SAVE_t power_save;
|
||||
APEX_CONFIG9_SENSITIVITY_MODE_t sensitivity_mode;
|
||||
APEX_CONFIG2_LOW_ENERGY_AMP_TH_t low_energy_amp_th;
|
||||
APEX_CONFIG9_SMD_SENSITIVITY_t smd_sensitivity;
|
||||
APEX_CONFIG9_FF_DEBOUNCE_DURATION_t ff_debounce_duration;
|
||||
APEX_CONFIG12_FF_MAX_DURATION_t ff_max_duration_cm;
|
||||
APEX_CONFIG12_FF_MIN_DURATION_t ff_min_duration_cm;
|
||||
APEX_CONFIG10_LOWG_PEAK_TH_t lowg_peak_th;
|
||||
APEX_CONFIG5_LOWG_PEAK_TH_HYST_t lowg_peak_hyst;
|
||||
APEX_CONFIG10_LOWG_TIME_TH_SAMPLES_t lowg_samples_th;
|
||||
APEX_CONFIG11_HIGHG_PEAK_TH_t highg_peak_th;
|
||||
APEX_CONFIG5_HIGHG_PEAK_TH_HYST_t highg_peak_hyst;
|
||||
APEX_CONFIG11_HIGHG_TIME_TH_SAMPLES_t highg_samples_th;
|
||||
} inv_imu_apex_parameters_t;
|
||||
|
||||
/** @brief APEX pedometer outputs
|
||||
*/
|
||||
typedef struct inv_imu_apex_step_activity {
|
||||
uint16_t step_cnt; /**< Number of steps taken */
|
||||
uint8_t step_cadence; /**< Walk/run cadence in number of samples.
|
||||
Format is u6.2. E.g, At 50Hz and 2Hz walk frequency, if the cadency is 25 samples.
|
||||
The register will output 100. */
|
||||
uint8_t activity_class; /**< Detected activity unknown (0), walk (1) or run (2) */
|
||||
} inv_imu_apex_step_activity_t;
|
||||
|
||||
/** @brief Enable Free Fall.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_apex_enable_ff(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Disable Free Fall.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_apex_disable_ff(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Enable Significant Motion Detection.
|
||||
* note : SMD requests to have the accelerometer enabled to work.
|
||||
* To have good performance, it's recommended to set accelerometer ODR (Output Data Rate) to 20ms
|
||||
* and the accelerometer in Low Power Mode.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_apex_enable_smd(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Disable Significant Motion Detection.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_apex_disable_smd(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Fill the APEX parameters structure with all the default parameters for APEX algorithms (pedometer, tilt)
|
||||
* @param[out] apex_inputs Default input parameters. See @sa inv_imu_apex_parameters_t
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_apex_init_parameters_struct(struct inv_imu_device *s, inv_imu_apex_parameters_t *apex_inputs);
|
||||
|
||||
/** @brief Configures DMP parameters for APEX algorithms (pedometer, tilt, lowg, highg).
|
||||
* This programmable parameters will be decoded and propagate to the SRAM to be executed at DMP start.
|
||||
* @param[in] apex_inputs The requested input parameters. See @sa inv_imu_apex_parameters_t
|
||||
* @warning APEX inputs can't change on the fly, this API should be called before enabling any APEX features.
|
||||
* @warning APEX configuration can't be done too frequently, but only once every 10ms.
|
||||
* Otherwise it can create unknown behavior.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_apex_configure_parameters(struct inv_imu_device *s, const inv_imu_apex_parameters_t *apex_inputs);
|
||||
|
||||
/** @brief Returns current DMP parameters for APEX algorithms (pedometer, tilt).
|
||||
* @param[out] apex_params The current parameter, fetched from registers. See @sa inv_imu_apex_parameters_t
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_apex_get_parameters(struct inv_imu_device *s, inv_imu_apex_parameters_t *apex_params);
|
||||
|
||||
/** @brief Configure DMP Output Data Rate for APEX algorithms (pedometer, tilt)
|
||||
* @param[in] frequency The requested frequency.
|
||||
* @sa APEX_CONFIG1_DMP_ODR_t
|
||||
* @warning DMP_ODR can change on the fly, and the DMP code will accommodate necessary modifications
|
||||
* @warning The user needs to take care to set Accel frequency >= DMP frequency. This is a hard constraint
|
||||
since HW will not handle incorrect setting.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_apex_set_frequency(struct inv_imu_device *s, const APEX_CONFIG1_DMP_ODR_t frequency);
|
||||
|
||||
/** @brief Enable APEX algorithm Pedometer.
|
||||
* note : Pedometer request to have the accelerometer enabled to works
|
||||
* with accelerometer frequency less than dmp frequency.
|
||||
* @return 0 on success, negative value on error.
|
||||
* @warning Pedometer must be turned OFF to reconfigure it
|
||||
*/
|
||||
int inv_imu_apex_enable_pedometer(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Disable APEX algorithm Pedometer.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_apex_disable_pedometer(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Enable APEX algorithm Tilt.
|
||||
* note : Tilt request to have the accelerometer enabled to works
|
||||
* with accelerometer frequency less than dmp frequency.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_apex_enable_tilt(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Disable APEX algorithm Tilt.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_apex_disable_tilt(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Retrieve APEX pedometer outputs and format them
|
||||
* @param[out] apex_activity Apex step and activity data value.
|
||||
* @return 0 in case of success, negative value on error. See enum inv_error
|
||||
*/
|
||||
int inv_imu_apex_get_data_activity(struct inv_imu_device *s, inv_imu_apex_step_activity_t *apex_activity);
|
||||
|
||||
/** @brief Retrieve APEX free fall outputs and format them
|
||||
* @param[out] Free fall duration in number of sample.
|
||||
* @return 0 in case of success, negative value on error. See enum inv_error
|
||||
*/
|
||||
int inv_imu_apex_get_data_free_fall(struct inv_imu_device *s, uint16_t *freefall_duration);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INV_IMU_APEX_H_ */
|
||||
|
||||
/** @} */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,520 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2017 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively "Software") is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
/** @defgroup Driver IMU driver high level functions
|
||||
* @brief High-level function to setup an IMU device
|
||||
* @ingroup DriverIcm
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @file inv_imu_driver.h
|
||||
* High-level function to setup an IMU device
|
||||
*/
|
||||
|
||||
#ifndef _INV_IMU_DRIVER_H_
|
||||
#define _INV_IMU_DRIVER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "inv_imu_defs.h"
|
||||
#include "inv_imu_transport.h"
|
||||
|
||||
#include "InvError.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/** @brief IMU max FSR values for accel and gyro
|
||||
* Dependent on chip
|
||||
*/
|
||||
#define ACCEL_CONFIG0_FS_SEL_MAX ACCEL_CONFIG0_FS_SEL_16g
|
||||
#define GYRO_CONFIG0_FS_SEL_MAX GYRO_CONFIG0_FS_SEL_2000dps
|
||||
|
||||
#define ACCEL_OFFUSER_MAX_MG 1000
|
||||
#define GYRO_OFFUSER_MAX_DPS 64
|
||||
|
||||
/** @brief IMU maximum buffer size mirrored from FIFO at polling time
|
||||
* @warning fifo_idx type variable must be large enough to parse the FIFO_MIRRORING_SIZE
|
||||
*/
|
||||
#define FIFO_MIRRORING_SIZE 16 * 258 // packet size * max_count = 4kB
|
||||
|
||||
/** @brief IMU Accelerometer start-up time before having correct data
|
||||
*/
|
||||
#define ACC_STARTUP_TIME_US 10000
|
||||
|
||||
/** @brief IMU Gyroscope start-up time before having correct data
|
||||
*/
|
||||
#define GYR_STARTUP_TIME_US 70000
|
||||
|
||||
/** @brief IMU Gyroscope power off to power on duration
|
||||
*/
|
||||
#define GYR_POWER_OFF_DUR_US 20000
|
||||
|
||||
/** @brief Sensor identifier for UI control function
|
||||
*/
|
||||
enum inv_imu_sensor {
|
||||
INV_SENSOR_ACCEL, /**< Accelerometer */
|
||||
INV_SENSOR_GYRO, /**< Gyroscope */
|
||||
INV_SENSOR_FSYNC_EVENT, /**< FSYNC */
|
||||
INV_SENSOR_TEMPERATURE, /**< Chip temperature */
|
||||
INV_SENSOR_DMP_PEDOMETER_EVENT, /**< Pedometer: step detected */
|
||||
INV_SENSOR_DMP_PEDOMETER_COUNT, /**< Pedometer: step counter */
|
||||
INV_SENSOR_DMP_TILT, /**< Tilt */
|
||||
INV_SENSOR_DMP_FF, /**< FreeFall */
|
||||
INV_SENSOR_DMP_LOWG, /**< Low G */
|
||||
INV_SENSOR_DMP_SMD, /**< Significant Motion Detection */
|
||||
INV_SENSOR_MAX
|
||||
};
|
||||
|
||||
/** @brief Configure Fifo usage
|
||||
*/
|
||||
typedef enum {
|
||||
INV_IMU_FIFO_DISABLED = 0, /**< Fifo is disabled and data source is sensors registers */
|
||||
INV_IMU_FIFO_ENABLED = 1, /**< Fifo is used as data source */
|
||||
}INV_IMU_FIFO_CONFIG_t;
|
||||
|
||||
/** @brief Sensor event structure definition
|
||||
*/
|
||||
typedef struct {
|
||||
int sensor_mask;
|
||||
uint16_t timestamp_fsync;
|
||||
int16_t accel[3];
|
||||
int16_t gyro[3];
|
||||
int16_t temperature;
|
||||
int8_t accel_high_res[3];
|
||||
int8_t gyro_high_res[3];
|
||||
} inv_imu_sensor_event_t;
|
||||
|
||||
/** @brief IMU driver states definition
|
||||
*/
|
||||
struct inv_imu_device {
|
||||
struct inv_imu_transport transport; /**< Transport layer
|
||||
Must be the first one of struct inv_imu_device */
|
||||
void (*sensor_event_cb)(inv_imu_sensor_event_t *event); /**< callback executed by:
|
||||
inv_imu_get_data_from_fifo (if FIFO is used)
|
||||
inv_imu_get_data_from_registers (if FIFO isn't used)
|
||||
May be NULL if above API are not used by application */
|
||||
uint8_t fifo_data[FIFO_MIRRORING_SIZE]; /**< FIFO mirroring memory area */
|
||||
uint8_t dmp_is_on; /**< DMP started status */
|
||||
uint8_t endianness_data; /**< Data endianness configuration */
|
||||
uint8_t fifo_highres_enabled; /**< Highres mode configuration */
|
||||
INV_IMU_FIFO_CONFIG_t fifo_is_used; /**< FIFO configuration */
|
||||
uint64_t gyro_start_time_us; /**< Gyro start time used to discard first samples */
|
||||
uint64_t accel_start_time_us; /**< Accel start time used to discard first samples */
|
||||
uint64_t gyro_power_off_tmst; /**< Gyro power off time */
|
||||
};
|
||||
|
||||
|
||||
/* Interrupt enum state for INT1, INT2, and IBI */
|
||||
typedef enum {
|
||||
INV_IMU_DISABLE = 0,
|
||||
INV_IMU_ENABLE
|
||||
} inv_imu_interrupt_value;
|
||||
|
||||
/** @brief Interrupt definition
|
||||
*/
|
||||
typedef struct {
|
||||
inv_imu_interrupt_value INV_UI_FSYNC;
|
||||
inv_imu_interrupt_value INV_UI_DRDY;
|
||||
inv_imu_interrupt_value INV_FIFO_THS;
|
||||
inv_imu_interrupt_value INV_FIFO_FULL;
|
||||
inv_imu_interrupt_value INV_SMD;
|
||||
inv_imu_interrupt_value INV_WOM_X;
|
||||
inv_imu_interrupt_value INV_WOM_Y;
|
||||
inv_imu_interrupt_value INV_WOM_Z;
|
||||
inv_imu_interrupt_value INV_FF;
|
||||
inv_imu_interrupt_value INV_LOWG;
|
||||
inv_imu_interrupt_value INV_STEP_DET;
|
||||
inv_imu_interrupt_value INV_STEP_CNT_OVFL;
|
||||
inv_imu_interrupt_value INV_TILT_DET;
|
||||
} inv_imu_interrupt_parameter_t;
|
||||
|
||||
|
||||
/** @brief Configure the serial interface used to access the device and execute hardware initialization.
|
||||
*
|
||||
* This functions first configures serial interface passed in parameter to make sure device
|
||||
* is accessible both in read and write. Thus no serial access should be done before
|
||||
* successfully executing the present function.
|
||||
*
|
||||
* Then if requested serial interface is a primary interface (aka UI interface or AP
|
||||
* interface), this function initializes the device using the following hardware settings:
|
||||
* - set timestamp resolution to 16us
|
||||
* - enable FIFO mechanism with the following configuration:
|
||||
* - FIFO record mode i.e FIFO count unit is packet
|
||||
* - FIFO snapshot mode i.e drop the data when the FIFO overflows
|
||||
* - Timestamp is logged in FIFO
|
||||
* - Little Endian fifo_count and fifo_data
|
||||
* - generate FIFO threshold interrupt when packet count reaches FIFO watermark
|
||||
* - set FIFO watermark to 1 packet
|
||||
* - enable temperature and timestamp data to go to FIFO
|
||||
*
|
||||
*
|
||||
* @param[in] s driver structure. Note that first field of this structure MUST be a struct
|
||||
* inv_imu_serif.
|
||||
*
|
||||
* @param[in] serif pointer on serial interface structure to be used to access inv_device.
|
||||
*
|
||||
* @param[in] sensor_event_cb callback executed by inv_imu_get_data_from_fifo function
|
||||
* each time it extracts some valid data from fifo. Or inv_imu_get_data_from_registers read data
|
||||
* from register. Thus this parameter is optional as long
|
||||
* as inv_imu_get_data_from_fifo/inv_imu_get_data_from_registers function is not used.
|
||||
*
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_init(struct inv_imu_device *s,
|
||||
struct inv_imu_serif *serif,
|
||||
void (*sensor_event_cb)(inv_imu_sensor_event_t *event));
|
||||
|
||||
/** @brief Perform a soft reset of the device
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_device_reset(struct inv_imu_device *s);
|
||||
|
||||
/** @brief return WHOAMI value
|
||||
* @param[out] who_am_i WHOAMI for device
|
||||
* @return 0 on success, negative value on error
|
||||
*/
|
||||
int inv_imu_get_who_am_i(struct inv_imu_device *s, uint8_t *who_am_i);
|
||||
|
||||
/** @brief Enable/put accel in low power mode
|
||||
* @return 0 on success, negative value on error.
|
||||
* @details
|
||||
* It enables accel and gyro data in the FIFO (so
|
||||
* the packet format is 16 bytes). If called first,
|
||||
* the configuration will be applied, otherwise it
|
||||
* will be ignored if the FIFO is not empty (but since
|
||||
* the new configuration is identical it is not a issue).
|
||||
* @warning inv_device::register_cache::pwr_mgmt0_reg is modified by this function
|
||||
*/
|
||||
int inv_imu_enable_accel_low_power_mode(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Enable/put accel in low noise mode
|
||||
* @return 0 on success, negative value on error.
|
||||
* @details
|
||||
* It enables accel and gyro data in the FIFO (so
|
||||
* the packet format is 16 bytes). If called first,
|
||||
* the configuration will be applied, otherwise it
|
||||
* will be ignored if the FIFO is not empty (but since
|
||||
* the new configuration is identical it is not a issue).
|
||||
* @warning inv_device::register_cache::pwr_mgmt0_reg is modified by this function
|
||||
*/
|
||||
int inv_imu_enable_accel_low_noise_mode(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Disable all 3 axes of accel
|
||||
* @return 0 on success, negative value on error.
|
||||
* @details
|
||||
* If both accel and gyro are turned off as a result of this
|
||||
* function, they will also be removed from the FIFO and a
|
||||
* FIFO reset will be performed (to guarantee no side effects
|
||||
* until the next enable sensor call)
|
||||
* @warning inv_device::register_cache::pwr_mgmt0_reg is modified by this function
|
||||
*/
|
||||
int inv_imu_disable_accel(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Enable/put gyro in low noise mode
|
||||
* @return 0 on success, negative value on error.
|
||||
* @details
|
||||
* It enables gyro and accel data in the FIFO (so
|
||||
* the packet format is 16 bytes). If called first,
|
||||
* the configuration will be applied, otherwise it
|
||||
* will be ignored if the FIFO is not empty (but since
|
||||
* the new configuration is identical it is not a issue).
|
||||
* @warning inv_device::register_cache::pwr_mgmt0_reg is modified by this function
|
||||
*/
|
||||
int inv_imu_enable_gyro_low_noise_mode(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Disable all 3 axes of gyro
|
||||
* @return 0 on success, negative value on error.
|
||||
* @details
|
||||
* If both accel and gyro are turned off as a result of this
|
||||
* function, they will also be removed from the FIFO and a
|
||||
* FIFO reset will be performed (to guarantee no side effects
|
||||
* until the next enable sensor call)
|
||||
* @warning inv_device::register_cache::pwr_mgmt0_reg is modified by this function
|
||||
*/
|
||||
int inv_imu_disable_gyro(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Enable fsync tagging functionality.
|
||||
* In details it:
|
||||
* - enables fsync
|
||||
* - enables timestamp to registers. Once fsync is enabled fsync counter is pushed to
|
||||
* fifo instead of timestamp. So timestamp is made available in registers. Note that
|
||||
* this increase power consumption.
|
||||
* - enables fsync related interrupt
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_enable_fsync(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Disable fsync tagging functionality.
|
||||
* In details it:
|
||||
* - disables fsync
|
||||
* - disables timestamp to registers. Once fsync is disabled timestamp is pushed to fifo
|
||||
* instead of fsync counter. So in order to decrease power consumption, timestamp is no
|
||||
* more available in registers.
|
||||
* - disables fsync related interrupt
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_disable_fsync(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Configure which interrupt source can trigger INT1.
|
||||
* @param[in] interrupt_to_configure structure with the corresponding state to manage INT1.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_set_config_int1(struct inv_imu_device *s,
|
||||
inv_imu_interrupt_parameter_t *interrupt_to_configure);
|
||||
|
||||
/** @brief Retrieve interrupts configuration.
|
||||
* @param[in] interrupt_to_configure structure with the corresponding state to manage INT1.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_get_config_int1(struct inv_imu_device *s,
|
||||
inv_imu_interrupt_parameter_t *interrupt_to_configure);
|
||||
|
||||
/** @brief Configure which interrupt source can trigger INT2.
|
||||
* @param[in] interrupt_to_configure structure with the corresponding state to INT2.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_set_config_int2(struct inv_imu_device *s,
|
||||
inv_imu_interrupt_parameter_t *interrupt_to_configure);
|
||||
|
||||
/** @brief Retrieve interrupts configuration.
|
||||
* @param[in] interrupt_to_configure structure with the corresponding state to manage INT2.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_get_config_int2(struct inv_imu_device *s,
|
||||
inv_imu_interrupt_parameter_t *interrupt_to_configure);
|
||||
|
||||
/** @brief Read all registers containing data (temperature, accelerometer and gyroscope). Then it calls
|
||||
* sensor_event_cb function passed in parameter of inv_imu_init function for each packet
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_get_data_from_registers(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Read all available packets from the FIFO. For each packet function builds a
|
||||
* sensor event containing packet data and validity information. Then it calls
|
||||
* sensor_event_cb funtion passed in parameter of inv_imu_init function for each
|
||||
* packet.
|
||||
* @return number of valid packets read on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_get_data_from_fifo(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Converts ACCEL_CONFIG0_ODR_t or GYRO_CONFIG0_ODR_t enums to period expressed in us
|
||||
* @param[in] odr_bitfield An ACCEL_CONFIG0_ODR_t or GYRO_CONFIG0_ODR_t enum
|
||||
* @return The corresponding period expressed in us
|
||||
*/
|
||||
uint32_t inv_imu_convert_odr_bitfield_to_us(uint32_t odr_bitfield);
|
||||
|
||||
/** @brief Configure accel Output Data Rate
|
||||
* @param[in] frequency The requested frequency.
|
||||
* @sa ACCEL_CONFIG0_ODR_t
|
||||
* @return 0 on success, negative value on error.
|
||||
* @warning inv_device::register_cache::accel_config0_reg is modified by this function
|
||||
*/
|
||||
int inv_imu_set_accel_frequency(struct inv_imu_device *s,
|
||||
const ACCEL_CONFIG0_ODR_t frequency);
|
||||
|
||||
/** @brief Configure gyro Output Data Rate
|
||||
* @param[in] frequency The requested frequency.
|
||||
* @sa GYRO_CONFIG0_ODR_t
|
||||
* @return 0 on success, negative value on error.
|
||||
* @warning inv_device::register_cache::gyro_config0_reg is modified by this function
|
||||
*/
|
||||
int inv_imu_set_gyro_frequency(struct inv_imu_device *s,
|
||||
const GYRO_CONFIG0_ODR_t frequency);
|
||||
|
||||
/** @brief Set accel full scale range
|
||||
* @param[in] accel_fsr_g requested full scale range.
|
||||
* @sa ACCEL_CONFIG0_FS_SEL_t.
|
||||
* @return 0 on success, negative value on error.
|
||||
* @warning inv_device::register_cache::accel_config0_reg is modified by this function
|
||||
*/
|
||||
int inv_imu_set_accel_fsr(struct inv_imu_device *s,
|
||||
ACCEL_CONFIG0_FS_SEL_t accel_fsr_g);
|
||||
|
||||
/** @brief Access accel full scale range
|
||||
* @param[out] accel_fsr_g current full scale range.
|
||||
* @sa ACCEL_CONFIG0_FS_SEL_t.
|
||||
* @return 0 on success, negative value on error.
|
||||
* @warning inv_device::register_cache::accel_config0_reg is relied upon by this function
|
||||
*/
|
||||
int inv_imu_get_accel_fsr(struct inv_imu_device *s,
|
||||
ACCEL_CONFIG0_FS_SEL_t *accel_fsr_g);
|
||||
|
||||
/** @brief Set gyro full scale range
|
||||
* @param[in] gyro_fsr_dps requested full scale range.
|
||||
* @sa GYRO_CONFIG0_FS_SEL_t.
|
||||
* @return 0 on success, negative value on error.
|
||||
* @warning inv_device::register_cache::gyro_config0_reg is modified by this function
|
||||
*/
|
||||
int inv_imu_set_gyro_fsr(struct inv_imu_device *s,
|
||||
GYRO_CONFIG0_FS_SEL_t gyro_fsr_dps);
|
||||
|
||||
/** @brief Access gyro full scale range
|
||||
* @param[out] gyro_fsr_dps current full scale range.
|
||||
* @sa GYRO_CONFIG0_FS_SEL_t.
|
||||
* @return 0 on success, negative value on error.
|
||||
* @warning inv_device::register_cache::gyro_config0_reg is relied upon by this function
|
||||
*/
|
||||
int inv_imu_get_gyro_fsr(struct inv_imu_device *s,
|
||||
GYRO_CONFIG0_FS_SEL_t *gyro_fsr_dps);
|
||||
|
||||
/** @brief Set accel Low-Power averaging value
|
||||
* @param[in] acc_avg requested averaging value
|
||||
* @sa ACCEL_CONFIG1_ACCEL_FILT_AVG_t
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_set_accel_lp_avg(struct inv_imu_device *s,
|
||||
ACCEL_CONFIG1_ACCEL_FILT_AVG_t acc_avg);
|
||||
|
||||
/** @brief Set accel Low-Noise bandwidth value
|
||||
* @param[in] acc_bw requested averaging value
|
||||
* @sa ACCEL_CONFIG1_ACCEL_FILT_BW_t
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_set_accel_ln_bw(struct inv_imu_device *s,
|
||||
ACCEL_CONFIG1_ACCEL_FILT_BW_t acc_bw);
|
||||
|
||||
/** @brief Set gyro Low-Noise bandwidth value
|
||||
* @param[in] gyr_bw requested averaging value
|
||||
* @sa GYRO_CONFIG1_GYRO_FILT_BW_t
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_set_gyro_ln_bw(struct inv_imu_device *s,
|
||||
GYRO_CONFIG1_GYRO_FILT_BW_t gyr_bw);
|
||||
|
||||
/** @brief Set timestamp resolution
|
||||
* @param[in] timestamp_resol requested timestamp resolution
|
||||
* @sa TMST_CONFIG1_RESOL_t
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_set_timestamp_resolution(struct inv_imu_device *s,
|
||||
const TMST_CONFIG1_RESOL_t timestamp_resol);
|
||||
|
||||
/** @brief reset IMU fifo
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_reset_fifo(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Enable 20 bits raw acc and raw gyr data in fifo.
|
||||
* @return 0 on success, negative return code otherwise
|
||||
*/
|
||||
int inv_imu_enable_high_resolution_fifo(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Disable 20 bits raw acc and raw gyr data in fifo.
|
||||
* @return 0 on success, negative return code otherwise
|
||||
*/
|
||||
int inv_imu_disable_high_resolution_fifo(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Configure Fifo
|
||||
* @param[in] fifo_config Fifo configuration method :
|
||||
* if FIFO is enabled, data are pushed to FIFO and FIFO THS interrupt is set
|
||||
* if FIFO is disabled, data are not pused to FIFO and DRDY interrupt is set
|
||||
* @sa INV_IMU_FIFO_CONFIG_t
|
||||
*/
|
||||
int inv_imu_configure_fifo(struct inv_imu_device *s,
|
||||
INV_IMU_FIFO_CONFIG_t fifo_config);
|
||||
|
||||
/** @brief Get FIFO timestamp resolution
|
||||
* @return the timestamp resolution in us as a q24 or 0 in case of error
|
||||
*/
|
||||
int32_t inv_imu_get_fifo_timestamp_resolution_us_q24(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Get register timestamp resolution
|
||||
* @return the timestamp resolution in us as a q24 or 0 in case of error
|
||||
*/
|
||||
uint32_t inv_imu_get_reg_timestamp_resolution_us_q24(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Enable Wake On Motion.
|
||||
* @param[in] wom_x_th threshold value for the Wake on Motion Interrupt for X-axis accelerometer.
|
||||
* @param[in] wom_y_th threshold value for the Wake on Motion Interrupt for Y-axis accelerometer.
|
||||
* @param[in] wom_z_th threshold value for the Wake on Motion Interrupt for Z-axis accelerometer.
|
||||
* @param[in] wom_int select which mode between AND/OR is used to generate interrupt.
|
||||
* @param[in] wom_dur select the number of overthreshold event to wait before generating interrupt.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_configure_wom(struct inv_imu_device *s,
|
||||
const uint8_t wom_x_th,
|
||||
const uint8_t wom_y_th,
|
||||
const uint8_t wom_z_th,
|
||||
WOM_CONFIG_WOM_INT_MODE_t wom_int,
|
||||
WOM_CONFIG_WOM_INT_DUR_t wom_dur);
|
||||
|
||||
/** @brief Enable Wake On Motion.
|
||||
* note : WoM requests to have the accelerometer enabled to work.
|
||||
* As a consequence Fifo water-mark interrupt is disabled to only trigger WoM interrupts.
|
||||
* To have good performance, it's recommended to set accelerometer ODR (Output Data Rate) to 20ms
|
||||
* and the accelerometer in Low Power Mode.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_enable_wom(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Disable Wake On Motion.
|
||||
* note : Fifo water-mark interrupt is re-enabled when WoM is disabled.
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_disable_wom(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Start DMP for APEX algorithms and selftest
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_start_dmp(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Reset DMP for APEX algorithms and selftest
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_reset_dmp(struct inv_imu_device *s,
|
||||
const APEX_CONFIG0_DMP_MEM_RESET_t sram_reset);
|
||||
|
||||
/** @breif Set the UI endianness and set the inv_device endianness field
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_set_endianness(struct inv_imu_device *s,
|
||||
INTF_CONFIG0_DATA_ENDIAN_t endianness);
|
||||
|
||||
/** @breif Read the UI endianness and set the inv_device endianness field
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_get_endianness(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Configure Fifo decimation
|
||||
* @param[in] requested decimation factor value from 2 to 256
|
||||
* @return 0 on success, negative value on error.
|
||||
*/
|
||||
int inv_imu_configure_fifo_data_rate(struct inv_imu_device *s,
|
||||
FDR_CONFIG_FDR_SEL_t dec_factor);
|
||||
|
||||
/** @brief Return driver version x.y.z-suffix as a char array
|
||||
* @retval driver version a char array "x.y.z-suffix"
|
||||
*/
|
||||
const char * inv_imu_get_version(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INV_IMU_DRIVER_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2017 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively "Software") is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
/** @defgroup DriverExt IMU driver extern functions
|
||||
* @brief Extern functions for IMU devices
|
||||
* @ingroup Driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @file inv_imu_extfunc.h
|
||||
* Extern functions for IMU devices
|
||||
*/
|
||||
|
||||
#ifndef _INV_IMU_EXTFUNC_H_
|
||||
#define _INV_IMU_EXTFUNC_H_
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/** @brief Hook for low-level high res system sleep() function to be implemented by upper layer
|
||||
* ~100us resolution is sufficient
|
||||
* @param[in] us number of us the calling thread should sleep
|
||||
*/
|
||||
extern void inv_imu_sleep_us(uint32_t us);
|
||||
|
||||
/** @brief Hook for low-level high res system get_time() function to be implemented by upper layer
|
||||
* Value shall be on 64bit with a 1 us resolution
|
||||
* @return The current time in us
|
||||
*/
|
||||
extern uint64_t inv_imu_get_time_us(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INV_IMU_EXTFUNC_H_ */
|
||||
|
||||
/** @} */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively "Software") is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
#include "inv_imu_selftest.h"
|
||||
#include "inv_imu_extfunc.h"
|
||||
#include "inv_imu_transport.h"
|
||||
|
||||
static int configure_selftest_parameters(struct inv_imu_device *s,
|
||||
const inv_imu_selftest_parameters_t st_params);
|
||||
|
||||
int inv_imu_run_selftest(struct inv_imu_device *s,
|
||||
const inv_imu_selftest_parameters_t st_params,
|
||||
inv_imu_selftest_output_t *st_output)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t value;
|
||||
uint8_t data[2] = {0};
|
||||
uint8_t st_done = 0;
|
||||
int polling_timeout_ms = 1000;
|
||||
|
||||
/* Disables Gyro/Accel sensors */
|
||||
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &value);
|
||||
value &= ~(PWR_MGMT0_ACCEL_MODE_MASK | PWR_MGMT0_GYRO_MODE_MASK);
|
||||
status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &value);
|
||||
|
||||
/* Enable RC oscillator */
|
||||
status |= inv_imu_switch_on_mclk(s);
|
||||
|
||||
/* Clear DMP SRAM (1 ms wait included in `inv_imu_reset_dmp()`) */
|
||||
status |= inv_imu_reset_dmp(s, APEX_CONFIG0_DMP_MEM_RESET_APEX_ST_EN);
|
||||
/* Update `dmp_is_on` since APEX features will have to restart from scratch */
|
||||
s->dmp_is_on = 0;
|
||||
|
||||
/* Load self-test data */
|
||||
status |= inv_imu_load_selftest_data(s);
|
||||
|
||||
/* Set self-test parameters */
|
||||
status |= configure_selftest_parameters(s, st_params);
|
||||
|
||||
/*
|
||||
* Enable accel and/or gyro self-test.
|
||||
* If both accel and gyro self-test are enabled,
|
||||
* they should be set simultaneously in the same write access
|
||||
*/
|
||||
status |= inv_imu_read_reg(s, SELFTEST_MREG1, 1, &value);
|
||||
value &= ~SELFTEST_EN;
|
||||
value |= (uint8_t)st_params.st_control;
|
||||
status |= inv_imu_write_reg(s, SELFTEST_MREG1, 1, &value);
|
||||
|
||||
/* Poll int_status_st_done bit */
|
||||
do {
|
||||
inv_imu_sleep_us(1000);
|
||||
status |= inv_imu_read_reg(s, INT_STATUS, 1, &st_done);
|
||||
st_done &= INT_STATUS_ST_INT_MASK;
|
||||
|
||||
if (0 == --polling_timeout_ms)
|
||||
return (status | -1); /* Return error if timeout is reached */
|
||||
|
||||
} while ( !st_done /* Exit if ST_DONE */
|
||||
&& !status /* Or if error is detected */);
|
||||
|
||||
/* Read self-test results */
|
||||
status |= inv_imu_read_reg(s, ST_STATUS1_MREG1, 2, &data[0]);
|
||||
st_output->accel_status = (data[0] & ST_STATUS1_ACCEL_ST_PASS_MASK) >> ST_STATUS1_ACCEL_ST_PASS_POS;
|
||||
st_output->ax_status = (data[0] & ST_STATUS1_AX_ST_PASS_MASK) >> ST_STATUS1_AX_ST_PASS_POS;
|
||||
st_output->ay_status = (data[0] & ST_STATUS1_AY_ST_PASS_MASK) >> ST_STATUS1_AY_ST_PASS_POS;
|
||||
st_output->az_status = (data[0] & ST_STATUS1_AZ_ST_PASS_MASK) >> ST_STATUS1_AZ_ST_PASS_POS;
|
||||
st_output->gyro_status = (data[1] & ST_STATUS2_GYRO_ST_PASS_MASK) >> ST_STATUS2_GYRO_ST_PASS_POS;
|
||||
st_output->gyro_status |= ((data[1] & ST_STATUS2_ST_INCOMPLETE_MASK) >> ST_STATUS2_ST_INCOMPLETE_POS) << 1;
|
||||
st_output->gx_status = (data[1] & ST_STATUS2_GX_ST_PASS_MASK) >> ST_STATUS2_GX_ST_PASS_POS;
|
||||
st_output->gy_status = (data[1] & ST_STATUS2_GY_ST_PASS_MASK) >> ST_STATUS2_GY_ST_PASS_POS;
|
||||
st_output->gz_status = (data[1] & ST_STATUS2_GZ_ST_PASS_MASK) >> ST_STATUS2_GZ_ST_PASS_POS;
|
||||
|
||||
/* Disable self-test */
|
||||
status |= inv_imu_read_reg(s, SELFTEST_MREG1, 1, &value);
|
||||
value &= ~SELFTEST_EN;
|
||||
value |= (uint8_t)SELFTEST_DIS;
|
||||
status |= inv_imu_write_reg(s, SELFTEST_MREG1, 1, &value);
|
||||
|
||||
/* Reset FIFO because ST data may have been pushed to it */
|
||||
status |= inv_imu_reset_fifo(s);
|
||||
|
||||
/* Restore idle bit */
|
||||
status |= inv_imu_switch_off_mclk(s);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_init_selftest_parameters_struct(struct inv_imu_device *s,
|
||||
inv_imu_selftest_parameters_t *st_params)
|
||||
{
|
||||
(void)s;
|
||||
st_params->st_num_samples = ST_CONFIG_16_SAMPLES;
|
||||
st_params->st_control = (SELFTEST_ACCEL_GYRO_ST_EN_t)SELFTEST_EN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inv_imu_load_selftest_data(struct inv_imu_device *s)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t value;
|
||||
|
||||
/* Enable RC oscillator */
|
||||
status |= inv_imu_switch_on_mclk(s);
|
||||
|
||||
/* Set up OTP controller to reload factory-trimmed self-test response into SRAM */
|
||||
status |= inv_imu_read_reg(s, OTP_CONFIG_MREG1, 1, &value);
|
||||
value &= ~OTP_CONFIG_OTP_COPY_MODE_MASK;
|
||||
value |= (uint8_t)OTP_CONFIG_OTP_COPY_ST_DATA;
|
||||
status |= inv_imu_write_reg(s, OTP_CONFIG_MREG1, 1, &value);
|
||||
|
||||
/* Take the OTP macro out of power-down mode */
|
||||
status |= inv_imu_read_reg(s, OTP_CTRL7_MREG2, 1, &value);
|
||||
value &= ~OTP_CTRL7_OTP_PWR_DOWN_MASK;
|
||||
value |= (uint8_t)OTP_CTRL7_PWR_DOWN_DIS;
|
||||
status |= inv_imu_write_reg(s, OTP_CTRL7_MREG2, 1, &value);
|
||||
|
||||
/* Wait for voltage generator to power on */
|
||||
inv_imu_sleep_us(100);
|
||||
|
||||
/* Host should disable INT function first before kicking off OTP copy operation */
|
||||
|
||||
/* Trigger OTP to reload data (this time in self-test mode) */
|
||||
status |= inv_imu_read_reg(s, OTP_CTRL7_MREG2, 1, &value);
|
||||
value &= ~OTP_CTRL7_OTP_RELOAD_MASK;
|
||||
value |= (uint8_t)OTP_CTRL7_OTP_RELOAD_EN;
|
||||
status |= inv_imu_write_reg(s, OTP_CTRL7_MREG2, 1, &value);
|
||||
|
||||
/* Wait for OTP reload */
|
||||
inv_imu_sleep_us(20);
|
||||
|
||||
/* Disable RC oscillator */
|
||||
status |= inv_imu_switch_off_mclk(s);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int configure_selftest_parameters(struct inv_imu_device *s,
|
||||
const inv_imu_selftest_parameters_t st_params)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t value;
|
||||
|
||||
/* Self-test configuration cannot be updated if it already running */
|
||||
status |= inv_imu_read_reg(s, SELFTEST_MREG1, 1, &value);
|
||||
if ((value & SELFTEST_EN) != SELFTEST_DIS)
|
||||
return INV_ERROR_UNEXPECTED;
|
||||
|
||||
status |= inv_imu_read_reg(s, ST_CONFIG_MREG1, 1, &value);
|
||||
value &= ~((uint8_t)ST_CONFIG_ST_NUMBER_SAMPLE_MASK
|
||||
| (uint8_t)ST_CONFIG_ACCEL_ST_LIM_MASK
|
||||
| (uint8_t)ST_CONFIG_GYRO_ST_LIM_MASK);
|
||||
value |= (uint8_t)st_params.st_num_samples
|
||||
| (uint8_t)ST_CONFIG_ACCEL_ST_LIM_50
|
||||
| (uint8_t)ST_CONFIG_GYRO_ST_LIM_50;
|
||||
status |= inv_imu_write_reg(s, ST_CONFIG_MREG1, 1, &value);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively "Software") is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
/** @defgroup DriverST SelfTest IMU selftest
|
||||
* @brief Low-level function to run selftest on a IMU device
|
||||
* @ingroup Driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @file inv_imu_selftest.h
|
||||
* Low-level function to run selftest on a IMU device
|
||||
*/
|
||||
|
||||
#ifndef _INV_IMU_SELFTEST_H_
|
||||
#define _INV_IMU_SELFTEST_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "InvExport.h"
|
||||
#include "inv_imu_defs.h"
|
||||
#include "inv_imu_driver.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* forward declaration */
|
||||
struct inv_imu_device;
|
||||
|
||||
|
||||
/** @brief Self-test input parameters
|
||||
*/
|
||||
typedef struct {
|
||||
ST_CONFIG_NUM_SAMPLES_t st_num_samples; /**< Number of samples used to perform self-test */
|
||||
SELFTEST_ACCEL_GYRO_ST_EN_t st_control; /**< Define which sensor is under self-test */
|
||||
} inv_imu_selftest_parameters_t;
|
||||
|
||||
/** @brief Self-test routine outputs
|
||||
*/
|
||||
typedef struct {
|
||||
int8_t accel_status; /**< global accelerometer self-test passed */
|
||||
int8_t gyro_status; /**< global gyroscope self-test status: st_pass (bit0), st_incomplete (bit1) */
|
||||
int8_t ax_status; /**< AX self-test status */
|
||||
int8_t ay_status; /**< AY self-test status */
|
||||
int8_t az_status; /**< AZ self-test status */
|
||||
int8_t gx_status; /**< GX self-test status */
|
||||
int8_t gy_status; /**< GY self-test status */
|
||||
int8_t gz_status; /**< GZ self-test status */
|
||||
} inv_imu_selftest_output_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Perform hardware self-test for Accel and Gyro
|
||||
* @param[in] Self-test parameters (see inv_imu_selftest_parameters_t)
|
||||
* @param[out] Self-test results (see inv_imu_selftest_output_t)
|
||||
* @return 0 on completion, negative number if intermediate errors occurred
|
||||
*/
|
||||
int inv_imu_run_selftest(struct inv_imu_device *s,
|
||||
const inv_imu_selftest_parameters_t st_params,
|
||||
inv_imu_selftest_output_t *st_output);
|
||||
|
||||
/** @brief Fill the self-test configuration structure with default configuration
|
||||
* @param[in] selftest_params self-test parameters to be initialized
|
||||
* @return 0 on success, negative return code otherwise
|
||||
*/
|
||||
int inv_imu_init_selftest_parameters_struct(struct inv_imu_device *s,
|
||||
inv_imu_selftest_parameters_t *selftest_params);
|
||||
|
||||
/** @brief Load self-test data
|
||||
* @return 0 on success, negative return code otherwise
|
||||
*/
|
||||
int inv_imu_load_selftest_data(struct inv_imu_device *s);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INV_IMU_SELFTEST_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively "Software") is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
#include "inv_imu_extfunc.h"
|
||||
#include "inv_imu_transport.h"
|
||||
#include "inv_imu_regmap.h"
|
||||
|
||||
#include "InvError.h"
|
||||
|
||||
|
||||
/* Function definition */
|
||||
static uint8_t *get_register_cache_addr(struct inv_imu_device *s, uint32_t reg);
|
||||
static int write_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, const uint8_t *buf);
|
||||
static int read_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, uint8_t *buf);
|
||||
static int write_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t wr_cnt, const uint8_t *buf);
|
||||
static int read_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t rd_cnt, uint8_t *buf);
|
||||
|
||||
|
||||
int inv_imu_init_transport(struct inv_imu_device *s)
|
||||
{
|
||||
int status = 0;
|
||||
struct inv_imu_transport *t = (struct inv_imu_transport *)s;
|
||||
|
||||
status |= read_sreg(s, (uint8_t)PWR_MGMT0, 1, &(t->register_cache.pwr_mgmt0_reg));
|
||||
status |= read_sreg(s, (uint8_t)GYRO_CONFIG0, 1, &(t->register_cache.gyro_config0_reg));
|
||||
status |= read_sreg(s, (uint8_t)ACCEL_CONFIG0, 1, &(t->register_cache.accel_config0_reg));
|
||||
|
||||
status |= read_mclk_reg(s, (TMST_CONFIG1_MREG1 & 0xFFFF), 1, &(t->register_cache.tmst_config1_reg));
|
||||
|
||||
t->need_mclk_cnt = 0;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_read_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, uint8_t *buf)
|
||||
{
|
||||
uint32_t i;
|
||||
int rc = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
uint8_t *cache_addr = get_register_cache_addr(s, reg + i);
|
||||
|
||||
if (cache_addr) {
|
||||
buf[i] = *cache_addr;
|
||||
} else {
|
||||
if (!(reg & 0x10000)) {
|
||||
rc |= read_mclk_reg(s, ((reg + i) & 0xFFFF), 1, &buf[i]);
|
||||
} else {
|
||||
rc |= read_sreg(s, (uint8_t)reg + i, len - i, &buf[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int inv_imu_write_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, const uint8_t *buf)
|
||||
{
|
||||
uint32_t i;
|
||||
int rc = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
uint8_t *cache_addr = get_register_cache_addr(s, reg + i);
|
||||
|
||||
if (cache_addr)
|
||||
*cache_addr = buf[i];
|
||||
|
||||
if (!(reg & 0x10000))
|
||||
rc |= write_mclk_reg(s, ((reg + i) & 0xFFFF), 1, &buf[i]);
|
||||
}
|
||||
|
||||
if (reg & 0x10000)
|
||||
rc |= write_sreg(s, (uint8_t)reg, len, buf);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int inv_imu_switch_on_mclk(struct inv_imu_device *s)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t data;
|
||||
struct inv_imu_transport *t = (struct inv_imu_transport *)s;
|
||||
|
||||
/* set IDLE bit only if it is not set yet */
|
||||
if (t->need_mclk_cnt == 0) {
|
||||
|
||||
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data);
|
||||
data |= PWR_MGMT0_IDLE_MASK;
|
||||
status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* Check if MCLK is ready */
|
||||
do {
|
||||
status = inv_imu_read_reg(s, MCLK_RDY, 1, &data);
|
||||
} while ((status != 0) || !(data & MCLK_RDY_MCLK_RDY_MASK));
|
||||
} else {
|
||||
|
||||
/* Make sure it is already on */
|
||||
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data);
|
||||
if (0 == (data &= PWR_MGMT0_IDLE_MASK))
|
||||
status |= INV_ERROR;
|
||||
}
|
||||
|
||||
/* Increment the counter to keep track of number of MCLK requesters */
|
||||
t->need_mclk_cnt++;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int inv_imu_switch_off_mclk(struct inv_imu_device *s)
|
||||
{
|
||||
int status = 0;
|
||||
uint8_t data;
|
||||
struct inv_imu_transport *t = (struct inv_imu_transport *)s;
|
||||
|
||||
/* Reset the IDLE but only if there is one requester left */
|
||||
if (t->need_mclk_cnt == 1) {
|
||||
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data);
|
||||
data &= ~PWR_MGMT0_IDLE_MASK;
|
||||
status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data);
|
||||
} else {
|
||||
/* Make sure it is still on */
|
||||
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data);
|
||||
if (0 == (data &= PWR_MGMT0_IDLE_MASK))
|
||||
status |= INV_ERROR;
|
||||
}
|
||||
|
||||
/* Decrement the counter */
|
||||
t->need_mclk_cnt--;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* Static function */
|
||||
|
||||
static uint8_t *get_register_cache_addr(struct inv_imu_device *s, uint32_t reg)
|
||||
{
|
||||
struct inv_imu_transport *t = (struct inv_imu_transport *)s;
|
||||
|
||||
switch(reg) {
|
||||
case PWR_MGMT0: return &(t->register_cache.pwr_mgmt0_reg);
|
||||
case GYRO_CONFIG0: return &(t->register_cache.gyro_config0_reg);
|
||||
case ACCEL_CONFIG0: return &(t->register_cache.accel_config0_reg);
|
||||
case TMST_CONFIG1_MREG1: return &(t->register_cache.tmst_config1_reg);
|
||||
default: return (uint8_t *)0; // Not found
|
||||
}
|
||||
}
|
||||
|
||||
static int read_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, uint8_t *buf)
|
||||
{
|
||||
struct inv_imu_serif *serif = (struct inv_imu_serif *)s;
|
||||
|
||||
if (len > serif->max_read)
|
||||
return INV_ERROR_SIZE;
|
||||
|
||||
if (serif->read_reg(serif, reg, buf, len) != 0)
|
||||
return INV_ERROR_TRANSPORT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, const uint8_t *buf)
|
||||
{
|
||||
struct inv_imu_serif *serif = (struct inv_imu_serif *)s;
|
||||
|
||||
if (len > serif->max_write)
|
||||
return INV_ERROR_SIZE;
|
||||
|
||||
if (serif->write_reg(serif, reg, buf, len) != 0)
|
||||
return INV_ERROR_TRANSPORT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t rd_cnt, uint8_t *buf)
|
||||
{
|
||||
uint8_t data;
|
||||
uint8_t blk_sel = (regaddr & 0xFF00) >> 8;
|
||||
int status = 0;
|
||||
|
||||
// Have IMU not in IDLE mode to access MCLK domain
|
||||
status |= inv_imu_switch_on_mclk(s);
|
||||
|
||||
// optimize by changing BLK_SEL only if not NULL
|
||||
if (blk_sel)
|
||||
status |= write_sreg(s, (uint8_t)BLK_SEL_R & 0xff, 1, &blk_sel);
|
||||
|
||||
data = (regaddr & 0x00FF);
|
||||
status |= write_sreg(s, (uint8_t)MADDR_R, 1, &data);
|
||||
inv_imu_sleep_us(10);
|
||||
status |= read_sreg(s, (uint8_t)M_R, rd_cnt, buf);
|
||||
inv_imu_sleep_us(10);
|
||||
|
||||
if (blk_sel) {
|
||||
data = 0;
|
||||
status |= write_sreg(s, (uint8_t)BLK_SEL_R, 1, &data);
|
||||
}
|
||||
|
||||
// switch OFF MCLK if needed
|
||||
status |= inv_imu_switch_off_mclk(s);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int write_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t wr_cnt, const uint8_t *buf)
|
||||
{
|
||||
uint8_t data;
|
||||
uint8_t blk_sel = (regaddr & 0xFF00) >> 8;
|
||||
int status = 0;
|
||||
|
||||
// Have IMU not in IDLE mode to access MCLK domain
|
||||
status |= inv_imu_switch_on_mclk(s);
|
||||
|
||||
// optimize by changing BLK_SEL only if not NULL
|
||||
if (blk_sel)
|
||||
status |= write_sreg(s, (uint8_t)BLK_SEL_W, 1, &blk_sel);
|
||||
|
||||
data = (regaddr & 0x00FF);
|
||||
status |= write_sreg(s, (uint8_t)MADDR_W, 1, &data);
|
||||
for (uint8_t i = 0; i < wr_cnt; i++) {
|
||||
status |= write_sreg(s, (uint8_t)M_W, 1, &buf[i]);
|
||||
inv_imu_sleep_us(10);
|
||||
}
|
||||
|
||||
if (blk_sel) {
|
||||
data = 0;
|
||||
status = write_sreg(s, (uint8_t)BLK_SEL_W, 1, &data);
|
||||
}
|
||||
|
||||
status |= inv_imu_switch_off_mclk(s);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively "Software") is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
/** @defgroup Transport IMU transport
|
||||
* @brief Low-level IMU SCLK register access
|
||||
* @ingroup Driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @file inv_imu_transport.h
|
||||
* Low-level IMU SCLK register access
|
||||
*/
|
||||
|
||||
#ifndef _INV_IMU_TRANSPORT_H_
|
||||
#define _INV_IMU_TRANSPORT_H_
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* forward declaration */
|
||||
struct inv_imu_device;
|
||||
|
||||
|
||||
/** @brief enumeration of serial interfaces available on IMU */
|
||||
typedef enum
|
||||
{
|
||||
UI_I2C,
|
||||
UI_SPI4,
|
||||
UI_SPI3
|
||||
} SERIAL_IF_TYPE_t;
|
||||
|
||||
/** @brief basesensor serial interface
|
||||
*/
|
||||
struct inv_imu_serif {
|
||||
void *context;
|
||||
int (*read_reg)(struct inv_imu_serif *serif, uint8_t reg, uint8_t *buf, uint32_t len);
|
||||
int (*write_reg)(struct inv_imu_serif *serif, uint8_t reg, const uint8_t *buf, uint32_t len);
|
||||
int (*configure)(struct inv_imu_serif *serif);
|
||||
uint32_t max_read;
|
||||
uint32_t max_write;
|
||||
SERIAL_IF_TYPE_t serif_type;
|
||||
};
|
||||
|
||||
/** @brief transport interface
|
||||
*/
|
||||
struct inv_imu_transport {
|
||||
struct inv_imu_serif serif; /**< Warning : this field MUST be the first one of struct inv_imu_transport */
|
||||
|
||||
/** @brief Contains mirrored values of some IP registers */
|
||||
struct register_cache {
|
||||
uint8_t pwr_mgmt0_reg; /**< PWR_MGMT0, Bank: 0 */
|
||||
uint8_t gyro_config0_reg; /**< GYRO_CONFIG0, Bank: 0 */
|
||||
uint8_t accel_config0_reg; /**< ACCEL_CONFIG0, Bank: 0 */
|
||||
uint8_t tmst_config1_reg; /**< TMST_CONFIG1, Bank: MREG_TOP1 */
|
||||
} register_cache; /**< Store mostly used register values on SRAM */
|
||||
|
||||
uint8_t need_mclk_cnt; /**< internal counter to keep track of everyone that needs MCLK */
|
||||
|
||||
};
|
||||
|
||||
/** @brief Init cache variable.
|
||||
* @return 0 in case of success, -1 for any error
|
||||
*/
|
||||
int inv_imu_init_transport(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Reads data from a register on IMU.
|
||||
* @param[in] reg register address to be read
|
||||
* @param[in] len number of byte to be read
|
||||
* @param[out] buf output data from the register
|
||||
* @return 0 in case of success, -1 for any error
|
||||
*/
|
||||
int inv_imu_read_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, uint8_t *buf);
|
||||
|
||||
/** @brief Writes data to a register on IMU.
|
||||
* @param[in] reg register address to be written
|
||||
* @param[in] len number of byte to be written
|
||||
* @param[in] buf input data to write
|
||||
* @return 0 in case of success, -1 for any error
|
||||
*/
|
||||
int inv_imu_write_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, const uint8_t *buf);
|
||||
|
||||
/** @brief Enable MCLK so that MREG are clocked and system beyond SOI can be safely accessed
|
||||
* @return 0 in case of success, -1 for any error
|
||||
*/
|
||||
int inv_imu_switch_on_mclk(struct inv_imu_device *s);
|
||||
|
||||
/** @brief Disable MCLK so that MREG are not clocked anymore, hence reducing power consumption
|
||||
* @return 0 in case of success, -1 for any error
|
||||
*/
|
||||
int inv_imu_switch_off_mclk(struct inv_imu_device *s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INV_IMU_TRANSPORT_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* ________________________________________________________________________________________________________
|
||||
* Copyright (c) 2019 InvenSense Inc. All rights reserved.
|
||||
*
|
||||
* This software, related documentation and any modifications thereto (collectively “Software”) is subject
|
||||
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
* and other intellectual property rights laws.
|
||||
*
|
||||
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
* from InvenSense is strictly prohibited.
|
||||
*
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
|
||||
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
|
||||
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
|
||||
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THE SOFTWARE.
|
||||
* ________________________________________________________________________________________________________
|
||||
*/
|
||||
|
||||
#ifndef _INV_IMU_VERSION_H_
|
||||
#define _INV_IMU_VERSION_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define INV_IMU_VERSION_STRING "2.0.4"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INV_IMU_VERSION_H_ */
|
||||
@@ -0,0 +1,153 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright © 2014-2015 InvenSense Inc. Portions Copyright © 2014-2015 Movea. All rights reserved.
|
||||
|
||||
This software, related documentation and any modifications thereto (collectively “Software”) is subject
|
||||
to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
|
||||
and other intellectual property rights laws.
|
||||
|
||||
InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
|
||||
and any use, reproduction, disclosure or distribution of the Software without an express license agreement
|
||||
from InvenSense is strictly prohibited.
|
||||
*/
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef INVN_COMMON_INVN_TYPES_H_
|
||||
#define INVN_COMMON_INVN_TYPES_H_
|
||||
|
||||
/**
|
||||
* @defgroup invn_types Types
|
||||
* @brief Motion Library - Type definitions.
|
||||
* \details Definition of codes and error codes used within the MPL and
|
||||
* returned to the user.
|
||||
* Every function tries to return a meaningful error code basing
|
||||
* on the occuring error condition. The error code is numeric.
|
||||
*
|
||||
* The available error codes and their associated values are:
|
||||
* - (0) INV_SUCCESS
|
||||
* - (32) INV_ERROR
|
||||
* - (22 / EINVAL) INV_ERROR_INVALID_PARAMETER
|
||||
* - (1 / EPERM) INV_ERROR_FEATURE_NOT_ENABLED
|
||||
* - (36) INV_ERROR_FEATURE_NOT_IMPLEMENTED
|
||||
* - (64) INV_ERROR_FIFO_READ_COUNT
|
||||
* \todo Clean up the details documentation in order to use only the \\def one.
|
||||
* \todo Add documentation to all the definitions
|
||||
* \ingroup Common
|
||||
* @file invn_types.h
|
||||
*/
|
||||
|
||||
//=======================================//
|
||||
//========= Integer Definition =========//
|
||||
//=======================================//
|
||||
#ifdef _MSC_VER
|
||||
# include "inttypes.h"
|
||||
#else
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
//=======================================//
|
||||
//======= Fixed Point Conversion =======//
|
||||
//=======================================//
|
||||
|
||||
//! \def INVN_FLT_TO_FXP
|
||||
//! Convert the \a value from float to QN value. \ingroup invn_macro
|
||||
#define INVN_FLT_TO_FXP(value, shift) ( (int32_t) ((float)(value)*(1ULL << (shift)) + ( (value>=0)-0.5f )) )
|
||||
//! \def INVN_DBL_TO_FXP
|
||||
//! Convert the \a value from double to QN value. \ingroup invn_macro
|
||||
#define INVN_DBL_TO_FXP(value, shift) ( (int32_t) ((double)(value)*(1ULL << (shift)) + ( (value>=0)-0.5 )) )
|
||||
//! \def INVN_FLT_TO_UFXP
|
||||
//! Convert the \a value from float to unsigned QN value. \ingroup invn_macro
|
||||
#define INVN_FLT_TO_UFXP(value, shift) ( (uint32_t) ((float)(value)*(1ULL << (shift)) + 0.5f) )
|
||||
//! \def INVN_DBL_TO_UFXP
|
||||
//! Convert the \a value from double to unsigned QN value. \ingroup invn_macro
|
||||
#define INVN_DBL_TO_UFXP(value, shift) ( (uint32_t) ((double)(value)*(1ULL << (shift)) + 0.5) )
|
||||
//! \def INVN_FXP_TO_FLT
|
||||
//! Convert the \a value from QN value to float. \ingroup invn_macro
|
||||
#define INVN_FXP_TO_FLT(value, shift) ( (float) (int32_t)(value) / (float)(1ULL << (shift)) )
|
||||
//! \def INVN_FXP_TO_DBL
|
||||
//! Convert the \a value from QN value to double. \ingroup invn_macro
|
||||
#define INVN_FXP_TO_DBL(value, shift) ( (double) (int32_t)(value) / (double)(1ULL << (shift)) )
|
||||
//! \def INVN_UFXP_TO_FLT
|
||||
//! Convert the \a value from unsigned QN value to float. \ingroup invn_macro
|
||||
#define INVN_UFXP_TO_FLT(value, shift) ( (float) (uint32_t)(value) / (float)(1ULL << (shift)) )
|
||||
//! \def INVN_UFXP_TO_DBL
|
||||
//! Convert the \a value from unsigned QN value to double. \ingroup invn_macro
|
||||
#define INVN_UFXP_TO_DBL(value, shift) ( (double) (uint32_t)(value) / (double)(1ULL << (shift)) )
|
||||
//! \def INVN_CONVERT_FLT_TO_FXP
|
||||
//! Macro to convert float values from an address into QN values, and copy them to another address. \ingroup invn_macro
|
||||
#define INVN_CONVERT_FLT_TO_FXP(fltptr, fixptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fixptr)[i] = INVN_FLT_TO_FXP((fltptr)[i], shift); }
|
||||
//! \def INVN_CONVERT_FLT_TO_UFXP
|
||||
//! Macro to convert float values from an address into unsigned QN values, and copy them to another address. \ingroup invn_macro
|
||||
#define INVN_CONVERT_FLT_TO_UFXP(fltptr, fixptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fixptr)[i] = INVN_FLT_TO_UFXP((fltptr)[i], shift); }
|
||||
//! \def INVN_CONVERT_DBL_TO_FXP
|
||||
//! Macro to convert double values from an address into QN values, and copy them to another address. \ingroup invn_macro
|
||||
#define INVN_CONVERT_DBL_TO_FXP(fltptr, fixptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fixptr)[i] = INVN_DBL_TO_FXP((fltptr)[i], shift); }
|
||||
//! \def INVN_CONVERT_DBL_TO_UFXP
|
||||
//! Macro to convert double values from an address into unsigned QN values, and copy them to another address. \ingroup invn_macro
|
||||
#define INVN_CONVERT_DBL_TO_UFXP(fltptr, fixptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fixptr)[i] = INVN_DBL_TO_UFXP((fltptr)[i], shift); }
|
||||
//! \def INVN_CONVERT_FXP_TO_FLT
|
||||
//! Macro to convert QN values from an address into float values, and copy them to another address. \ingroup invn_macro
|
||||
#define INVN_CONVERT_FXP_TO_FLT(fixptr, fltptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fltptr)[i] = INVN_FXP_TO_FLT((fixptr)[i], shift); }
|
||||
//! \def INVN_CONVERT_UFXP_TO_FLT
|
||||
//! Macro to convert unsigned QN values from an address into float values, and copy them to another address. \ingroup invn_macro
|
||||
#define INVN_CONVERT_UFXP_TO_FLT(fixptr, fltptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fltptr)[i] = INVN_UFXP_TO_FLT((fixptr)[i], shift); }
|
||||
//! \def INVN_CONVERT_FXP_TO_DBL
|
||||
//! Macro to convert QN values from an address into double values, and copy them to another address. \ingroup invn_macro
|
||||
#define INVN_CONVERT_FXP_TO_DBL(fixptr, fltptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fltptr)[i] = INVN_FXP_TO_DBL((fixptr)[i], shift); }
|
||||
//! \def INVN_CONVERT_UFXP_TO_DBL
|
||||
//! \brief Macro to convert unsigned QN values from an address into double values, and copy them to another address. \ingroup invn_macro
|
||||
#define INVN_CONVERT_UFXP_TO_DBL(fixptr, fltptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fltptr)[i] = INVN_UFXP_TO_DBL((fixptr)[i], shift); }
|
||||
|
||||
|
||||
//=====================================//
|
||||
//========= Error Definition =========//
|
||||
//=====================================//
|
||||
|
||||
#ifndef REMOVE_INV_ERROR_T
|
||||
typedef int32_t inv_error_t; /*!< Type used for error definitions. \ingroup invn_types */
|
||||
#endif
|
||||
|
||||
//typedef int32_t mpu_error_t;
|
||||
typedef int64_t mpu_time_t; /*!< Type used for mpu time. \ingroup invn_types */
|
||||
|
||||
// Typically I2C addresses are 8-bit, but some specifications allow for a 10-bit address
|
||||
// This definition allows the length to be optimally defined for the platform
|
||||
typedef uint8_t inv_i2c_addr_t; /*!< Type used for I2C addresses. \ingroup invn_types */
|
||||
|
||||
#ifdef __IAR_SYSTEMS_ICC__
|
||||
// These are defined in standard C errno.h
|
||||
#define EINVAL (22)
|
||||
#define EPERM (1)
|
||||
#define ENOMEM (12)
|
||||
#else
|
||||
#include "errno.h"
|
||||
#endif
|
||||
|
||||
#define INVN_SUCCESS (0) /*!< Constant definition for success. \ingroup invn_types */
|
||||
#define INVN_ERROR_BASE (0x20) /*!< Constant definition for basic error. Value is \b 32 \ingroup invn_types */
|
||||
#define INVN_ERROR (INVN_ERROR_BASE) /*!< Constant definition for error. Value is \b 32 \ingroup invn_types */
|
||||
#define INVN_ERROR_FEATURE_NOT_ENABLED (EPERM) /*!< Constant definition for feature not enabled error. \ingroup invn_types */
|
||||
#define INVN_ERROR_FEATURE_NOT_IMPLEMENTED (INVN_ERROR_BASE + 4) /*!< Constant definition for feature not implemented error. \ingroup invn_types */
|
||||
#define INVN_ERROR_INVALID_PARAMETER (EINVAL) /*!< Constant definition for invalid parameter error. \ingroup invn_types */
|
||||
#define INVN_ERROR_FILE_OPEN (INVN_ERROR_BASE + 14) /*!< Constant definition for opening file error. \ingroup invn_types */
|
||||
#define INVN_ERROR_FILE_READ (INVN_ERROR_BASE + 15) /*!< Constant definition for reading file error. \ingroup invn_types */
|
||||
#define INVN_ERROR_FILE_WRITE (INVN_ERROR_BASE + 16) /*!< Constant definition for writing file error. \ingroup invn_types */
|
||||
#define INVN_ERROR_INVALID_CONFIGURATION (INVN_ERROR_BASE + 17) /*!< Constant definition for invalid configuration error. \ingroup invn_types */
|
||||
/* Serial Communication */
|
||||
#define INVN_ERROR_SERIAL_OPEN_ERROR (INVN_ERROR_BASE + 21) /*!< Constant definition for serial open error. \ingroup invn_types */
|
||||
#define INVN_ERROR_SERIAL_READ (INVN_ERROR_BASE + 22) /*!< Constant definition for serial read error. \ingroup invn_types */
|
||||
#define INVN_ERROR_SERIAL_WRITE (INVN_ERROR_BASE + 23) /*!< Constant definition for serial write error. \ingroup invn_types */
|
||||
/* Fifo */
|
||||
#define INVN_ERROR_FIFO_OVERFLOW (INVN_ERROR_BASE + 30) /*!< Constant definition for fifo overflow error. \ingroup invn_types */
|
||||
#define INVN_ERROR_FIFO_FOOTER (INVN_ERROR_BASE + 31) /*!< Constant definition for fifo footer error. \ingroup invn_types */
|
||||
#define INVN_ERROR_FIFO_READ_COUNT (INVN_ERROR_BASE + 32) /*!< Constant definition for fifo read count error. \ingroup invn_types */
|
||||
#define INVN_ERROR_FIFO_READ_DATA (INVN_ERROR_BASE + 33) /*!< Constant definition for fifo read data error. \ingroup invn_types */
|
||||
/* OS interface errors */
|
||||
#define INVN_ERROR_OS_BAD_HANDLE (INVN_ERROR_BASE + 61) /*!< Constant definition for OS bad handle error. \ingroup invn_types */
|
||||
#define INVN_ERROR_OS_CREATE_FAILED (INVN_ERROR_BASE + 62) /*!< Constant definition for OS create failed error. \ingroup invn_types */
|
||||
#define INVN_ERROR_OS_LOCK_FAILED (INVN_ERROR_BASE + 63) /*!< Constant definition for OS lock failed error. \ingroup invn_types */
|
||||
/* Warning */
|
||||
#define INVN_WARNING_SEMAPHORE_TIMEOUT (INVN_ERROR_BASE + 86) /*!< Constant definition for semaphore timeout warning. \ingroup invn_types */
|
||||
|
||||
|
||||
#endif // INVN_COMMON_INVN_TYPES_H_
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
$License:
|
||||
Copyright (C) 2018 InvenSense Corporation, All Rights Reserved.
|
||||
$
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _INVN_ALGO_AGM_H_
|
||||
#define _INVN_ALGO_AGM_H_
|
||||
|
||||
#include "invn_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup AGM AGM
|
||||
* \brief Algorithm that provides device orientation. Algorithm inputs are raw Accelerometer, Gyroscope and Magnetometer data.
|
||||
* Algorithm outputs: calibrated sensor and 9-axis sensor fusion.
|
||||
* \warning supported sampling frequency [50 Hz-1000 Hz]
|
||||
* \warning supported gyroscope FSR [250 dps, 500 dps, 1000 dps, 2000 dps, 4000 dps]
|
||||
* \warning supported accelerometer FSR [1 g, 2 g, 4 g, 8 g, 16 g]
|
||||
*/
|
||||
|
||||
#define INVN_ALGO_AGM_INPUT_MASK_ACC 1 ///< Raw Accel update mask
|
||||
#define INVN_ALGO_AGM_INPUT_MASK_GYR 2 ///< Raw Gyro update mask
|
||||
#define INVN_ALGO_AGM_INPUT_MASK_MAG 4 ///< Raw Mag update mask
|
||||
|
||||
#define INVN_ALGO_AGM_OUTPUT_MASK_ACCEL_CAL 1 ///< Accel cal output update mask
|
||||
#define INVN_ALGO_AGM_OUTPUT_MASK_GYRO_CAL 2 ///< Gyro cal output update mask
|
||||
#define INVN_ALGO_AGM_OUTPUT_MASK_MAG_CAL 4 ///< Mag cal output update mask
|
||||
#define INVN_ALGO_AGM_OUTPUT_MASK_QUAT_AG 8 ///< Game Rotation Vector (Accel and Gyro Fusion) output update mask
|
||||
#define INVN_ALGO_AGM_OUTPUT_MASK_QUAT_AGM 16 ///< Rotation Vector (Accel, Gyro and Magnetometer Fusion) output update mask
|
||||
#define INVN_ALGO_AGM_OUTPUT_MASK_GRAVITY 32 ///< Gravity vector output update mask
|
||||
#define INVN_ALGO_AGM_OUTPUT_MASK_LINEARACC 64 ///< Linear acceleration vector output update mask
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
struct inv_icm426xx;
|
||||
|
||||
/*! \struct InvnAlgoAGMInput
|
||||
* AGM input structure (raw data) \ingroup AGM
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int32_t mask; /*!< mask to specify updated inputs. */
|
||||
int64_t sRimu_time_us; /*!< timestamp \f$ [\mu s]\f$ of raw accel and gyro */
|
||||
int32_t sRacc_data[3]; /*!< raw accelerometer in high resolution mode. Expect Full Scale Value coded on 20 bit (i.e. +/- FSR g = 1<<19 LSB) */
|
||||
int32_t sRgyr_data[3]; /*!< raw gyroscope in high resolution mode. Expect Full Scale Value coded on 20 bit (i.e. +/- FSR dps = 1<<19 LSB) */
|
||||
int16_t sRtemp_data; /*!< raw temperature */
|
||||
int64_t sRmag_time_us; /*!< timestamp of raw mag */
|
||||
int32_t sRmag_data[3]; /*!< raw mag */
|
||||
} InvnAlgoAGMInput;
|
||||
|
||||
|
||||
/*! \struct InvnAlgoAGMOutput
|
||||
* AGM output structure (calibrated sensors and fusion output) \ingroup AGM
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int32_t mask; /*!< mask to specify updated outputs */
|
||||
int32_t acc_uncal_q16[3]; /*!< uncalibrated accelerometer (1 g = 1<<16) */
|
||||
int32_t acc_cal_q16[3]; /*!< calibrated accelerometer (1 g = 1<<16) */
|
||||
int32_t acc_bias_q16[3]; /*!< accelerometer bias (1 g = 1<<16)*/
|
||||
int8_t acc_accuracy_flag; /*!< accelerometer accuracy from 0(non calibrated) to 3(well calibrated) */
|
||||
|
||||
int32_t gyr_uncal_q16[3]; /*!< uncalibrated gyroscope (1 dps = 1<<16) */
|
||||
int32_t gyr_cal_q16[3]; /*!< calibrated gyroscope (1 dps = 1<<16) */
|
||||
int32_t gyr_bias_q16[3]; /*!< gyro bias (1 dps = 1<<16)*/
|
||||
int8_t gyr_accuracy_flag; /*!< gyro accuracy, from 0(non calibrated) to 3(well calibrated) */
|
||||
|
||||
int32_t mag_uncal_q16[3]; /*!< uncalibrated magnetometer (1uT = 1<<16) */
|
||||
int32_t mag_cal_q16[3]; /*!< calibrated magnetometer (1uT = 1<<16) */
|
||||
int32_t mag_bias_q16[3]; /*!< magnetometer bias (1uT = 1<<16) */
|
||||
int8_t mag_accuracy_flag; /*!< magnetometer accuracy, from 0(non calibrated) to 3(well calibrated) */
|
||||
|
||||
int32_t grv_quat_q30[4]; /*!< 6-axis (accel and gyro fusion) quaternion */
|
||||
int32_t rv_quat_q30[4]; /*!< 9-axis (accel, gyro and magnetometer fusion) quaternion */
|
||||
int32_t rv_accuracy_q27; /*!< 9-axis (accel, gyro and magnetometer fusion) 3\sigma accuracy in rad */
|
||||
int32_t gravity_q16[3]; /*!< gravity estimation in sensor frame */
|
||||
int32_t linear_acc_q16[3]; /*!< linear acceleration estimation in sensor frame */
|
||||
|
||||
int32_t temp_degC_q16; /*!< temperature (1 \f$ [^{\circ}C]\f$ = 1<<16)*/
|
||||
} InvnAlgoAGMOutput;
|
||||
|
||||
|
||||
/*! \struct InvnAlgoAGMConfig
|
||||
* AGM configuration structure (sensor related settings) \ingroup AGM
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int32_t * acc_bias_q16; /*!< Previously stored accel bias pointer. If pointer is NULL or 0, offset will be set to { 0, 0, 0} */
|
||||
int32_t * gyr_bias_q16; /*!< Previously stored gyro bias pointer. If pointer is NULL or 0, offset will be set to { 0, 0, 0} */
|
||||
int32_t * mag_bias_q16; /*!< mag_bias_q16 Previously stored mag bias pointer If pointer is NULL or 0, offset will be set to { 0, 0, 0} */
|
||||
int8_t acc_accuracy; /*!< Previously stored accelerometer bias accuracy (0 to 3) */
|
||||
int8_t gyr_accuracy; /*!< Previously stored gyroscope bias accuracy (0 to 3) */
|
||||
int8_t mag_accuracy; /*!< Previously stored magnetometer bias accuracy (0 to 3) */
|
||||
|
||||
int32_t acc_fsr; /*!< accelerometer full scale range [g] */
|
||||
int32_t gyr_fsr; /*!< gyroscope full scale range [dps] */
|
||||
|
||||
uint32_t acc_odr_us; /*!< accelerometer output data rate in \f$ [\mu s]\f$ */
|
||||
uint32_t gyr_odr_us; /*!< gyroscope output data rate \f$ [\mu s]\f$ */
|
||||
|
||||
int32_t mag_sc_q16; /*!< magnetometer sensitivity (uT/LSB, e.g. mag_uT = (mag_sc_q16 * raw_mag_LSB)/65536) */
|
||||
uint32_t mag_odr_us; /*!< magnetometer output data rate \f$ [\mu s]\f$ */
|
||||
|
||||
int32_t temp_sensitivity; /*!< temperature sensitivity in q30 (if temperature(\f$ ^{\circ}C \f$) = LSB * k + z, then temp_sensitivity = k) */
|
||||
int32_t temp_offset; /*!< temperature offset in q16 (if temperature(\f$ ^{\circ}C \f$) = LSB * k + z, then temp_offset = z) */
|
||||
} InvnAlgoAGMConfig;
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Return library version x.y.z-suffix as a char array
|
||||
* \retval library version a char array "x.y.z-suffix"
|
||||
* \ingroup AGM
|
||||
*/
|
||||
const char * invn_algo_agm_version(void);
|
||||
|
||||
/*!
|
||||
* \brief Initializes algorithms with default parameters and reset states.
|
||||
* (\icm_device[in] Invensense ICM426XX device pointer. Only when gyro assisted is enabled.)
|
||||
* \config[in] algo init parameters structure.
|
||||
* \return initialization success indicator.
|
||||
* \retval 0 Success
|
||||
* \retval 1 Fail
|
||||
* \ingroup AGM
|
||||
*/
|
||||
#ifdef WITH_GYRO_ASSIST
|
||||
uint8_t invn_algo_agm_init_a(struct inv_icm426xx * icm_device, const InvnAlgoAGMConfig * config);
|
||||
#else
|
||||
uint8_t invn_algo_agm_init(const InvnAlgoAGMConfig * config);
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief Sets algo config structure.
|
||||
* \config[in] config structure of the algo.
|
||||
* \ingroup AGM
|
||||
*/
|
||||
void invn_algo_agm_set_config(const InvnAlgoAGMConfig * config);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Performs algorithm computation.
|
||||
* \in inputs algorithm input. Input mask (inputs->mask) should be set with respect to new sensor data in InvnAlgoAGMInput.
|
||||
* \out outputs algorithm output. Output mask (outputs->mask) reports updated outputs in InvnAlgoAGMOutput.
|
||||
* \ingroup AGM
|
||||
*/
|
||||
void invn_algo_agm_process(const InvnAlgoAGMInput *inputs, InvnAlgoAGMOutput *outputs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,324 @@
|
||||
/*******************************************************************************
|
||||
* @file app_raw.c
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#include "sdk_config.h"
|
||||
#include "app_raw.h"
|
||||
#include "inv_imu_extfunc.h"
|
||||
#include "inv_imu_driver.h"
|
||||
#include "ble_nus.h"
|
||||
#include "nrf_log.h"
|
||||
#include "nrf_log_ctrl.h"
|
||||
#include "nrf_log_default_backends.h"
|
||||
|
||||
#include "app_util_platform.h"
|
||||
#include "main.h"
|
||||
#include "meas_pd_48.h"
|
||||
#include <cmd_parse.h>
|
||||
|
||||
|
||||
/*
|
||||
* Print raw data or scaled data
|
||||
* 0 : print raw accel, gyro and temp data
|
||||
* 1 : print scaled accel, gyro and temp data in g, dps and degree Celsius
|
||||
*/
|
||||
#define SCALED_DATA_G_DPS 0
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------------------
|
||||
* Static and extern variables
|
||||
* -------------------------------------------------------------------------------------- */
|
||||
|
||||
/* Just a handy variable to handle the IMU object */
|
||||
static struct inv_imu_device icm_driver;
|
||||
uint8_t imu_bin_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
/*
|
||||
* ICM mounting matrix
|
||||
* Coefficients are coded as Q30 integer
|
||||
*/
|
||||
#if (SM_BOARD_REV == SM_REVB_DB) /* when DB or EVB are used */
|
||||
static int32_t icm_mounting_matrix[9] = { 0, -(1<<30), 0,
|
||||
(1<<30), 0, 0,
|
||||
0, 0, (1<<30) };
|
||||
#else /* For SmartMotion */
|
||||
static int32_t icm_mounting_matrix[9] = {(1<<30), 0, 0,
|
||||
0, (1<<30), 0,
|
||||
0, 0, (1<<30)};
|
||||
#endif
|
||||
extern bool custom_add_data;
|
||||
extern bool motion_raw_data_enabled;
|
||||
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
extern bool go_pdread;
|
||||
extern which_cmd_t cmd_type_t;
|
||||
uint16_t ssp_data[6]={0,};
|
||||
extern bool info4; //cmd_parse
|
||||
extern volatile uint16_t info_imu[6];
|
||||
|
||||
/* --------------------------------------------------------------------------------------
|
||||
* static function declaration
|
||||
* -------------------------------------------------------------------------------------- */
|
||||
static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3]);
|
||||
|
||||
/* --------------------------------------------------------------------------------------
|
||||
* Functions definition
|
||||
* -------------------------------------------------------------------------------------- */
|
||||
|
||||
int setup_imu_device(struct inv_imu_serif *icm_serif)
|
||||
{
|
||||
int rc = 0;
|
||||
uint8_t who_am_i;
|
||||
|
||||
/* Init device */
|
||||
rc = inv_imu_init(&icm_driver, icm_serif, imu_callback);
|
||||
if (rc != INV_ERROR_SUCCESS) {
|
||||
printf("!!! ERROR : Failed to initialize IMU!\r\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Check WHOAMI */
|
||||
rc = inv_imu_get_who_am_i(&icm_driver, &who_am_i);
|
||||
if (rc != INV_ERROR_SUCCESS) {
|
||||
printf("!!! ERROR : Failed to read whoami!\r\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (who_am_i != ICM_WHOAMI) {
|
||||
printf("!!! ERROR : Bad WHOAMI value! Read 0x%02x, expected 0x%02x\r\n", who_am_i, ICM_WHOAMI);
|
||||
return INV_ERROR;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int configure_imu_device(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!USE_FIFO)
|
||||
rc |= inv_imu_configure_fifo(&icm_driver, INV_IMU_FIFO_DISABLED);
|
||||
|
||||
if (USE_HIGH_RES_MODE) {
|
||||
rc |= inv_imu_enable_high_resolution_fifo(&icm_driver);
|
||||
} else {
|
||||
rc |= inv_imu_set_accel_fsr(&icm_driver, ACCEL_CONFIG0_FS_SEL_4g);
|
||||
rc |= inv_imu_set_gyro_fsr(&icm_driver, GYRO_CONFIG0_FS_SEL_2000dps);
|
||||
}
|
||||
|
||||
if (USE_LOW_NOISE_MODE) {
|
||||
rc |= inv_imu_set_accel_frequency(&icm_driver, ACCEL_CONFIG0_ODR_800_HZ);
|
||||
rc |= inv_imu_set_gyro_frequency(&icm_driver, GYRO_CONFIG0_ODR_800_HZ);
|
||||
rc |= inv_imu_enable_accel_low_noise_mode(&icm_driver);
|
||||
} else {
|
||||
rc |= inv_imu_set_accel_frequency(&icm_driver, ACCEL_CONFIG0_ODR_100_HZ);
|
||||
rc |= inv_imu_set_gyro_frequency(&icm_driver, GYRO_CONFIG0_ODR_100_HZ);
|
||||
rc |= inv_imu_enable_accel_low_power_mode(&icm_driver);
|
||||
}
|
||||
|
||||
rc |= inv_imu_enable_gyro_low_noise_mode(&icm_driver);
|
||||
|
||||
if (!USE_FIFO)
|
||||
inv_imu_sleep_us(GYR_STARTUP_TIME_US);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int get_imu_data(void)
|
||||
{
|
||||
#if USE_FIFO
|
||||
return inv_imu_get_data_from_fifo(&icm_driver);
|
||||
#else
|
||||
|
||||
return inv_imu_get_data_from_registers(&icm_driver);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SCALED_DATA_G_DPS
|
||||
static void get_accel_and_gyr_fsr(int16_t * accel_fsr_g, int16_t * gyro_fsr_dps)
|
||||
{
|
||||
ACCEL_CONFIG0_FS_SEL_t accel_fsr_bitfield;
|
||||
GYRO_CONFIG0_FS_SEL_t gyro_fsr_bitfield;
|
||||
|
||||
inv_imu_get_accel_fsr(&icm_driver, &accel_fsr_bitfield);
|
||||
switch(accel_fsr_bitfield) {
|
||||
case ACCEL_CONFIG0_FS_SEL_2g: *accel_fsr_g = 2;
|
||||
break;
|
||||
case ACCEL_CONFIG0_FS_SEL_4g: *accel_fsr_g = 4;
|
||||
break;
|
||||
case ACCEL_CONFIG0_FS_SEL_8g: *accel_fsr_g = 8;
|
||||
break;
|
||||
case ACCEL_CONFIG0_FS_SEL_16g: *accel_fsr_g = 16;
|
||||
break;
|
||||
default: *accel_fsr_g = -1;
|
||||
}
|
||||
|
||||
inv_imu_get_gyro_fsr(&icm_driver, &gyro_fsr_bitfield);
|
||||
switch(gyro_fsr_bitfield) {
|
||||
case GYRO_CONFIG0_FS_SEL_250dps: *gyro_fsr_dps = 250;
|
||||
break;
|
||||
case GYRO_CONFIG0_FS_SEL_500dps: *gyro_fsr_dps = 500;
|
||||
break;
|
||||
case GYRO_CONFIG0_FS_SEL_1000dps: *gyro_fsr_dps = 1000;
|
||||
break;
|
||||
case GYRO_CONFIG0_FS_SEL_2000dps: *gyro_fsr_dps = 2000;
|
||||
break;
|
||||
default: *gyro_fsr_dps = -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void imu_callback(inv_imu_sensor_event_t *event)
|
||||
{
|
||||
int32_t accel[3], gyro[3];
|
||||
|
||||
#if SCALED_DATA_G_DPS
|
||||
float accel_g[3];
|
||||
float gyro_dps[3];
|
||||
float temp_degc;
|
||||
int16_t accel_fsr_g, gyro_fsr_dps;
|
||||
#endif
|
||||
|
||||
#if USE_FIFO
|
||||
static uint64_t last_fifo_timestamp = 0;
|
||||
static uint32_t rollover_num = 0;
|
||||
|
||||
// Handle rollover
|
||||
if (last_fifo_timestamp > event->timestamp_fsync)
|
||||
rollover_num++;
|
||||
last_fifo_timestamp = event->timestamp_fsync;
|
||||
|
||||
// Compute timestamp in us
|
||||
timestamp = event->timestamp_fsync + rollover_num * UINT16_MAX;
|
||||
timestamp *= inv_imu_get_fifo_timestamp_resolution_us_q24(&icm_driver);
|
||||
timestamp /= (1UL << 24);
|
||||
|
||||
if (icm_driver.fifo_highres_enabled) {
|
||||
accel[0] = (((int32_t)event->accel[0] << 4)) | event->accel_high_res[0];
|
||||
accel[1] = (((int32_t)event->accel[1] << 4)) | event->accel_high_res[1];
|
||||
accel[2] = (((int32_t)event->accel[2] << 4)) | event->accel_high_res[2];
|
||||
|
||||
gyro[0] = (((int32_t)event->gyro[0] << 4)) | event->gyro_high_res[0];
|
||||
gyro[1] = (((int32_t)event->gyro[1] << 4)) | event->gyro_high_res[1];
|
||||
gyro[2] = (((int32_t)event->gyro[2] << 4)) | event->gyro_high_res[2];
|
||||
|
||||
} else {
|
||||
accel[0] = event->accel[0];
|
||||
accel[1] = event->accel[1];
|
||||
accel[2] = event->accel[2];
|
||||
|
||||
gyro[0] = event->gyro[0];
|
||||
gyro[1] = event->gyro[1];
|
||||
gyro[2] = event->gyro[2];
|
||||
}
|
||||
#else
|
||||
|
||||
accel[0] = event->accel[0];
|
||||
accel[1] = event->accel[1];
|
||||
accel[2] = event->accel[2];
|
||||
|
||||
gyro[0] = event->gyro[0];
|
||||
gyro[1] = event->gyro[1];
|
||||
gyro[2] = event->gyro[2];
|
||||
|
||||
// Force sensor_mask so it gets displayed below
|
||||
event->sensor_mask |= (1 << INV_SENSOR_TEMPERATURE);
|
||||
event->sensor_mask |= (1 << INV_SENSOR_ACCEL);
|
||||
event->sensor_mask |= (1 << INV_SENSOR_GYRO);
|
||||
#endif
|
||||
|
||||
apply_mounting_matrix(icm_mounting_matrix, accel);
|
||||
apply_mounting_matrix(icm_mounting_matrix, gyro);
|
||||
|
||||
#if SCALED_DATA_G_DPS
|
||||
/*
|
||||
* Convert raw data into scaled data in g and dps
|
||||
*/
|
||||
get_accel_and_gyr_fsr(&accel_fsr_g, &gyro_fsr_dps);
|
||||
accel_g[0] = (float)(accel[0] * accel_fsr_g) / INT16_MAX;
|
||||
accel_g[1] = (float)(accel[1] * accel_fsr_g) / INT16_MAX;
|
||||
accel_g[2] = (float)(accel[2] * accel_fsr_g) / INT16_MAX;
|
||||
gyro_dps[0] = (float)(gyro[0] * gyro_fsr_dps) / INT16_MAX;
|
||||
gyro_dps[1] = (float)(gyro[1] * gyro_fsr_dps) / INT16_MAX;
|
||||
gyro_dps[2] = (float)(gyro[2] * gyro_fsr_dps) / INT16_MAX;
|
||||
if (USE_HIGH_RES_MODE || !USE_FIFO)
|
||||
temp_degc = 25 + ((float)event->temperature / 128);
|
||||
else
|
||||
temp_degc = 25 + ((float)event->temperature / 2);
|
||||
|
||||
/*
|
||||
* Output scaled data on UART link
|
||||
*/
|
||||
if (event->sensor_mask & (1 << INV_SENSOR_ACCEL) && event->sensor_mask & (1 << INV_SENSOR_GYRO))
|
||||
printf("%u: %.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f\r\n",
|
||||
(uint32_t)timestamp,
|
||||
accel_g[0], accel_g[1], accel_g[2],
|
||||
temp_degc,
|
||||
gyro_dps[0], gyro_dps[1], gyro_dps[2]);
|
||||
#else
|
||||
|
||||
/*
|
||||
* Output raw data on UART link
|
||||
*/
|
||||
if (event->sensor_mask & (1 << INV_SENSOR_ACCEL) && event->sensor_mask & (1 << INV_SENSOR_GYRO) || motion_raw_data_enabled)
|
||||
{
|
||||
motion_raw_data_enabled = false;
|
||||
|
||||
if (info4 == true)
|
||||
{
|
||||
info_imu[0] = (uint16_t)accel[0];
|
||||
info_imu[1] = (uint16_t)accel[1];
|
||||
info_imu[2] = (uint16_t) accel[2];
|
||||
info_imu[3] = (uint16_t)gyro[0];
|
||||
info_imu[4] = (uint16_t) gyro[1];
|
||||
info_imu[5] = (uint16_t)gyro[2];
|
||||
go_pdread = true;
|
||||
}
|
||||
|
||||
else if(cmd_type_t == CMD_UART) {
|
||||
printf("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
|
||||
} else if(cmd_type_t == CMD_BLE) {
|
||||
//sprintf(ble_tx_buffer, "Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
|
||||
ssp_data[0] = (uint16_t)accel[0];
|
||||
ssp_data[1] = (uint16_t)accel[1];
|
||||
ssp_data[2] = (uint16_t)accel[2];
|
||||
ssp_data[3] = (uint16_t)gyro[0];
|
||||
ssp_data[4] = (uint16_t)gyro[1];
|
||||
ssp_data[5] = (uint16_t)gyro[2];
|
||||
format_data(imu_bin_buffer, "rsp:", ssp_data,12);
|
||||
printf("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
|
||||
binary_tx_handler(imu_bin_buffer,8);
|
||||
if(custom_add_data==true)
|
||||
{
|
||||
custom_add_data = false;
|
||||
}
|
||||
else{
|
||||
//data_tx_handler(ble_tx_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------
|
||||
* Static functions definition
|
||||
* -------------------------------------------------------------------------------------- */
|
||||
|
||||
static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3])
|
||||
{
|
||||
unsigned i;
|
||||
int64_t data_q30[3];
|
||||
|
||||
for(i = 0; i < 3; i++) {
|
||||
data_q30[i] = ((int64_t)matrix[3*i+0] * raw[0]);
|
||||
data_q30[i] += ((int64_t)matrix[3*i+1] * raw[1]);
|
||||
data_q30[i] += ((int64_t)matrix[3*i+2] * raw[2]);
|
||||
}
|
||||
raw[0] = (int32_t)(data_q30[0]>>30);
|
||||
raw[1] = (int32_t)(data_q30[1]>>30);
|
||||
raw[2] = (int32_t)(data_q30[2]>>30);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*******************************************************************************
|
||||
* @file app_raw.h
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _APP_RAW_H_
|
||||
#define _APP_RAW_H_
|
||||
#include "sdk_config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include "inv_imu_transport.h"
|
||||
#include "inv_imu_defs.h"
|
||||
#include "inv_imu_driver.h"
|
||||
|
||||
|
||||
/*** Example configuration ***/
|
||||
|
||||
/*
|
||||
* Select communication link between SmartMotion and IMU
|
||||
*/
|
||||
#define SERIF_TYPE UI_I2C
|
||||
|
||||
/*
|
||||
* Set power mode flag
|
||||
* Set this flag to run example in low-noise mode.
|
||||
* Reset this flag to run example in low-power mode.
|
||||
* Note: low-noise mode is not available with sensor data frequencies less than 12.5Hz.
|
||||
*/
|
||||
#define USE_LOW_NOISE_MODE 1
|
||||
|
||||
/*
|
||||
* Select Fifo resolution Mode (default is low resolution mode)
|
||||
* Low resolution mode: 16 bits data format
|
||||
* High resolution mode: 20 bits data format
|
||||
* Warning: Enabling High Res mode will force FSR to 16g and 2000dps
|
||||
*/
|
||||
#define USE_HIGH_RES_MODE 0
|
||||
|
||||
/*
|
||||
* Select to use FIFO or to read data from registers
|
||||
*/
|
||||
#define USE_FIFO 0
|
||||
|
||||
|
||||
/**
|
||||
* \brief This function is in charge of reseting and initializing IMU device. It should
|
||||
* be successfully executed before any access to IMU device.
|
||||
*
|
||||
* \return 0 on success, negative value on error.
|
||||
*/
|
||||
int setup_imu_device(struct inv_imu_serif *icm_serif);
|
||||
|
||||
/**
|
||||
* \brief This function configures the device in order to output gyro and accelerometer.
|
||||
* \return 0 on success, negative value on error.
|
||||
*/
|
||||
int configure_imu_device(void);
|
||||
|
||||
/**
|
||||
* \brief This function extracts data from the IMU FIFO.
|
||||
* \return 0 on success, negative value on error.
|
||||
*/
|
||||
int get_imu_data(void);
|
||||
|
||||
/**
|
||||
* \brief This function is the custom handling packet function.
|
||||
* \param[in] event structure containing sensor data from one packet
|
||||
*/
|
||||
void imu_callback(inv_imu_sensor_event_t *event);
|
||||
|
||||
|
||||
#endif /* !_APP_RAW_H_ */
|
||||
@@ -0,0 +1,189 @@
|
||||
/*******************************************************************************
|
||||
* @file app_raw_main.c
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
#include "sdk_config.h"
|
||||
#include "app_raw.h"
|
||||
#include "app_raw_main.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "inv_imu_driver.h"
|
||||
|
||||
#include "system_interface.h"
|
||||
|
||||
/* std */
|
||||
#include <stdio.h>
|
||||
#include "nrf.h"
|
||||
#include "app_error.h"
|
||||
#include "boards.h"
|
||||
#include "nrfx_gpiote.h"
|
||||
#include "nrf_delay.h"
|
||||
|
||||
#include "app_util_platform.h"
|
||||
#include "meas_pd_48.h"
|
||||
#include <cmd_parse.h>
|
||||
#include "i2c_manager.h"
|
||||
/* --------------------------------------------------------------------------------------
|
||||
* Global variables
|
||||
* -------------------------------------------------------------------------------------- */
|
||||
|
||||
/* --------------------------------------------------------------------------------------
|
||||
* Static variables
|
||||
* -------------------------------------------------------------------------------------- */
|
||||
|
||||
/* Flag set from IMU device irq handler */
|
||||
static volatile int irq_from_device;
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------------------
|
||||
* Forward declaration
|
||||
* -------------------------------------------------------------------------------------- */
|
||||
static int setup_mcu(struct inv_imu_serif *icm_serif);
|
||||
|
||||
/*!
|
||||
* @brief Sensor general interrupt handler, calls specific handlers.
|
||||
*
|
||||
* This function is called when an external interrupt is triggered by the sensor,
|
||||
* checks interrupt registers of InvenSense Sensor to determine the source and type of interrupt
|
||||
* and calls the specific interrupt handler accordingly.
|
||||
*
|
||||
* @param[in] NULL
|
||||
*
|
||||
* @param[out] NULL
|
||||
*
|
||||
* @return NULL
|
||||
*
|
||||
*/
|
||||
static void inv_gpio_sensor_interrupt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
|
||||
{
|
||||
irq_from_device = 1;
|
||||
}
|
||||
|
||||
|
||||
void inv_gpio_sensor_irq_init(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
/* Initialize int pin */
|
||||
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_HITOLO(true);
|
||||
in_config.pull = NRF_GPIO_PIN_PULLUP;
|
||||
|
||||
err_code = nrfx_gpiote_in_init(ICM42670_INT1_PIN, &in_config, inv_gpio_sensor_interrupt_handler);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
nrfx_gpiote_in_event_enable(ICM42670_INT1_PIN, true);
|
||||
}
|
||||
|
||||
|
||||
void inv_gpio_sensor_irq_uninit(void)
|
||||
{
|
||||
|
||||
nrfx_gpiote_in_event_disable(ICM42670_INT1_PIN);
|
||||
|
||||
nrfx_gpiote_in_uninit(ICM42670_INT1_PIN);
|
||||
|
||||
/* Initialize int pin */
|
||||
if (nrfx_gpiote_is_init())
|
||||
{
|
||||
nrfx_gpiote_uninit();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------
|
||||
* Main
|
||||
* -------------------------------------------------------------------------------------- */
|
||||
int icm42670_init(void)
|
||||
{
|
||||
int rc = 0;
|
||||
struct inv_imu_serif icm_serif;
|
||||
|
||||
rc |= setup_mcu(&icm_serif);
|
||||
rc |= setup_imu_device(&icm_serif);
|
||||
rc |= configure_imu_device();
|
||||
|
||||
if(rc != 0){
|
||||
printf("!!!error during initialization\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
inv_gpio_sensor_irq_init();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void icm42670_main(void)
|
||||
{
|
||||
int rc = 0;
|
||||
hw_i2c_init_once();
|
||||
/* Poll device for data */
|
||||
|
||||
if (irq_from_device) {
|
||||
rc = get_imu_data();
|
||||
|
||||
if(rc < 0) {
|
||||
printf("error while getting data\r\n");
|
||||
}
|
||||
|
||||
irq_from_device = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------------------
|
||||
* Functions definitions
|
||||
* -------------------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* This function initializes MCU on which this software is running.
|
||||
* It configures:
|
||||
* - a UART link used to print some messages
|
||||
* - interrupt priority group and GPIO so that MCU can receive interrupts from IMU
|
||||
* - a microsecond timer requested by IMU driver to compute some delay
|
||||
* - a microsecond timer used to get some timestamps
|
||||
* - a serial link to communicate from MCU to IMU
|
||||
*/
|
||||
static int setup_mcu(struct inv_imu_serif *icm_serif)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/* Initialize serial interface between MCU and IMU */
|
||||
icm_serif->context = 0; /* no need */
|
||||
icm_serif->read_reg = inv_io_hal_read_reg;
|
||||
icm_serif->write_reg = inv_io_hal_write_reg;
|
||||
icm_serif->max_read = 1024*32; /* maximum number of bytes allowed per serial read */
|
||||
icm_serif->max_write = 1024*32; /* maximum number of bytes allowed per serial write */
|
||||
icm_serif->serif_type = SERIF_TYPE;
|
||||
rc |= inv_io_hal_init(icm_serif);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------------------
|
||||
* Extern functions definition
|
||||
* -------------------------------------------------------------------------------------- */
|
||||
|
||||
/* Sleep implementation */
|
||||
void inv_imu_sleep_us(uint32_t us)
|
||||
{
|
||||
nrf_delay_us(us);
|
||||
}
|
||||
|
||||
|
||||
/* Get time implementation */
|
||||
uint64_t inv_imu_get_time_us(void)
|
||||
{
|
||||
return NRF_RTC1->COUNTER;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*******************************************************************************
|
||||
* @file app_raw_main.h
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _APP_RAW_MAIN_H_
|
||||
#define _APP_RAW_MAIN_H_
|
||||
#include "sdk_config.h"
|
||||
|
||||
int icm42670_init(void);
|
||||
void icm42670_main(void);
|
||||
int icm42670_uninit(void);
|
||||
|
||||
#endif /* !_APP_RAW_MAIN_H_ */
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
/*******************************************************************************
|
||||
* @file system_interface.c
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
/* board driver */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nrf.h"
|
||||
#include "app_error.h"
|
||||
#include "boards.h"
|
||||
#include "nrfx_gpiote.h"
|
||||
#include "nrfx_twi.h"
|
||||
|
||||
#include "system_interface.h"
|
||||
#include "nrf_delay.h"
|
||||
#include "meas_pd_48.h"
|
||||
|
||||
/* I2C number and slave address for INV device */
|
||||
#define ICM_I2C_ADDR 0x68
|
||||
#define INV_MAX_SERIAL_WRITE 16
|
||||
|
||||
/* TWI instance. */
|
||||
const nrfx_twi_t m_twi_icm42670 = NRFX_TWI_INSTANCE(ICM42670_I2C_INSTANCE);
|
||||
|
||||
void inv_i2c_master_uninitialize(void){
|
||||
nrfx_twi_disable(&m_twi_icm42670);
|
||||
nrfx_twi_uninit(&m_twi_icm42670);
|
||||
}
|
||||
|
||||
static void inv_i2c_master_initialize(void){
|
||||
ret_code_t err_code;
|
||||
|
||||
const nrfx_twi_config_t twi_icm42670_config = {
|
||||
.scl = ICM42670_I2C_SCL_PIN,
|
||||
.sda = ICM42670_I2C_SDA_PIN,
|
||||
.frequency = NRF_TWI_FREQ_100K,
|
||||
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
|
||||
};
|
||||
|
||||
err_code = nrfx_twi_init(&m_twi_icm42670, &twi_icm42670_config, NULL, NULL);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
nrfx_twi_enable(&m_twi_icm42670);
|
||||
}
|
||||
|
||||
|
||||
uint32_t icm42670_twi_tx( uint8_t device_id,
|
||||
uint8_t const * p_data,
|
||||
uint8_t length,
|
||||
bool no_stop)
|
||||
{
|
||||
ret_code_t ret;
|
||||
ret = nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
uint32_t icm42670_twi_rx( uint8_t device_id,
|
||||
uint8_t * p_data,
|
||||
uint8_t length)
|
||||
{
|
||||
ret_code_t ret;
|
||||
ret = nrfx_twi_rx(&m_twi_icm42670, device_id, p_data, length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static unsigned long inv_i2c_master_read_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, unsigned char *RegisterValue){
|
||||
//ret_code_t ret;
|
||||
uint32_t ret;
|
||||
uint8_t addr8 = (uint8_t)RegisterAddr;
|
||||
|
||||
ret = icm42670_twi_tx(Address, &addr8, 1, true);
|
||||
if(ret != NRF_SUCCESS) {
|
||||
ret = icm42670_twi_tx(Address, &addr8, 1, true);
|
||||
if(ret != NRF_SUCCESS) {
|
||||
printf("ERR! i2c read-1\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
|
||||
if(ret != NRF_SUCCESS) {
|
||||
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
|
||||
if(ret != NRF_SUCCESS) {
|
||||
printf("ERR! i2c read-2\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned long inv_i2c_master_write_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, const unsigned char *RegisterValue){
|
||||
uint32_t ret;
|
||||
uint8_t buffer[1 + INV_MAX_SERIAL_WRITE]; /* Addr + data */
|
||||
|
||||
buffer[0] = (uint8_t)RegisterAddr;
|
||||
memcpy(buffer+1, RegisterValue, RegisterLen);
|
||||
ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false);
|
||||
if(ret != NRF_SUCCESS) {
|
||||
ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false);
|
||||
if(ret != NRF_SUCCESS) {
|
||||
printf("ERR! i2c write\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int inv_io_hal_init(struct inv_imu_serif *serif)
|
||||
{
|
||||
|
||||
switch (serif->serif_type) {
|
||||
case UI_SPI4:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case UI_I2C:
|
||||
inv_i2c_master_initialize();
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen)
|
||||
{
|
||||
switch (serif->serif_type) {
|
||||
case UI_SPI4:
|
||||
return 0;
|
||||
|
||||
case UI_I2C:
|
||||
return inv_i2c_master_read_register(ICM_I2C_ADDR, reg, rlen, rbuffer);
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen)
|
||||
{
|
||||
switch (serif->serif_type) {
|
||||
case UI_SPI4:
|
||||
return 0;
|
||||
|
||||
case UI_I2C:
|
||||
return inv_i2c_master_write_register(ICM_I2C_ADDR, reg, wlen, wbuffer);
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
uint8_t cat_read(uint8_t device_id, uint8_t address, uint8_t *data)
|
||||
{
|
||||
|
||||
uint8_t read_data = 0;
|
||||
char adata[8];
|
||||
ret_code_t err_code;
|
||||
//address = 1|(address<<1);
|
||||
address = (address & 0xFF);
|
||||
err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, &address, 1, true);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
// Handle error
|
||||
// return;
|
||||
}
|
||||
|
||||
err_code = nrfx_twi_rx(&m_twi_icm42670, device_id, data, 8);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
// Handle error
|
||||
return 0;
|
||||
}
|
||||
read_data = data[0];
|
||||
memcpy(adata,data,8);
|
||||
printf("Data %s . \r\n", adata);
|
||||
return read_data;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void cat_write(uint8_t device_id, uint8_t address, uint8_t *data){
|
||||
|
||||
uint8_t buffer[7]={0x00,0x00,0x00,0x00,0x00,0x00,0x00};
|
||||
|
||||
address = (address & 0xFF);
|
||||
|
||||
buffer[0] = (address);
|
||||
//buffer[1] =(data & 0xFF);
|
||||
memcpy(buffer+1,data,6);
|
||||
ret_code_t err_code;
|
||||
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, 0x00, 1, false);
|
||||
err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, buffer, 2, false);
|
||||
// err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 2, false);
|
||||
// nrfx_twi_rx(&m_twi_icm42670, device_id, p_data, length);
|
||||
// nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop);
|
||||
printf("Data %x %x %x %x. \r\n", buffer[0], buffer[1], buffer[2], buffer[3]);
|
||||
|
||||
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 6, false);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
|
||||
printf("TWI Error.");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*******************************************************************************
|
||||
* @file system_interface.h
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _SYSTEM_INTERFACE_H_
|
||||
#define _SYSTEM_INTERFACE_H_
|
||||
|
||||
#include "inv_imu_transport.h"
|
||||
#include <stdbool.h>
|
||||
/* TODO: Move that somewhere else */
|
||||
#ifndef TO_MASK
|
||||
#define TO_MASK(a) (1U << (unsigned)(a))
|
||||
#endif
|
||||
|
||||
|
||||
#define ICM42670_I2C_INSTANCE 0 /**< I2C instance index. */
|
||||
#define ICM42670_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15)
|
||||
#define ICM42670_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,14)
|
||||
#define ICM42670_INT1_PIN NRF_GPIO_PIN_MAP(1,13)
|
||||
#define ICM42670_INT2_PIN NRF_GPIO_PIN_MAP(0,26)
|
||||
uint32_t icm42670_twi_tx( uint8_t device_id,
|
||||
uint8_t const * p_data,
|
||||
uint8_t length,
|
||||
bool no_stop);
|
||||
uint32_t icm42670_twi_rx( uint8_t device_id,
|
||||
uint8_t * p_data,
|
||||
uint8_t length);
|
||||
uint8_t cat_read (uint8_t device_id, uint8_t address, uint8_t *data);
|
||||
void cat_write (uint8_t device_id, uint8_t address, uint8_t *data);
|
||||
void inv_i2c_master_uninitialize(void);
|
||||
int inv_io_hal_init(struct inv_imu_serif *serif);
|
||||
int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen);
|
||||
int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen);
|
||||
|
||||
#endif /* !_SYSTEM_INTERFACE_H_ */
|
||||
|
||||
57
project/ble_peripheral/ble_app_vivaMayo/imu_stub.c
Normal file
57
project/ble_peripheral/ble_app_vivaMayo/imu_stub.c
Normal file
@@ -0,0 +1,57 @@
|
||||
/*******************************************************************************
|
||||
* @file imu_stub.c
|
||||
* @brief Stub implementations for IMU and meas_config functions
|
||||
* @note TODO: Replace with real implementations
|
||||
******************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/*==============================================================================
|
||||
* IMU STUBS
|
||||
*============================================================================*/
|
||||
|
||||
volatile bool g_imu_active = false;
|
||||
|
||||
int imu_read_direct(void)
|
||||
{
|
||||
/* TODO: Implement direct I2C register read + BLE send */
|
||||
return -1;
|
||||
}
|
||||
|
||||
int imu_read_cached(void)
|
||||
{
|
||||
/* TODO: Implement cached memory read + BLE send */
|
||||
return -1;
|
||||
}
|
||||
|
||||
void imu_active_timer_start(void)
|
||||
{
|
||||
/* TODO: Start 1-sec IMU active streaming timer */
|
||||
}
|
||||
|
||||
void imu_active_timer_stop(void)
|
||||
{
|
||||
/* TODO: Stop 1-sec IMU active streaming timer */
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* MEASUREMENT CONFIG STUBS
|
||||
*============================================================================*/
|
||||
|
||||
static uint8_t s_freq_idx = 0;
|
||||
static uint8_t s_cycles = 5;
|
||||
static uint8_t s_avg_count = 1;
|
||||
|
||||
uint8_t meas_config_get_freq_idx(void) { return s_freq_idx; }
|
||||
uint8_t meas_config_get_cycles(void) { return s_cycles; }
|
||||
uint8_t meas_config_get_avg_count(void) { return s_avg_count; }
|
||||
|
||||
void meas_config_set_freq_idx(uint8_t v) { s_freq_idx = v; }
|
||||
void meas_config_set_cycles(uint8_t v) { s_cycles = v; }
|
||||
void meas_config_set_avg_count(uint8_t v) { s_avg_count = v; }
|
||||
|
||||
void meas_config_save(void)
|
||||
{
|
||||
/* TODO: Save measurement config to FDS */
|
||||
}
|
||||
23
project/ble_peripheral/ble_app_vivaMayo/imu_stub.h
Normal file
23
project/ble_peripheral/ble_app_vivaMayo/imu_stub.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef IMU_STUB_H
|
||||
#define IMU_STUB_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* IMU */
|
||||
extern volatile bool g_imu_active;
|
||||
int imu_read_direct(void);
|
||||
int imu_read_cached(void);
|
||||
void imu_active_timer_start(void);
|
||||
void imu_active_timer_stop(void);
|
||||
|
||||
/* Measurement Config */
|
||||
uint8_t meas_config_get_freq_idx(void);
|
||||
uint8_t meas_config_get_cycles(void);
|
||||
uint8_t meas_config_get_avg_count(void);
|
||||
void meas_config_set_freq_idx(uint8_t v);
|
||||
void meas_config_set_cycles(uint8_t v);
|
||||
void meas_config_set_avg_count(uint8_t v);
|
||||
void meas_config_save(void);
|
||||
|
||||
#endif
|
||||
168
project/ble_peripheral/ble_app_vivaMayo/ir_i2c.c
Normal file
168
project/ble_peripheral/ble_app_vivaMayo/ir_i2c.c
Normal file
@@ -0,0 +1,168 @@
|
||||
/*******************************************************************************
|
||||
* @file ir_i2c.c
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
/* board driver */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nrf.h"
|
||||
#include "app_error.h"
|
||||
#include "boards.h"
|
||||
#include "nrfx_gpiote.h"
|
||||
#include "nrfx_twi.h"
|
||||
#include "nrf_drv_twi.h"
|
||||
|
||||
#include "nrf_delay.h"
|
||||
#include "ir_i2c.h"
|
||||
#include "debug_print.h"
|
||||
|
||||
/* I2C number and slave address for DS39305272 */
|
||||
#define LED_1_I2C_ADDR 0x50
|
||||
#define AD5272_MAX_SERIAL_WRITE 16
|
||||
|
||||
int16_t read_from_DS3930 = 0;
|
||||
uint16_t data_160_to_write = 0;
|
||||
static volatile bool m_xfer_done = false;
|
||||
|
||||
/* TWI instance. */
|
||||
//const nrfx_twi_t m_twi_ir = NRFX_TWI_INSTANCE(IR_I2C_INSTANCE);
|
||||
const nrf_drv_twi_t m_twi_ir = NRF_DRV_TWI_INSTANCE(IR_I2C_INSTANCE);
|
||||
//void twi_handler(nrfx_twi_evt_t const * p_event, void * p_context)
|
||||
//{
|
||||
// m_xfer_done = true;
|
||||
//}
|
||||
//void ir_irq_init(void){
|
||||
// ret_code_t err_code;
|
||||
|
||||
// /* Initialize int pin */
|
||||
// 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);
|
||||
//}
|
||||
|
||||
|
||||
//void ir_irq_uninit(void){
|
||||
|
||||
// nrfx_gpiote_in_event_disable(ADA2200_SYNCO_PIN);
|
||||
// nrfx_gpiote_in_uninit(ADA2200_SYNCO_PIN);
|
||||
//}
|
||||
void ir_i2c_uninit(void){
|
||||
nrf_drv_twi_disable(&m_twi_ir);
|
||||
nrf_drv_twi_uninit(&m_twi_ir);
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
void ir_i2c_init(void){
|
||||
ret_code_t err_code;
|
||||
|
||||
const nrf_drv_twi_config_t twi_ir_config = {
|
||||
.scl = IR_I2C_SCL_PIN,
|
||||
.sda = IR_I2C_SDA_PIN,
|
||||
.frequency = NRF_DRV_TWI_FREQ_100K,
|
||||
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
|
||||
.clear_bus_init = false
|
||||
};
|
||||
|
||||
err_code = nrf_drv_twi_init(&m_twi_ir, &twi_ir_config, NULL, NULL);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
DBG_PRINTF("TWI ir_irq_init Error.");
|
||||
}
|
||||
|
||||
nrf_drv_twi_enable(&m_twi_ir);
|
||||
// ir_irq_init();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
uint8_t ir_command_read(uint8_t device_id, uint8_t address, uint8_t *data)
|
||||
{
|
||||
uint8_t read_data = 0;
|
||||
char adata[8];
|
||||
ret_code_t err_code;
|
||||
//address = 1|(address<<1);
|
||||
address = (address & 0xFF);
|
||||
err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, &address, 1, true);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
// Handle error
|
||||
// return;
|
||||
}
|
||||
|
||||
err_code = nrf_drv_twi_rx(&m_twi_ir, device_id, data, 8);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
// Handle error
|
||||
DBG_PRINTF("TWI read Error.");
|
||||
return 0;
|
||||
}
|
||||
read_data = data[0];
|
||||
memcpy(adata,data,8);
|
||||
// DBG_PRINTF("ir Data %x %x %x %x. \r\n", device_id, address, data[0], data[1]);
|
||||
//DBG_PRINTF("ir Data read %s . \r\n", adata);
|
||||
return read_data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ir_command_write(uint8_t device_id, uint8_t address, uint8_t data)
|
||||
{
|
||||
//uint16_t data_to_write = 0;
|
||||
|
||||
uint8_t buffer[7]={0x00,0x00,0x00,0x00,0x00,0x00,0x00};
|
||||
|
||||
address = (address & 0xFF);
|
||||
|
||||
//buffer[0] = 0x00;
|
||||
buffer[0] = (address);
|
||||
buffer[1] =(data & 0xFF);
|
||||
// buffer[2] = data1+1;
|
||||
// buffer[3] = data1+2;
|
||||
// buffer[4] = data1+3;
|
||||
// buffer[5] = data1+4;
|
||||
// buffer[6] = data1+5;
|
||||
//memcpy(&buffer[1], data, length );
|
||||
|
||||
|
||||
ret_code_t err_code;
|
||||
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, 0x00, 1, false);
|
||||
|
||||
err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 2, false);
|
||||
|
||||
|
||||
DBG_PRINTF("ir Write Data %x %x %x %x. \r\n", device_id, buffer[0], buffer[1], buffer[2]);
|
||||
|
||||
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 6, false);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
|
||||
DBG_PRINTF("TWI Error.");
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
113
project/ble_peripheral/ble_app_vivaMayo/ir_i2c.h
Normal file
113
project/ble_peripheral/ble_app_vivaMayo/ir_i2c.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*******************************************************************************
|
||||
* @file ir_i2c.h
|
||||
* @date 2024-07-17
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _IR_I2C_H_
|
||||
#define _IR_I2C_H_
|
||||
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nrf.h"
|
||||
#include "nrf_drv_gpiote.h"
|
||||
|
||||
#define IR_I2C_INSTANCE 1 /**< I2C instance index. */
|
||||
//#define IR_I2C_INSTANCE 1 /**< I2C instance index. */
|
||||
//#define IR_I2C_SDA_PIN NRF_GPIO_PIN_MAP(0,10)
|
||||
//#define IR_I2C_SCL_PIN NRF_GPIO_PIN_MAP(0,9)
|
||||
#define IR_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15)
|
||||
#define IR_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,14)
|
||||
/**
|
||||
* COMMAND CONSTANTS
|
||||
* Commands are 16-bit writes: bits 15:14 are 0s
|
||||
* Bits 13:10 are the command value below
|
||||
* Bits 9:0 are data for the command, but not all bits are used with all commands
|
||||
*/
|
||||
|
||||
// The NOP command is included for completeness. It is a valid I2C operation.
|
||||
#define AD5272_COMMAND_NOP 0x00 // Do nothing. Why you would want to do this I don't know
|
||||
|
||||
// write the 10 or 8 data bits to the RDAC wiper register (it must be unlocked first)
|
||||
#define AD5272_RDAC_WRITE 0x01
|
||||
|
||||
#define AD5272_RDAC_READ 0x02 // read the RDAC wiper register
|
||||
|
||||
#define AD5272_50TP_WRITE 0x03 // store RDAC setting to 50-TP
|
||||
|
||||
// SW reset: refresh RDAC with last 50-TP stored value
|
||||
// If not 50-TP value, reset to 50% I think???
|
||||
// data bits are all dont cares
|
||||
#define AD5272_RDAC_REFRESH 0x04 // TODO refactor this to AD5272_SOFT_RESET
|
||||
|
||||
// read contents of 50-TP in next frame, at location in data bits 5:0,
|
||||
// see Table 16 page 22 Rev D datasheet
|
||||
// location 0x0 is reserved, 0x01 is first programmed wiper location, 0x32 is 50th programmed wiper location
|
||||
#define AD5272_50TP_WIPER_READ 0x05
|
||||
|
||||
/**
|
||||
* Read contents of last-programmed 50-TP location
|
||||
* This is the location used in SW Reset command 4 or on POR
|
||||
*/
|
||||
#define AD5272_50TP_LAST_USED 0x06
|
||||
|
||||
#define AD5272_CONTROL_WRITE 0x07 // data bits 2:0 are the control bits
|
||||
|
||||
#define AD5272_CONTROL_READ 0x08 // data bits all dont cares
|
||||
|
||||
#define AD5272_SHUTDOWN 0x09 // data bit 0 set = shutdown, cleared = normal mode
|
||||
|
||||
|
||||
/**
|
||||
* Control bits are three bits written with command 7
|
||||
*/
|
||||
// enable writing to the 50-TP memory by setting this control bit C0
|
||||
// default is cleared so 50-TP writing is disabled
|
||||
// only 50 total writes are possible!
|
||||
#define AD5272_50TP_WRITE_ENABLE 0x01
|
||||
|
||||
// enable writing to volatile RADC wiper by setting this control bit C1
|
||||
// otherwise it is frozen to the value in the 50-TP memory
|
||||
// default is cleared, can't write to the wiper
|
||||
#define AD5272_RDAC_WIPER_WRITE_ENABLE 0x02
|
||||
|
||||
// enable high precision calibration by clearing this control bit C2
|
||||
// set this bit to disable high accuracy mode (dunno why you would want to)
|
||||
// default is 0 = emabled
|
||||
#define AD5272_RDAC_CALIB_DISABLE 0x04
|
||||
|
||||
// 50TP memory has been successfully programmed if this bit is set
|
||||
#define AD5272_50TP_WRITE_SUCCESS 0x08
|
||||
|
||||
#define AD5272_NORMAL_MODE 0x01
|
||||
#define AD5272_SHUTDOWN_MODE 0x01
|
||||
#ifndef ADA2200_SYNCO_PIN
|
||||
#define ADA2200_SYNCO_PIN NRF_GPIO_PIN_MAP(0,17)
|
||||
#endif
|
||||
void ir_i2c_uninit(void);
|
||||
void ir_i2c_init(void);
|
||||
|
||||
void ir_irq_init(void);
|
||||
void ir_irq_uninit(void);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
uint8_t ir_command_read(uint8_t device_id, uint8_t address, uint8_t *data);
|
||||
void ir_command_write(uint8_t device_id, uint8_t address, uint8_t data);
|
||||
void ad5272_i2c_is_busy(void);
|
||||
int8_t ad5272_write_and_read_rdac (uint16_t data_16_to_write);
|
||||
int16_t ad5272_read_rdac (void);
|
||||
int8_t ad5272_write_rdac (uint16_t data_16_to_write);
|
||||
|
||||
void read_all_50tp (void);
|
||||
void ad5272_RDAC_refresh(void);
|
||||
void ad5272_shutdown_mode(void);
|
||||
void ad5272_normal_mode(void);
|
||||
|
||||
#endif /* !_ADA5272_I2C_H_ */
|
||||
|
||||
407
project/ble_peripheral/ble_app_vivaMayo/main.c
Normal file
407
project/ble_peripheral/ble_app_vivaMayo/main.c
Normal file
@@ -0,0 +1,407 @@
|
||||
/*******************************************************************************
|
||||
* @file main.c
|
||||
* @brief MEDiThings VivaMayo - Main Application Entry Point
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @date 2025-01-30
|
||||
* @copyright (c) 2025 Medithings Inc. All rights reserved.
|
||||
*
|
||||
* @version 1.17
|
||||
* @note 2025-01-30 Refactored into modular structure (Charles KWON)
|
||||
* @note 2025-12-31 Added comprehensive function documentation (Charles KWON)
|
||||
* @note 2025-11-27 Firmware cleanup and refactoring (Charles KWON)
|
||||
******************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "debug_print.h"
|
||||
#include "app_uart.h"
|
||||
|
||||
/* Nordic SDK */
|
||||
#include "nordic_common.h"
|
||||
#include "nrf.h"
|
||||
#include "nrf_gpio.h"
|
||||
#include "nrf_sdh.h"
|
||||
#include "nrf_sdh_soc.h"
|
||||
#include "nrf_sdh_ble.h"
|
||||
#include "nrf_pwr_mgmt.h"
|
||||
#include "nrf_delay.h"
|
||||
#include "nrf_log.h"
|
||||
#include "nrf_log_ctrl.h"
|
||||
#include "nrf_log_default_backends.h"
|
||||
#include "app_timer.h"
|
||||
#include "app_error.h"
|
||||
#include "bsp.h"
|
||||
#include "bsp_btn_ble.h"
|
||||
#include "ble_nus.h"
|
||||
|
||||
/* Application Modules */
|
||||
#include "ble/ble_core.h"
|
||||
#include "ble/ble_services.h"
|
||||
#include "ble/ble_data_tx.h"
|
||||
#include "power/power_ctrl.h"
|
||||
#include "peripheral/uart_handler.h"
|
||||
#include "config/device_config.h"
|
||||
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
#include "ble/ble_security.h"
|
||||
#include "nrf_ble_lesc.h"
|
||||
#endif
|
||||
|
||||
/* Hardware and Measurement Modules */
|
||||
#include "main.h"
|
||||
#include "main_timer.h"
|
||||
#include "battery_saadc.h"
|
||||
#include "measurements.h"
|
||||
#include "full_agc.h"
|
||||
#include "meas_pd_voltage_simple.h"
|
||||
#include "meas_pd_voltage_half.h"
|
||||
/* #include "meas_pd_voltage_full.h" */ /* Moved to unuse/ - FEATURE_PRINTF=0, not used */
|
||||
#include "meas_pd_voltage_custom.h"
|
||||
#include "meas_pd_imm.h"
|
||||
#include "meas_pd_48.h"
|
||||
#include "power_control.h"
|
||||
#include "cat_interface.h"
|
||||
#include "fstorage.h"
|
||||
#include "drivers/w25q32/w25q32.h"
|
||||
|
||||
/* Crypto */
|
||||
#include "nrf_crypto.h"
|
||||
|
||||
/*******************************************************************************
|
||||
* @section BUILD_CONFIG Build Configuration
|
||||
* @brief Compile-time feature flags and debug settings
|
||||
* @details Controls boot mode selection between minimal (Power+BLE only) and
|
||||
* full initialization (includes EEPROM and all peripherals).
|
||||
|
||||
* Set DEBUG_MINIMAL_BOOT=1 for rapid debugging without EEPROM dependency.
|
||||
******************************************************************************/
|
||||
|
||||
#define DEBUG_MINIMAL_BOOT 1 /**< 1: Power+BLE only (no EEPROM), 0: Full boot */
|
||||
|
||||
/*******************************************************************************
|
||||
* @section CONSTANTS Symbolic Constants
|
||||
* @brief Application-wide constant definitions
|
||||
* @details Magic numbers and compile-time constants used for error handling,
|
||||
* fault detection, and system-level operations.
|
||||
******************************************************************************/
|
||||
|
||||
#define DEAD_BEEF 0xDEADBEEF
|
||||
|
||||
/*******************************************************************************
|
||||
* @section GLOBALS Global Variables
|
||||
* @brief Shared runtime state and data buffers
|
||||
* @details BLE transmission buffers, command type state, and cross-module
|
||||
* flags. External declarations reference state from other modules.
|
||||
* @warning Global state should be accessed with care in interrupt context.
|
||||
******************************************************************************/
|
||||
|
||||
char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
uint16_t ble_bin_buff[BLE_NUS_MAX_DATA_LEN/2];
|
||||
which_cmd_t cmd_type_t;
|
||||
|
||||
/* External state flags from other modules */
|
||||
extern bool ble_got_new_data;
|
||||
extern bool motion_data_once;
|
||||
extern bool adc_enabled;
|
||||
extern bool con_single;
|
||||
extern bool info4;
|
||||
extern uint8_t add_cycle;
|
||||
extern bool motion_raw_data_enabled;
|
||||
|
||||
/*******************************************************************************
|
||||
* @section FWD_DECL Forward Declarations
|
||||
* @brief Static function prototypes
|
||||
* @details Internal initialization and handler functions declared here for
|
||||
* proper ordering. Visibility limited to this translation unit.
|
||||
******************************************************************************/
|
||||
|
||||
static void timers_init(void);
|
||||
static void log_init(void);
|
||||
static void power_management_init(void);
|
||||
static void buttons_leds_init(bool * p_erase_bonds);
|
||||
#if !DEBUG_MINIMAL_BOOT
|
||||
static void gpio_init(void);
|
||||
#endif
|
||||
static void idle_state_handle(void);
|
||||
|
||||
/*******************************************************************************
|
||||
* @section ASSERT Assert Handler
|
||||
* @brief System fault callback for SDK assertions
|
||||
* @details Invoked by Nordic SDK on fatal errors. Provides fault location
|
||||
* (file and line) for debugging via app_error_handler.
|
||||
******************************************************************************/
|
||||
|
||||
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
|
||||
{
|
||||
app_error_handler(DEAD_BEEF, line_num, p_file_name);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* @section INIT Initialization Functions
|
||||
* @brief System and peripheral initialization routines
|
||||
* @details Configures logging, power management, timers, and BSP components.
|
||||
* Call order is critical - see main() for proper sequencing.
|
||||
******************************************************************************/
|
||||
|
||||
static void log_init(void)
|
||||
{
|
||||
ret_code_t err_code = NRF_LOG_INIT(NULL);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
NRF_LOG_DEFAULT_BACKENDS_INIT();
|
||||
}
|
||||
|
||||
static void power_management_init(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
err_code = nrf_pwr_mgmt_init();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
static void timers_init(void)
|
||||
{
|
||||
ret_code_t err_code = app_timer_init();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
/* Power control timers */
|
||||
power_ctrl_timers_init();
|
||||
|
||||
/* Application timers */
|
||||
main_timer_init();
|
||||
battery_timer_init();
|
||||
imm_check_timer_init();
|
||||
m48_check_timer_init();
|
||||
full_agc_timer_init();
|
||||
full_agc_send_timer_init();
|
||||
mea_send_timer_init();
|
||||
power_timer_init();
|
||||
w25q_test_timer_init();
|
||||
|
||||
/* full_timer_init() - Moved to unuse/, FEATURE_PRINTF=0 so not used */
|
||||
}
|
||||
|
||||
static void buttons_leds_init(bool * p_erase_bonds)
|
||||
{
|
||||
bsp_event_t startup_event;
|
||||
|
||||
uint32_t err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, power_bsp_event_handler);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = bsp_btn_ble_init(NULL, &startup_event);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
*p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* @section PWR_HOLD Power Hold Initialization
|
||||
* @brief Latches power supply on boot
|
||||
* @details Configures P0.08 (POWER_HOLD) as output HIGH to maintain VCC.
|
||||
* CRITICAL: Must be the first call in main() - any delay risks
|
||||
* power loss if button is released before latch is set.
|
||||
* @note Uses direct register access for minimum latency.
|
||||
******************************************************************************/
|
||||
static void power_hold_init(void)
|
||||
{
|
||||
/* P0.08 = POWER_HOLD, configure as output and set HIGH to maintain power */
|
||||
NRF_P0->DIRSET = (1 << 8);
|
||||
NRF_P0->OUTSET = (1 << 8);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* @section MIN_GPIO Minimal GPIO Initialization (Debug Mode)
|
||||
* @brief Reduced GPIO setup for rapid debugging
|
||||
* @details Initializes only essential pins: POWER_BUTTON input, POWER_HOLD
|
||||
* output, and power control GPIOs. Skips LEDs, PD array, gain
|
||||
* switches, and EEPROM control for faster boot.
|
||||
* @pre DEBUG_MINIMAL_BOOT must be set to 1
|
||||
******************************************************************************/
|
||||
static void minimal_gpio_init(void)
|
||||
{
|
||||
nrf_gpio_cfg_input(POWER_BUTTON, NRF_GPIO_PIN_NOPULL);
|
||||
nrf_gpio_cfg_output(POWER_HOLD);
|
||||
nrf_gpio_pin_set(POWER_HOLD);
|
||||
power_gpio_init();
|
||||
LOG_PRINTF("[GPIO] Minimal OK\r\n");
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* @section MIN_CFG Default Configuration Loader (Debug Mode)
|
||||
* @brief Loads hardcoded defaults bypassing EEPROM
|
||||
* @details Sets factory defaults for serial number, passkey, PD timing,
|
||||
* and bonding state. Used when EEPROM is unavailable or for
|
||||
* rapid iteration during development.
|
||||
* @pre DEBUG_MINIMAL_BOOT must be set to 1
|
||||
******************************************************************************/
|
||||
static void load_default_config(void)
|
||||
{
|
||||
/* Direct assignment of variables defined in device_config.h */
|
||||
memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
|
||||
memcpy(SERIAL_NO, "2025VIVAM00001", 14);
|
||||
|
||||
memset(m_static_passkey, 0, sizeof(m_static_passkey));
|
||||
memcpy(m_static_passkey, "123456", 6);
|
||||
|
||||
m_pd_delay_us = 8000;
|
||||
m_pd_adc_cnt = 8;
|
||||
bond_data_delete = true;
|
||||
|
||||
LOG_PRINTF("[CFG] Default (S/N=%s)\r\n", SERIAL_NO);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* @section FULL_GPIO Full GPIO Initialization (Production Mode)
|
||||
* @brief Complete peripheral GPIO configuration
|
||||
* @details Initializes all hardware interfaces: LEDs, photodiode array (PD),
|
||||
* trigger switches, AGC gain control, power management, and EEPROM.
|
||||
* All outputs are set to safe default states (LEDs off, PD off).
|
||||
* @pre DEBUG_MINIMAL_BOOT must be set to 0
|
||||
******************************************************************************/
|
||||
#if !DEBUG_MINIMAL_BOOT
|
||||
static void gpio_init(void)
|
||||
{
|
||||
nrf_gpio_cfg_input(POWER_BUTTON, NRF_GPIO_PIN_NOPULL);
|
||||
|
||||
LED_CONFIG();
|
||||
LED_ALLOFF();
|
||||
|
||||
PD_CONFIG();
|
||||
PD_ALLOFF();
|
||||
|
||||
trig_r_CONFIG();
|
||||
trig_SW(false);
|
||||
|
||||
GAIN_SW_CONFIG();
|
||||
AGC_GAIN_SW(false);
|
||||
|
||||
power_gpio_init();
|
||||
eeprom_control(OFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* @section IDLE Idle State Handler
|
||||
* @brief Low-power idle loop processing
|
||||
* @details Handles security operations (LESC if enabled), flushes NRF_LOG
|
||||
* buffer, then enters System ON sleep via nrf_pwr_mgmt_run().
|
||||
* CPU wakes on any enabled interrupt (BLE, timer, GPIO).
|
||||
******************************************************************************/
|
||||
|
||||
static void idle_state_handle(void)
|
||||
{
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
security_idle_state_handle();
|
||||
#endif
|
||||
|
||||
if (NRF_LOG_PROCESS() == false) {
|
||||
nrf_pwr_mgmt_run();
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* @section MAIN Application Entry Point
|
||||
* @brief System initialization and main loop
|
||||
* @details Boot sequence: Power latch → UART → Logging → GPIO → Timers →
|
||||
* Config → BSP → Power Mgmt → BLE Stack → GAP → GATT → Services →
|
||||
* Advertising → Connection Params → Power Button Handler.
|
||||
* Main loop runs idle_state_handle() for low-power operation.
|
||||
* @note Initialization order is critical for proper hardware bringup.
|
||||
******************************************************************************/
|
||||
|
||||
/* LED slow blink macro (approximately 0.5 second period) */
|
||||
#define LED_BLINK() do { \
|
||||
NRF_P0->OUTSET = (1 << 12); \
|
||||
for (volatile uint32_t _d = 0; _d < 1000000; _d++); \
|
||||
NRF_P0->OUTCLR = (1 << 12); \
|
||||
for (volatile uint32_t _d = 0; _d < 1000000; _d++); \
|
||||
} while(0)
|
||||
|
||||
#define LED_PAUSE() do { \
|
||||
for (volatile uint32_t _d = 0; _d < 2000000; _d++); \
|
||||
} while(0)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
bool erase_bonds_local = false;
|
||||
#endif
|
||||
|
||||
/* Configure LED output (P0.12) */
|
||||
NRF_P0->DIRSET = (1 << 12);
|
||||
|
||||
power_hold_init();
|
||||
|
||||
if (power_off_duble_prohibit) return 0;
|
||||
cnt_s = 0;
|
||||
|
||||
uart_handler_init();
|
||||
nrf_delay_ms(100); /* Wait for UART peripheral stabilization */
|
||||
log_init();
|
||||
|
||||
g_log_enable = true; /* Enable boot logging (set to false to disable boot logs) */
|
||||
|
||||
LOG_PRINTF("\r\n\r\n");
|
||||
LOG_PRINTF("========================================\r\n");
|
||||
LOG_PRINTF(" vivaMayo UART OK\r\n");
|
||||
LOG_PRINTF("========================================\r\n");
|
||||
|
||||
#if DEBUG_MINIMAL_BOOT
|
||||
LOG_PRINTF("[1] GPIO (minimal)\r\n");
|
||||
minimal_gpio_init();
|
||||
#else
|
||||
LOG_PRINTF("[1] GPIO (full)\r\n");
|
||||
gpio_init();
|
||||
#endif
|
||||
info4 = false;
|
||||
|
||||
LOG_PRINTF("[2] Timers\r\n");
|
||||
timers_init();
|
||||
|
||||
LOG_PRINTF("[4] Buttons/LEDs\r\n");
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
buttons_leds_init(&erase_bonds_local);
|
||||
erase_bonds = erase_bonds_local;
|
||||
#else
|
||||
bool dummy_erase;
|
||||
buttons_leds_init(&dummy_erase);
|
||||
#endif
|
||||
|
||||
LOG_PRINTF("[5] PWR\r\n"); nrf_delay_ms(10);
|
||||
power_management_init();
|
||||
LOG_PRINTF("[6] BLE\r\n"); nrf_delay_ms(10);
|
||||
ble_stack_init();
|
||||
|
||||
/* Flash Storage - must be after BLE stack (SoftDevice required) */
|
||||
/* and before GAP params (to use loaded config for device name) */
|
||||
LOG_PRINTF("[6.5] FDS\r\n"); nrf_delay_ms(10);
|
||||
fs_storage_init();
|
||||
config_load();
|
||||
|
||||
LOG_PRINTF("[6.6] Config\r\n");
|
||||
load_device_configuration();
|
||||
|
||||
/* W25Q32 Flash - NOT initialized during boot
|
||||
* Initialize via BLE command after advertising starts
|
||||
* (부팅 시 SPIM + BSP 이벤트 충돌로 SYSTEM OFF 발생 방지) */
|
||||
|
||||
LOG_PRINTF("[7] GAP\r\n"); nrf_delay_ms(10);
|
||||
gap_params_init();
|
||||
gatt_init();
|
||||
services_init();
|
||||
LOG_PRINTF("[8] ADV\r\n"); nrf_delay_ms(10);
|
||||
advertising_init();
|
||||
conn_params_init();
|
||||
power_ctrl_timers_start();
|
||||
/* advertising_start() is invoked from power_ctrl.c after 5ms delay */
|
||||
|
||||
nrf_delay_ms(20);
|
||||
LOG_PRINTF("=== READY ===\r\n");
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* Main Loop: Idle State Handler
|
||||
*----------------------------------------------------------*/
|
||||
for (;;)
|
||||
{
|
||||
idle_state_handle();
|
||||
}
|
||||
}
|
||||
215
project/ble_peripheral/ble_app_vivaMayo/main.h
Normal file
215
project/ble_peripheral/ble_app_vivaMayo/main.h
Normal file
@@ -0,0 +1,215 @@
|
||||
/*******************************************************************************
|
||||
* @file main.h
|
||||
* @brief Main application header for BLE Bladder Patch NIRS device
|
||||
* @author Charles KWON <charleskwon@medithings.co.kr>
|
||||
* @version V2.0.0
|
||||
* @date 2025-12-31
|
||||
*
|
||||
* Copyright (c) 2025 Medithings Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file contains type definitions, enumerations, and function prototypes
|
||||
* for the main application module of the BLE-enabled NIRS bladder monitoring
|
||||
* device based on nRF52840.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef MAIN_H__
|
||||
#define MAIN_H__
|
||||
|
||||
/*============================================================================*/
|
||||
/* Includes - Charles KWON */
|
||||
/*============================================================================*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include "boards.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* Type Definitions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Generic ON/OFF control enumeration
|
||||
* - Charles KWON
|
||||
*
|
||||
* Used for controlling power states of various device components
|
||||
* such as LEDs, photodetectors, and other peripherals.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
OFF = 0, /**< Device/component is OFF */
|
||||
ON = 1 /**< Device/component is ON */
|
||||
} on_off_cont_t;
|
||||
|
||||
/**
|
||||
* @brief Command source type enumeration
|
||||
* - Charles KWON
|
||||
*
|
||||
* Identifies the source of incoming commands to route
|
||||
* responses appropriately.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
CMD_BLE = 0, /**< Command received via BLE (Nordic UART Service) */
|
||||
CMD_UART = 1 /**< Command received via physical UART interface */
|
||||
} which_cmd_t;
|
||||
|
||||
#if FEATURE_CHAMBER_AUTO_TEST
|
||||
/**
|
||||
* @brief Automatic measurement mode enumeration
|
||||
* - Charles KWON
|
||||
*
|
||||
* Defines different automatic testing modes for chamber calibration
|
||||
* and production testing scenarios.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
SIMPLE_AUTO_MODE = 0, /**< Simple automatic measurement mode */
|
||||
HALF_AUTO_MODE = 1, /**< Semi-automatic measurement mode */
|
||||
FULL_AUTO_MODE = 2, /**< Full automatic measurement mode */
|
||||
NONE_AUTO_MODE = 3 /**< Manual mode (no automation) */
|
||||
} auto_meas_mode_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief BLE connection status enumeration
|
||||
* - Charles KWON
|
||||
*
|
||||
* Tracks the current BLE connection state for controlling
|
||||
* device behavior based on connectivity.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
BLE_DISCONNECTED_ST = 0, /**< BLE is disconnected */
|
||||
BLE_CONNECTED_ST = 1 /**< BLE is connected to a central device */
|
||||
} ble_status_t;
|
||||
|
||||
/*============================================================================*/
|
||||
/* Function Prototypes - Command Processing - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Process received command data
|
||||
* - Charles KWON
|
||||
*
|
||||
* Parses and executes commands received from BLE or UART.
|
||||
* Commands control measurement modes, device settings, and queries.
|
||||
*
|
||||
* @param[in] data_array Pointer to received command data buffer
|
||||
* @param[in] cmd_t Source of the command (BLE or UART)
|
||||
* @param[in] length Length of the command data in bytes
|
||||
*/
|
||||
void received_command_process(uint8_t const *data_array, which_cmd_t cmd_t, uint8_t length);
|
||||
|
||||
/*============================================================================*/
|
||||
/* Function Prototypes - Data Transmission - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Transmit ASCII string data
|
||||
* - Charles KWON
|
||||
*
|
||||
* Sends null-terminated string data over the active communication
|
||||
* channel (BLE NUS or UART) with flow control.
|
||||
*
|
||||
* @param[in] p_data_to_send Pointer to null-terminated string to transmit
|
||||
*/
|
||||
void data_tx_handler(char const *p_data_to_send);
|
||||
|
||||
/**
|
||||
* @brief Transmit binary data
|
||||
* - Charles KWON
|
||||
*
|
||||
* Sends raw binary data over BLE NUS with specified length.
|
||||
* Used for transmitting measurement data and binary payloads.
|
||||
*
|
||||
* @param[in] ble_bin_buff Pointer to binary data buffer
|
||||
* @param[in] length Number of bytes to transmit
|
||||
*/
|
||||
void binary_tx_handler(uint8_t const *ble_bin_buff, uint16_t length);
|
||||
|
||||
/**
|
||||
* @brief Format single 16-bit value with tag
|
||||
* - Charles KWON
|
||||
*
|
||||
* Creates a tagged binary packet containing a single 16-bit value.
|
||||
* Format: [tag bytes][value_high][value_low]
|
||||
*
|
||||
* @param[out] buffer Destination buffer for formatted data
|
||||
* @param[in] tag 4-character tag string identifier
|
||||
* @param[in] value 16-bit value to encode
|
||||
*/
|
||||
void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value);
|
||||
|
||||
/**
|
||||
* @brief Format 16-bit array with tag
|
||||
* - Charles KWON
|
||||
*
|
||||
* Creates a tagged binary packet containing an array of 16-bit values.
|
||||
* Format: [tag bytes][data_0_high][data_0_low]...[data_n_high][data_n_low]
|
||||
*
|
||||
* @param[out] buffer Destination buffer for formatted data
|
||||
* @param[in] tag 4-character tag string identifier
|
||||
* @param[in] data_array Pointer to array of 16-bit values
|
||||
* @param[in] length Number of elements in the array
|
||||
*/
|
||||
void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Format 8-bit array with tag
|
||||
* - Charles KWON
|
||||
*
|
||||
* Creates a tagged binary packet containing an array of 8-bit values.
|
||||
* Format: [tag bytes][data_0]...[data_n]
|
||||
*
|
||||
* @param[out] buffer Destination buffer for formatted data
|
||||
* @param[in] tag 4-character tag string identifier
|
||||
* @param[in] data_array Pointer to array of 8-bit values
|
||||
* @param[in] length Number of elements in the array
|
||||
*/
|
||||
void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_array, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Format ASCII string with tag
|
||||
* - Charles KWON
|
||||
*
|
||||
* Creates a tagged packet containing ASCII string data.
|
||||
* Format: [tag bytes][ascii_char_0]...[ascii_char_n]
|
||||
*
|
||||
* @param[out] buffer Destination buffer for formatted data
|
||||
* @param[in] tag 4-character tag string identifier
|
||||
* @param[in] data_ascii Pointer to ASCII string data
|
||||
* @param[in] length Number of characters to include
|
||||
*/
|
||||
void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length);
|
||||
|
||||
/*============================================================================*/
|
||||
/* External Variables - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Flag indicating BLE TX operation in progress
|
||||
*
|
||||
* Set to true when a BLE transmission is pending completion.
|
||||
* Used to prevent overlapping transmissions and implement flow control.
|
||||
*/
|
||||
extern volatile bool data_tx_in_progress;
|
||||
|
||||
/**
|
||||
* @brief Current BLE connection status
|
||||
*
|
||||
* True when device is connected to a BLE central.
|
||||
* Used to control device behavior based on connectivity state.
|
||||
*/
|
||||
extern volatile bool ble_connection_st;
|
||||
|
||||
/**
|
||||
* @brief Flag indicating command processing in progress
|
||||
*
|
||||
* Set to true during command execution to prevent
|
||||
* concurrent command processing.
|
||||
*/
|
||||
extern volatile bool processing;
|
||||
|
||||
#endif /* MAIN_H__ */
|
||||
152
project/ble_peripheral/ble_app_vivaMayo/main_timer.c
Normal file
152
project/ble_peripheral/ble_app_vivaMayo/main_timer.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/*******************************************************************************
|
||||
* @file main_timer.c
|
||||
* @brief Main Loop Timer Handler
|
||||
******************************************************************************/
|
||||
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "nrf.h"
|
||||
#include "nrf_drv_saadc.h"
|
||||
#include "nrf_drv_ppi.h"
|
||||
#include "nrf_drv_timer.h"
|
||||
#include "boards.h"
|
||||
#include "app_error.h"
|
||||
#include "nrf_delay.h"
|
||||
#include "app_util_platform.h"
|
||||
#include "nrf_pwr_mgmt.h"
|
||||
|
||||
#include "nrf_log.h"
|
||||
#include "nrf_drv_gpiote.h"
|
||||
#include "battery_saadc.h"
|
||||
#include "app_timer.h"
|
||||
|
||||
#include "main.h"
|
||||
#include "app_raw_main.h"
|
||||
#include "main_timer.h"
|
||||
#include "tmp235_q1.h"
|
||||
#include "meas_pd_imm.h"
|
||||
/* #include "meas_pd_voltage_full.h" */ /* Moved to unuse/ */
|
||||
#include "mcp4725_i2c.h"
|
||||
#include "power_control.h"
|
||||
#include "power/power_ctrl.h"
|
||||
#include <cmd_parse.h>
|
||||
#include "meas_pd_48.h"
|
||||
#include "debug_print.h"
|
||||
#include "i2c_manager.h"
|
||||
|
||||
APP_TIMER_DEF(m_main_loop_timer_id);
|
||||
|
||||
#if FEATURE_DETAIL_VALUE_FULL
|
||||
#define MAIN_LOOP_INTERVAL 80
|
||||
extern which_cmd_t cmd_type_t;
|
||||
#else
|
||||
#define MAIN_LOOP_INTERVAL 10
|
||||
#endif
|
||||
|
||||
bool go_batt = false;
|
||||
bool go_temp = false;
|
||||
bool go_pdread = false;
|
||||
|
||||
/* Defined in power_ctrl.c */
|
||||
extern bool go_device_power_off;
|
||||
extern bool go_sleep_mode_enter;
|
||||
extern bool go_NVIC_SystemReset;
|
||||
|
||||
bool motion_raw_data_enabled = false;
|
||||
bool ble_got_new_data = false;
|
||||
bool motion_data_once = false;
|
||||
bool adc_enabled = false;
|
||||
static uint16_t cnt_adc = 0;
|
||||
|
||||
void main_loop(void * p_context)
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
|
||||
/* Motion Data Sampling */
|
||||
if (motion_raw_data_enabled == true) {
|
||||
main_timer_stop();
|
||||
if (motion_data_once == true) {
|
||||
hw_i2c_init_once();
|
||||
icm42670_main();
|
||||
} else {
|
||||
if (ble_got_new_data == false) {
|
||||
DBG_PRINTF("IMU \r\n");
|
||||
icm42670_main();
|
||||
nrf_delay_ms(10);
|
||||
motion_raw_data_enabled = true;
|
||||
main_timer_start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (go_batt == true) {
|
||||
DBG_PRINTF("IMU BATT\r\n");
|
||||
main_timer_stop();
|
||||
go_batt = false;
|
||||
battery_level_meas();
|
||||
}
|
||||
|
||||
if (go_temp == true) {
|
||||
DBG_PRINTF("IMU Temp\r\n");
|
||||
main_timer_stop();
|
||||
go_temp = false;
|
||||
motion_data_once = true;
|
||||
tmp235_voltage_level_meas();
|
||||
}
|
||||
|
||||
if (go_pdread == true) {
|
||||
main_timer_stop();
|
||||
go_pdread = false;
|
||||
m48_adc_start_init();
|
||||
}
|
||||
|
||||
if (adc_enabled == true) {
|
||||
main_timer_stop();
|
||||
DBG_PRINTF("PD48 ADC=%d\r\n", cnt_adc);
|
||||
if (ble_got_new_data == false) {
|
||||
if (cnt_adc < 500) {
|
||||
cnt_adc++;
|
||||
} else if (cnt_adc == 500) {
|
||||
cnt_adc = 0;
|
||||
}
|
||||
main_timer_start();
|
||||
}
|
||||
}
|
||||
|
||||
/* System Control */
|
||||
if (go_device_power_off == true) {
|
||||
main_timer_stop();
|
||||
DBG_PRINTF("Off main_timer\r\n");
|
||||
device_power_off();
|
||||
}
|
||||
|
||||
if (go_sleep_mode_enter == true) {
|
||||
main_timer_stop();
|
||||
DBG_PRINTF("sleep main timer\r\n");
|
||||
sleep_mode_enter();
|
||||
}
|
||||
|
||||
if (go_NVIC_SystemReset == true) {
|
||||
main_timer_stop();
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
}
|
||||
|
||||
void main_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_main_loop_timer_id, APP_TIMER_TICKS(MAIN_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
void main_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_main_loop_timer_id));
|
||||
}
|
||||
|
||||
void main_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_main_loop_timer_id, APP_TIMER_MODE_SINGLE_SHOT, main_loop));
|
||||
}
|
||||
17
project/ble_peripheral/ble_app_vivaMayo/main_timer.h
Normal file
17
project/ble_peripheral/ble_app_vivaMayo/main_timer.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*******************************************************************************
|
||||
* @file timer_routine.h
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef TIMER_ROUTINE_H__
|
||||
#define TIMER_ROUTINE_H__
|
||||
|
||||
void main_timer_start(void);
|
||||
void main_timer_stop(void);
|
||||
void main_timer_init(void);
|
||||
|
||||
#endif //TIMER_ROUTINE_H__
|
||||
|
||||
114
project/ble_peripheral/ble_app_vivaMayo/mcp4725_adc.c
Normal file
114
project/ble_peripheral/ble_app_vivaMayo/mcp4725_adc.c
Normal file
@@ -0,0 +1,114 @@
|
||||
/*******************************************************************************
|
||||
* @file mcp4725_adc.c
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "nrf.h"
|
||||
#include "boards.h"
|
||||
#include "app_error.h"
|
||||
#include "nrf_drv_saadc.h"
|
||||
#include "ble_nus.h"
|
||||
#include "mcp4725_adc.h"
|
||||
#include "main.h"
|
||||
#include "debug_print.h"
|
||||
#define MCP4725_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
|
||||
#define MCP4725_PRE_SCALING_COMPENSATION 6.0f /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
|
||||
#define MCP4725_ADC_RES_10BITS 1024.0f /**< Maximum digital value for 10-bit ADC conversion. */
|
||||
|
||||
/**@brief Macro to convert the result of ADC conversion in millivolts.
|
||||
*
|
||||
* @param[in] ADC_VALUE ADC result.
|
||||
*
|
||||
* @retval Result converted to millivolts.
|
||||
*/
|
||||
#define MCP4725_VOUT_IN_MILLI_VOLTS(ADC_VALUE)\
|
||||
((((ADC_VALUE) * MCP4725_REF_VOLTAGE_IN_MILLIVOLTS) / MCP4725_ADC_RES_10BITS) * MCP4725_PRE_SCALING_COMPENSATION)
|
||||
|
||||
#define ADC_SAMPLES_IN_BUFFER 1
|
||||
|
||||
static nrf_saadc_value_t mcp4725_adc_buf[2][ADC_SAMPLES_IN_BUFFER];
|
||||
float mcp4725_voltage_in_milli_volts = 0;
|
||||
|
||||
//extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
|
||||
extern which_cmd_t cmd_type_t;
|
||||
extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] ;
|
||||
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
void mcp4725_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* ADC_GAIN reading */
|
||||
{
|
||||
float Vref = 3.3f; /* It same as Vdd */
|
||||
float dac_value = 0.0f;
|
||||
uint16_t dac_value_16=0;
|
||||
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
|
||||
{
|
||||
nrf_saadc_value_t adc_result;
|
||||
|
||||
nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, ADC_SAMPLES_IN_BUFFER);
|
||||
adc_result = p_event->data.done.p_buffer[0];
|
||||
|
||||
nrf_drv_saadc_uninit();
|
||||
nrf_drv_saadc_channel_uninit(0);
|
||||
|
||||
mcp4725_voltage_in_milli_volts = MCP4725_VOUT_IN_MILLI_VOLTS(adc_result);
|
||||
#if FEATURE_DETAIL_VALUE_AGC
|
||||
DBG_PRINTF("AGC read Vol: %f(mV)\r\n", mcp4725_voltage_in_milli_volts);
|
||||
#endif
|
||||
/* For MCP4725, Dn = (Vout/Vref) x 4096, Vref = Vdd */
|
||||
dac_value = (((mcp4725_voltage_in_milli_volts/Vref) * 4096.0f)/1000.0f); /* Unit is Volt */
|
||||
if(cmd_type_t == CMD_UART) {
|
||||
DBG_PRINTF("Te%d\r\n\r\n",(uint16_t)(dac_value + 0.5f));
|
||||
} else if(cmd_type_t == CMD_BLE) {
|
||||
dac_value_16 = (uint16_t)(dac_value + 0.5f);
|
||||
single_format_data(ble_bin_buffer, "rse:", dac_value_16);
|
||||
|
||||
|
||||
|
||||
|
||||
binary_tx_handler(ble_bin_buffer,3);
|
||||
// sprintf(ble_tx_buffer, "Te%d\r\n",(uint16_t)(dac_value + 0.5f));
|
||||
// data_tx_handler(ble_tx_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mcp4725_adc_init(void)
|
||||
{
|
||||
ret_code_t err_code = nrf_drv_saadc_init(NULL, mcp4725_voltage_handler);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
nrf_saadc_channel_config_t config =
|
||||
NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6); /* FSA5157P6X Voltage Output Measurement */
|
||||
|
||||
err_code = nrf_drv_saadc_channel_init(0, &config);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = nrf_drv_saadc_buffer_convert(mcp4725_adc_buf[0], ADC_SAMPLES_IN_BUFFER);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
err_code = nrf_drv_saadc_buffer_convert(mcp4725_adc_buf[1], ADC_SAMPLES_IN_BUFFER);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = nrf_drv_saadc_sample();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
void mcp4725_voltage_level_meas(void)
|
||||
{
|
||||
mcp4725_adc_init();
|
||||
}
|
||||
|
||||
16
project/ble_peripheral/ble_app_vivaMayo/mcp4725_adc.h
Normal file
16
project/ble_peripheral/ble_app_vivaMayo/mcp4725_adc.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*******************************************************************************
|
||||
* @file mcp4725.h
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _MCP4725_ADC_H_
|
||||
#define _MCP4725_ADC_H_
|
||||
|
||||
void mcp4725_adc_init(void);
|
||||
void mcp4725_voltage_level_meas(void);
|
||||
|
||||
#endif /* !_MCP4725_ADC_H_ */
|
||||
|
||||
513
project/ble_peripheral/ble_app_vivaMayo/mcp4725_i2c.c
Normal file
513
project/ble_peripheral/ble_app_vivaMayo/mcp4725_i2c.c
Normal file
@@ -0,0 +1,513 @@
|
||||
/*******************************************************************************
|
||||
* @file mcp4725_i2c.c
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
/* board driver */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
#include "nrf.h"
|
||||
#include "app_error.h"
|
||||
#include "boards.h"
|
||||
#include "nrfx_gpiote.h"
|
||||
|
||||
#include "mcp4725_i2c.h"
|
||||
#include "nrf_delay.h"
|
||||
#include "debug_print.h"
|
||||
|
||||
/* I2C number and slave address for MCP4725 */
|
||||
#define MCP4725_I2C_ADDR_7bit 0x66
|
||||
uint8_t MCP4725_I2C_ADDR = MCP4725_I2C_ADDR_7bit << 1;
|
||||
|
||||
uint16_t _lastValue;
|
||||
uint8_t _powerDownMode;
|
||||
uint32_t _lastWriteEEPROM;
|
||||
|
||||
|
||||
void mcp4725_i2c_initialize(void)
|
||||
{
|
||||
SCL_OUT();
|
||||
SDA_OUT();
|
||||
SCL_H();
|
||||
SDA_H();
|
||||
}
|
||||
|
||||
|
||||
static uint8_t mcp4725_i2c_write(uint8_t data)
|
||||
{
|
||||
for(uint8_t i = 0; i < 8; i++)
|
||||
{
|
||||
// MSB first
|
||||
if(data & 0x80) SDA_H();
|
||||
else SDA_L();
|
||||
i2c_clock();
|
||||
data = data << 1;
|
||||
}
|
||||
|
||||
// read ACK
|
||||
SDA_H(); // leave SDA HI
|
||||
SDA_IN(); // change direction to input on SDA line
|
||||
|
||||
i2c_delay();
|
||||
SCL_H(); // clock back up
|
||||
i2c_delay();
|
||||
uint8_t ack = SDA_READ(); // get the ACK bit
|
||||
SCL_L();
|
||||
|
||||
SDA_OUT(); // change direction back to output
|
||||
|
||||
if(ack) {
|
||||
DBG_PRINTF("ACK Extra=%d,Data=%d\r\n", ack,data);
|
||||
}
|
||||
|
||||
return ack;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t mcp4725_i2c_read(bool ack)
|
||||
{
|
||||
uint8_t data;
|
||||
|
||||
SDA_H(); // leave SDA HI
|
||||
SDA_IN(); // change direction to input on SDA line
|
||||
|
||||
data = 0;
|
||||
for(uint8_t i = 0; i < 8; i++)
|
||||
{
|
||||
// MSB first
|
||||
data = data << 1;
|
||||
|
||||
SCL_H();
|
||||
do{
|
||||
}while(SCL_READ() != 0); // Wait for any SCL clock stratching
|
||||
|
||||
i2c_delay();
|
||||
if(SDA_READ()) // get the Data bit
|
||||
data |= 1;
|
||||
else
|
||||
data |= 0;
|
||||
SCL_L(); // clock LO
|
||||
i2c_delay();
|
||||
}
|
||||
|
||||
SDA_OUT(); // change direction back to output
|
||||
|
||||
// send ACK
|
||||
if(ack == ACK) SDA_L();
|
||||
else if(ack == NACK) SDA_H();
|
||||
i2c_clock();
|
||||
SDA_H(); // leave with SDA HI
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
void mcp4725_writeFastMode(const uint16_t value)
|
||||
{
|
||||
uint8_t low = value & 0xFF;
|
||||
uint8_t high = ((value >> 8) & 0x0F); // set C2,c1 == 0,0(FastMode Write) pd1,pd0 == 0,0 (Normal Mode)
|
||||
|
||||
i2c_start();
|
||||
mcp4725_i2c_write(MCP4725_I2C_ADDR|TWI_WRITE); // slave address
|
||||
|
||||
//uint8_t address = MCP4725_I2C_ADDR | TWI_WRITE;
|
||||
//DBG_PRINTF("[I2C] Write to 0x%02X\r\n", address); //oxCC
|
||||
|
||||
mcp4725_i2c_write(high); // value1 ~ 3
|
||||
mcp4725_i2c_write(low);
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
|
||||
void mcp4725_generalCall(const uint8_t gc)
|
||||
{
|
||||
i2c_start();
|
||||
mcp4725_i2c_write(0x00); // First Byte 0x00
|
||||
mcp4725_i2c_write(gc); // Second Byte, General Call Reset = 0x06, General Call Wake-Up = 0x09
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
|
||||
void mcp4725_init(void)
|
||||
{
|
||||
mcp4725_i2c_initialize();
|
||||
}
|
||||
|
||||
// DAC value is reset to EEPROM value
|
||||
// need to reflect this in cached value
|
||||
void mcp4725_powerOnReset(void)
|
||||
{
|
||||
mcp4725_generalCall(MCP4725_GC_RESET);
|
||||
}
|
||||
|
||||
|
||||
// _powerDownMode DAC resets to 0 -- PDM EEPROM stays same !!!
|
||||
// need to reflect this in cached value
|
||||
void mcp4725_powerOnWakeUp(void)
|
||||
{
|
||||
mcp4725_generalCall(MCP4725_GC_WAKEUP);
|
||||
}
|
||||
|
||||
/* 현재 DAC값 읽어서 그 값에 Power Down 넣고 Write */
|
||||
void mcp4725_PowerDownMode(void)
|
||||
{
|
||||
uint8_t low = 0x00; //read_value & 0xFF;
|
||||
uint8_t high = 0x00; //((read_value >> 8) & 0x0F);
|
||||
high = high | (MCP4725_PDMODE_1K << 4); // set C2,c1 == 0,0(FastMode Write) pd1,pd0 == 0,1 (Power Down Mode, 1Kohm to GND)
|
||||
|
||||
i2c_start();
|
||||
mcp4725_i2c_write(MCP4725_I2C_ADDR|TWI_WRITE); // slave address
|
||||
mcp4725_i2c_write(high); // value
|
||||
mcp4725_i2c_write(low);
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint16_t mcp4725_readDAC(void)
|
||||
{
|
||||
while(!mcp4725_ready());
|
||||
uint8_t buffer[3];
|
||||
mcp4725_readRegister(buffer, 3);
|
||||
uint16_t value = buffer[1];
|
||||
value = value << 4;
|
||||
value = value + (buffer[2] >> 4);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
// ready checks if the last write to EEPROM has been written.
|
||||
// until ready all writes to the MCP4725 are ignored!
|
||||
bool mcp4725_ready(void)
|
||||
{
|
||||
uint8_t buffer[1];
|
||||
mcp4725_readRegister(buffer, 1);
|
||||
return ((buffer[0] & 0x80) > 0);
|
||||
}
|
||||
|
||||
|
||||
void mcp4725_writeRegisterMode(const uint16_t value, uint8_t reg)
|
||||
{
|
||||
uint8_t high = (value / 16);
|
||||
uint8_t low = (value & 0x0F) << 4;
|
||||
reg = reg | (_powerDownMode << 1);
|
||||
|
||||
i2c_start();
|
||||
mcp4725_i2c_write(MCP4725_I2C_ADDR|TWI_WRITE); // slave address
|
||||
mcp4725_i2c_write(reg); // configuration
|
||||
mcp4725_i2c_write(high); // value1 ~ 3
|
||||
mcp4725_i2c_write(low);
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
|
||||
void mcp4725_readRegister(uint8_t* buffer, const uint8_t length)
|
||||
{
|
||||
i2c_start();
|
||||
mcp4725_i2c_write(MCP4725_I2C_ADDR|TWI_READ); // slave address with READ
|
||||
switch(length) {
|
||||
case 1:
|
||||
buffer[0] = mcp4725_i2c_read(NACK); // NACK, SDA = 1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
buffer[0] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
||||
buffer[1] = mcp4725_i2c_read(NACK); // NACK, SDA = 1;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
buffer[0] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
||||
buffer[1] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
||||
buffer[2] = mcp4725_i2c_read(NACK); // NACK, SDA = 1;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
buffer[0] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
||||
buffer[1] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
||||
buffer[2] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
||||
buffer[3] = mcp4725_i2c_read(NACK); // NACK, SDA = 1;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
buffer[0] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
||||
buffer[1] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
||||
buffer[2] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
||||
buffer[3] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
||||
buffer[4] = mcp4725_i2c_read(NACK); // NACK, SDA = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
#if FEATURE_PRINTF
|
||||
DBG_PRINTF("ERR!! mcp4725_i2c_readregister\r\n");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
i2c_stop();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void mcp4725_setValue(const uint16_t value)
|
||||
{
|
||||
if (value == _lastValue) return;
|
||||
if (value > MCP4725_MAXVALUE) return;
|
||||
mcp4725_writeFastMode(value);
|
||||
_lastValue = value;
|
||||
}
|
||||
|
||||
|
||||
uint16_t mcp4725_getValue(void)
|
||||
{
|
||||
return _lastValue;
|
||||
}
|
||||
|
||||
|
||||
void mcp4725_setPercentage(float percentage)
|
||||
{
|
||||
if ((percentage > 100) || (percentage < 0))
|
||||
DBG_PRINTF("ERR!!! mcp4725 percentage error\r\n");
|
||||
mcp4725_setValue(round(percentage * (0.01 * MCP4725_MAXVALUE)));
|
||||
}
|
||||
|
||||
|
||||
// unfortunately it is not possible to write a different value
|
||||
// to the DAC and EEPROM simultaneously or write EEPROM only.
|
||||
void mcp4725_writeDAC(const uint16_t value, const bool EEPROM)
|
||||
{
|
||||
if (value > MCP4725_MAXVALUE)
|
||||
#if FEATURE_PRINTF
|
||||
DBG_PRINTF("ERR!!! mcp4725 writeDAC error\r\n");
|
||||
#endif
|
||||
while(!mcp4725_ready());
|
||||
mcp4725_writeRegisterMode(value, EEPROM ? MCP4725_DACEEPROM : MCP4725_DAC);
|
||||
_lastValue = value;
|
||||
}
|
||||
|
||||
|
||||
uint16_t mcp4725_readEEPROM(void)
|
||||
{
|
||||
while(!mcp4725_ready());
|
||||
uint8_t buffer[5];
|
||||
mcp4725_readRegister(buffer, 5);
|
||||
uint16_t value = buffer[3] & 0x0F;
|
||||
value = value << 8;
|
||||
value = value + buffer[4];
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
// depending on bool EEPROM the value of PDM is written to
|
||||
// (false) DAC or
|
||||
// (true) DAC & EEPROM,
|
||||
void mcp4725_writePowerDownMode(const uint8_t PDM, const bool EEPROM)
|
||||
{
|
||||
_powerDownMode = (PDM & 0x03); // mask PDM bits only (written later low level)
|
||||
|
||||
_lastValue = mcp4725_readDAC();
|
||||
_powerDownMode = mcp4725_readPowerDownModeDAC();
|
||||
mcp4725_writeDAC(_lastValue, EEPROM);
|
||||
}
|
||||
|
||||
|
||||
uint8_t mcp4725_readPowerDownModeEEPROM(void)
|
||||
{
|
||||
while(!mcp4725_ready());
|
||||
uint8_t buffer[4];
|
||||
mcp4725_readRegister(buffer, 4);
|
||||
uint8_t value = (buffer[3] >> 5) & 0x03;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
uint8_t mcp4725_readPowerDownModeDAC(void)
|
||||
{
|
||||
while(!mcp4725_ready()); // TODO needed?
|
||||
uint8_t buffer[1];
|
||||
mcp4725_readRegister(buffer, 1);
|
||||
uint8_t value = (buffer[0] >> 1) & 0x03;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ds_i2c_start(void) {
|
||||
SDA_H();//nrf_gpio_pin_set(I2C_SDA_PIN);
|
||||
SCL_H();//nrf_gpio_pin_set(I2C_SCL_PIN);
|
||||
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
||||
SDA_L();//nrf_gpio_pin_clear(I2C_SDA_PIN);
|
||||
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
||||
SCL_L();//nrf_gpio_pin_clear(I2C_SCL_PIN);
|
||||
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
||||
}
|
||||
|
||||
void ds_i2c_stop(void) {
|
||||
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
||||
SDA_L();//nrf_gpio_pin_clear(I2C_SDA_PIN);
|
||||
SCL_H();// nrf_gpio_pin_set(I2C_SCL_PIN);
|
||||
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
||||
SDA_H();//nrf_gpio_pin_set(I2C_SDA_PIN);
|
||||
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void i2c_write_bit(uint8_t bit) {
|
||||
if (bit) {
|
||||
SDA_H();
|
||||
} else {
|
||||
SDA_L();
|
||||
}
|
||||
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
||||
SCL_H(); //nrf_gpio_pin_set(SCL_PIN);
|
||||
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
||||
SCL_L();//nrf_gpio_pin_clear(SCL_PIN);
|
||||
}
|
||||
|
||||
uint8_t i2c_read_bit(void) {
|
||||
uint8_t bit;
|
||||
SDA_IN();//nrf_gpio_cfg_input(SDA_PIN, NRF_GPIO_PIN_NOPULL);
|
||||
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
||||
SCL_H(); ////nrf_gpio_pin_set(SCL_PIN);
|
||||
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
||||
bit = SDA_READ();//nrf_gpio_pin_read(SDA_PIN);
|
||||
SCL_L(); //nrf_gpio_pin_clear(SCL_PIN);
|
||||
SDA_OUT();//nrf_gpio_cfg_output(SDA_PIN);
|
||||
return bit;
|
||||
}
|
||||
uint8_t i2c_write_byte(uint8_t byte) {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
i2c_write_bit(byte & 0x80);
|
||||
byte <<= 1;
|
||||
}
|
||||
return i2c_read_bit(); // Read ACK/NACK
|
||||
}
|
||||
|
||||
uint8_t i2c_read_byte(uint8_t ack) {
|
||||
uint8_t byte = 0;
|
||||
SDA_IN();//nrf_gpio_cfg_input(SDA_PIN, NRF_GPIO_PIN_NOPULL);
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
byte <<= 1;
|
||||
byte |= i2c_read_bit();
|
||||
}
|
||||
SDA_OUT();//nrf_gpio_cfg_output(SDA_PIN);
|
||||
i2c_write_bit(!ack); // Send ACK/NACK
|
||||
return byte;
|
||||
}
|
||||
void i2c_repeated_start(void) {
|
||||
SDA_H();//nrf_gpio_pin_set(SDA_PIN);
|
||||
SCL_H();//nrf_gpio_pin_set(SCL_PIN);
|
||||
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
||||
SDA_L();////nrf_gpio_pin_clear(SDA_PIN);
|
||||
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
||||
SCL_L();//nrf_gpio_pin_clear(SCL_PIN);
|
||||
}
|
||||
|
||||
// i2c_start();
|
||||
// i2c_write_byte(I2C_ADDRESS << 1); // Write address
|
||||
// i2c_write_byte(0x00); // Register address
|
||||
// i2c_write_byte(0xFF); // Data
|
||||
// i2c_stop();
|
||||
|
||||
// // Read example
|
||||
// i2c_start();
|
||||
// i2c_write_byte((I2C_ADDRESS << 1) | 1); // Read address
|
||||
// uint8_t data = i2c_read_byte(0); // Read data with NACK
|
||||
// i2c_stop();
|
||||
|
||||
|
||||
void DS3930_write(uint8_t id, uint8_t addr, uint8_t wdata)
|
||||
{
|
||||
//i2c_start();
|
||||
// ds_i2c_stop();
|
||||
ds_i2c_start();
|
||||
i2c_write_byte(id << 1); // Write address
|
||||
i2c_write_byte(addr); // Register address
|
||||
i2c_write_byte(wdata); // Data
|
||||
|
||||
// mcp4725_i2c_write(id << 1);
|
||||
// mcp4725_i2c_write(addr);
|
||||
// mcp4725_i2c_write(wdata);
|
||||
//
|
||||
// }
|
||||
//i2c_delay();
|
||||
//i2c_stop();
|
||||
ds_i2c_stop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t DS3930_read(uint8_t id, uint8_t addr, uint8_t* rdata)
|
||||
{
|
||||
ds_i2c_start();
|
||||
i2c_write_byte(id << 1); // Write address
|
||||
i2c_write_byte(addr); // Register address
|
||||
i2c_repeated_start();
|
||||
i2c_write_byte((id << 1) | 1); // Read address
|
||||
uint8_t data = i2c_read_byte(0); // Read data with NACK
|
||||
ds_i2c_stop();
|
||||
rdata[0] = data;
|
||||
DBG_PRINTF("Data 0x%x . \r\n", data);
|
||||
//i2c_stop();
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//void CAT24_write(uint8_t id, uint8_t addr, uint8_t* wdata)
|
||||
//{
|
||||
////i2c_start();
|
||||
//// ds_i2c_stop();
|
||||
// ds_i2c_start();
|
||||
// i2c_write_byte(id << 1); // Write address
|
||||
// i2c_write_byte(addr); // Register address
|
||||
// i2c_write_byte(wdata); // Data
|
||||
//
|
||||
//// mcp4725_i2c_write(id << 1);
|
||||
//// mcp4725_i2c_write(addr);
|
||||
//// mcp4725_i2c_write(wdata);
|
||||
////
|
||||
//// }
|
||||
////i2c_delay();
|
||||
////i2c_stop();
|
||||
// ds_i2c_stop();
|
||||
//}
|
||||
|
||||
|
||||
//uint8_t CAT24_read(uint8_t id, uint8_t addr, uint8_t* rdata,uint8_t length)
|
||||
//{
|
||||
// ds_i2c_start();
|
||||
// i2c_write_byte(id << 1); // Write address
|
||||
// i2c_write_byte(addr); // Register address
|
||||
// i2c_repeated_start();
|
||||
// i2c_write_byte((id << 1) | 1); // Read address
|
||||
// uint8_t data = i2c_read_byte(0); // Read data with NACK
|
||||
// ds_i2c_stop();
|
||||
// rdata[0] = data;
|
||||
// DBG_PRINTF("Data 0x%x . \r\n", data);
|
||||
// //i2c_stop();
|
||||
// return data;
|
||||
//}
|
||||
|
||||
92
project/ble_peripheral/ble_app_vivaMayo/mcp4725_i2c.h
Normal file
92
project/ble_peripheral/ble_app_vivaMayo/mcp4725_i2c.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*******************************************************************************
|
||||
* @file mcp4725_i2c.h
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nrf.h"
|
||||
#include "nrf_drv_gpiote.h"
|
||||
#ifndef _MCP4725_I2C_H_
|
||||
#define _MCP4725_I2C_H_
|
||||
|
||||
#define MCP4725_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15)//cj edit
|
||||
#define MCP4725_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,14) //cd eidt
|
||||
|
||||
#define SDA_H() nrf_gpio_pin_set(MCP4725_I2C_SDA_PIN) // SDA pin high
|
||||
#define SDA_L() nrf_gpio_pin_clear(MCP4725_I2C_SDA_PIN) // SDA pin low
|
||||
#define SCL_H() nrf_gpio_pin_set(MCP4725_I2C_SCL_PIN) // SCL pin high
|
||||
#define SCL_L() nrf_gpio_pin_clear(MCP4725_I2C_SCL_PIN) // SCL pin low
|
||||
#define SDA_OUT() nrf_gpio_cfg_output(MCP4725_I2C_SDA_PIN) // SDA pin output mode
|
||||
#define SCL_OUT() nrf_gpio_cfg_output(MCP4725_I2C_SCL_PIN) // SCL pin output mode
|
||||
#define SDA_IN() nrf_gpio_cfg_input(MCP4725_I2C_SDA_PIN, NRF_GPIO_PIN_NOPULL) // SDA pin input mode
|
||||
#define SDA_READ() nrf_gpio_pin_read(MCP4725_I2C_SDA_PIN) // SDA pin read
|
||||
#define SCL_READ() nrf_gpio_pin_read(MCP4725_I2C_SCL_PIN) // SCL pin read
|
||||
#define i2c_delay() nrf_delay_us(1) // delay time for 400khz clock 1
|
||||
#define i2c_clock() { i2c_delay(); SCL_H(); i2c_delay(); SCL_L(); }
|
||||
#define i2c_start() { SDA_H(); i2c_delay(); SCL_H(); i2c_delay(); SDA_L(); i2c_delay(); SCL_L(); i2c_delay();}
|
||||
#define i2c_stop() { SDA_L(); i2c_delay(); SCL_H(); i2c_delay(); SDA_H(); i2c_delay(); }
|
||||
|
||||
#define TWI_WRITE 0x00
|
||||
#define TWI_READ 0x01
|
||||
|
||||
#define ACK true
|
||||
#define NACK false
|
||||
|
||||
#define DAC_write false
|
||||
#define EEPROM_write true
|
||||
|
||||
// constants
|
||||
#define MCP4725_MAXVALUE 4095
|
||||
|
||||
// registerMode
|
||||
#define MCP4725_DAC 0x40
|
||||
#define MCP4725_DACEEPROM 0x60
|
||||
|
||||
// reset & wake up
|
||||
#define MCP4725_GC_RESET 0x06
|
||||
#define MCP4725_GC_WAKEUP 0x09
|
||||
|
||||
// powerDown Mode - TODO ENUM?
|
||||
#define MCP4725_PDMODE_NORMAL 0x00
|
||||
#define MCP4725_PDMODE_1K 0x01
|
||||
#define MCP4725_PDMODE_100K 0x02
|
||||
#define MCP4725_PDMODE_500K 0x03
|
||||
|
||||
void mcp4725_i2c_initialize(void);
|
||||
void mcp4725_writeFastMode(const uint16_t value);
|
||||
void mcp4725_generalCall(const uint8_t gc);
|
||||
void mcp4725_init(void);
|
||||
void mcp4725_powerOnReset(void);
|
||||
void mcp4725_powerOnWakeUp(void);
|
||||
void mcp4725_PowerDownMode(void);
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
uint16_t mcp4725_readDAC(void);
|
||||
bool mcp4725_ready(void);
|
||||
uint16_t mcp4725_readDAC(void);
|
||||
bool mcp4725_ready(void);
|
||||
|
||||
void mcp4725_writeRegisterMode(const uint16_t value, uint8_t reg);
|
||||
void mcp4725_readRegister(uint8_t* buffer, const uint8_t length);
|
||||
void mcp4725_setValue(const uint16_t value);
|
||||
uint16_t mcp4725_getValue(void);
|
||||
void mcp4725_setPercentage(float percentage);
|
||||
void mcp4725_writeDAC(const uint16_t value, const bool EEPROM);
|
||||
uint16_t mcp4725_readEEPROM(void);
|
||||
void mcp4725_writePowerDownMode(const uint8_t PDM, const bool EEPROM);
|
||||
uint8_t mcp4725_readPowerDownModeEEPROM(void);
|
||||
uint8_t mcp4725_readPowerDownModeDAC(void);
|
||||
|
||||
|
||||
|
||||
void DS3930_write(uint8_t id, uint8_t addr, uint8_t wdata);
|
||||
uint8_t DS3930_read(uint8_t id, uint8_t addr, uint8_t* rdata);
|
||||
|
||||
|
||||
#endif /* !_MCP4725_I2C_H_ */
|
||||
|
||||
905
project/ble_peripheral/ble_app_vivaMayo/meas_pd_48.c
Normal file
905
project/ble_peripheral/ble_app_vivaMayo/meas_pd_48.c
Normal file
@@ -0,0 +1,905 @@
|
||||
/*******************************************************************************
|
||||
* @file meas_pd_48.c
|
||||
* @brief M48 LED-PD measurement 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 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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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 <cmd_parse.h>
|
||||
#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));
|
||||
}
|
||||
265
project/ble_peripheral/ble_app_vivaMayo/meas_pd_48.h
Normal file
265
project/ble_peripheral/ble_app_vivaMayo/meas_pd_48.h
Normal file
@@ -0,0 +1,265 @@
|
||||
/*******************************************************************************
|
||||
* @file meas_pd_48.h
|
||||
* @brief M48 LED-PD Measurement Module Header 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 header defines the interface for the M48 measurement module which
|
||||
* controls 24/48 LED-PD pair measurements using SAADC with PPI triggering
|
||||
* from the ADA2200 Lock-in Amplifier SYNCO signal.
|
||||
*
|
||||
* Hardware Configuration:
|
||||
* - ADC Input: Differential measurement on AIN0/AIN1
|
||||
* - Trigger: ADA2200 SYNCO pin (P0.17) via GPIOTE/PPI
|
||||
* - Gain Control: MCP4725 DAC via I2C
|
||||
* - LED Control: CAT9532 via I2C
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _MEAS_PD_48_H__
|
||||
#define _MEAS_PD_48_H__
|
||||
|
||||
/*============================================================================*/
|
||||
/* Includes - Charles KWON */
|
||||
/*============================================================================*/
|
||||
#include "sdk_common.h"
|
||||
#include "nrf_drv_saadc.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* Hardware Pin Configuration - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief ADA2200 Lock-in Amplifier SYNCO output pin
|
||||
* - Charles KWON
|
||||
*
|
||||
* This pin provides the synchronization signal for ADC sampling.
|
||||
* Connected to GPIOTE for triggering SAADC via PPI.
|
||||
*/
|
||||
#ifndef ADA2200_SYNCO_PIN
|
||||
#define ADA2200_SYNCO_PIN NRF_GPIO_PIN_MAP(0, 17)
|
||||
#endif
|
||||
|
||||
/*============================================================================*/
|
||||
/* Measurement Configuration Constants - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Number of photodetectors in the system
|
||||
* - Charles KWON
|
||||
*
|
||||
* Currently configured for single PD operation (PD0).
|
||||
*/
|
||||
#define m48_PD_NO 1
|
||||
|
||||
/**
|
||||
* @brief Number of LEDs for full measurement sequence
|
||||
* - Charles KWON
|
||||
*
|
||||
* Full measurement uses 24 LEDs (LED0-LED23) with single PD.
|
||||
* Modified from original 48 LED configuration (cj edit 25/10/14).
|
||||
*/
|
||||
#define m48_LED_NO 24
|
||||
|
||||
/**
|
||||
* @brief Number of LEDs for half measurement sequence
|
||||
* - Charles KWON
|
||||
*
|
||||
* Half measurement uses 24 LEDs for either:
|
||||
* - Part A: LED0-LED23 (ADC_PD_MODE 3)
|
||||
* - Part B: LED24-LED47 (ADC_PD_MODE 4)
|
||||
*/
|
||||
#define m48_LED_NO_H 24
|
||||
|
||||
/**
|
||||
* @brief Number of ADC samples per LED measurement cycle
|
||||
* - Charles KWON
|
||||
*
|
||||
* Each LED measurement consists of 32 consecutive ADC samples
|
||||
* triggered by SYNCO signal for averaging/processing.
|
||||
*/
|
||||
#define m48_CYCLE_CNT 32
|
||||
|
||||
/*============================================================================*/
|
||||
/* PPI Configuration Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Initialize PPI channel for SYNCO-triggered ADC sampling
|
||||
* - Charles KWON
|
||||
*
|
||||
* Configures PPI to connect GPIOTE SYNCO event to SAADC SAMPLE task.
|
||||
* This enables hardware-triggered ADC sampling synchronized with
|
||||
* the ADA2200 lock-in amplifier output.
|
||||
*/
|
||||
void m48_ppi_init(void);
|
||||
|
||||
/**
|
||||
* @brief Uninitialize PPI channel
|
||||
* - Charles KWON
|
||||
*
|
||||
* Releases PPI resources after measurement completion.
|
||||
*/
|
||||
void m48_ppi_uninit(void);
|
||||
|
||||
/**
|
||||
* @brief Enable PPI sampling event
|
||||
* - Charles KWON
|
||||
*
|
||||
* Activates the SYNCO-to-SAADC PPI channel to begin
|
||||
* hardware-triggered sampling.
|
||||
*/
|
||||
void m48_sampling_event_enable(void);
|
||||
|
||||
/**
|
||||
* @brief Disable PPI sampling event
|
||||
* - Charles KWON
|
||||
*
|
||||
* Deactivates the PPI channel to stop hardware-triggered sampling.
|
||||
*/
|
||||
void m48_sampling_event_disable(void);
|
||||
|
||||
/*============================================================================*/
|
||||
/* SAADC Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief ADC interrupt handler for voltage measurements
|
||||
* - Charles KWON
|
||||
*
|
||||
* Callback function invoked when ADC buffer is full.
|
||||
* Processes raw ADC values and advances to next LED in sequence.
|
||||
*
|
||||
* @param[in] p_event Pointer to SAADC event structure containing
|
||||
* conversion results and event type
|
||||
*
|
||||
* @note This is a static function - declaration kept for documentation
|
||||
*/
|
||||
static void m48_voltage_handler(nrf_drv_saadc_evt_t const * p_event);
|
||||
|
||||
/**
|
||||
* @brief Initialize and start first ADC measurement sequence
|
||||
* - Charles KWON
|
||||
*
|
||||
* Entry point for M48 measurement. Initializes hardware,
|
||||
* configures first LED, and begins ADC sampling.
|
||||
*/
|
||||
void m48_adc_start_init(void);
|
||||
|
||||
/**
|
||||
* @brief Start ADC sampling for current LED
|
||||
* - Charles KWON
|
||||
*
|
||||
* Configures ADC buffer and enables sampling for the
|
||||
* currently selected LED in the measurement sequence.
|
||||
*/
|
||||
void m48_adc_start(void);
|
||||
|
||||
/**
|
||||
* @brief Start ADC sampling with secondary buffer
|
||||
* - Charles KWON
|
||||
*
|
||||
* Alternative start function using secondary ADC buffer
|
||||
* for double-buffered operation.
|
||||
*/
|
||||
void m48_adc_start2(void);
|
||||
|
||||
/**
|
||||
* @brief End current LED measurement and advance to next
|
||||
* - Charles KWON
|
||||
*
|
||||
* Called after ADC interrupt to transition to next LED
|
||||
* in the measurement sequence.
|
||||
*/
|
||||
void m48_adc_end(void);
|
||||
|
||||
/**
|
||||
* @brief Finalize measurement sequence
|
||||
* - Charles KWON
|
||||
*
|
||||
* Called when all LEDs have been measured. Sends results
|
||||
* over BLE and cleans up hardware resources.
|
||||
*/
|
||||
void m48_adc_end_final(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize SAADC for differential measurement
|
||||
* - Charles KWON
|
||||
*
|
||||
* Configures SAADC with:
|
||||
* - Differential input on AIN0 (positive) / AIN1 (negative)
|
||||
* - Internal reference (0.6V)
|
||||
* - 1/4 gain for optimal range
|
||||
* - 14-bit resolution
|
||||
*/
|
||||
void m48_adc_init(void);
|
||||
|
||||
/**
|
||||
* @brief Uninitialize SAADC
|
||||
* - Charles KWON
|
||||
*
|
||||
* Releases SAADC resources for use by other modules.
|
||||
*/
|
||||
void m48_adc_uninit(void);
|
||||
|
||||
/*============================================================================*/
|
||||
/* GPIO Interrupt Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Initialize GPIOTE for SYNCO signal detection
|
||||
* - Charles KWON
|
||||
*
|
||||
* Configures GPIOTE channel to detect rising edges on
|
||||
* ADA2200 SYNCO pin for PPI triggering.
|
||||
*/
|
||||
void m48_irq_init(void);
|
||||
|
||||
/**
|
||||
* @brief Uninitialize GPIOTE
|
||||
* - Charles KWON
|
||||
*
|
||||
* Releases GPIOTE resources after measurement completion.
|
||||
*/
|
||||
void m48_irq_uninit(void);
|
||||
|
||||
/*============================================================================*/
|
||||
/* Measurement Timer Functions - Charles KWON */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Measurement check timer callback
|
||||
* - Charles KWON
|
||||
*
|
||||
* Periodic timer callback for monitoring measurement progress
|
||||
* and handling timeout conditions.
|
||||
*
|
||||
* @param[in] p_context Unused timer context parameter
|
||||
*/
|
||||
void m48_check_loop(void * p_context);
|
||||
|
||||
/**
|
||||
* @brief Start measurement check timer
|
||||
* - Charles KWON
|
||||
*/
|
||||
void m48_check_timer_start(void);
|
||||
|
||||
/**
|
||||
* @brief Stop measurement check timer
|
||||
* - Charles KWON
|
||||
*/
|
||||
void m48_check_timer_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize measurement check timer
|
||||
* - Charles KWON
|
||||
*
|
||||
* Creates the timer used for measurement monitoring.
|
||||
* Must be called during system initialization.
|
||||
*/
|
||||
void m48_check_timer_init(void);
|
||||
|
||||
#endif /* _MEAS_PD_48_H__ */
|
||||
785
project/ble_peripheral/ble_app_vivaMayo/meas_pd_buff.c
Normal file
785
project/ble_peripheral/ble_app_vivaMayo/meas_pd_buff.c
Normal file
@@ -0,0 +1,785 @@
|
||||
/*******************************************************************************
|
||||
TEST medi50 Dec 23
|
||||
******************************************************************************/
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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 "fstorage.h"
|
||||
#include "measurements.h"
|
||||
#include "meas_pd_buff.h"
|
||||
#include "mcp4725_i2c.h"
|
||||
#include "ad5272_i2c.h"
|
||||
#include "main_timer.h"
|
||||
#include "battery_saadc.h"
|
||||
#include "main.h"
|
||||
#include "app_raw_main.h"
|
||||
|
||||
//#define CUSTOM_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
|
||||
//#define CUSTOM_PRE_SCALING_COMPENSATION 6.0f /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
|
||||
//#define CUSTOM_ADC_RES_10BITS 1024.0f /**< Maximum digital value for 10-bit ADC conversion. */
|
||||
//#define st_c_max 20
|
||||
///**@brief Macro to convert the result of ADC conversion in millivolts.
|
||||
// *
|
||||
// * @param[in] ADC_VALUE ADC result.
|
||||
// *
|
||||
// * @retval Result converted to millivolts.
|
||||
// */
|
||||
//#define CUSTOM_VOUT_IN_MILLI_VOLTS(ADC_VALUE)\
|
||||
// (((((ADC_VALUE) * CUSTOM_REF_VOLTAGE_IN_MILLIVOLTS) / CUSTOM_ADC_RES_10BITS) * CUSTOM_PRE_SCALING_COMPENSATION)*2)
|
||||
|
||||
uint16_t b_t_cnt=5;
|
||||
uint8_t buff_samples_in_buffer = 8;
|
||||
|
||||
//uint32_t s_cnt;
|
||||
uint8_t LED_list_buff[buff_LED_NO]={0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0
|
||||
,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0};
|
||||
//uint8_t PD_list_custom[4];
|
||||
|
||||
//uint8_t led_custom_list_a[5] = {2,3,4,5,6};
|
||||
//uint8_t pd_custom_list_a[4] = {5,7,8,10};
|
||||
|
||||
//uint8_t led_custom_list_b[5] = {20,21,22,23,24};
|
||||
//uint8_t pd_custom_list_b[4] = {16,14,13,11};
|
||||
|
||||
//int32_t c_max;
|
||||
extern volatile bool processing;
|
||||
extern volatile bool data_tx_in_progress;
|
||||
extern volatile bool ble_connection_st;
|
||||
|
||||
|
||||
uint32_t buff_cnt;
|
||||
static int8_t pd_no = -1;
|
||||
static int8_t led_no = -1;
|
||||
static int8_t buf_no = 0;
|
||||
static int8_t order = 0;
|
||||
uint16_t bsel_led_index0 =0;
|
||||
uint16_t bsel_led_index1 =1;
|
||||
uint16_t bsel_led_index2 =2;
|
||||
uint16_t bsel_led_index3 =3;
|
||||
|
||||
|
||||
static int32_t t_ms = 0;
|
||||
int16_t buff_cycle_buff[buff_LED_NO][buff_CYCLE_CNT] =
|
||||
{
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //0
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //3
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //4
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //5
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //6
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //7
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //8
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //9
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //10
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //3
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //4
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //5
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //6
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //7
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //8
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //9
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //20
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //3
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //4
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //5
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //6
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //7
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //8
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //9
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //30
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //3
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //4
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //5
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //6
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //7
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //8
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //9
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //40
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //3
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //4
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //5
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //6
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //7
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //8
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //9
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //50
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //3
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //4
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //5
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //6
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //7
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //8
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //9
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //60
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //3
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //4
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //5
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //6
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //7
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //8
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //9
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //70
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //3
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //4
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //5
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //6
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //7
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //8
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //9
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //80
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //3
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //4
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //5
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //6
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //7
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //8
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //9
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //90
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //3
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //4
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //5
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //6
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //7
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //8
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //9
|
||||
};
|
||||
int16_t buff_cycle_send_buff[buff_LED_NO] = {0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0
|
||||
,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0};
|
||||
//int16_t buff_cycle_send_buff1[buff_LED_NO] = {0, };
|
||||
//int16_t buff_cycle_send_buff2[buff_LED_NO] = {0, };
|
||||
//int16_t buff_cycle_send_buff3[buff_LED_NO] = {0, };
|
||||
//int16_t buff_cycle_send_buff4[buff_LED_NO] = {0, };
|
||||
//int16_t buff_cycle_send_buff5[buff_LED_NO] = {0, };
|
||||
//int16_t buff_cycle_send_buff6[buff_LED_NO] = {0, };
|
||||
//int16_t buff_cycle_send_buff7[buff_LED_NO] = {0, };
|
||||
//int16_t buff_cycle_send_buff8[buff_LED_NO] = {0, };
|
||||
//int16_t buff_cycle_send_buff9[buff_LED_NO] = {0, };
|
||||
uint16_t bi_buff_cycle_send_buff[60][buff_LED_NO] = {
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0
|
||||
,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0},
|
||||
};
|
||||
uint16_t single_bi_buff_cycle_send_buff[buff_LED_NO] =
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0
|
||||
,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0,0, 0, 0, 0, 0, 0, 0, 0, 0,0};
|
||||
#define SAMPLES_IN_BUFFER 4095+32
|
||||
static nrf_saadc_value_t pd_buff_adc_buf[2][SAMPLES_IN_BUFFER];
|
||||
|
||||
//bool pd_adc_custom_a_start = false;
|
||||
//bool pd_adc_custom_b_start = false;
|
||||
//bool pd_adc_custom_c_start = false;
|
||||
//bool pd_adc_custom_d_start = false;
|
||||
//bool pd_adc_custom_end = false;
|
||||
//bool pd_adc_custom_start = false;
|
||||
bool pd_adc_buff_start = false;
|
||||
bool pd_adc_buff_running = false;
|
||||
bool buff_testing = false;
|
||||
extern uint16_t m_pd_delay_us;
|
||||
//
|
||||
//bool custom_add_data;
|
||||
extern bool ble_got_new_data;
|
||||
extern bool motion_raw_data_enabled;
|
||||
//extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
uint8_t buff_bin_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
//add imu
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern which_cmd_t cmd_type_t;
|
||||
|
||||
APP_TIMER_DEF(m_buff_check_loop_timer_id);
|
||||
APP_TIMER_DEF(m_buff_send_loop_timer_id);
|
||||
#if FEATURE_DELAY
|
||||
#define CUSTOM_SEND_LOOP_INTERVAL 500
|
||||
#else
|
||||
#define BUFF_SEND_LOOP_INTERVAL 100
|
||||
#endif
|
||||
#define BUFF_CHECK_LOOP_INTERVAL 1
|
||||
|
||||
|
||||
|
||||
|
||||
static nrf_ppi_channel_t m_ppi_channel;
|
||||
|
||||
|
||||
|
||||
#if !FEATURE_PRINTF
|
||||
void buff_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();
|
||||
|
||||
/* setup ppi channel so that timer compare event is triggering sample task in SAADC */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void buff_ppi_uninit(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
err_code = nrf_drv_ppi_uninit();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
void buff_sampling_event_enable(void)
|
||||
{
|
||||
ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
void buff_sampling_event_disable(void)
|
||||
{
|
||||
ret_code_t err_code = nrf_drv_ppi_channel_disable(m_ppi_channel);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
static void buff_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* PD Voltage reading */
|
||||
{
|
||||
ret_code_t err_code;
|
||||
int16_t sum = 0;
|
||||
|
||||
uint32_t buff_clk_delay = m_pd_delay_us/16;
|
||||
if(ble_connection_st == 0) {
|
||||
//imm_adc_end();
|
||||
//custom_send_timer_stop();
|
||||
printf("Custom ADC STOP 1\r\n");
|
||||
}
|
||||
|
||||
else{
|
||||
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
|
||||
{
|
||||
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, buff_samples_in_buffer + buff_clk_delay);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
if(led_no == -1) {
|
||||
led_no = 0;
|
||||
pd_no = 0;
|
||||
|
||||
led_on(LED_list_buff[led_no]);
|
||||
led_pd_matching_value_set(LED_list_buff[led_no]); /* MCP4725 DAC setting and PD on */
|
||||
|
||||
} else {
|
||||
|
||||
for(uint16_t i = buff_clk_delay; i < buff_clk_delay + buff_samples_in_buffer; i++){
|
||||
buff_cycle_buff[buf_no][i-buff_clk_delay] = p_event->data.done.p_buffer[i];
|
||||
}
|
||||
#if FEATURE_PRINTF
|
||||
printf("-----------------Read ADC // led_no = %d(%d), pd_no = %d(%d), buf_no = %d\r\n\r\n", led_no, LED_list_custom[led_no], pd_no, PD_list_custom[pd_no], buf_no);
|
||||
#endif
|
||||
|
||||
buf_no++;
|
||||
|
||||
if(pd_no >= - 1) {
|
||||
//pd_no = 0;
|
||||
|
||||
|
||||
|
||||
if(led_no < buff_LED_NO - 1) {
|
||||
led_no++;
|
||||
led_on(LED_list_buff[led_no]);
|
||||
led_pd_matching_value_set(LED_list_buff[led_no]); /* MCP4725 DAC setting and PD on */
|
||||
|
||||
} else if(led_no >= buff_LED_NO - 1) {
|
||||
pd_no = -1;
|
||||
led_no = -1;
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\nEnded\r\n");
|
||||
#endif
|
||||
//imm_adc_end();
|
||||
//imm_adc_end_final();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t k =0;
|
||||
sum = 0;
|
||||
for(uint16_t i = 0; i < buff_LED_NO; i++){
|
||||
for(uint16_t j = 0; j < buff_samples_in_buffer; j++){
|
||||
sum += buff_cycle_buff[i][j];
|
||||
}
|
||||
|
||||
buff_cycle_send_buff[k++] = sum;
|
||||
sum = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
buf_no = 0;
|
||||
|
||||
if(ble_connection_st == 0) {
|
||||
|
||||
printf("Custom ADC STOP 1");
|
||||
pd_adc_buff_start=false;
|
||||
pd_adc_buff_running=false;
|
||||
printf ("FINISH SEND\r\n");
|
||||
processing = false;
|
||||
buff_testing = false;
|
||||
buff_adc_end_final();
|
||||
}
|
||||
else {
|
||||
|
||||
|
||||
if(cmd_type_t == CMD_UART) {
|
||||
//custom_send_timer_stop();
|
||||
|
||||
}else if(cmd_type_t == CMD_BLE) {
|
||||
// printf("%d ms \r\n",t_ms);
|
||||
//printf("value 4: %d,%d,%d,%d \r\n",imm_cycle_send_buff[0] ,imm_cycle_send_buff[1] ,imm_cycle_send_buff[2] ,imm_cycle_send_buff[3] );
|
||||
for(uint16_t i = 0; i < buff_LED_NO; i++){
|
||||
printf("%d,",buff_cycle_send_buff[i]);
|
||||
bi_buff_cycle_send_buff[buff_cnt][i]=(uint16_t)(buff_cycle_send_buff[i]);
|
||||
//single_bi_buff_cycle_send_buff[i]=(uint16_t)(buff_cycle_send_buff[i]);
|
||||
|
||||
}
|
||||
printf("%d ms \r\n",t_ms);
|
||||
// format_data(buff_bin_buffer, "rjb:", bi_buff_cycle_send_buff, 200);
|
||||
//
|
||||
//
|
||||
// binary_tx_handler(buff_bin_buffer,102);
|
||||
}
|
||||
|
||||
// if(ble_connection_st == 1) {
|
||||
// battery_timer_start();
|
||||
// }
|
||||
|
||||
led_off(99);
|
||||
pd_off(99);
|
||||
buff_adc_start_init();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void buff_check_loop(void * p_context)
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
buff_check_timer_stop();
|
||||
if ( buff_testing == false)
|
||||
{
|
||||
printf("%d ms \r\n",t_ms);
|
||||
// sprintf(custom_tx_buffer, "Measure Time :%d ms \r\n",t_ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
t_ms++;
|
||||
buff_check_timer_start();
|
||||
}
|
||||
}
|
||||
|
||||
void buff_send_loop(void * p_context) /* For x ms */
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
buff_send_timer_stop();
|
||||
|
||||
char resp[4];
|
||||
|
||||
if(ble_connection_st == BLE_DISCONNECTED_ST) {
|
||||
// order=0;
|
||||
// full_send_timer_stop();
|
||||
buff_adc_end_final();
|
||||
printf("BUFF ADC STOP 2\r\n");
|
||||
processing = false;
|
||||
}
|
||||
|
||||
else if(order<4){
|
||||
sprintf(resp,"r%02d:", order);
|
||||
format_data(buff_bin_buffer, resp, bi_buff_cycle_send_buff[order],100);
|
||||
binary_tx_handler(buff_bin_buffer,102);
|
||||
order++;
|
||||
buff_send_timer_start();
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("ddddd%d ms \r\n",t_ms);
|
||||
processing = false;
|
||||
buff_adc_end_final();
|
||||
//battery_timer_start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// switch(order) {
|
||||
// case 0:
|
||||
// sprintf(resp,"r%02X:", order);
|
||||
// format_data(buff_bin_buffer, resp, bi_buff_cycle_send_buff[0],100);
|
||||
// binary_tx_handler(buff_bin_buffer,102);
|
||||
// break;
|
||||
|
||||
// case 1:
|
||||
// format_data(buff_bin_buffer, "rb1:", bi_buff_cycle_send_buff[1],100);
|
||||
// binary_tx_handler(buff_bin_buffer,102);
|
||||
//
|
||||
// break;
|
||||
// case 2:
|
||||
// format_data(buff_bin_buffer, "rb2:", bi_buff_cycle_send_buff[2],100);
|
||||
// binary_tx_handler(buff_bin_buffer,102);
|
||||
//
|
||||
// break;
|
||||
|
||||
// case 3:
|
||||
// format_data(buff_bin_buffer, "rb3:", bi_buff_cycle_send_buff[3],100);
|
||||
// binary_tx_handler(buff_bin_buffer,102);
|
||||
// break;
|
||||
|
||||
|
||||
// case 4:
|
||||
// format_data(buff_bin_buffer, "rb4:", bi_buff_cycle_send_buff[4],100);
|
||||
// binary_tx_handler(buff_bin_buffer,102);
|
||||
// break;
|
||||
//
|
||||
// case 5:
|
||||
// format_data(buff_bin_buffer, "rb5:", bi_buff_cycle_send_buff[5],100);
|
||||
// binary_tx_handler(buff_bin_buffer,102);
|
||||
// break;
|
||||
//
|
||||
//
|
||||
// case 6:
|
||||
// format_data(buff_bin_buffer, "rb6:", bi_buff_cycle_send_buff[6],100);
|
||||
// binary_tx_handler(buff_bin_buffer,102);
|
||||
// break;
|
||||
|
||||
// case 7:
|
||||
// format_data(buff_bin_buffer, "rb7:", bi_buff_cycle_send_buff[7],100);
|
||||
// binary_tx_handler(buff_bin_buffer,102);
|
||||
//
|
||||
// break;
|
||||
// case 8:
|
||||
// format_data(buff_bin_buffer, "rb8:", bi_buff_cycle_send_buff[8],100);
|
||||
// binary_tx_handler(buff_bin_buffer,102);
|
||||
//
|
||||
// break;
|
||||
|
||||
// case 9:
|
||||
// format_data(buff_bin_buffer, "rb9:", bi_buff_cycle_send_buff[9],100);
|
||||
// binary_tx_handler(buff_bin_buffer,102);
|
||||
// break;
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// case 10:
|
||||
// printf("Measure Time : %d ms \r\n",t_ms);
|
||||
// //sprintf(full_tx_buffer, "Measure Time :%d ms \r\n",t_ms);
|
||||
// // data_tx_handler(full_tx_buffer);
|
||||
// order = 0;
|
||||
// return;
|
||||
//#if FEATURE_PRINTF
|
||||
// printf("full_send Completed\r\n");
|
||||
//#endif
|
||||
|
||||
|
||||
|
||||
// default:
|
||||
//
|
||||
// break;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
void buff_adc_start_init(void)
|
||||
{
|
||||
if (ble_got_new_data ==true)
|
||||
{
|
||||
pd_adc_buff_start=false;
|
||||
pd_adc_buff_running=false;
|
||||
printf ("FINISH SEND\r\n");
|
||||
led_off(99);
|
||||
pd_off(99);
|
||||
processing = false;
|
||||
buff_testing = false;
|
||||
buff_adc_end_final();
|
||||
}
|
||||
|
||||
else if (pd_adc_buff_running==true){
|
||||
|
||||
if(buff_cnt>=b_t_cnt){
|
||||
led_off(99);
|
||||
pd_off(99);
|
||||
pd_adc_buff_start=false;
|
||||
pd_adc_buff_running=false;
|
||||
printf ("FIFNISH SEND\r\n");
|
||||
|
||||
buff_testing = false;
|
||||
|
||||
order =0;
|
||||
buff_send_timer_start();
|
||||
//buff_adc_start();
|
||||
}
|
||||
else{
|
||||
buff_adc_start();
|
||||
buff_cnt++;
|
||||
}
|
||||
}
|
||||
else if (pd_adc_buff_start==true)
|
||||
{
|
||||
// custom_testing = false;
|
||||
// motion_raw_data_enabled = true;
|
||||
// custom_add_data = true;
|
||||
// icm42670_main();
|
||||
for(uint8_t i =0; i < 25; i++) {
|
||||
LED_list_buff[i*4] = bsel_led_index0;
|
||||
LED_list_buff[i*4+1] = bsel_led_index1;
|
||||
LED_list_buff[i*4+2] = bsel_led_index2;
|
||||
LED_list_buff[i*4+3] = bsel_led_index3;
|
||||
// data_tx_handler(ble_tx_buffer);
|
||||
}
|
||||
|
||||
for(uint8_t i =0; i < 60; i++) {
|
||||
for(uint8_t j =0; j < 100; j++) {
|
||||
bi_buff_cycle_send_buff[i][j]=0;
|
||||
}
|
||||
}
|
||||
t_ms=0;
|
||||
buff_cnt=0;
|
||||
pd_adc_buff_start=false;
|
||||
pd_adc_buff_running=true;
|
||||
buff_testing = true;
|
||||
// custom_check_timer_start();
|
||||
// c_cnt=0;
|
||||
// pd_adc_custom_start=false;
|
||||
// pd_adc_custom_a_start=true;
|
||||
|
||||
buff_adc_start2();
|
||||
buff_check_timer_start();
|
||||
// c_cnt++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void buff_adc_start(void)
|
||||
{
|
||||
pd_no = -1;
|
||||
led_no = -1;
|
||||
buf_no = 0;
|
||||
|
||||
|
||||
|
||||
for(uint16_t i = 0; i < buff_LED_NO; i++) {
|
||||
for(uint16_t j = 0; j < buff_CYCLE_CNT; j++) {
|
||||
buff_cycle_buff[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void buff_adc_start2(void)
|
||||
{
|
||||
buff_adc_start();
|
||||
buff_adc_init();
|
||||
buff_irq_init();
|
||||
buff_ppi_init();
|
||||
buff_sampling_event_enable();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void buff_adc_end(void)
|
||||
{
|
||||
|
||||
printf("adc_end\r\n");
|
||||
|
||||
buff_sampling_event_disable();
|
||||
|
||||
}
|
||||
|
||||
void buff_adc_end_final(void)
|
||||
{
|
||||
|
||||
printf("adc_end_for_good\r\n");
|
||||
|
||||
buff_sampling_event_disable();
|
||||
buff_irq_uninit();
|
||||
buff_ppi_uninit();
|
||||
buff_adc_uninit();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void buff_adc_init(void)
|
||||
{
|
||||
#if FEATURE_PRINTF
|
||||
printf("custom_adc_init\r\n");
|
||||
#endif
|
||||
|
||||
static nrfx_saadc_config_t default_config;
|
||||
default_config.resolution = (nrf_saadc_resolution_t)NRFX_SAADC_CONFIG_RESOLUTION; /* Resolution is 10bits */
|
||||
default_config.oversample = (nrf_saadc_oversample_t)NRFX_SAADC_CONFIG_OVERSAMPLE; /* Over Sampling Disabled */
|
||||
default_config.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY; /* Interrupt Priority is 0(Highest) */
|
||||
default_config.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE; /* Low Power Mode is Disabled */
|
||||
|
||||
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, buff_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_buff_adc_buf[0], buff_samples_in_buffer + m_pd_delay_us/16);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void buff_adc_uninit(void)
|
||||
{
|
||||
#if FEATURE_PRINTF
|
||||
printf("pd_custom_adc_uninit\r\n");
|
||||
#endif
|
||||
|
||||
nrf_drv_saadc_uninit();
|
||||
nrf_drv_saadc_channel_uninit(0);
|
||||
}
|
||||
|
||||
|
||||
#if !FEATURE_PRINTF
|
||||
void buff_irq_init(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
/* Initialize int pin */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void buff_irq_uninit(void)
|
||||
{
|
||||
nrfx_gpiote_in_event_disable(ADA2200_SYNCO_PIN);
|
||||
nrfx_gpiote_in_uninit(ADA2200_SYNCO_PIN);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void buff_send_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_buff_send_loop_timer_id, APP_TIMER_TICKS(BUFF_SEND_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
|
||||
void buff_send_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_buff_send_loop_timer_id));
|
||||
|
||||
}
|
||||
|
||||
|
||||
void buff_send_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_buff_send_loop_timer_id, APP_TIMER_MODE_SINGLE_SHOT, buff_send_loop));
|
||||
}
|
||||
|
||||
|
||||
void buff_check_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_buff_check_loop_timer_id, APP_TIMER_TICKS(BUFF_CHECK_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
void buff_check_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_buff_check_loop_timer_id));
|
||||
|
||||
}
|
||||
void buff_check_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_buff_check_loop_timer_id, APP_TIMER_MODE_SINGLE_SHOT, buff_check_loop));
|
||||
}
|
||||
60
project/ble_peripheral/ble_app_vivaMayo/meas_pd_buff.h
Normal file
60
project/ble_peripheral/ble_app_vivaMayo/meas_pd_buff.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*******************************************************************************
|
||||
* @file meas_pd_buff.h
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _MEAS_PD_BUFF_H__
|
||||
#define _MEAS_PD_BUFF_H__
|
||||
|
||||
#include "sdk_common.h"
|
||||
#include "nrf_drv_saadc.h"
|
||||
|
||||
#ifndef ADA2200_SYNCO_PIN
|
||||
#define ADA2200_SYNCO_PIN NRF_GPIO_PIN_MAP(0,17)
|
||||
#endif
|
||||
|
||||
|
||||
#define buff_PD_NO 1/////4
|
||||
#define buff_LED_NO 100////5
|
||||
#define buff_CYCLE_CNT 8 ////32
|
||||
|
||||
|
||||
void buff_ppi_init(void);
|
||||
void buff_ppi_uninit(void);
|
||||
void buff_sampling_event_enable(void);
|
||||
void buff_sampling_event_disable(void);
|
||||
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
static void buff_voltage_handler(nrf_drv_saadc_evt_t const * p_event); /* PD Voltage reading */
|
||||
void buff_adc_start_init(void);
|
||||
void buff_adc_start(void);
|
||||
void buff_adc_start2(void);
|
||||
//void custom_adc_start2(void);
|
||||
//void custom_adc_total_start(void);
|
||||
void buff_adc_end(void);
|
||||
void buff_adc_end_final(void);
|
||||
void buff_adc_init(void);
|
||||
void buff_adc_uninit(void);
|
||||
|
||||
void buff_irq_init(void);
|
||||
void buff_irq_uninit(void);
|
||||
|
||||
void buff_send_start(void);
|
||||
|
||||
void buff_send_loop(void * p_context); /* For x ms */
|
||||
void buff_send_timer_start(void);
|
||||
void buff_send_timer_stop(void);;
|
||||
void buff_send_timer_init(void);
|
||||
|
||||
|
||||
void buff_check_loop(void * p_context);
|
||||
void buff_check_timer_start(void);
|
||||
void buff_check_timer_stop(void);;
|
||||
void buff_check_timer_init(void);
|
||||
|
||||
#endif /* _MEAS_PD_buff_H__ */
|
||||
|
||||
522
project/ble_peripheral/ble_app_vivaMayo/meas_pd_imm.c
Normal file
522
project/ble_peripheral/ble_app_vivaMayo/meas_pd_imm.c
Normal file
@@ -0,0 +1,522 @@
|
||||
/*******************************************************************************
|
||||
TEST medi50 Dec 23
|
||||
******************************************************************************/
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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 "fstorage.h"
|
||||
#include "measurements.h"
|
||||
#include "meas_pd_imm.h"
|
||||
#include "mcp4725_i2c.h"
|
||||
//#include "ad5272_i2c.h"
|
||||
#include "main_timer.h"
|
||||
#include "battery_saadc.h"
|
||||
#include "main.h"
|
||||
#include "app_raw_main.h"
|
||||
#include "debug_print.h"
|
||||
//#define CUSTOM_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
|
||||
//#define CUSTOM_PRE_SCALING_COMPENSATION 6.0f /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
|
||||
//#define CUSTOM_ADC_RES_10BITS 1024.0f /**< Maximum digital value for 10-bit ADC conversion. */
|
||||
//#define st_c_max 20
|
||||
///**@brief Macro to convert the result of ADC conversion in millivolts.
|
||||
// *
|
||||
// * @param[in] ADC_VALUE ADC result.
|
||||
// *
|
||||
// * @retval Result converted to millivolts.
|
||||
// */
|
||||
//#define CUSTOM_VOUT_IN_MILLI_VOLTS(ADC_VALUE)\
|
||||
// (((((ADC_VALUE) * CUSTOM_REF_VOLTAGE_IN_MILLIVOLTS) / CUSTOM_ADC_RES_10BITS) * CUSTOM_PRE_SCALING_COMPENSATION)*2)
|
||||
|
||||
|
||||
static const uint8_t imm_samples_in_buffer = 8;
|
||||
|
||||
//uint32_t s_cnt;
|
||||
uint8_t LED_list_imm[4];
|
||||
//uint8_t PD_list_custom[4];
|
||||
|
||||
//uint8_t led_custom_list_a[5] = {2,3,4,5,6};
|
||||
//uint8_t pd_custom_list_a[4] = {5,7,8,10};
|
||||
|
||||
//uint8_t led_custom_list_b[5] = {20,21,22,23,24};
|
||||
//uint8_t pd_custom_list_b[4] = {16,14,13,11};
|
||||
|
||||
int32_t c_max;
|
||||
extern volatile bool processing;
|
||||
extern volatile bool data_tx_in_progress;
|
||||
extern volatile bool ble_connection_st;
|
||||
|
||||
extern uint16_t m_pd_delay_us;
|
||||
uint32_t c_cnt;
|
||||
static int8_t pd_no = -1;
|
||||
static int8_t led_no = -1;
|
||||
static int8_t buf_no = 0;
|
||||
|
||||
uint16_t sel_led_index0 =0;
|
||||
uint16_t sel_led_index1 =1;
|
||||
uint16_t sel_led_index2 =2;
|
||||
uint16_t sel_led_index3 =3;
|
||||
|
||||
//static int8_t c_max = 5;
|
||||
static int32_t t_ms = 0;
|
||||
int16_t imm_cycle_buff[4][imm_CYCLE_CNT] =
|
||||
{
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //0
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0} //3
|
||||
};
|
||||
int16_t imm_cycle_send_buff[imm_LED_NO] = {0, 0, 0, 0};
|
||||
uint16_t bi_imm_cycle_send_buff[imm_LED_NO] = {0, 0, 0, 0};
|
||||
|
||||
#define SAMPLES_IN_BUFFER 4095+32
|
||||
static nrf_saadc_value_t pd_imm_adc_buf[2][SAMPLES_IN_BUFFER];
|
||||
|
||||
bool pd_adc_custom_a_start = false;
|
||||
bool pd_adc_custom_b_start = false;
|
||||
bool pd_adc_custom_c_start = false;
|
||||
bool pd_adc_custom_d_start = false;
|
||||
bool pd_adc_custom_end = false;
|
||||
bool pd_adc_custom_start = false;
|
||||
bool pd_adc_imm_start = false;
|
||||
bool pd_adc_imm_running = false;
|
||||
bool custom_testing = false;
|
||||
|
||||
//
|
||||
bool custom_add_data;
|
||||
extern bool ble_got_new_data;
|
||||
extern bool motion_raw_data_enabled;
|
||||
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
uint8_t imm_bin_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
//add imu
|
||||
uint8_t order_imm=0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern which_cmd_t cmd_type_t;
|
||||
|
||||
APP_TIMER_DEF(m_custom_check_loop_timer_id);
|
||||
//APP_TIMER_DEF(m_custom_send_loop_timer_id);
|
||||
#if FEATURE_DELAY
|
||||
#define CUSTOM_SEND_LOOP_INTERVAL 500
|
||||
#else
|
||||
#define CUSTOM_SEND_LOOP_INTERVAL 100
|
||||
#endif
|
||||
#define CUSTOM_CHECK_LOOP_INTERVAL 1
|
||||
|
||||
|
||||
|
||||
|
||||
static nrf_ppi_channel_t m_ppi_channel;
|
||||
|
||||
|
||||
|
||||
#if !FEATURE_PRINTF
|
||||
void imm_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();
|
||||
|
||||
/* setup ppi channel so that timer compare event is triggering sample task in SAADC */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void imm_ppi_uninit(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
err_code = nrf_drv_ppi_uninit();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
void imm_sampling_event_enable(void)
|
||||
{
|
||||
ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
void imm_sampling_event_disable(void)
|
||||
{
|
||||
ret_code_t err_code = nrf_drv_ppi_channel_disable(m_ppi_channel);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
static void imm_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* PD Voltage reading */
|
||||
{
|
||||
ret_code_t err_code;
|
||||
int16_t sum = 0;
|
||||
|
||||
uint32_t custom_clk_delay = m_pd_delay_us/16;
|
||||
if(ble_connection_st == 0) {
|
||||
//imm_adc_end();
|
||||
//custom_send_timer_stop();
|
||||
DBG_PRINTF("Custom ADC STOP 1\r\n");
|
||||
}
|
||||
|
||||
else{
|
||||
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
|
||||
{
|
||||
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, imm_samples_in_buffer + custom_clk_delay);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
if(led_no == -1) {
|
||||
led_no = 0;
|
||||
pd_no = 0;
|
||||
|
||||
led_on(LED_list_imm[led_no]);
|
||||
led_pd_matching_value_set(LED_list_imm[led_no]); /* MCP4725 DAC setting and PD on */
|
||||
|
||||
} else {
|
||||
|
||||
for(uint16_t i = custom_clk_delay; i < custom_clk_delay + imm_samples_in_buffer; i++){
|
||||
imm_cycle_buff[buf_no][i-custom_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_custom[led_no], pd_no, PD_list_custom[pd_no], buf_no);
|
||||
#endif
|
||||
|
||||
buf_no++;
|
||||
|
||||
if(pd_no >= - 1) {
|
||||
//pd_no = 0;
|
||||
|
||||
|
||||
|
||||
if(led_no < imm_LED_NO - 1) {
|
||||
led_no++;
|
||||
led_on(LED_list_imm[led_no]);
|
||||
led_pd_matching_value_set(LED_list_imm[led_no]); /* MCP4725 DAC setting and PD on */
|
||||
|
||||
} else if(led_no >= imm_LED_NO - 1) {
|
||||
pd_no = -1;
|
||||
led_no = -1;
|
||||
#if FEATURE_PRINTF
|
||||
DBG_PRINTF("\r\nEnded\r\n");
|
||||
#endif
|
||||
//imm_adc_end();
|
||||
//imm_adc_end_final();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t k =0;
|
||||
sum = 0;
|
||||
for(uint16_t i = 0; i < 4; i++){
|
||||
for(uint16_t j = 0; j < imm_samples_in_buffer; j++){
|
||||
sum += imm_cycle_buff[i][j];
|
||||
}
|
||||
|
||||
imm_cycle_send_buff[k++] = sum;
|
||||
sum = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
buf_no = 0;
|
||||
|
||||
if(ble_connection_st == 0) {
|
||||
|
||||
DBG_PRINTF("Custom ADC STOP 1");
|
||||
pd_adc_imm_start=false;
|
||||
pd_adc_imm_running=false;
|
||||
DBG_PRINTF ("FINISH SEND\r\n");
|
||||
processing = false;
|
||||
custom_testing = false;
|
||||
imm_adc_end_final();
|
||||
}
|
||||
else {
|
||||
char resp[4];
|
||||
|
||||
if(cmd_type_t == CMD_UART) {
|
||||
//custom_send_timer_stop();
|
||||
DBG_PRINTF("value 4: %d,%d,%d,%d \r\n",imm_cycle_send_buff[0] ,imm_cycle_send_buff[1] ,imm_cycle_send_buff[2] ,imm_cycle_send_buff[3] );
|
||||
}else if(cmd_type_t == CMD_BLE) {
|
||||
DBG_PRINTF("%d ms \r\n",t_ms);
|
||||
DBG_PRINTF("value 4: %d,%d,%d,%d \r\n",imm_cycle_send_buff[0] ,imm_cycle_send_buff[1] ,imm_cycle_send_buff[2] ,imm_cycle_send_buff[3] );
|
||||
for(uint16_t i = 0; i < 4; i++){
|
||||
bi_imm_cycle_send_buff[i]=(uint16_t)(imm_cycle_send_buff[i]);
|
||||
}
|
||||
sprintf(resp,"rF%01X:", order_imm);
|
||||
format_data(imm_bin_buffer, resp, bi_imm_cycle_send_buff, 4);
|
||||
|
||||
|
||||
binary_tx_handler(imm_bin_buffer,6);
|
||||
}
|
||||
|
||||
// if(ble_connection_st == 1) {
|
||||
// battery_timer_start();
|
||||
// }
|
||||
|
||||
// led_off(99);
|
||||
// pd_off(99);
|
||||
imm_adc_start_init();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void imm_check_loop(void * p_context)
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
imm_check_timer_stop();
|
||||
if ( custom_testing == false)
|
||||
{
|
||||
DBG_PRINTF("%d ms \r\n",t_ms);
|
||||
// sprintf(custom_tx_buffer, "Measure Time :%d ms \r\n",t_ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
t_ms++;
|
||||
imm_check_timer_start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void imm_adc_start_init(void)
|
||||
{
|
||||
if (ble_got_new_data ==true)
|
||||
{
|
||||
pd_adc_imm_start=false;
|
||||
pd_adc_imm_running=false;
|
||||
DBG_PRINTF ("FINISH SEND\r\n");
|
||||
led_off(99);
|
||||
pd_off(99);
|
||||
processing = false;
|
||||
custom_testing = false;
|
||||
imm_adc_end_final();
|
||||
}
|
||||
|
||||
else if (pd_adc_imm_running==true){
|
||||
imm_adc_start();
|
||||
}
|
||||
|
||||
else if (pd_adc_imm_start==true)
|
||||
{
|
||||
// custom_testing = false;
|
||||
// motion_raw_data_enabled = true;
|
||||
// custom_add_data = true;
|
||||
// icm42670_main();
|
||||
//for(uint8_t i =0; i < imm_LED_NO; i++) {
|
||||
LED_list_imm[0] = sel_led_index0;
|
||||
LED_list_imm[1] = sel_led_index1;
|
||||
LED_list_imm[2] = sel_led_index2;
|
||||
LED_list_imm[3] = sel_led_index3;
|
||||
// data_tx_handler(ble_tx_buffer);
|
||||
|
||||
t_ms=0;
|
||||
pd_adc_imm_start=false;
|
||||
pd_adc_imm_running=true;
|
||||
custom_testing = true;
|
||||
// custom_check_timer_start();
|
||||
c_cnt=0;
|
||||
// pd_adc_custom_start=false;
|
||||
// pd_adc_custom_a_start=true;
|
||||
|
||||
imm_adc_start2();
|
||||
imm_check_timer_start();
|
||||
c_cnt++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void imm_adc_start(void)
|
||||
{
|
||||
pd_no = -1;
|
||||
led_no = -1;
|
||||
buf_no = 0;
|
||||
t_ms=0;
|
||||
|
||||
|
||||
for(uint16_t i = 0; i < 4; i++) {
|
||||
for(uint16_t j = 0; j < imm_CYCLE_CNT; j++) {
|
||||
imm_cycle_buff[i][j] = 0;
|
||||
}
|
||||
}
|
||||
if(order_imm>=15){
|
||||
order_imm=0;
|
||||
}
|
||||
else
|
||||
{ order_imm++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void imm_adc_start2(void)
|
||||
{
|
||||
imm_adc_start();
|
||||
imm_adc_init();
|
||||
imm_irq_init();
|
||||
imm_ppi_init();
|
||||
imm_sampling_event_enable();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void imm_adc_end(void)
|
||||
{
|
||||
|
||||
DBG_PRINTF("adc_end\r\n");
|
||||
|
||||
imm_sampling_event_disable();
|
||||
|
||||
}
|
||||
|
||||
void imm_adc_end_final(void)
|
||||
{
|
||||
|
||||
DBG_PRINTF("adc_end_for_good\r\n");
|
||||
|
||||
imm_sampling_event_disable();
|
||||
imm_irq_uninit();
|
||||
imm_ppi_uninit();
|
||||
imm_adc_uninit();
|
||||
battery_timer_start();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void imm_adc_init(void)
|
||||
{
|
||||
#if FEATURE_PRINTF
|
||||
DBG_PRINTF("custom_adc_init\r\n");
|
||||
#endif
|
||||
|
||||
static nrfx_saadc_config_t default_config;
|
||||
default_config.resolution = (nrf_saadc_resolution_t)NRFX_SAADC_CONFIG_RESOLUTION; /* Resolution is 10bits */
|
||||
default_config.oversample = (nrf_saadc_oversample_t)NRFX_SAADC_CONFIG_OVERSAMPLE; /* Over Sampling Disabled */
|
||||
default_config.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY; /* Interrupt Priority is 0(Highest) */
|
||||
default_config.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE; /* Low Power Mode is Disabled */
|
||||
|
||||
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, imm_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_imm_adc_buf[0], imm_samples_in_buffer + m_pd_delay_us/16);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void imm_adc_uninit(void)
|
||||
{
|
||||
#if FEATURE_PRINTF
|
||||
DBG_PRINTF("pd_custom_adc_uninit\r\n");
|
||||
#endif
|
||||
|
||||
nrf_drv_saadc_uninit();
|
||||
nrf_drv_saadc_channel_uninit(0);
|
||||
}
|
||||
|
||||
|
||||
#if !FEATURE_PRINTF
|
||||
void imm_irq_init(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
/* Initialize int pin */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void imm_irq_uninit(void)
|
||||
{
|
||||
nrfx_gpiote_in_event_disable(ADA2200_SYNCO_PIN);
|
||||
nrfx_gpiote_in_uninit(ADA2200_SYNCO_PIN);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void imm_check_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_custom_check_loop_timer_id, APP_TIMER_TICKS(CUSTOM_CHECK_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
void imm_check_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_custom_check_loop_timer_id));
|
||||
|
||||
}
|
||||
void imm_check_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_custom_check_loop_timer_id, APP_TIMER_MODE_SINGLE_SHOT, imm_check_loop));
|
||||
}
|
||||
60
project/ble_peripheral/ble_app_vivaMayo/meas_pd_imm.h
Normal file
60
project/ble_peripheral/ble_app_vivaMayo/meas_pd_imm.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*******************************************************************************
|
||||
* @file meas_pd_imm.h
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _MEAS_PD_IMM_H__
|
||||
#define _MEAS_PD_IMM_H__
|
||||
|
||||
#include "sdk_common.h"
|
||||
#include "nrf_drv_saadc.h"
|
||||
|
||||
#ifndef ADA2200_SYNCO_PIN
|
||||
#define ADA2200_SYNCO_PIN NRF_GPIO_PIN_MAP(0,17)
|
||||
#endif
|
||||
|
||||
|
||||
#define imm_PD_NO 1/////4
|
||||
#define imm_LED_NO 4////5
|
||||
#define imm_CYCLE_CNT 8 ////32
|
||||
|
||||
|
||||
void imm_ppi_init(void);
|
||||
void imm_ppi_uninit(void);
|
||||
void imm_sampling_event_enable(void);
|
||||
void imm_sampling_event_disable(void);
|
||||
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
static void imm_voltage_handler(nrf_drv_saadc_evt_t const * p_event); /* PD Voltage reading */
|
||||
void imm_adc_start_init(void);
|
||||
void imm_adc_start(void);
|
||||
void imm_adc_start2(void);
|
||||
//void custom_adc_start2(void);
|
||||
//void custom_adc_total_start(void);
|
||||
void imm_adc_end(void);
|
||||
void imm_adc_end_final(void);
|
||||
void imm_adc_init(void);
|
||||
void imm_adc_uninit(void);
|
||||
|
||||
void imm_irq_init(void);
|
||||
void imm_irq_uninit(void);
|
||||
|
||||
//void custom_send_start(void);
|
||||
|
||||
//void custom_send_loop(void * p_context); /* For x ms */
|
||||
//void custom_send_timer_start(void);
|
||||
//void custom_send_timer_stop(void);;
|
||||
//void custom_send_timer_init(void);
|
||||
|
||||
|
||||
void imm_check_loop(void * p_context);
|
||||
void imm_check_timer_start(void);
|
||||
void imm_check_timer_stop(void);;
|
||||
void imm_check_timer_init(void);
|
||||
|
||||
#endif /* _MEAS_PD_VOLTAGE_CUSTOM_H__ */
|
||||
|
||||
885
project/ble_peripheral/ble_app_vivaMayo/meas_pd_voltage_custom.c
Normal file
885
project/ble_peripheral/ble_app_vivaMayo/meas_pd_voltage_custom.c
Normal file
@@ -0,0 +1,885 @@
|
||||
/*******************************************************************************
|
||||
TEST medi50 Dec 23
|
||||
******************************************************************************/
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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 "fstorage.h"
|
||||
#include "measurements.h"
|
||||
#include "meas_pd_voltage_custom.h"
|
||||
#include "mcp4725_i2c.h"
|
||||
#include "ad5272_i2c.h"
|
||||
#include "main_timer.h"
|
||||
#include "battery_saadc.h"
|
||||
#include "main.h"
|
||||
#include "app_raw_main.h"
|
||||
|
||||
#define CUSTOM_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
|
||||
#define CUSTOM_PRE_SCALING_COMPENSATION 6.0f /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
|
||||
#define CUSTOM_ADC_RES_10BITS 1024.0f /**< Maximum digital value for 10-bit ADC conversion. */
|
||||
#define st_c_max 20
|
||||
/**@brief Macro to convert the result of ADC conversion in millivolts.
|
||||
*
|
||||
* @param[in] ADC_VALUE ADC result.
|
||||
*
|
||||
* @retval Result converted to millivolts.
|
||||
*/
|
||||
#define CUSTOM_VOUT_IN_MILLI_VOLTS(ADC_VALUE)\
|
||||
(((((ADC_VALUE) * CUSTOM_REF_VOLTAGE_IN_MILLIVOLTS) / CUSTOM_ADC_RES_10BITS) * CUSTOM_PRE_SCALING_COMPENSATION)*2)
|
||||
|
||||
|
||||
uint8_t custom_samples_in_buffer = 8;
|
||||
|
||||
uint32_t s_cnt;
|
||||
uint8_t LED_list_custom[5];
|
||||
uint8_t PD_list_custom[4];
|
||||
|
||||
uint8_t led_custom_list_a[5] = {2,3,4,5,6};
|
||||
uint8_t pd_custom_list_a[4] = {5,7,8,10};
|
||||
|
||||
uint8_t led_custom_list_b[5] = {20,21,22,23,24};
|
||||
uint8_t pd_custom_list_b[4] = {16,14,13,11};
|
||||
|
||||
int32_t c_max;
|
||||
extern volatile bool processing;
|
||||
extern volatile bool data_tx_in_progress;
|
||||
extern volatile bool ble_connection_st;
|
||||
|
||||
|
||||
uint32_t c_cnt;
|
||||
static int8_t pd_no = -1;
|
||||
static int8_t led_no = -1;
|
||||
static int8_t buf_no = 0;
|
||||
//static int8_t c_max = 5;
|
||||
static int32_t t_ms = 0;
|
||||
double custom_cycle_buff[36][CUSTOM_CYCLE_CNT] =
|
||||
{
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //0
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //3
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //4
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //5
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //6
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //7
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //8
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //9
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //10
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //11
|
||||
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //12
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //13
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //14
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //15
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //16
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //17
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //18
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //19
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //20
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //21
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //22
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //23
|
||||
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //24
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //25
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //26
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //27
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //28
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //29
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //30
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //31
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //32
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //33
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //34
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //35
|
||||
};
|
||||
double custom_cycle_send_buff[CUSTOM_LED_NO][CUSTOM_PD_NO] =
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
|
||||
#define SAMPLES_IN_BUFFER 4095+32
|
||||
static nrf_saadc_value_t pd_custom_adc_buf[2][SAMPLES_IN_BUFFER];
|
||||
|
||||
bool pd_adc_custom_a_start = false;
|
||||
bool pd_adc_custom_b_start = false;
|
||||
bool pd_adc_custom_c_start = false;
|
||||
bool pd_adc_custom_d_start = false;
|
||||
bool pd_adc_custom_end = false;
|
||||
bool pd_adc_custom_start = false;
|
||||
|
||||
bool custom_testing = false;
|
||||
|
||||
//
|
||||
bool custom_add_data;
|
||||
extern bool motion_raw_data_enabled;
|
||||
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
//add imu
|
||||
|
||||
|
||||
|
||||
char custom_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
|
||||
|
||||
|
||||
char custom_tx_d_buffer_0[st_c_max][BLE_NUS_MAX_DATA_LEN];
|
||||
char custom_tx_d_buffer_1[st_c_max][BLE_NUS_MAX_DATA_LEN];
|
||||
char custom_tx_d_buffer_2[st_c_max][BLE_NUS_MAX_DATA_LEN];
|
||||
char custom_tx_d_buffer_3[st_c_max][BLE_NUS_MAX_DATA_LEN];
|
||||
char custom_tx_d_buffer_4[st_c_max][BLE_NUS_MAX_DATA_LEN];
|
||||
|
||||
char custom_tx_d_buffer_5[st_c_max][BLE_NUS_MAX_DATA_LEN];
|
||||
char custom_tx_d_buffer_6[st_c_max][BLE_NUS_MAX_DATA_LEN];
|
||||
char custom_tx_d_buffer_7[st_c_max][BLE_NUS_MAX_DATA_LEN];
|
||||
char custom_tx_d_buffer_8[st_c_max][BLE_NUS_MAX_DATA_LEN];
|
||||
char custom_tx_d_buffer_9[st_c_max][BLE_NUS_MAX_DATA_LEN];
|
||||
|
||||
|
||||
|
||||
extern which_cmd_t cmd_type_t;
|
||||
|
||||
APP_TIMER_DEF(m_custom_check_loop_timer_id);
|
||||
APP_TIMER_DEF(m_custom_send_loop_timer_id);
|
||||
#if FEATURE_DELAY
|
||||
#define CUSTOM_SEND_LOOP_INTERVAL 500
|
||||
#else
|
||||
#define CUSTOM_SEND_LOOP_INTERVAL 100
|
||||
#endif
|
||||
#define CUSTOM_CHECK_LOOP_INTERVAL 1
|
||||
|
||||
|
||||
|
||||
#if FEATURE_DEBUG_REPEAT_TEST
|
||||
extern uint32_t rpt_cnt;
|
||||
#endif
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
APP_TIMER_DEF(m_custom_loop_timer_id);
|
||||
#define CUSTOM_LOOP_INTERVAL 1
|
||||
|
||||
void custom_timer_loop(void * p_context)
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
|
||||
ret_code_t err_code = nrf_drv_saadc_sample();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
void custom_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_custom_loop_timer_id, APP_TIMER_TICKS(CUSTOM_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
void custom_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_custom_loop_timer_id));
|
||||
}
|
||||
|
||||
void custom_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_custom_loop_timer_id, APP_TIMER_MODE_REPEATED, custom_timer_loop));
|
||||
}
|
||||
#else
|
||||
static nrf_ppi_channel_t m_ppi_channel;
|
||||
#endif
|
||||
|
||||
|
||||
#if !FEATURE_PRINTF
|
||||
void custom_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();
|
||||
|
||||
/* setup ppi channel so that timer compare event is triggering sample task in SAADC */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void custom_ppi_uninit(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
err_code = nrf_drv_ppi_uninit();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
void custom_sampling_event_enable(void)
|
||||
{
|
||||
ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
void custom_sampling_event_disable(void)
|
||||
{
|
||||
ret_code_t err_code = nrf_drv_ppi_channel_disable(m_ppi_channel);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
static void custom_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* PD Voltage reading */
|
||||
{
|
||||
ret_code_t err_code;
|
||||
double sum = 0.0f;
|
||||
|
||||
uint32_t custom_clk_delay = m_config.pd_delay_us/16;
|
||||
if(ble_connection_st == 0) {
|
||||
custom_adc_end();
|
||||
custom_send_timer_stop();
|
||||
printf("Custom ADC STOP 1\r\n");
|
||||
}
|
||||
|
||||
else{
|
||||
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
|
||||
{
|
||||
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, custom_samples_in_buffer + custom_clk_delay);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
if(led_no == -1) {
|
||||
led_no = 0;
|
||||
pd_no = 0;
|
||||
|
||||
led_on(LED_list_custom[led_no]);
|
||||
led_pd_matching_value_set(LED_list_custom[led_no]); /* MCP4725 DAC setting and PD on */
|
||||
|
||||
} else {
|
||||
|
||||
for(uint16_t i = custom_clk_delay; i < custom_clk_delay + custom_samples_in_buffer; i++){
|
||||
custom_cycle_buff[buf_no][i-custom_clk_delay] = (CUSTOM_VOUT_IN_MILLI_VOLTS(p_event->data.done.p_buffer[i])) * -1;
|
||||
}
|
||||
#if FEATURE_PRINTF
|
||||
printf("-----------------Read ADC // led_no = %d(%d), pd_no = %d(%d), buf_no = %d\r\n\r\n", led_no, LED_list_custom[led_no], pd_no, PD_list_custom[pd_no], buf_no);
|
||||
#endif
|
||||
|
||||
buf_no++;
|
||||
|
||||
if(pd_no < CUSTOM_PD_NO - 1) {
|
||||
pd_no++;
|
||||
led_pd_matching_value_set(LED_list_custom[led_no]); /* MCP4725 DAC setting and PD on */
|
||||
|
||||
}else if(pd_no >= CUSTOM_PD_NO - 1) {
|
||||
pd_no = 0;
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n\r\n");
|
||||
#endif
|
||||
|
||||
if(led_no < CUSTOM_LED_NO - 1) {
|
||||
led_no++;
|
||||
led_on(LED_list_custom[led_no]);
|
||||
led_pd_matching_value_set(LED_list_custom[led_no]); /* MCP4725 DAC setting and PD on */
|
||||
|
||||
} else if(led_no >= CUSTOM_LED_NO - 1) {
|
||||
pd_no = -1;
|
||||
led_no = -1;
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\nEnded\r\n");
|
||||
#endif
|
||||
custom_adc_end();
|
||||
|
||||
#if FEATURE_DETAIL_VALUE_CUSTOM
|
||||
sum = 0;
|
||||
printf("\r\nCustom_============custom_cycle_buff =\r\n");
|
||||
for(uint16_t i = 0; i < 36; i++){
|
||||
for(uint16_t j = 0; j < custom_samples_in_buffer; j++){
|
||||
printf("%lf\r\n", custom_cycle_buff[i][j]);
|
||||
}
|
||||
for(uint16_t k = 0; k < custom_samples_in_buffer; k++){
|
||||
sum += custom_cycle_buff[i][k];
|
||||
}
|
||||
printf("\t%lf\r\n", sum);
|
||||
sum = 0;
|
||||
}
|
||||
printf("\r\n");
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t k =0;
|
||||
sum = 0;
|
||||
for(uint16_t i = 0; i < 20; i++){
|
||||
for(uint16_t j = 0; j < custom_samples_in_buffer; j++){
|
||||
sum += custom_cycle_buff[i][j];
|
||||
}
|
||||
|
||||
custom_cycle_send_buff[i/4][k++] = sum;
|
||||
sum = 0;
|
||||
|
||||
if(k >= 4) k = 0;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* Send data */
|
||||
/*********************/
|
||||
if(cmd_type_t == CMD_UART) {
|
||||
printf("S%dTi%d,\t%lf,\t%lf,\t%lf,\t%lf,\r\n",c_cnt, LED_list_custom[0], custom_cycle_send_buff[0][0], custom_cycle_send_buff[0][1], custom_cycle_send_buff[0][2], custom_cycle_send_buff[0][3]);//, custom_cycle_send_buff[0][4], custom_cycle_send_buff[0][5]);
|
||||
printf("Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\r\n", LED_list_custom[1], custom_cycle_send_buff[1][0], custom_cycle_send_buff[1][1], custom_cycle_send_buff[1][2], custom_cycle_send_buff[1][3]);//, custom_cycle_send_buff[1][4], custom_cycle_send_buff[1][5]);
|
||||
printf("Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\r\n", LED_list_custom[2], custom_cycle_send_buff[2][0], custom_cycle_send_buff[2][1], custom_cycle_send_buff[2][2], custom_cycle_send_buff[2][3]);//, custom_cycle_send_buff[2][4], custom_cycle_send_buff[2][5]);
|
||||
printf("Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\r\n", LED_list_custom[3], custom_cycle_send_buff[3][0], custom_cycle_send_buff[3][1], custom_cycle_send_buff[3][2], custom_cycle_send_buff[3][3]);//, custom_cycle_send_buff[3][4], custom_cycle_send_buff[3][5]);
|
||||
printf("Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\r\n", LED_list_custom[4], custom_cycle_send_buff[4][0], custom_cycle_send_buff[4][1], custom_cycle_send_buff[4][2], custom_cycle_send_buff[4][3]);//, custom_cycle_send_buff[4][4], custom_cycle_send_buff[4][5]);
|
||||
printf("\r\n");
|
||||
} else if(cmd_type_t == CMD_BLE ) {
|
||||
if(pd_adc_custom_a_start == true ) {
|
||||
sprintf(custom_tx_d_buffer_0[c_cnt], "M%d,Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\r\n",c_cnt, LED_list_custom[0], custom_cycle_send_buff[0][0], custom_cycle_send_buff[0][1], custom_cycle_send_buff[0][2], custom_cycle_send_buff[0][3]);
|
||||
sprintf(custom_tx_d_buffer_1[c_cnt], "M%d,Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\r\n",c_cnt, LED_list_custom[1], custom_cycle_send_buff[1][0], custom_cycle_send_buff[1][1], custom_cycle_send_buff[1][2], custom_cycle_send_buff[1][3]);
|
||||
sprintf(custom_tx_d_buffer_2[c_cnt], "M%d,Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\r\n",c_cnt, LED_list_custom[2], custom_cycle_send_buff[2][0], custom_cycle_send_buff[2][1], custom_cycle_send_buff[2][2], custom_cycle_send_buff[2][3]);
|
||||
sprintf(custom_tx_d_buffer_3[c_cnt], "M%d,Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\r\n",c_cnt, LED_list_custom[3], custom_cycle_send_buff[3][0], custom_cycle_send_buff[3][1], custom_cycle_send_buff[3][2], custom_cycle_send_buff[3][3]);
|
||||
sprintf(custom_tx_d_buffer_4[c_cnt], "M%d,Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\r\n",c_cnt, LED_list_custom[4], custom_cycle_send_buff[4][0], custom_cycle_send_buff[4][1], custom_cycle_send_buff[4][2], custom_cycle_send_buff[4][3]);
|
||||
|
||||
}else if(pd_adc_custom_b_start == true) {
|
||||
sprintf(custom_tx_d_buffer_5[c_cnt], "M%d,Ti%d,\t%lf,\t%lf,\t%lf,\t%lf\r\n",c_cnt, LED_list_custom[0], custom_cycle_send_buff[0][0], custom_cycle_send_buff[0][1], custom_cycle_send_buff[0][2], custom_cycle_send_buff[0][3]);
|
||||
sprintf(custom_tx_d_buffer_6[c_cnt], "M%d,Ti%d,\t%lf,\t%lf,\t%lf,\t%lf\r\n",c_cnt, LED_list_custom[1], custom_cycle_send_buff[1][0], custom_cycle_send_buff[1][1], custom_cycle_send_buff[1][2], custom_cycle_send_buff[1][3]);
|
||||
sprintf(custom_tx_d_buffer_7[c_cnt], "M%d,Ti%d,\t%lf,\t%lf,\t%lf,\t%lf\r\n",c_cnt, LED_list_custom[2], custom_cycle_send_buff[2][0], custom_cycle_send_buff[2][1], custom_cycle_send_buff[2][2], custom_cycle_send_buff[2][3]);
|
||||
sprintf(custom_tx_d_buffer_8[c_cnt], "M%d,Ti%d,\t%lf,\t%lf,\t%lf,\t%lf\r\n",c_cnt, LED_list_custom[3], custom_cycle_send_buff[3][0], custom_cycle_send_buff[3][1], custom_cycle_send_buff[3][2], custom_cycle_send_buff[3][3]);
|
||||
sprintf(custom_tx_d_buffer_9[c_cnt], "M%d,Ti%d,\t%lf,\t%lf,\t%lf,\t%lf\r\n",c_cnt, LED_list_custom[4], custom_cycle_send_buff[4][0], custom_cycle_send_buff[4][1], custom_cycle_send_buff[4][2], custom_cycle_send_buff[4][3]);
|
||||
|
||||
}
|
||||
}
|
||||
/*********************/
|
||||
|
||||
buf_no = 0;
|
||||
|
||||
if(ble_connection_st == 0) {
|
||||
custom_adc_end();
|
||||
custom_send_timer_stop();
|
||||
printf("Custom ADC STOP 1");
|
||||
}
|
||||
else if(pd_adc_custom_a_start == true) { // B mode
|
||||
pd_adc_custom_a_start = false;
|
||||
pd_adc_custom_b_start = true;
|
||||
|
||||
custom_adc_start();
|
||||
|
||||
}else if(pd_adc_custom_b_start == true) { // Completed
|
||||
pd_adc_custom_b_start = false;
|
||||
|
||||
|
||||
|
||||
// }
|
||||
// else if(pd_adc_custom_a_start == true) { // Completed
|
||||
// pd_adc_custom_a_start = false;
|
||||
|
||||
|
||||
if(cmd_type_t == CMD_UART) {
|
||||
custom_send_timer_stop();
|
||||
|
||||
}else if(cmd_type_t == CMD_BLE) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// if(ble_connection_st == 1) {
|
||||
// battery_timer_start();
|
||||
// }
|
||||
|
||||
led_off(99);
|
||||
pd_off(99);
|
||||
custom_adc_total_start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void custom_check_loop(void * p_context)
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
custom_check_timer_stop();
|
||||
if ( custom_testing == false)
|
||||
{
|
||||
printf("%d ms \r\n",t_ms);
|
||||
sprintf(custom_tx_buffer, "Measure Time :%d ms \r\n",t_ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
t_ms++;
|
||||
custom_check_timer_start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void custom_send_loop(void * p_context) /* For x ms */
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
custom_send_timer_stop();
|
||||
if(ble_connection_st){
|
||||
if (data_tx_in_progress==true) {
|
||||
// Data transmission is still in progress
|
||||
printf("skip \r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static uint8_t order = 0;
|
||||
|
||||
switch(order) {
|
||||
case 0:
|
||||
data_tx_handler(custom_tx_d_buffer_0[s_cnt]);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
data_tx_handler(custom_tx_d_buffer_1[s_cnt]);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
data_tx_handler(custom_tx_d_buffer_2[s_cnt]);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
data_tx_handler(custom_tx_d_buffer_3[s_cnt]);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
data_tx_handler(custom_tx_d_buffer_4[s_cnt]);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
data_tx_handler(custom_tx_d_buffer_5[s_cnt]);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
data_tx_handler(custom_tx_d_buffer_6[s_cnt]);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
data_tx_handler(custom_tx_d_buffer_7[s_cnt]);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
data_tx_handler(custom_tx_d_buffer_8[s_cnt]);
|
||||
break;
|
||||
|
||||
case 9:
|
||||
data_tx_handler(custom_tx_d_buffer_9[s_cnt]);
|
||||
|
||||
|
||||
order = 0;
|
||||
custom_send_start();
|
||||
#if FEATURE_PRINTF
|
||||
printf("custom_send Completed\r\n");
|
||||
#endif
|
||||
|
||||
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(ble_connection_st == 0) {
|
||||
custom_send_timer_stop();
|
||||
order = 0;
|
||||
printf("Custom ADC STOP 2\r\n");}
|
||||
else{
|
||||
|
||||
|
||||
|
||||
|
||||
order++;
|
||||
custom_send_timer_start();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
void custom_send_start(void)
|
||||
{
|
||||
s_cnt++;
|
||||
if (s_cnt>c_max)
|
||||
{
|
||||
custom_send_timer_stop();
|
||||
|
||||
|
||||
data_tx_handler(ble_tx_buffer);
|
||||
data_tx_handler(custom_tx_buffer);
|
||||
battery_timer_start();
|
||||
}
|
||||
else
|
||||
{
|
||||
custom_send_timer_start();
|
||||
}
|
||||
}
|
||||
void custom_adc_total_start(void)
|
||||
{
|
||||
|
||||
if (pd_adc_custom_start==true)
|
||||
{
|
||||
// custom_testing = false;
|
||||
// motion_raw_data_enabled = true;
|
||||
// custom_add_data = true;
|
||||
// icm42670_main();
|
||||
//
|
||||
// data_tx_handler(ble_tx_buffer);
|
||||
|
||||
t_ms=0;
|
||||
custom_testing = true;
|
||||
custom_check_timer_start();
|
||||
c_cnt=0;
|
||||
pd_adc_custom_start=false;
|
||||
pd_adc_custom_a_start=true;
|
||||
custom_adc_start();
|
||||
c_cnt++;
|
||||
|
||||
}
|
||||
|
||||
else if (c_cnt<c_max)
|
||||
{
|
||||
pd_adc_custom_a_start=true;
|
||||
custom_adc_start();
|
||||
c_cnt++;
|
||||
}
|
||||
else if (c_cnt==c_max)
|
||||
{
|
||||
custom_testing = false;
|
||||
motion_raw_data_enabled = true;
|
||||
custom_add_data = true;
|
||||
icm42670_main();
|
||||
if(cmd_type_t == CMD_BLE){
|
||||
s_cnt=0;
|
||||
custom_send_start();
|
||||
|
||||
|
||||
}
|
||||
c_cnt++;
|
||||
printf ("FINISH SEND\r\n");
|
||||
processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void custom_adc_start(void)
|
||||
{
|
||||
pd_no = -1;
|
||||
led_no = -1;
|
||||
buf_no = 0;
|
||||
|
||||
if(pd_adc_custom_a_start == true) {
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n===== Custom_A start ====================\r\n");
|
||||
printf("LED : ");
|
||||
#endif
|
||||
for(uint8_t i =0; i < CUSTOM_LED_NO; i++) {
|
||||
LED_list_custom[i] = led_custom_list_a[i];
|
||||
#if FEATURE_PRINTF
|
||||
printf("%d ", LED_list_custom[i]);
|
||||
#endif
|
||||
}
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n");
|
||||
printf("PD : ");
|
||||
#endif
|
||||
for(uint8_t i =0; i < CUSTOM_PD_NO; i++) {
|
||||
PD_list_custom[i] = pd_custom_list_a[i];
|
||||
#if FEATURE_PRINTF
|
||||
printf("%d ", PD_list_custom[i]);
|
||||
#endif
|
||||
}
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n\r\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if(pd_adc_custom_b_start == true) {
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n===== Custom_B start ====================\r\n");
|
||||
printf("LED : ");
|
||||
#endif
|
||||
for(uint8_t i =0; i < CUSTOM_LED_NO; i++) {
|
||||
LED_list_custom[i] = led_custom_list_b[i];
|
||||
#if FEATURE_PRINTF
|
||||
printf("%d ", LED_list_custom[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n");
|
||||
printf("PD : ");
|
||||
#endif
|
||||
|
||||
for(uint8_t i =0; i < CUSTOM_PD_NO; i++) {
|
||||
PD_list_custom[i] = pd_custom_list_b[i];
|
||||
#if FEATURE_PRINTF
|
||||
printf("%d ", PD_list_custom[i]);
|
||||
#endif
|
||||
}
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n\r\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// if(pd_adc_custom_c_start == true) {
|
||||
//#if FEATURE_PRINTF
|
||||
// printf("\r\n===== Custom_C start ====================\r\n");
|
||||
// printf("LED : ");
|
||||
//#endif
|
||||
// for(uint8_t i =0; i < CUSTOM_LED_NO; i++) {
|
||||
// LED_list_custom[i] = led_custom_list_c[i];
|
||||
//#if FEATURE_PRINTF
|
||||
// printf("%d ", LED_list_custom[i]);
|
||||
//#endif
|
||||
// }
|
||||
//
|
||||
//#if FEATURE_PRINTF
|
||||
// printf("\r\n");
|
||||
// printf("PD : ");
|
||||
//#endif
|
||||
|
||||
// for(uint8_t i =0; i < CUSTOM_PD_NO; i++) {
|
||||
// PD_list_custom[i] = pd_custom_list_c[i];
|
||||
//#if FEATURE_PRINTF
|
||||
// printf("%d ", PD_list_custom[i]);
|
||||
//#endif
|
||||
// }
|
||||
//#if FEATURE_PRINTF
|
||||
// printf("\r\n\r\n");
|
||||
//#endif
|
||||
// }
|
||||
|
||||
// if(pd_adc_custom_d_start == true) {
|
||||
//#if FEATURE_PRINTF
|
||||
// printf("\r\n===== Custom_D start ====================\r\n");
|
||||
// printf("LED : ");
|
||||
//#endif
|
||||
// for(uint8_t i =0; i < CUSTOM_LED_NO; i++) {
|
||||
// LED_list_custom[i] = led_custom_list_d[i];
|
||||
//#if FEATURE_PRINTF
|
||||
// printf("%d ", LED_list_custom[i]);
|
||||
//#endif
|
||||
// }
|
||||
//
|
||||
//#if FEATURE_PRINTF
|
||||
// printf("\r\n");
|
||||
// printf("PD : ");
|
||||
//#endif
|
||||
|
||||
// for(uint8_t i =0; i < CUSTOM_PD_NO; i++) {
|
||||
// PD_list_custom[i] = pd_custom_list_d[i];
|
||||
//#if FEATURE_PRINTF
|
||||
// printf("%d ", PD_list_custom[i]);
|
||||
//#endif
|
||||
// }
|
||||
//#if FEATURE_PRINTF
|
||||
// printf("\r\n\r\n");
|
||||
//#endif
|
||||
// }
|
||||
|
||||
for(uint16_t i = 0; i < 72; i++) {
|
||||
for(uint16_t j = 0; j < CUSTOM_CYCLE_CNT; j++) {
|
||||
custom_cycle_buff[i][j] = 0.0f;
|
||||
}
|
||||
}
|
||||
for(uint8_t i = 0; i < CUSTOM_LED_NO; i++) {
|
||||
for(uint8_t j = 0; j < CUSTOM_PD_NO; j++) {
|
||||
custom_cycle_send_buff[i][j] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
custom_adc_start2();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void custom_adc_start2(void)
|
||||
{
|
||||
custom_adc_init();
|
||||
#if FEATURE_PRINTF
|
||||
custom_timer_init();
|
||||
custom_timer_start();
|
||||
#else
|
||||
custom_irq_init();
|
||||
custom_ppi_init();
|
||||
custom_sampling_event_enable();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void custom_adc_end(void)
|
||||
{
|
||||
#if FEATURE_PRINTF
|
||||
printf("custom_adc_end\r\n");
|
||||
custom_timer_stop();
|
||||
#endif
|
||||
|
||||
#if !FEATURE_PRINTF
|
||||
custom_sampling_event_disable();
|
||||
custom_irq_uninit();
|
||||
custom_ppi_uninit();
|
||||
#endif
|
||||
custom_adc_uninit();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void custom_adc_end_2(void)
|
||||
{
|
||||
#if FEATURE_PRINTF
|
||||
printf("custom_adc_end\r\n");
|
||||
custom_timer_stop();
|
||||
#endif
|
||||
|
||||
#if !FEATURE_PRINTF
|
||||
custom_sampling_event_disable();
|
||||
custom_irq_uninit();
|
||||
custom_ppi_uninit();
|
||||
#endif
|
||||
custom_adc_uninit();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void custom_adc_init(void)
|
||||
{
|
||||
#if FEATURE_PRINTF
|
||||
printf("custom_adc_init\r\n");
|
||||
#endif
|
||||
|
||||
static nrfx_saadc_config_t default_config;
|
||||
default_config.resolution = (nrf_saadc_resolution_t)NRFX_SAADC_CONFIG_RESOLUTION; /* Resolution is 10bits */
|
||||
default_config.oversample = (nrf_saadc_oversample_t)NRFX_SAADC_CONFIG_OVERSAMPLE; /* Over Sampling Disabled */
|
||||
default_config.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY; /* Interrupt Priority is 0(Highest) */
|
||||
default_config.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE; /* Low Power Mode is Disabled */
|
||||
|
||||
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, custom_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_custom_adc_buf[0], custom_samples_in_buffer + m_config.pd_delay_us/16);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void custom_adc_uninit(void)
|
||||
{
|
||||
#if FEATURE_PRINTF
|
||||
printf("pd_custom_adc_uninit\r\n");
|
||||
#endif
|
||||
|
||||
nrf_drv_saadc_uninit();
|
||||
nrf_drv_saadc_channel_uninit(0);
|
||||
}
|
||||
|
||||
|
||||
#if !FEATURE_PRINTF
|
||||
void custom_irq_init(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
/* Initialize int pin */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void custom_irq_uninit(void)
|
||||
{
|
||||
nrfx_gpiote_in_event_disable(ADA2200_SYNCO_PIN);
|
||||
nrfx_gpiote_in_uninit(ADA2200_SYNCO_PIN);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void custom_send_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_custom_send_loop_timer_id, APP_TIMER_TICKS(CUSTOM_SEND_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
|
||||
void custom_send_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_custom_send_loop_timer_id));
|
||||
|
||||
}
|
||||
|
||||
|
||||
void custom_send_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_custom_send_loop_timer_id, APP_TIMER_MODE_SINGLE_SHOT, custom_send_loop));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void custom_check_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_custom_check_loop_timer_id, APP_TIMER_TICKS(CUSTOM_CHECK_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
void custom_check_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_custom_check_loop_timer_id));
|
||||
|
||||
}
|
||||
void custom_check_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_custom_check_loop_timer_id, APP_TIMER_MODE_SINGLE_SHOT, custom_check_loop));
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*******************************************************************************
|
||||
* @file meas_pd_voltage_custom.h
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _MEAS_PD_VOLTAGE_CUSTOM_H__
|
||||
#define _MEAS_PD_VOLTAGE_CUSTOM_H__
|
||||
|
||||
#include "sdk_common.h"
|
||||
#include "nrf_drv_saadc.h"
|
||||
|
||||
#ifndef ADA2200_SYNCO_PIN
|
||||
#define ADA2200_SYNCO_PIN NRF_GPIO_PIN_MAP(0,17)
|
||||
#endif
|
||||
|
||||
|
||||
#define CUSTOM_PD_NO 4
|
||||
#define CUSTOM_LED_NO 5
|
||||
#define CUSTOM_CYCLE_CNT 32
|
||||
|
||||
|
||||
void custom_ppi_init(void);
|
||||
void custom_ppi_uninit(void);
|
||||
void custom_sampling_event_enable(void);
|
||||
void custom_sampling_event_disable(void);
|
||||
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
static void imm_voltage_handler(nrf_drv_saadc_evt_t const * p_event); /* PD Voltage reading */
|
||||
void custom_adc_start(void);
|
||||
void custom_adc_start2(void);
|
||||
void custom_adc_total_start(void);
|
||||
void custom_adc_end(void);
|
||||
void custom_adc_end_2(void);
|
||||
void custom_adc_init(void);
|
||||
void custom_adc_uninit(void);
|
||||
#if !FEATURE_PRINTF
|
||||
void custom_irq_init(void);
|
||||
void custom_irq_uninit(void);
|
||||
#endif
|
||||
void custom_send_start(void);
|
||||
|
||||
|
||||
|
||||
void custom_send_loop(void * p_context); /* For x ms */
|
||||
void custom_send_timer_start(void);
|
||||
void custom_send_timer_stop(void);;
|
||||
void custom_send_timer_init(void);
|
||||
|
||||
|
||||
void custom_check_loop(void * p_context);
|
||||
void custom_check_timer_start(void);
|
||||
void custom_check_timer_stop(void);;
|
||||
void custom_check_timer_init(void);
|
||||
|
||||
#endif /* _MEAS_PD_VOLTAGE_CUSTOM_H__ */
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*******************************************************************************
|
||||
* @file meas_pd_voltage_full.h
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _MEAS_PD_VOLTAGE_FULL_H__
|
||||
#define _MEAS_PD_VOLTAGE_FULL_H__
|
||||
|
||||
#include "sdk_common.h"
|
||||
#include "nrf_drv_saadc.h"
|
||||
|
||||
#ifndef ADA2200_SYNCO_PIN
|
||||
#define ADA2200_SYNCO_PIN NRF_GPIO_PIN_MAP(0,17)
|
||||
#endif
|
||||
|
||||
|
||||
#define FULL_PD_NO 1
|
||||
#define FULL_LED_NO 24
|
||||
#define FULL_CYCLE_CNT 32 /* Dummy=65535/16us(Max Delay), Cycle_Max_Cnt=32*/
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
void full_timer_loop(void * p_context);
|
||||
void full_timer_start(void);
|
||||
void full_timer_stop(void);
|
||||
void full_timer_init(void);
|
||||
#endif
|
||||
|
||||
void full_ppi_init(void);
|
||||
void full_ppi_uninit(void);
|
||||
void full_sampling_event_enable(void);
|
||||
void full_sampling_event_disable(void);
|
||||
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
static void full_voltage_handler(nrf_drv_saadc_evt_t const * p_event); /* PD Voltage reading */
|
||||
void full_adc_start_first(uint8_t type,uint8_t PD_PICK ,uint8_t LED_PICK);
|
||||
void full_adc_start(void);
|
||||
void full_adc_start2(void);
|
||||
void full_adc_end(void);
|
||||
void full_adc_init(void);
|
||||
void full_adc_uninit(void);
|
||||
#if !FEATURE_PRINTF
|
||||
void full_irq_init(void);
|
||||
void full_irq_uninit(void);
|
||||
#endif
|
||||
|
||||
void ble_Tx_process(void);
|
||||
void full_send_loop(void * p_context); /* For x ms */
|
||||
void full_send_timer_start(void);
|
||||
void full_send_timer_stop(void);;
|
||||
void full_send_timer_init(void);
|
||||
|
||||
|
||||
void full_check_loop(void * p_context);
|
||||
void full_check_timer_start(void);
|
||||
void full_check_timer_stop(void);;
|
||||
void full_check_timer_init(void);
|
||||
|
||||
#endif /* _MEAS_PD_VOLTAGE_FULL_H__ */
|
||||
|
||||
836
project/ble_peripheral/ble_app_vivaMayo/meas_pd_voltage_half.c
Normal file
836
project/ble_peripheral/ble_app_vivaMayo/meas_pd_voltage_half.c
Normal file
@@ -0,0 +1,836 @@
|
||||
/*******************************************************************************
|
||||
TEST medi50 Dec 23
|
||||
******************************************************************************/
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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 "fstorage.h"
|
||||
#include "measurements.h"
|
||||
#include "meas_pd_voltage_half.h"
|
||||
#include "mcp4725_i2c.h"
|
||||
#include "ad5272_i2c.h"
|
||||
#include "main_timer.h"
|
||||
#include "battery_saadc.h"
|
||||
#include "main.h"
|
||||
#include "power_control.h"
|
||||
#define HALF_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
|
||||
#define HALF_PRE_SCALING_COMPENSATION 6.0f /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
|
||||
#define HALF_ADC_RES_10BITS 1024.0f /**< Maximum digital value for 10-bit ADC conversion. */
|
||||
|
||||
/**@brief Macro to convert the result of ADC conversion in millivolts.
|
||||
*
|
||||
* @param[in] ADC_VALUE ADC result.
|
||||
*
|
||||
* @retval Result converted to millivolts.
|
||||
*/
|
||||
#define HALF_VOUT_IN_MILLI_VOLTS(ADC_VALUE)\
|
||||
(((((ADC_VALUE) * HALF_REF_VOLTAGE_IN_MILLIVOLTS) / HALF_ADC_RES_10BITS) * HALF_PRE_SCALING_COMPENSATION)*2)
|
||||
|
||||
|
||||
uint8_t half_samples_in_buffer = 8;
|
||||
|
||||
|
||||
uint8_t LED_list_half[6];
|
||||
uint8_t PD_list_half[6];
|
||||
|
||||
uint8_t led_half_list_a[6] = {1,2,3,4,5,6};
|
||||
uint8_t pd_half_list_a[6] = {5,6,7,8,9,10};
|
||||
|
||||
uint8_t led_half_list_b[6] = {7,8,9,10,11,12};
|
||||
uint8_t pd_half_list_b[6] = {6,5,4,3,2,1};
|
||||
|
||||
uint8_t led_half_list_c[6] = {13,14,15,16,17,18};
|
||||
uint8_t pd_half_list_c[6] = {15,16,17,18,19,20};
|
||||
|
||||
uint8_t led_half_list_d[6] = {19,20,21,22,23,24};
|
||||
uint8_t pd_half_list_d[6] = {16,15,14,13,12,11};
|
||||
|
||||
bool prestatus;
|
||||
static int8_t pd_no = -1;
|
||||
static int8_t led_no = -1;
|
||||
static int8_t buf_no = 0;
|
||||
static uint8_t order = 0;
|
||||
double half_cycle_buff[36][HALF_CYCLE_CNT] =
|
||||
{
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //0
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //1
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //2
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //3
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //4
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //5
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //6
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //7
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //8
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //9
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //10
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //11
|
||||
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //12
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //13
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //14
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //15
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //16
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //17
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //18
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //19
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //20
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //21
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //22
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //23
|
||||
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //24
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //25
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //26
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //27
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //28
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //29
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //30
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //31
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //32
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //33
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //34
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //35
|
||||
};
|
||||
double half_cycle_send_buff[HALF_LED_NO][HALF_PD_NO] =
|
||||
{
|
||||
{0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
|
||||
#define SAMPLES_IN_BUFFER 4095+32
|
||||
static nrf_saadc_value_t pd_half_adc_buf[2][SAMPLES_IN_BUFFER];
|
||||
|
||||
bool pd_adc_half_a_start = false;
|
||||
bool pd_adc_half_b_start = false;
|
||||
bool pd_adc_half_c_start = false;
|
||||
bool pd_adc_half_d_start = false;
|
||||
bool pd_adc_half_end = false;
|
||||
|
||||
|
||||
char half_tx_buffer_0[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_1[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_2[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_3[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_4[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_5[BLE_NUS_MAX_DATA_LEN];
|
||||
|
||||
char half_tx_buffer_6[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_7[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_8[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_9[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_10[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_11[BLE_NUS_MAX_DATA_LEN];
|
||||
|
||||
char half_tx_buffer_12[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_13[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_14[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_15[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_16[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_17[BLE_NUS_MAX_DATA_LEN];
|
||||
|
||||
char half_tx_buffer_18[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_19[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_20[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_21[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_22[BLE_NUS_MAX_DATA_LEN];
|
||||
char half_tx_buffer_23[BLE_NUS_MAX_DATA_LEN];
|
||||
|
||||
extern which_cmd_t cmd_type_t;
|
||||
|
||||
|
||||
APP_TIMER_DEF(m_half_send_loop_timer_id);
|
||||
#if FEATURE_DELAY
|
||||
#define HALF_SEND_LOOP_INTERVAL 500
|
||||
#else
|
||||
#define HALF_SEND_LOOP_INTERVAL 100
|
||||
#endif
|
||||
|
||||
extern volatile bool ble_connection_st;
|
||||
#if FEATURE_DEBUG_REPEAT_TEST
|
||||
extern uint32_t rpt_cnt;
|
||||
#endif
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
APP_TIMER_DEF(m_half_loop_timer_id);
|
||||
#define HALF_LOOP_INTERVAL 1
|
||||
|
||||
void half_timer_loop(void * p_context)
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
|
||||
ret_code_t err_code = nrf_drv_saadc_sample();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
void half_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_half_loop_timer_id, APP_TIMER_TICKS(HALF_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
void half_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_half_loop_timer_id));
|
||||
}
|
||||
|
||||
void half_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_half_loop_timer_id, APP_TIMER_MODE_REPEATED, half_timer_loop));
|
||||
}
|
||||
#else
|
||||
static nrf_ppi_channel_t m_ppi_channel;
|
||||
#endif
|
||||
|
||||
|
||||
#if !FEATURE_PRINTF
|
||||
void half_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();
|
||||
|
||||
/* setup ppi channel so that timer compare event is triggering sample task in SAADC */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void half_ppi_uninit(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
err_code = nrf_drv_ppi_uninit();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
void half_sampling_event_enable(void)
|
||||
{
|
||||
ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
void half_sampling_event_disable(void)
|
||||
{
|
||||
ret_code_t err_code = nrf_drv_ppi_channel_disable(m_ppi_channel);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
static void half_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* PD Voltage reading */
|
||||
{
|
||||
ret_code_t err_code;
|
||||
double sum = 0.0f;
|
||||
|
||||
uint32_t half_clk_delay = m_config.pd_delay_us/16;
|
||||
if (prestatus==true)
|
||||
{ half_clk_delay =10;}
|
||||
if(ble_connection_st == 0) {
|
||||
half_adc_end();
|
||||
half_send_timer_stop();
|
||||
printf("Half ADC STOP 1\r\n");
|
||||
}
|
||||
|
||||
else{
|
||||
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
|
||||
{
|
||||
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, half_samples_in_buffer + half_clk_delay);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
if(led_no == -1) {
|
||||
led_no = 0;
|
||||
pd_no = 0;
|
||||
|
||||
led_on(LED_list_half[led_no]);
|
||||
led_pd_matching_value_set(LED_list_half[led_no], PD_list_half[pd_no]); /* MCP4725 DAC setting and PD on */
|
||||
|
||||
} else {
|
||||
#if FEATURE_FOR_SCOPE
|
||||
nrf_gpio_pin_set(ADC_CLK_18);
|
||||
nrf_gpio_pin_clear(ADC_CLK_18);
|
||||
#endif
|
||||
for(uint16_t i = half_clk_delay; i < half_clk_delay + half_samples_in_buffer; i++){
|
||||
half_cycle_buff[buf_no][i-half_clk_delay] = (HALF_VOUT_IN_MILLI_VOLTS(p_event->data.done.p_buffer[i])) * -1;
|
||||
}
|
||||
#if FEATURE_PRINTF
|
||||
printf("-----------------Read ADC // led_no = %d(%d), pd_no = %d(%d), buf_no = %d\r\n\r\n", led_no, LED_list_half[led_no], pd_no, PD_list_half[pd_no], buf_no);
|
||||
#endif
|
||||
|
||||
buf_no++;
|
||||
|
||||
if(pd_no < HALF_PD_NO - 1) {
|
||||
pd_no++;
|
||||
led_pd_matching_value_set(LED_list_half[led_no], PD_list_half[pd_no]); /* MCP4725 DAC setting and PD on */
|
||||
|
||||
}else if(pd_no >= HALF_PD_NO - 1) {
|
||||
pd_no = 0;
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n\r\n");
|
||||
#endif
|
||||
|
||||
if(led_no < HALF_LED_NO - 1) {
|
||||
led_no++;
|
||||
led_on(LED_list_half[led_no]);
|
||||
led_pd_matching_value_set(LED_list_half[led_no], PD_list_half[pd_no]); /* MCP4725 DAC setting and PD on */
|
||||
|
||||
} else if(led_no >= HALF_LED_NO - 1) {
|
||||
pd_no = -1;
|
||||
led_no = -1;
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\nEnded\r\n");
|
||||
#endif
|
||||
half_adc_end();
|
||||
|
||||
#if FEATURE_DETAIL_VALUE_HALF
|
||||
sum = 0;
|
||||
printf("\r\nHalf_============half_cycle_buff =\r\n");
|
||||
for(uint16_t i = 0; i < 36; i++){
|
||||
for(uint16_t j = 0; j < half_samples_in_buffer; j++){
|
||||
printf("%lf\r\n", half_cycle_buff[i][j]);
|
||||
}
|
||||
for(uint16_t k = 0; k < half_samples_in_buffer; k++){
|
||||
sum += half_cycle_buff[i][k];
|
||||
}
|
||||
printf("\t%lf\r\n", sum);
|
||||
sum = 0;
|
||||
}
|
||||
printf("\r\n");
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t k =0;
|
||||
sum = 0;
|
||||
for(uint16_t i = 0; i < 36; i++){
|
||||
for(uint16_t j = 0; j < half_samples_in_buffer; j++){
|
||||
sum += half_cycle_buff[i][j];
|
||||
}
|
||||
|
||||
half_cycle_send_buff[i/6][k++] = sum;
|
||||
sum = 0;
|
||||
|
||||
if(k >= 6) k = 0;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* Send data */
|
||||
/*********************/
|
||||
if(cmd_type_t == CMD_UART && prestatus==false) {
|
||||
printf("Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[0], half_cycle_send_buff[0][0], half_cycle_send_buff[0][1], half_cycle_send_buff[0][2], half_cycle_send_buff[0][3], half_cycle_send_buff[0][4], half_cycle_send_buff[0][5]);
|
||||
printf("Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[1], half_cycle_send_buff[1][0], half_cycle_send_buff[1][1], half_cycle_send_buff[1][2], half_cycle_send_buff[1][3], half_cycle_send_buff[1][4], half_cycle_send_buff[1][5]);
|
||||
printf("Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[2], half_cycle_send_buff[2][0], half_cycle_send_buff[2][1], half_cycle_send_buff[2][2], half_cycle_send_buff[2][3], half_cycle_send_buff[2][4], half_cycle_send_buff[2][5]);
|
||||
printf("Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[3], half_cycle_send_buff[3][0], half_cycle_send_buff[3][1], half_cycle_send_buff[3][2], half_cycle_send_buff[3][3], half_cycle_send_buff[3][4], half_cycle_send_buff[3][5]);
|
||||
printf("Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[4], half_cycle_send_buff[4][0], half_cycle_send_buff[4][1], half_cycle_send_buff[4][2], half_cycle_send_buff[4][3], half_cycle_send_buff[4][4], half_cycle_send_buff[4][5]);
|
||||
printf("Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[5], half_cycle_send_buff[5][0], half_cycle_send_buff[5][1], half_cycle_send_buff[5][2], half_cycle_send_buff[5][3], half_cycle_send_buff[5][4], half_cycle_send_buff[5][5]);
|
||||
printf("\r\n");
|
||||
} else if(cmd_type_t == CMD_BLE && prestatus==false) {
|
||||
if(pd_adc_half_a_start == true) {
|
||||
sprintf(half_tx_buffer_0, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[0], half_cycle_send_buff[0][0], half_cycle_send_buff[0][1], half_cycle_send_buff[0][2], half_cycle_send_buff[0][3], half_cycle_send_buff[0][4], half_cycle_send_buff[0][5]);
|
||||
sprintf(half_tx_buffer_1, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[1], half_cycle_send_buff[1][0], half_cycle_send_buff[1][1], half_cycle_send_buff[1][2], half_cycle_send_buff[1][3], half_cycle_send_buff[1][4], half_cycle_send_buff[1][5]);
|
||||
sprintf(half_tx_buffer_2, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[2], half_cycle_send_buff[2][0], half_cycle_send_buff[2][1], half_cycle_send_buff[2][2], half_cycle_send_buff[2][3], half_cycle_send_buff[2][4], half_cycle_send_buff[2][5]);
|
||||
sprintf(half_tx_buffer_3, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[3], half_cycle_send_buff[3][0], half_cycle_send_buff[3][1], half_cycle_send_buff[3][2], half_cycle_send_buff[3][3], half_cycle_send_buff[3][4], half_cycle_send_buff[3][5]);
|
||||
sprintf(half_tx_buffer_4, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[4], half_cycle_send_buff[4][0], half_cycle_send_buff[4][1], half_cycle_send_buff[4][2], half_cycle_send_buff[4][3], half_cycle_send_buff[4][4], half_cycle_send_buff[4][5]);
|
||||
sprintf(half_tx_buffer_5, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[5], half_cycle_send_buff[5][0], half_cycle_send_buff[5][1], half_cycle_send_buff[5][2], half_cycle_send_buff[5][3], half_cycle_send_buff[5][4], half_cycle_send_buff[5][5]);
|
||||
}else if(pd_adc_half_b_start == true) {
|
||||
sprintf(half_tx_buffer_6, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[0], half_cycle_send_buff[0][0], half_cycle_send_buff[0][1], half_cycle_send_buff[0][2], half_cycle_send_buff[0][3], half_cycle_send_buff[0][4], half_cycle_send_buff[0][5]);
|
||||
sprintf(half_tx_buffer_7, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[1], half_cycle_send_buff[1][0], half_cycle_send_buff[1][1], half_cycle_send_buff[1][2], half_cycle_send_buff[1][3], half_cycle_send_buff[1][4], half_cycle_send_buff[1][5]);
|
||||
sprintf(half_tx_buffer_8, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[2], half_cycle_send_buff[2][0], half_cycle_send_buff[2][1], half_cycle_send_buff[2][2], half_cycle_send_buff[2][3], half_cycle_send_buff[2][4], half_cycle_send_buff[2][5]);
|
||||
sprintf(half_tx_buffer_9, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[3], half_cycle_send_buff[3][0], half_cycle_send_buff[3][1], half_cycle_send_buff[3][2], half_cycle_send_buff[3][3], half_cycle_send_buff[3][4], half_cycle_send_buff[3][5]);
|
||||
sprintf(half_tx_buffer_10, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[4], half_cycle_send_buff[4][0], half_cycle_send_buff[4][1], half_cycle_send_buff[4][2], half_cycle_send_buff[4][3], half_cycle_send_buff[4][4], half_cycle_send_buff[4][5]);
|
||||
sprintf(half_tx_buffer_11, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[5], half_cycle_send_buff[5][0], half_cycle_send_buff[5][1], half_cycle_send_buff[5][2], half_cycle_send_buff[5][3], half_cycle_send_buff[5][4], half_cycle_send_buff[5][5]);
|
||||
}else if(pd_adc_half_c_start == true) {
|
||||
sprintf(half_tx_buffer_12, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[0], half_cycle_send_buff[0][0], half_cycle_send_buff[0][1], half_cycle_send_buff[0][2], half_cycle_send_buff[0][3], half_cycle_send_buff[0][4], half_cycle_send_buff[0][5]);
|
||||
sprintf(half_tx_buffer_13, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[1], half_cycle_send_buff[1][0], half_cycle_send_buff[1][1], half_cycle_send_buff[1][2], half_cycle_send_buff[1][3], half_cycle_send_buff[1][4], half_cycle_send_buff[1][5]);
|
||||
sprintf(half_tx_buffer_14, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[2], half_cycle_send_buff[2][0], half_cycle_send_buff[2][1], half_cycle_send_buff[2][2], half_cycle_send_buff[2][3], half_cycle_send_buff[2][4], half_cycle_send_buff[2][5]);
|
||||
sprintf(half_tx_buffer_15, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[3], half_cycle_send_buff[3][0], half_cycle_send_buff[3][1], half_cycle_send_buff[3][2], half_cycle_send_buff[3][3], half_cycle_send_buff[3][4], half_cycle_send_buff[3][5]);
|
||||
sprintf(half_tx_buffer_16, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[4], half_cycle_send_buff[4][0], half_cycle_send_buff[4][1], half_cycle_send_buff[4][2], half_cycle_send_buff[4][3], half_cycle_send_buff[4][4], half_cycle_send_buff[4][5]);
|
||||
sprintf(half_tx_buffer_17, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[5], half_cycle_send_buff[5][0], half_cycle_send_buff[5][1], half_cycle_send_buff[5][2], half_cycle_send_buff[5][3], half_cycle_send_buff[5][4], half_cycle_send_buff[5][5]);
|
||||
}else if(pd_adc_half_d_start == true) {
|
||||
sprintf(half_tx_buffer_18, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[0], half_cycle_send_buff[0][0], half_cycle_send_buff[0][1], half_cycle_send_buff[0][2], half_cycle_send_buff[0][3], half_cycle_send_buff[0][4], half_cycle_send_buff[0][5]);
|
||||
sprintf(half_tx_buffer_19, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[1], half_cycle_send_buff[1][0], half_cycle_send_buff[1][1], half_cycle_send_buff[1][2], half_cycle_send_buff[1][3], half_cycle_send_buff[1][4], half_cycle_send_buff[1][5]);
|
||||
sprintf(half_tx_buffer_20, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[2], half_cycle_send_buff[2][0], half_cycle_send_buff[2][1], half_cycle_send_buff[2][2], half_cycle_send_buff[2][3], half_cycle_send_buff[2][4], half_cycle_send_buff[2][5]);
|
||||
sprintf(half_tx_buffer_21, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[3], half_cycle_send_buff[3][0], half_cycle_send_buff[3][1], half_cycle_send_buff[3][2], half_cycle_send_buff[3][3], half_cycle_send_buff[3][4], half_cycle_send_buff[3][5]);
|
||||
sprintf(half_tx_buffer_22, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[4], half_cycle_send_buff[4][0], half_cycle_send_buff[4][1], half_cycle_send_buff[4][2], half_cycle_send_buff[4][3], half_cycle_send_buff[4][4], half_cycle_send_buff[4][5]);
|
||||
sprintf(half_tx_buffer_23, "Ti%d,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf,\t%lf\r\n", LED_list_half[5], half_cycle_send_buff[5][0], half_cycle_send_buff[5][1], half_cycle_send_buff[5][2], half_cycle_send_buff[5][3], half_cycle_send_buff[5][4], half_cycle_send_buff[5][5]);
|
||||
}
|
||||
}
|
||||
/*********************/
|
||||
|
||||
buf_no = 0;
|
||||
|
||||
if(ble_connection_st == 0) {
|
||||
half_adc_end();
|
||||
half_send_timer_stop();
|
||||
printf("Half ADC STOP 1\r\n");
|
||||
}
|
||||
else if(pd_adc_half_a_start == true) { // B mode
|
||||
pd_adc_half_a_start = false;
|
||||
pd_adc_half_b_start = true;
|
||||
|
||||
half_adc_start();
|
||||
}else if(pd_adc_half_b_start == true) { // C mode
|
||||
pd_adc_half_b_start = false;
|
||||
pd_adc_half_c_start = true;
|
||||
|
||||
half_adc_start();
|
||||
}else if(pd_adc_half_c_start == true) { // D mode
|
||||
pd_adc_half_c_start = false;
|
||||
pd_adc_half_d_start = true;
|
||||
|
||||
half_adc_start();
|
||||
}else if(pd_adc_half_d_start == true) { // Completed
|
||||
pd_adc_half_d_start = false;
|
||||
|
||||
if(cmd_type_t == CMD_UART && prestatus==false) {
|
||||
half_send_timer_stop();
|
||||
|
||||
}else if(cmd_type_t == CMD_BLE && prestatus==false) {
|
||||
half_send_timer_start();
|
||||
|
||||
}
|
||||
|
||||
if(ble_connection_st == BLE_CONNECTED_ST) {
|
||||
battery_timer_start();
|
||||
}
|
||||
|
||||
led_off(99);
|
||||
pd_off(99);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void half_send_loop(void * p_context) /* For x ms */
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
half_send_timer_stop();
|
||||
|
||||
|
||||
|
||||
switch(order) {
|
||||
case 0:
|
||||
data_tx_handler(half_tx_buffer_0);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
data_tx_handler(half_tx_buffer_1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
data_tx_handler(half_tx_buffer_2);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
data_tx_handler(half_tx_buffer_3);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
data_tx_handler(half_tx_buffer_4);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
data_tx_handler(half_tx_buffer_5);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
data_tx_handler(half_tx_buffer_6);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
data_tx_handler(half_tx_buffer_7);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
data_tx_handler(half_tx_buffer_8);
|
||||
break;
|
||||
|
||||
case 9:
|
||||
data_tx_handler(half_tx_buffer_9);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
data_tx_handler(half_tx_buffer_10);
|
||||
break;
|
||||
|
||||
case 11:
|
||||
data_tx_handler(half_tx_buffer_11);
|
||||
break;
|
||||
|
||||
case 12:
|
||||
data_tx_handler(half_tx_buffer_12);
|
||||
break;
|
||||
|
||||
case 13:
|
||||
data_tx_handler(half_tx_buffer_13);
|
||||
break;
|
||||
|
||||
case 14:
|
||||
data_tx_handler(half_tx_buffer_14);
|
||||
break;
|
||||
|
||||
case 15:
|
||||
data_tx_handler(half_tx_buffer_15);
|
||||
break;
|
||||
|
||||
case 16:
|
||||
data_tx_handler(half_tx_buffer_16);
|
||||
break;
|
||||
|
||||
case 17:
|
||||
data_tx_handler(half_tx_buffer_17);
|
||||
break;
|
||||
|
||||
case 18:
|
||||
data_tx_handler(half_tx_buffer_18);
|
||||
break;
|
||||
|
||||
case 19:
|
||||
data_tx_handler(half_tx_buffer_19);
|
||||
break;
|
||||
|
||||
case 20:
|
||||
data_tx_handler(half_tx_buffer_20);
|
||||
break;
|
||||
|
||||
case 21:
|
||||
data_tx_handler(half_tx_buffer_21);
|
||||
break;
|
||||
|
||||
case 22:
|
||||
data_tx_handler(half_tx_buffer_22);
|
||||
break;
|
||||
|
||||
case 23:
|
||||
data_tx_handler(half_tx_buffer_23);
|
||||
|
||||
order = 0;
|
||||
#if FEATURE_PRINTF
|
||||
printf("half_send Completed\r\n");
|
||||
#endif
|
||||
|
||||
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
order++;
|
||||
if(ble_connection_st == 0) {
|
||||
half_send_timer_stop();
|
||||
order = 0;
|
||||
printf("Half ADC STOP 2\r\n");}
|
||||
else{
|
||||
half_send_timer_start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void half_adc_start(void)
|
||||
{
|
||||
pd_no = -1;
|
||||
led_no = -1;
|
||||
buf_no = 0;
|
||||
|
||||
|
||||
if(pd_adc_half_a_start == true) {
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n===== Half_A start ====================\r\n");
|
||||
printf("LED : ");
|
||||
#endif
|
||||
for(uint8_t i =0; i < HALF_LED_NO; i++) {
|
||||
LED_list_half[i] = led_half_list_a[i];
|
||||
#if FEATURE_PRINTF
|
||||
printf("%d ", LED_list_half[i]);
|
||||
#endif
|
||||
}
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n");
|
||||
printf("PD : ");
|
||||
#endif
|
||||
for(uint8_t i =0; i < HALF_PD_NO; i++) {
|
||||
PD_list_half[i] = pd_half_list_a[i];
|
||||
#if FEATURE_PRINTF
|
||||
printf("%d ", PD_list_half[i]);
|
||||
#endif
|
||||
}
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n\r\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if(pd_adc_half_b_start == true) {
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n===== Half_B start ====================\r\n");
|
||||
printf("LED : ");
|
||||
#endif
|
||||
for(uint8_t i =0; i < HALF_LED_NO; i++) {
|
||||
LED_list_half[i] = led_half_list_b[i];
|
||||
#if FEATURE_PRINTF
|
||||
printf("%d ", LED_list_half[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n");
|
||||
printf("PD : ");
|
||||
#endif
|
||||
|
||||
for(uint8_t i =0; i < HALF_PD_NO; i++) {
|
||||
PD_list_half[i] = pd_half_list_b[i];
|
||||
#if FEATURE_PRINTF
|
||||
printf("%d ", PD_list_half[i]);
|
||||
#endif
|
||||
}
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n\r\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if(pd_adc_half_c_start == true) {
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n===== Half_C start ====================\r\n");
|
||||
printf("LED : ");
|
||||
#endif
|
||||
for(uint8_t i =0; i < HALF_LED_NO; i++) {
|
||||
LED_list_half[i] = led_half_list_c[i];
|
||||
#if FEATURE_PRINTF
|
||||
printf("%d ", LED_list_half[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n");
|
||||
printf("PD : ");
|
||||
#endif
|
||||
|
||||
for(uint8_t i =0; i < HALF_PD_NO; i++) {
|
||||
PD_list_half[i] = pd_half_list_c[i];
|
||||
#if FEATURE_PRINTF
|
||||
printf("%d ", PD_list_half[i]);
|
||||
#endif
|
||||
}
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n\r\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if(pd_adc_half_d_start == true) {
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n===== Half_D start ====================\r\n");
|
||||
printf("LED : ");
|
||||
#endif
|
||||
for(uint8_t i =0; i < HALF_LED_NO; i++) {
|
||||
LED_list_half[i] = led_half_list_d[i];
|
||||
#if FEATURE_PRINTF
|
||||
printf("%d ", LED_list_half[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n");
|
||||
printf("PD : ");
|
||||
#endif
|
||||
|
||||
for(uint8_t i =0; i < HALF_PD_NO; i++) {
|
||||
PD_list_half[i] = pd_half_list_d[i];
|
||||
#if FEATURE_PRINTF
|
||||
printf("%d ", PD_list_half[i]);
|
||||
#endif
|
||||
}
|
||||
#if FEATURE_PRINTF
|
||||
printf("\r\n\r\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
for(uint16_t i = 0; i < 72; i++) {
|
||||
for(uint16_t j = 0; j < HALF_CYCLE_CNT; j++) {
|
||||
half_cycle_buff[i][j] = 0.0f;
|
||||
}
|
||||
}
|
||||
for(uint8_t i = 0; i < HALF_LED_NO; i++) {
|
||||
for(uint8_t j = 0; j < HALF_PD_NO; j++) {
|
||||
half_cycle_send_buff[i][j] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
half_adc_start2();
|
||||
order =0;
|
||||
}
|
||||
|
||||
|
||||
void half_adc_start2(void)
|
||||
{
|
||||
half_adc_init();
|
||||
#if FEATURE_PRINTF
|
||||
half_timer_init();
|
||||
half_timer_start();
|
||||
#else
|
||||
half_irq_init();
|
||||
half_ppi_init();
|
||||
half_sampling_event_enable();
|
||||
//ada2200_start();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void half_adc_end(void)
|
||||
{
|
||||
#if FEATURE_PRINTF
|
||||
printf("half_adc_end\r\n");
|
||||
half_timer_stop();
|
||||
#endif
|
||||
|
||||
#if !FEATURE_PRINTF
|
||||
half_sampling_event_disable();
|
||||
half_irq_uninit();
|
||||
half_ppi_uninit();
|
||||
#endif
|
||||
half_adc_uninit();
|
||||
//ada2200_stop();
|
||||
nrf_delay_ms(5);
|
||||
if(prestatus == true){
|
||||
printf("1st\r\n");
|
||||
if(device_activated() == 0)
|
||||
{printf("2nd\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void half_adc_init(void)
|
||||
{
|
||||
#if FEATURE_PRINTF
|
||||
printf("half_adc_init\r\n");
|
||||
#endif
|
||||
|
||||
static nrfx_saadc_config_t default_config;
|
||||
default_config.resolution = (nrf_saadc_resolution_t)NRFX_SAADC_CONFIG_RESOLUTION; /* Resolution is 10bits */
|
||||
default_config.oversample = (nrf_saadc_oversample_t)NRFX_SAADC_CONFIG_OVERSAMPLE; /* Over Sampling Disabled */
|
||||
default_config.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY; /* Interrupt Priority is 0(Highest) */
|
||||
default_config.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE; /* Low Power Mode is Disabled */
|
||||
|
||||
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, half_voltage_handler);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = nrf_drv_saadc_channel_init(0, &config);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
if (prestatus==true)
|
||||
{
|
||||
err_code = nrf_drv_saadc_buffer_convert(pd_half_adc_buf[0], half_samples_in_buffer + 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
err_code = nrf_drv_saadc_buffer_convert(pd_half_adc_buf[0], half_samples_in_buffer + m_config.pd_delay_us/16);
|
||||
}
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void half_adc_uninit(void)
|
||||
{
|
||||
#if FEATURE_PRINTF
|
||||
printf("pd_half_adc_uninit\r\n");
|
||||
#endif
|
||||
|
||||
nrf_drv_saadc_uninit();
|
||||
nrf_drv_saadc_channel_uninit(0);
|
||||
}
|
||||
|
||||
|
||||
#if !FEATURE_PRINTF
|
||||
void half_irq_init(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
/* Initialize int pin */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void half_irq_uninit(void)
|
||||
{
|
||||
nrfx_gpiote_in_event_disable(ADA2200_SYNCO_PIN);
|
||||
nrfx_gpiote_in_uninit(ADA2200_SYNCO_PIN);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void half_send_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_half_send_loop_timer_id, APP_TIMER_TICKS(HALF_SEND_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
|
||||
void half_send_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_half_send_loop_timer_id));
|
||||
|
||||
}
|
||||
|
||||
|
||||
void half_send_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_half_send_loop_timer_id, APP_TIMER_MODE_SINGLE_SHOT, half_send_loop));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*******************************************************************************
|
||||
* @file meas_pd_voltage_half.h
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _MEAS_PD_VOLTAGE_HALF_H__
|
||||
#define _MEAS_PD_VOLTAGE_HALF_H__
|
||||
|
||||
#include "sdk_common.h"
|
||||
#include "nrf_drv_saadc.h"
|
||||
|
||||
#ifndef ADA2200_SYNCO_PIN
|
||||
#define ADA2200_SYNCO_PIN NRF_GPIO_PIN_MAP(0,17)
|
||||
#endif
|
||||
|
||||
#define HALF_PD_NO 6
|
||||
#define HALF_LED_NO 6
|
||||
#define HALF_CYCLE_CNT 32
|
||||
|
||||
|
||||
void half_ppi_init(void);
|
||||
void half_ppi_uninit(void);
|
||||
void half_sampling_event_enable(void);
|
||||
void half_sampling_event_disable(void);
|
||||
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
static void half_voltage_handler(nrf_drv_saadc_evt_t const * p_event); /* PD Voltage reading */
|
||||
void half_adc_start(void);
|
||||
void half_adc_start2(void);
|
||||
void half_adc_end(void);
|
||||
void half_adc_init(void);
|
||||
void half_adc_uninit(void);
|
||||
#if !FEATURE_PRINTF
|
||||
void half_irq_init(void);
|
||||
void half_irq_uninit(void);
|
||||
#endif
|
||||
|
||||
void half_send_loop(void * p_context); /* For x ms */
|
||||
void half_send_timer_start(void);
|
||||
void half_send_timer_stop(void);;
|
||||
void half_send_timer_init(void);
|
||||
|
||||
#endif /* _MEAS_PD_VOLTAGE_HALF_H__ */
|
||||
|
||||
349
project/ble_peripheral/ble_app_vivaMayo/meas_pd_voltage_simple.c
Normal file
349
project/ble_peripheral/ble_app_vivaMayo/meas_pd_voltage_simple.c
Normal file
@@ -0,0 +1,349 @@
|
||||
/*******************************************************************************
|
||||
TEST medi50 Dec 23
|
||||
if(resetCount>=3) return true; 3 COUNT 25/11/04 CJ CHUN
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#include "sdk_common.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "nrf.h"
|
||||
#include "boards.h"
|
||||
#include "app_error.h"
|
||||
#include "nrf_drv_saadc.h"
|
||||
#include "app_timer.h"
|
||||
#include "nrfx_gpiote.h"
|
||||
#include "nrf_drv_gpiote.h"
|
||||
#include "nrf_drv_ppi.h"
|
||||
#include "nrf_drv_timer.h"
|
||||
#include "nrf_delay.h"
|
||||
#include "ada2200_spi.h"
|
||||
#include "battery_saadc.h"
|
||||
#include "ble_nus.h"
|
||||
#include "meas_pd_voltage_simple.h"
|
||||
#include "main.h"
|
||||
#include <cmd_parse.h>
|
||||
//#include "fstorage.h"
|
||||
#include "power_control.h"
|
||||
#include "debug_print.h"
|
||||
|
||||
#define PD_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
|
||||
#define PD_PRE_SCALING_COMPENSATION 6.0f /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
|
||||
#define PD_ADC_RES_10BITS 1023.0f /**< Maximum digital value for 10-bit ADC conversion. */
|
||||
|
||||
|
||||
|
||||
// remark for avoid warning error
|
||||
// static nrf_saadc_value_t pressure_adc_buf[2]; // chanul 2ea for simple each 1ea
|
||||
|
||||
|
||||
|
||||
/**@brief Macro to convert the result of ADC conversion in millivolts.
|
||||
|
||||
|
||||
|
||||
*
|
||||
* @param[in] ADC_VALUE ADC result.
|
||||
*
|
||||
* @retval Result converted to millivolts.
|
||||
*/
|
||||
#define PD_VOUT_IN_MILLI_VOLTS(ADC_VALUE)\
|
||||
(((((ADC_VALUE) * PD_REF_VOLTAGE_IN_MILLIVOLTS) / PD_ADC_RES_10BITS) * PD_PRE_SCALING_COMPENSATION)*2)
|
||||
|
||||
#define SIM_SAMPLES_IN_BUFFER 128
|
||||
static nrf_saadc_value_t pd_adc_buf[2][SIM_SAMPLES_IN_BUFFER];
|
||||
|
||||
uint8_t simple_samples_in_buffer = 8;
|
||||
|
||||
#define SAMPLE_CYCLE_CNT 128
|
||||
|
||||
float simple_cycle_buff[SAMPLE_CYCLE_CNT]; /* For Cycle-8, Cycle-16, Cycle-24, Cycle-32 */
|
||||
float simple_cycle_send_buff = 0.0f;
|
||||
int16_t bi_simple_cycle_buff[SAMPLE_CYCLE_CNT];
|
||||
uint16_t ubi_simple_cycle_buff[SAMPLE_CYCLE_CNT];/* For Cycle-8, Cycle-16, Cycle-24, Cycle-32 */
|
||||
int16_t bi_simple_cycle_send_buff = 0;
|
||||
extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] ;
|
||||
static nrf_ppi_channel_t m_ppi_channel;
|
||||
extern uint8_t m_pd_adc_cnt;
|
||||
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
|
||||
extern bool lock_check;
|
||||
extern which_cmd_t cmd_type_t;
|
||||
#if FEATURE_CHAMBER_AUTO_TEST
|
||||
extern auto_meas_mode_t auto_test_mode;
|
||||
#endif
|
||||
extern volatile bool ble_connection_st;
|
||||
extern bool ble_got_new_data;
|
||||
bool con_single;
|
||||
uint8_t rep = 0;
|
||||
extern uint8_t resetCount; //cj add
|
||||
//APP_TIMER_DEF(m_simple_send_loop_timer_id);
|
||||
|
||||
//#define SIMPLE_SEND_LOOP_INTERVAL 100
|
||||
|
||||
#define PATTERN_LENGTH 8
|
||||
#define NORMAL_AMPLITUDE_THRESHOLD 40 // values above ~50 in magnitude are normal
|
||||
|
||||
bool is_lockin_pattern(int16_t *pattern, uint8_t length) {
|
||||
int16_t avg_amplitude = 0;
|
||||
uint8_t positive_count = 0;
|
||||
uint8_t negative_count = 0;
|
||||
|
||||
// Calculate average amplitude and count positives/negatives
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
int16_t value = pattern[i];
|
||||
avg_amplitude += abs(value);
|
||||
|
||||
if (value >= 0) {
|
||||
positive_count++;
|
||||
} else {
|
||||
negative_count++;
|
||||
}
|
||||
}
|
||||
|
||||
avg_amplitude /= length;
|
||||
DBG_PRINTF("lock avg amplitude=%d,p_c=%d,n_c=%d\r\n\r\n", avg_amplitude,positive_count,negative_count);
|
||||
// Check conditions:
|
||||
// - Large enough average amplitude
|
||||
// - Exactly 1 positive and (length - 1) negative values
|
||||
if(resetCount>=3) return true;
|
||||
|
||||
if (avg_amplitude > NORMAL_AMPLITUDE_THRESHOLD &&
|
||||
positive_count == 1 && negative_count == (length - 1)) {
|
||||
return true; // lock-in (normal) pattern
|
||||
} else {
|
||||
|
||||
return false; // abnormal pattern
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void simple_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();
|
||||
|
||||
/* setup ppi channel so that timer compare event is triggering sample task in SAADC */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void simple_ppi_uninit(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
err_code = nrf_drv_ppi_uninit();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
void simple_sampling_event_enable(void)
|
||||
{
|
||||
ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
void simple_sampling_event_disable(void)
|
||||
{
|
||||
ret_code_t err_code = nrf_drv_ppi_channel_disable(m_ppi_channel);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
static void simple_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* PD Voltage reading */
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
|
||||
{
|
||||
|
||||
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, simple_samples_in_buffer);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
for(uint8_t i = 0; i < simple_samples_in_buffer; i++) {
|
||||
bi_simple_cycle_buff[i] = p_event->data.done.p_buffer[i];
|
||||
}
|
||||
|
||||
simple_mesurement_stop();
|
||||
|
||||
simple_cycle_send_buff = 0;
|
||||
bi_simple_cycle_send_buff = 0;
|
||||
|
||||
for(uint8_t i = 0; i < simple_samples_in_buffer; i++) {
|
||||
|
||||
simple_cycle_buff[i] = PD_VOUT_IN_MILLI_VOLTS(bi_simple_cycle_buff[i]);
|
||||
//simple_cycle_buff[i] = simple_cycle_buff[i] * -1;
|
||||
bi_simple_cycle_send_buff += bi_simple_cycle_buff[i];
|
||||
simple_cycle_send_buff += simple_cycle_buff[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(con_single==true)
|
||||
{
|
||||
//return;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if(cmd_type_t == CMD_UART) {
|
||||
DBG_PRINTF("Th%f\r\n\r\n", simple_cycle_send_buff);
|
||||
DBG_PRINTF( "MOD: %f,%f,%f,%f,%f,%f,%f,%f\r\n", simple_cycle_buff[0], simple_cycle_buff[1], simple_cycle_buff[2], simple_cycle_buff[3], simple_cycle_buff[4]
|
||||
, simple_cycle_buff[5], simple_cycle_buff[6], simple_cycle_buff[7]);
|
||||
} else if(cmd_type_t == CMD_BLE) {
|
||||
// if(lock_check == true){
|
||||
DBG_PRINTF("Th%f\r\n\r\n", simple_cycle_send_buff);
|
||||
DBG_PRINTF( "MOD: %f,%f,%f,%f,%f,%f,%f,%f\r\n", simple_cycle_buff[0], simple_cycle_buff[1], simple_cycle_buff[2], simple_cycle_buff[3], simple_cycle_buff[4]
|
||||
, simple_cycle_buff[5], simple_cycle_buff[6], simple_cycle_buff[7]);
|
||||
|
||||
for(uint8_t i = 0; i < simple_samples_in_buffer; i++) {
|
||||
ubi_simple_cycle_buff[i] = (uint16_t)bi_simple_cycle_buff[i];
|
||||
}
|
||||
|
||||
format_data(ble_bin_buffer, "rsh:", ubi_simple_cycle_buff, (m_pd_adc_cnt*2));
|
||||
|
||||
binary_tx_handler(ble_bin_buffer,(m_pd_adc_cnt+2));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
if (lock_check == true) {
|
||||
if (is_lockin_pattern(bi_simple_cycle_buff,8) == false){
|
||||
|
||||
if(device_reactivated() == 0)
|
||||
{
|
||||
DBG_PRINTF("reset!!\r\n");
|
||||
}
|
||||
|
||||
}
|
||||
resetCount++;
|
||||
DBG_PRINTF("Reset Count :%d \r\n",resetCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void simple_adc_init(void)
|
||||
{
|
||||
static nrfx_saadc_config_t default_config;
|
||||
default_config.resolution = (nrf_saadc_resolution_t)NRFX_SAADC_CONFIG_RESOLUTION; /* Resolution is 10bits */
|
||||
default_config.oversample = (nrf_saadc_oversample_t)NRFX_SAADC_CONFIG_OVERSAMPLE; /* Over Sampling Disabled */
|
||||
default_config.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY; /* Interrupt Priority is 0(Highest) */
|
||||
default_config.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE; /* Low Power Mode is Disabled */
|
||||
|
||||
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, simple_voltage_handler);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = nrf_drv_saadc_channel_init(0, &config);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
if(con_single==true){
|
||||
err_code = nrf_drv_saadc_buffer_convert(pd_adc_buf[0], SIM_SAMPLES_IN_BUFFER);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
else{
|
||||
err_code = nrf_drv_saadc_buffer_convert(pd_adc_buf[0], simple_samples_in_buffer);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void simple_adc_uninit(void)
|
||||
{
|
||||
nrf_drv_saadc_uninit();
|
||||
nrf_drv_saadc_channel_uninit(0);
|
||||
}
|
||||
|
||||
|
||||
void simple_irq_init(void){
|
||||
ret_code_t err_code;
|
||||
|
||||
/* Initialize int pin */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void simple_irq_uninit(void){
|
||||
|
||||
nrfx_gpiote_in_event_disable(ADA2200_SYNCO_PIN);
|
||||
nrfx_gpiote_in_uninit(ADA2200_SYNCO_PIN);
|
||||
}
|
||||
|
||||
|
||||
void simple_mesurement_start(void){
|
||||
|
||||
battery_timer_stop();
|
||||
nrf_delay_ms(10);
|
||||
memset(simple_cycle_buff, 0, SAMPLE_CYCLE_CNT);
|
||||
nrf_delay_ms(10);
|
||||
simple_irq_init();
|
||||
nrf_delay_ms(10);
|
||||
simple_adc_init();
|
||||
nrf_delay_ms(10);
|
||||
simple_ppi_init();
|
||||
nrf_delay_ms(10);
|
||||
simple_sampling_event_enable();
|
||||
nrf_delay_ms(10);
|
||||
}
|
||||
|
||||
|
||||
void simple_mesurement_stop(void){
|
||||
|
||||
if(ble_connection_st == 1) {
|
||||
battery_timer_start();
|
||||
}
|
||||
|
||||
|
||||
simple_sampling_event_disable();
|
||||
simple_adc_uninit();
|
||||
simple_ppi_uninit();
|
||||
simple_irq_uninit();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* @file meas_pd_voltage_simple.h
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _MEAS_PD_VOLTAGE_SIMPLE_H__
|
||||
#define _MEAS_PD_VOLTAGE_SIMPLE_H__
|
||||
|
||||
#include "sdk_common.h"
|
||||
|
||||
#ifndef ADA2200_SYNCO_PIN
|
||||
#define ADA2200_SYNCO_PIN NRF_GPIO_PIN_MAP(0,17)
|
||||
#endif
|
||||
|
||||
|
||||
void simple_ppi_init(void);
|
||||
void simple_ppi_uninit(void);
|
||||
void simple_sampling_event_enable(void);
|
||||
void simple_sampling_event_disable(void);
|
||||
void simple_adc_init(void);
|
||||
void simple_adc_uninit(void);
|
||||
void simple_irq_init(void);
|
||||
void simple_irq_uninit(void);
|
||||
void simple_mesurement_start(void);
|
||||
void simple_mesurement_stop(void);
|
||||
|
||||
|
||||
void simple_send_loop(void * p_context); /* For x ms */
|
||||
void simple_send_timer_start(void);
|
||||
void simple_send_timer_stop(void);;
|
||||
void simple_send_timer_init(void);
|
||||
#endif /* _MEAS_PD_VOLTAGE_SIMPLE_H__ */
|
||||
|
||||
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));
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user