开发环境: 软件: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 可以选择两种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。