nrf52xx 电池电压ADC低功耗滑动滤波采样

开发环境: 软件:15.3版本的SDK ,硬件:nRF52810

A、添加库文件

在这里插入图片描述

B、在配置文件 sdk_config.h 中修改宏

SAADC_ENABLED & NRFX_SAADC_ENABLED

// <e> SAADC_ENABLED - nrf_drv_saadc - SAADC peripheral driver - legacy layer
//==========================================================
#ifndef SAADC_ENABLED
#define SAADC_ENABLED 1//0
#endif
// <0=> 8 bit 
// <1=> 10 bit 
// <2=> 12 bit 
// <3=> 14 bit 

#ifndef SAADC_CONFIG_RESOLUTION
#define SAADC_CONFIG_RESOLUTION 1
#endif
// <e> NRFX_SAADC_ENABLED - nrfx_saadc - SAADC peripheral driver
//==========================================================
#ifndef NRFX_SAADC_ENABLED
#define NRFX_SAADC_ENABLED 1//0
#endif
// <0=> 8 bit 
// <1=> 10 bit 
// <2=> 12 bit 
// <3=> 14 bit 

#ifndef NRFX_SAADC_CONFIG_RESOLUTION
#define NRFX_SAADC_CONFIG_RESOLUTION 1
#endif

C、代码编写 & 手册阅读

下面某些具体计算用位运算替代了乘除法。

#include "nrf_drv_saadc.h"
#include "nrfx_saadc.h"
#include "nrf_saadc.h"
/*
*sdk_config.h 修改宏
*SAADC_ENABLED & NRFX_SAADC_ENABLED
*/
//阻塞模式不需要回调事件处理获取采样值
void saadc_evt_callback(nrf_drv_saadc_evt_t const * p_event)
{
    
    
}
//滤波后的电池电压x100的值 比如 300 表示 3.0v
uint16_t g_filteredBatteryVol = 0;
//读电池电压引脚工作模式
static bool s_isBatPinAdcMode = false;
/**
  * 函数功能:初始化ADC外设
  */
void SaadcInit()
{
    
    
    ret_code_t err_code;
    
    //定义 ADC 通道配置结构体,并使用单端采样配置宏 初始化
    //NRF_SAADC_INPUT_AIN3 是使用的模拟输入通道
    nrf_saadc_channel_config_t channel_config = 
      NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3);
    
    if(s_isBatPinAdcMode == true)
        return;
    
    //初始化 SAADC,注册事件回调函数
    err_code = nrf_drv_saadc_init(NULL,saadc_evt_callback);
    APP_ERROR_CHECK(err_code);
    //初始化 SAADC 通道 0
    err_code = nrfx_saadc_channel_init(0,&channel_config);
    APP_ERROR_CHECK(err_code);
    s_isBatPinAdcMode = true;
}
/**
  * 函数功能:关闭芯片的ADC外设,降低功耗
  */
void SaadcUnInit()
{
    
    
    if(s_isBatPinAdcMode == false)
        return;
    nrfx_saadc_channel_uninit(0);
    nrf_drv_saadc_uninit();
    s_isBatPinAdcMode = false;
}
/**
  *函数功能:获取滤波后的电池电压
  *返回值:滤波后的电池电压x100的值 比如 300 表示 3.0v
  */
uint16_t GetFilteredBatAdc(void)
{
    
    
    nrf_saadc_value_t saadc_val;
    static char index = 0;
    static uint16_t batteryVol[4];
    uint16_t maxVal, miniVal;
    
    SaadcInit();                                    //开启ADC
    nrfx_saadc_sample_convert(0,&saadc_val);        //采样当前ADC
    SaadcUnInit();                                  //关闭ADC降低功耗
    batteryVol[index] =                             //电压ADC采样补偿 +1
    (int16_t)(((saadc_val<<5) + (saadc_val<<3) + (saadc_val<<2) + saadc_val)>>7) + 1;
    if(g_filteredBatteryVol){
    
    }
    else
    {
    
                                                   //首次电压采样
        batteryVol[1] = batteryVol[0];
        batteryVol[2] = batteryVol[0];
        batteryVol[3] = batteryVol[0];
    }
    maxVal  = batteryVol[0];                        //获取4个值中的最大值最小值
    miniVal = batteryVol[0];
    for(char i = 1; i < 4; i++)
    {
    
    
        if(maxVal   < batteryVol[i])
            maxVal  = batteryVol[i];
        if(miniVal  > batteryVol[i])
            miniVal = batteryVol[i];
    }
    g_filteredBatteryVol =                                          \
    (batteryVol[0] + batteryVol[1] + batteryVol[2] + batteryVol[3]  \
     - maxVal - miniVal + 1) >> 1;                  //去掉最大最小值再取平均 计算补偿 +1
    index = (index + 1) & 0x03;                     //更新下一个ADC入队列索引
	return g_filteredBatteryVol;
}

手册阅读:
nRF52810
nRF52810 可以选择两种ADC的参考电压,一种是VDD,一种是内部参考电压。
外部输入的ADC电压范围可以通过增益调节。
比如用内部参考电压作为ADC参考电压
假设软件设置增益为 1/6,芯片 ADC核的输入电压范围为 0.6v,那么外部输入范围为

Input range = (0.6v)/(1/6) = 3.6v

在库给的默认参数宏中参考电压源是内部参考电压,增益是 1/6.

/**
 * @brief Macro for setting @ref nrf_saadc_channel_config_t to default settings
 *        in single ended mode.
 *
 * @param PIN_P Analog input.
 */
#define NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(PIN_P) \
{                                                   \
    .resistor_p = NRF_SAADC_RESISTOR_DISABLED,      \
    .resistor_n = NRF_SAADC_RESISTOR_DISABLED,      \
    .gain       = NRF_SAADC_GAIN1_6,                \
    .reference  = NRF_SAADC_REFERENCE_INTERNAL,     \
    .acq_time   = NRF_SAADC_ACQTIME_10US,           \
    .mode       = NRF_SAADC_MODE_SINGLE_ENDED,      \
    .burst      = NRF_SAADC_BURST_DISABLED,         \
    .pin_p      = (nrf_saadc_input_t)(PIN_P),       \
    .pin_n      = NRF_SAADC_INPUT_DISABLED          \
}

在初始化配置结构体参数时,直接使用库给的默认参数宏
我根据硬件使用的引脚选择输入的引脚参数NRF_SAADC_INPUT_AIN3

    ret_code_t err_code;
    //定义 ADC 通道配置结构体,并使用单端采样配置宏 初始化
    //NRF_SAADC_INPUT_AIN6 是使用的模拟输入通道
    nrf_saadc_channel_config_t channel_config = 
      NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3);
    
    if(s_isBatPinAdcMode == true)
        return;
    
    //初始化 SAADC,注册事件回调函数
    err_code = nrf_drv_saadc_init(NULL,saadc_evt_callback);
    APP_ERROR_CHECK(err_code);
    //初始化 SAADC 通道 0
    err_code = nrfx_saadc_channel_init(0,&channel_config);
    APP_ERROR_CHECK(err_code);
    s_isBatPinAdcMode = true;
nrfx_saadc_sample_convert(0,&saadc_val);

用库计算出来的值就是以3.6v为参考电压,10bit 精度的采样值。

vol = saadc_val * 360 / 1024;

通过上式我们可以获得 0.01v为单位的电压值。转化一下就是

vol = (int16_t)((saadc_val<<5) + (saadc_val<<3) + (saadc_val<<2) + saadc_val)>>7;

D、确定采样时间点

GetFilteredBatAdc 放在芯片睡眠后RTC秒中断唤醒采样
g_rtc1Tick 每 秒加 1,每64秒采样一次ADC

#define SystemIdleTask()        \
idle_state_handle();            \
if((g_rtc1Tick & 0x1) == 0)     \
{                               \
    FeedDog();                  \
    if(0 == (g_rtc1Tick & 0x3F))\
        GetFilteredBatAdc();    \
}

E、硬件原理图

由于这个项目的电池是 3.3v的,所以不需要电阻分压 (高于 3.6V时需要分压)

在这里插入图片描述

F、采样效果

下图是公司平台记录的数据,电压抖动为计算最小颗粒度 0.01V (温度基本恒定的情况下)
橘黄的点都是 3.01V, 红色的点是 3.0V。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_28851611/article/details/108415782