/******************************************************************** Copyright (c) 2021 Xiangyu Medical Co.,Ltd. All rights reserved. FileName : drv_saadc.c Author : zhangdawei Version : V1.0 Date : Note : History : ********************************************************************/ /* Includes ------------------------------------------------------*/ #include "nrf_drv_ppi.h" #include "app_timer.h" #include "bsp_btn_ble.h" #include "drv_saadc.h" #include "timer.h" #include "string.h" #include "drv_uart.h" #include "nrf_drv_ppi.h" #include "IIR.h" /* Private define ------------------------------------------------*/ /* Private typedef -----------------------------------------------*/ #define SAMPLES_BUFFER_LEN 10 /* Private constants ---------------------------------------------*/ //定义Timer1的驱动程序实例。驱动程序实例的ID对应Timer的ID,如NRF_DRV_TIMER_INSTANCE(1)对应Timer1 const nrfx_timer_t TIMER_ADC = NRF_DRV_TIMER_INSTANCE(4); static nrf_ppi_channel_t m_ppi_channel5; const nrfx_timer_t TIMER3_RMS = NRFX_TIMER_INSTANCE(3); //读取rms值的定时器3 static nrf_saadc_value_t m_buffer_pool[2][SAMPLES_BUFFER_LEN]; /* Private variables ---------------------------------------------*/ /* Private function prototypes -----------------------------------*/ /* Public constants ----------------------------------------------*/ /* Public variables ----------------------------------------------*/ //电池电压回调函数,堵塞模式,不需要事件,定义空的事件回调函数 void saadc_battery_callback(nrfx_saadc_evt_t const * p_event){} //刺激反馈电压,堵塞模式,不需要事件,定义空的事件回调函数 void saadc_stimulate_callback(nrfx_saadc_evt_t const * p_event){} /******************************************************************** * name : void battery_adc_init(void) * description : ADC初始化,刺激电压检测、电池电压检测、电极脱落电压检测 * Input : void * Output : void * Return : ********************************************************************/ void battery_adc_init(void) { ret_code_t err_code; //初始化SAADC,注册事件回调函数。空函数即可 err_code = nrf_drv_saadc_init(NULL, saadc_battery_callback); APP_ERROR_CHECK(err_code); //配置通道1,输入引脚为AIN7,电池电压 nrf_saadc_channel_config_t channeltwo_config = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7); err_code = nrfx_saadc_channel_init(SAADC_BATTERY_CHANNEL, &channeltwo_config); APP_ERROR_CHECK(err_code); // //配置通道2,输入引脚为AIN4,电极片脱落电压检测 // nrf_saadc_channel_config_t channelthree_config = // NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN4); // //初始化SAADC通道3 // err_code = nrfx_saadc_channel_init(SAADC_ELECTRODE_CHANNEL, &channelthree_config); // APP_ERROR_CHECK(err_code); } // 如需更高精度非线性计算(示例) float nonlinear_percentage(float voltage) { // 三元锂电池的典型放电曲线拟合多项式 float p = (voltage - 3.4f) / 0.8f; // 归一化到[0,1] return 100 * (1.0f - 0.2f*p - 0.8f*p*p); } // 锂电池电量计算函数 // 输入:当前电压(单位:伏特) // 输出:电量百分比(0~100) int calculate_battery_percentage(float voltage) { // 定义电压范围 const float VOLTAGE_MIN = 3.4f; // 0%电量对应电压 const float VOLTAGE_MAX = 4.195f; // 100%电量对应电压 // 边界检查 if (voltage <= VOLTAGE_MIN) { return 0; } else if (voltage >= VOLTAGE_MAX) { return 100; } // 线性插值计算百分比(可根据实际放电曲线修改算法) float percentage = (voltage - VOLTAGE_MIN) / (VOLTAGE_MAX - VOLTAGE_MIN) * 100.0f; // 四舍五入取整 return (int)(percentage + 0.5f); } /******************************************************************** * name : void CalculateBatteryPower(void) * description : 计算电池电量(0-100) * Input : void * Output : void * Return : void ********************************************************************/ void CalculateBatteryPower(void) { uint16_t Adctemp; nrf_saadc_value_t Battery_Saadc_Value; //启动一次ADC采样,存在Battery_Saadc_Value中 nrfx_saadc_sample_convert(SAADC_BATTERY_CHANNEL, &Battery_Saadc_Value); Adctemp = ((Battery_Saadc_Value * 3600) / 16384); static float lastBatteryPercentage = 100; int temp = calculate_battery_percentage(Adctemp * 5.12557); if(Uncharged == ChargeState) //如果没有处于充电状态,不允许电量上升,只能下降 { if(temp < lastBatteryPercentage) Battery_Percentage = temp; } else { Battery_Percentage = temp; } lastBatteryPercentage = Battery_Percentage; } short short_to_big_endian(short value) { uint8_t buf[2]; short resultShort; buf[0] = (value >> 8) & 0xFF; // 高字节(Big-Endian 第一个字节) buf[1] = value & 0xFF; // 低字节(Big-Endian 第二个字节) memcpy(&resultShort,buf,2); return resultShort; } /******************************************************************** * name : void rms_saadc_callback(nrfx_saadc_evt_t const * p_event) * description : 肌电采集SAADC事件回调函数,只有一个缓存填满后才会进入事件回调函数 * Input : nrfx_saadc_evt_t const * p_event:saadc事件 * Output : void * Return : ********************************************************************/ void rms_saadc_callback(nrfx_saadc_evt_t const * p_event) { static float32_t AdcFilter_g[AD_RAW_MAX] __attribute__((aligned(4))); float32_t outFilter __attribute__((aligned(4))); float32_t vol __attribute__((aligned(4))); static emg_data_t emgData= { .emgCnt = 0 }; if(p_event->type == NRF_DRV_SAADC_EVT_DONE) { ret_code_t err_code; err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_BUFFER_LEN); APP_ERROR_CHECK(err_code); } for(int i = 0; i < SAMPLES_BUFFER_LEN; i++) { if(p_event->data.done.p_buffer[i] < 0) vol = 0; else { vol = (p_event->data.done.p_buffer[i]*3600/16384) - 1545; // } bs_bp(&vol, &outFilter); //5.6us if(emgData.emgCnt < AD_RAW_MAX) //防止溢出 AdcFilter_g[emgData.emgCnt] = outFilter; emgData.emgCnt++; if(emgData.emgCnt >= AD_RAW_MAX) { emgData.emgCnt=0; float32_t temp __attribute__((aligned(4))); arm_rms_f32(AdcFilter_g, AD_RAW_MAX, &temp);//6us emgData.EmgValue = temp*0.8806 - 3.028; if(emgData.EmgValue < 0) emgData.EmgValue = 0; // if(temp < 0) // emgData.EmgValue = 0; // else // emgData.EmgValue = 0.6722 * temp + 3.7097; uint16_t outfRms; outfRms = (short)emgData.EmgValue; outfRms = short_to_big_endian(outfRms); ble_send_rms_data(outfRms); } //ble_send_rms_data(save_value[i]); } } void timer3_rms_handler(nrf_timer_event_t event_type, void* p_context) { } /******************************************************************** * name : void timer3_rms_init(void) * description : 定时器1初始化 * Input : void * Output : void * Return : ********************************************************************/ void timer3_rms_init(void) { uint32_t err_code = NRF_SUCCESS; uint32_t time_us = 500; //采样频率2kHz uint32_t time_ticks; nrfx_timer_config_t timer3_cfg = NRFX_TIMER_DEFAULT_CONFIG; err_code = nrfx_timer_init(&TIMER3_RMS, &timer3_cfg, timer3_rms_handler); APP_ERROR_CHECK(err_code); time_ticks = nrfx_timer_us_to_ticks(&TIMER3_RMS, time_us); nrfx_timer_extended_compare( &TIMER3_RMS, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true); } /******************************************************************** * name : void timer3_rms_start(void) * description : 开启定时器1 * Input : void * Output : void * Return : ********************************************************************/ void timer3_rms_start(void) { nrfx_timer_enable(&TIMER3_RMS); } /******************************************************************** * name : void timer3_rms_stop(void) * description : 停止定时器1 * Input : void * Output : void * Return : ********************************************************************/ void timer3_rms_stop(void) { nrfx_timer_disable(&TIMER3_RMS); } /******************************************************************** * name : void PPIEegAdcInit(void) * description : ADCPPI通道初始化 * Input : void * Output : void * Return : ********************************************************************/ void PPIEegAdcInit(void) { uint32_t err_code = NRF_SUCCESS; err_code = nrf_drv_ppi_init(); APP_ERROR_CHECK(err_code); //分配PPI通道,PPI通道的分配是由驱动函数完成的,分配的通道号保存到my_ppi_channel1中 err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel5); APP_ERROR_CHECK(err_code); //分配PPI通道的EEP和TEP err_code = nrf_drv_ppi_channel_assign(m_ppi_channel5, nrf_drv_timer_event_address_get(&TIMER3_RMS, NRF_TIMER_EVENT_COMPARE0), nrf_drv_saadc_sample_task_get()); APP_ERROR_CHECK(err_code); //使能PPI通道 err_code = nrf_drv_ppi_channel_enable(m_ppi_channel5); APP_ERROR_CHECK(err_code); } /******************************************************************** * name : void rms_saadc_init(void) * description : 肌电采集saadc初始化 * Input : void * Output : void * Return : ********************************************************************/ void rms_saadc_init(void) { ret_code_t err_code; nrf_drv_saadc_uninit(); //先反初始化 //配置通道0,输入引脚为AIN3,采集信号 nrf_saadc_channel_config_t channel_config = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2); //初始化SAADC,注册事件回调函数。 err_code = nrf_drv_saadc_init(NULL, rms_saadc_callback); APP_ERROR_CHECK(err_code); //初始化SAADC通道0 err_code = nrfx_saadc_channel_init(SAADC_RMS_SAMPLE_CHANNEL, &channel_config); APP_ERROR_CHECK(err_code); //配置缓存1,将缓存1地址赋值给SAADC驱动程序中的控制块m_cb的一级缓存指针 err_code = nrfx_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_BUFFER_LEN); APP_ERROR_CHECK(err_code); //配置缓存2,将缓存1地址赋值给SAADC驱动程序中的控制块m_cb的二级缓存指针 err_code = nrfx_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_BUFFER_LEN); APP_ERROR_CHECK(err_code); } // 电极脱落检测 void ElectFallOff(void) { } /******************************************************************** * name : void timer4_output_ctrl_handler(nrf_timer_event_t event_type, void* p_context) * description : 变压器电压读取、变压器开关控制、脱落检测、刺激自调节 * Input : nrf_timer_event_t event_type:定时器事件类型 void* p_context * Output : void * Return : ********************************************************************/ void timer1_output_ctrl_handler(nrf_timer_event_t event_type, void* p_context) { switch(event_type) { case NRF_TIMER_EVENT_COMPARE0: //ElectFallOff(); break; default: break; } } /******************************************************************** * name : void timer1_output_ctrl_init(void) * description : 输出控制定时器初始化 * Input : void * Output : void * Return : ********************************************************************/ void timer1_output_ctrl_init(void) { ret_code_t err_code = NRF_SUCCESS; uint32_t time_us = 50; uint32_t time_ticks; nrfx_timer_config_t timer_cfg = NRFX_TIMER_DEFAULT_CONFIG; err_code = nrfx_timer_init(&TIMER_ADC, &timer_cfg, timer1_output_ctrl_handler); APP_ERROR_CHECK(err_code); //定时时间(单位us)转换为ticks time_ticks = nrfx_timer_us_to_ticks(&TIMER_ADC, time_us); //设置定时器捕获/比较通道及该通道的比较值,使能通道的比较中断 nrfx_timer_extended_compare( &TIMER_ADC, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true); nrfx_timer_enable(&TIMER_ADC); } /*************************** END OF FILE ***************************/