nrf52 ADC采集
一、简介
Analog-to-Digital Converter 模拟信号/数字信号转换器,简称ADC。它是指将连续变化的模拟信号转换为离散的数字信号的模块,如现实生活中的声音、温度、压力等类型的模拟信号,需要ADC转换成处理器可识别的数字信号。在nRF52xx系列处理器中的ADC为一个逐次逼近(successive-approximation)模拟数字转换器,nordic的nrf52xx系列芯片的内部ADC称为SAADC。其具体属性如下:
1、8/10/12分辨率,采用过采样可达到14位分辨率;
2、8输入通道;
3、满量程输入范围位0和VDD;
4、通过软件触发采样任务启动采样,或者RTC定时器或PPI触发采样任务;
5、单次采样只能使用一个采集通道;
6、扫描模式是按照顺序依次采样配置通道,其延迟时间由tack + tconv决定,可配置tack改变采样间隔;
7、通过使用EasyDMA可以直接将采样结果保存到RAM中;
8、采样中断发生在单次采样结束后或者连续采样的数据缓存填满时;
9、无需外部定时器协同就可以实现连续采样。
二、原理
根据下图公式计算出ADC采样结果:
SAADC输入管脚和内部结构体
三、工程移植
1、添加驱动文件
并确保添加头文件路径
2、使能saadc
3、应用ADC
driver_adc.h
#ifndef __DRIVER_ADC_H
#define __DRIVER_ADC_H
/*********************************************************************
* INCLUDES
*********************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include "nrf_drv_saadc.h"
#include "ble_bas.h"
/*********************************************************************
* DEFINITIONS
*********************************************************************/
#define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
((((ADC_VALUE) * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_12BIT) * ADC_PRE_SCALING_COMPENSATION)
#define ADC_REF_VOLTAGE_IN_MILLIVOLTS 600 /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
#define ADC_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 DIODE_FWD_VOLT_DROP_MILLIVOLTS 270 /**< Typical forward voltage drop of the diode . */
#define ADC_RES_12BIT 1024 /**< Maximum digital value for 10-bit ADC conversion. */
//#define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE) ((ADC_VALUE * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_12BIT)
/*********************************************************************
* API FUNCTIONS
*********************************************************************/
void saadc_init(void); //初始化ADC
void refresh_ad_value(void *p_context); //更新AD值
uint8_t battery_percent_get(void); //获取电池百分比
void refresh_battery_level(void); //刷新电池ADC数据
void update_battery_service(ble_bas_t *x3_bas); //更新电池服务数据
void adc_disable(void);
void adc_enable(void);
#endif
driver_adc.c
/*********************************************************************
* INCLUDES
*********************************************************************/
#include "nrf_delay.h"
#include "nrf_log.h"
#include "driver_adc.h"
#define ADC2_SAMPLES_BUFFER 16
/*********************************************************************
* LOCAL VARIABLES
*********************************************************************/
static nrf_saadc_value_t adc_buf[2][ADC2_SAMPLES_BUFFER];
// static uint8_t percentage_batt_lvl = 0; //电池百分比
static uint16_t batt_lvl_in_milli_volts = 0; //电池AD值
static void saadc_callback(nrf_drv_saadc_evt_t const *p_event);
static void adc_read(void);
/*********************************************************************
* PUBLIC FUNCTIONS
*********************************************************************/
/**
* @brief Function is saadc of init.
*/
void saadc_init(void)
{
ret_code_t err_code = nrf_drv_saadc_init(NULL, saadc_callback); // saadc的初始化
if (NRF_ERROR_INVALID_STATE == err_code)
{
return;
}
else
APP_ERROR_CHECK(err_code);
nrf_saadc_channel_config_t config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2); //采集 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_buf[0], ADC2_SAMPLES_BUFFER);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_buffer_convert(adc_buf[1], ADC2_SAMPLES_BUFFER); //用双缓冲采集
APP_ERROR_CHECK(err_code);
}
/**
* @brief 刷新AD值
*/
void refresh_ad_value(void *p_context)
{
UNUSED_PARAMETER(p_context);
adc_read();
}
/**
* @brief 获取电池百分比
*/
uint8_t battery_percent_get()
{
batt_lvl_in_milli_volts = 0;
while (batt_lvl_in_milli_volts == 0)
{
adc_read();
nrf_delay_ms(10);
}
return battery_level_in_percent(batt_lvl_in_milli_volts);
}
/**
* @brief 刷新电池ADC数据
*/
void refresh_battery_level(void)
{
adc_read();
}
/**
* @brief 更新到服务
* 以及显示(未完成)
*/
void update_battery_service(ble_bas_t *x3_bas)
{
uint32_t err_code;
uint8_t percentage_batt_lvl = 0; //电池百分比
batt_lvl_in_milli_volts = 0;
while (batt_lvl_in_milli_volts == 0)
{
adc_read();
nrf_delay_ms(10);
}
percentage_batt_lvl = battery_level_in_percent(batt_lvl_in_milli_volts);
err_code = ble_bas_battery_level_update(x3_bas, percentage_batt_lvl, BLE_CONN_HANDLE_ALL); //更新到电池服务
if ((err_code != NRF_SUCCESS) &&
(err_code != NRF_ERROR_INVALID_STATE) &&
(err_code != NRF_ERROR_RESOURCES) &&
(err_code != NRF_ERROR_BUSY) &&
(err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING))
{
APP_ERROR_HANDLER(err_code);
}
// err_code = ble_advertising_advdata_update(&m_advertising, &new_advdata, NULL); //更新到广播
// APP_ERROR_CHECK(err_code);
}
/**
@brief 开启ADC,与初始化没有区别,为了与Disable成对出现
@param 无
@return 无
*/
void adc_enable(void)
{
saadc_init();
}
/**
@brief 禁用ADC
@param 无
@return 无
*/
void adc_disable(void)
{
nrfx_saadc_uninit();
}
/*********************************************************************
* LOCAL FUNCTIONS
*********************************************************************/
/**
* @brief saadc_callback adc回调处理函数
* @param nrf_drv_saadc_evt_t const * p_event 事件参数
*/
static void saadc_callback(nrf_drv_saadc_evt_t const *p_event)
{
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
KUI_QUEUE_PARAM uiParam;
// nrf_saadc_value_t adc_result;
uint16_t value_t[ADC2_SAMPLES_BUFFER] = {0};
uint8_t i;
uint32_t err_code, sum = 0, temp = 0;
// adc_result = p_event->data.done.p_buffer[ADC2_SAMPLES_BUFFER - 2]; //采集结果
for (i = 0; i < ADC2_SAMPLES_BUFFER; i++)
{
value_t[i] = p_event->data.done.p_buffer[i]; //电池电量计算公式--电压值
sum = sum + value_t[i];
// printf("%d\r\n", value_t[i]);
}
// printf("%d\r\n", adc_result);
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, ADC2_SAMPLES_BUFFER);
APP_ERROR_CHECK(err_code);
temp = (sum / (ADC2_SAMPLES_BUFFER));
batt_lvl_in_milli_volts = ADC_RESULT_IN_MILLI_VOLTS(temp); //电池电量计算公式--电压值
percentage_batt_lvl = battery_level_in_percent(batt_lvl_in_milli_volts); //配置电池水平
}
}
/**
@brief ADC读取
@param 无
@return 结果在回调函数的缓冲区中
*/
static void adc_read(void)
{
ret_code_t errCode;
errCode = nrf_drv_saadc_sample();
APP_ERROR_CHECK(errCode);
}
/****************************************************END OF FILE****************************************************/
采用双缓冲单通道采集,用算术平均滤波法去滤波(因为有时电压跳动比较大,所以选用滤波去滤掉它),保证电压稳定。如果电源没有很高的跳变,选择单次采集就可以,没必要多缓存去采集,ADC的值更新得没有那么快。
四、参考文章