NRF52832 中 ADC 为一个 逐次逼近(SAADC) 模拟数字转换器
NRF52832 的 ADC 有 16 个通道(其中 8个正端输入通道N ,和 8个负端输入P),因此信号的采样模式可分为单端输入和差分输入。
默认状态下,ADC 的配置模式为 单端输入 (即 CH[n].CONFIG 寄存器中的 MODE = 0),此时芯片内部将 ADC 负极输入短接到地。
而 差分输入 (即 CH[n].CONFIG 寄存器中的 MODE = 1),则是把负极通过负向端输入,通过计算两端的差值来换算出采样结果。
使用单端模式时,是将内部地和外部待测电压的参考地假设为一样来考虑的,但是地弹噪声会导致 ADC 产生误差,如果这个误差超过我们能接受的范围,建议使用差分输入。
在参考源上,相对于 nRF51822,nRF52832 取消了外部参考源,只能使用内部参考源。
ADC 输出的采样取决与 CH[n].CONFIG 和 RESOLUTION 寄存器配置的参数,采样结果计算公式如下:
其中:
SAADC 有三种工作模式:单次转换、连续转换和扫描模式。
配置ADC的寄存器CH[n].PSELP、CH[n].PSELN 和 CH[n].CONFIG 使得ADC工作于单次模式。触发采样任务后,ADC 开始采样输入电压,采样时间通过 CH[n].CONFIG.TACQ 配置。EVENTS_DONE 事件表明了一次采样的完成。
在没有过采样发生的情况下 EVENTS_RESULTDONE 事件等同于 EVENTS_DONE 事件,注意在实际采样数据通过EasyDMA 被保存到 RAM 之前,这两个事件都会产生。
可以通过下面两种方式实现连续模式:
使用 SAMPLERATE 的 Timer 实现的连续采样不能和扫描模式结合使用,连续采样模式下只能使用一个通道。
EVENTS_DONE 事件表明了一次采样的完成,连续采样模式下,在没有过采样发生的情况下 EVENTS_RESULTDONE 事件等同于 EVENTS_DONE 事件,注意在实际采样数据通过 EasyDMA 被保存到 RAM 之前,这两个事件都会产生。
当我们使能一个 ADC 通道,ADC 工作于单次模式,当使能的通道数量大于 1 个,ADC进入扫描模式。
扫描模式下,采样所有通道花费的总时间如下式:
EVENTS_DONE 事件表明了一次采样的完成,扫描模式下,在没有过采样发生的情况下EVENTS_RESULTDONE 事件等同于 EVENTS_DONE 事件,注意在实际采样数据通过EasyDMA 被保存到 RAM 之前,这两个事件都会产生。
功能口 | 引脚 |
---|---|
AIN0 | 0 |
AIN1 | 2 |
AIN2 | 3 |
AIN3 | 4 |
AIN4 | 5 |
注意:以下出现缺失common.h文件错误,去除即可。uint8改为uint8_t或unsigned char或自己宏定义
链接:https://pan.baidu.com/s/1rsa6nkGuU1QgEsQ0i1PGZQ 提取码:vpyt
将 board_adc.c 和 board_adc.h 两个文件加入工程的Application文件夹下
/*********************************************************************
* INCLUDES
*/
#include "nrfx_saadc.h"
#include "nrf_drv_saadc.h"
#include "app_error.h"
#include "board_adc.h"
#include "User/user_battery.h"
#include "nrf_log.h"
static void adcCallbackFunc(nrf_drv_saadc_evt_t const *pEvent);
/*********************************************************************
* LOCAL VARIABLES
*/
static nrf_saadc_value_t s_bufferPool[SAMPLES_IN_BUFFER];
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/**
@brief ADC的初始化函数
@param 无
@return 无
*/
void ADC_Init(void)
{
ret_code_t errCode;
// ADC初始化
errCode = nrf_drv_saadc_init(NULL, adcCallbackFunc);
APP_ERROR_CHECK(errCode);
// ADC通道配置
nrf_saadc_channel_config_t channelConfig = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0); // 单端输入
// ADC通道初始化
errCode = nrf_drv_saadc_channel_init(0, &channelConfig);
APP_ERROR_CHECK(errCode);
// 缓冲配置
errCode = nrf_drv_saadc_buffer_convert(s_bufferPool, SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(errCode);
}
/**
@brief ADC读取
@param 无
@return 结果在回调函数的缓冲区中
*/
void ADC_Read(void)
{
ret_code_t errCode;
errCode = nrf_drv_saadc_sample();
APP_ERROR_CHECK(errCode);
}
/**
@brief 开启ADC,与初始化没有区别,为了与Disable成对出现
@param 无
@return 无
*/
void ADC_Enable(void)
{
ADC_Init();
}
/**
@brief 禁用ADC
@param 无
@return 无
*/
void ADC_Disable(void)
{
nrfx_saadc_uninit();
}
/*********************************************************************
* LOCAL FUNCTIONS
*/
/**
@brief ADC中断处理回调函数
@param 无
@return 无
*/
static void adcCallbackFunc(nrf_drv_saadc_evt_t const *pEvent)
{
if(pEvent->type == NRF_DRV_SAADC_EVT_DONE) // 采样完成
{
nrf_saadc_value_t adcResult;
uint16 batteryVoltage;
uint8 batteryPercentage;
ret_code_t errCode;
// 设置好缓存,为下次转换缓冲做准备,并且把导入到缓冲的值提取出来
errCode = nrf_drv_saadc_buffer_convert(pEvent->data.done.p_buffer, SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(errCode);
adcResult = pEvent->data.done.p_buffer[0];
// 电池电压转换计算
batteryVoltage = ADC_RESULT_IN_MILLI_VOLTS(adcResult);
}
}
/****************************************************END OF FILE****************************************************/
#ifndef _BOARD_ADC_H_
#define _BOARD_ADC_H_
/*********************************************************************
* INCLUDES
*/
#include "common.h"
/*********************************************************************
* DEFINITIONS
*/
#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 1000 /**< Typical forward voltage drop of the diode . */
#define ADC_RES_10BIT 1024 /**< Maximum digital value for 10-bit ADC conversion. */
// VP = (RESULT * REFERENCE / 2^10) * 6
#define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
((((ADC_VALUE) * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_10BIT) * ADC_PRE_SCALING_COMPENSATION)
#define SAMPLES_IN_BUFFER 1
/*********************************************************************
* API FUNCTIONS
*/
void ADC_Init(void);
void ADC_Read(void);
void ADC_Enable(void);
void ADC_Disable(void);
#endif /* _BOARD_ADC_H_ */
需包含头文件 board_adc.h
ADC_Init
功能 | 初始化ADC驱动 |
---|---|
函数定义 | void ADC_Init(void) |
参数 | 无 |
返回 | 无 |
ADC_Read
功能 | ADC读取函数 |
---|---|
函数定义 | 无 |
参数 | 无 |
返回 | 无 |
ADC_Enable
功能 | 使能ADC,内部重新初始化ADC驱动,在ADC_Disable之后使用 |
---|---|
函数定义 | void ADC_Enable(void) |
参数 | 无 |
返回 | 无 |
ADC_Disable
功能 | 禁用ADC,以进入低功耗 |
---|---|
函数定义 | void ADC_Disable(void) |
参数 | 无 |
返回 | 无 |
点击 sdk_config.h 文件
选择 Configuration Wizard
nRF_Drivers 中勾选SAADC相关选项
1)添加头文件
#include "board_adc.h"
2)添加初始化代码(SDK15.3 中 ble_peripheral 的 ble_app_template 工程 main() 函数中)
加入 ADC_Init() 并在初始化后调用 ADC_Disable 进入低功耗,在需要用ADC时调用 ADC_Enable 开启ADC
int main(void)
{
bool erase_bonds;
/*-------------------------- 外设驱动初始化 ---------------------------*/
// Initialize.
log_init(); // 日志驱动初始化
timers_init(); // 定时器驱动初始化(在此加入自定义定时器)
ADC_Init(); // ADC驱动初始化
/*-------------------------- 蓝牙协议栈初始化 ---------------------------*/
power_management_init();
ble_stack_init(); // 协议栈初始化
gap_params_init();
gatt_init();
advertising_init(); // 广播初始化
services_init(); // 服务初始化
conn_params_init(); // 连接参数初始化
peer_manager_init();
/*-------------------------- 开启应用 ---------------------------*/
// Start execution.
NRF_LOG_INFO("Template example started.");
advertising_start(erase_bonds); // 开启广播
application_timers_start(); // 定时器应用开启(在此开启自定义定时器)
ADC_Disable(); // 禁用ADC,进入低功耗模式,等待读取时再开启
// Enter main loop.
for(;;)
{
idle_state_handle();
}
}
3)在定时器回调中开启ADC读取,然后再开一个10ms定时器关闭ADC进入低功耗
static void timer_batteryMeasureOnCallback(void *arg)
{
UNUSED_PARAMETER(arg);
ADC_Enable(); // 开启ADC
ADC_Read();
app_timer_start(s_batteryMeasureOffTimer, BATTERY_MEASURE_OFF_PERIOD, NULL);
}
4)在ADC中断处理回调函数中,将采集ADC值进行电压值转换
/**
@brief ADC中断处理回调函数
@param 无
@return 无
*/
static void adcCallbackFunc(nrf_drv_saadc_evt_t const *pEvent)
{
if(pEvent->type == NRF_DRV_SAADC_EVT_DONE) // 采样完成
{
nrf_saadc_value_t adcResult;
uint16 batteryVoltage;
ret_code_t errCode;
// 设置好缓存,为下次转换缓冲做准备,并且把导入到缓冲的值提取出来
errCode = nrf_drv_saadc_buffer_convert(pEvent->data.done.p_buffer, SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(errCode);
adcResult = pEvent->data.done.p_buffer[0];
// 电池电压转换计算
batteryVoltage = ADC_RESULT_IN_MILLI_VOLTS(adcResult);
}
}
• 由 Leung 写于 2020 年 1 月 6 日
• 参考:青风电子社区
Nordic–nrf52832–ADC