nrf52xx battery voltage ADC low power sliding filter sampling

Development environment: software: 15.3 version of the SDK, hardware: nRF52810

A. Add library files

Insert picture description here

B. Modify the macro in the configuration file 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. Code writing & manual reading

Some specific calculations below use bit operations instead of multiplication and division.

#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;
}

Manual reading:
nRF52810
nRF52810 can choose two ADC reference voltages, one is VDD and the other is internal reference voltage.
The ADC voltage range of external input can be adjusted by gain.
For example, the internal reference voltage is used as the ADC reference voltage.
Assuming the software sets the gain to 1/6 and the input voltage range of the chip ADC core is 0.6v, then the external input range is

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

In the default parameter macro provided by the library, the reference voltage source is the internal reference voltage, and the gain is 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          \
}

When initializing the configuration structure parameters, directly use the default parameter macro given by the library.
I select the input pin parameters according to the pins used by the hardware 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);

The value calculated by the library is the sampled value with 3.6v as the reference voltage and 10bit precision.

vol = saadc_val * 360 / 1024;

Through the above formula, we can get the voltage value of 0.01v as the unit. Transformation is

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

D. Determine the sampling time point

GetFilteredBatAdc puts the RTC second interrupt wakeup sampling after the chip sleeps.
g_rtc1Tick adds 1 per second, and the ADC is sampled every 64 seconds

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

E. Hardware schematic diagram

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

Insert picture description here

F. Sampling effect

The figure below is the data recorded by the company's platform. The voltage jitter is calculated as the minimum granularity of 0.01V (when the temperature is basically constant), the
orange points are all 3.01V, and the red points are 3.0V.
Insert picture description here

Guess you like

Origin blog.csdn.net/qq_28851611/article/details/108415782