持续创造,加速生长!这是我参加「日新计划 10 月更文挑战」的第7天,点击检查活动概况

一、简介

1.1 主要特点

NRF52832 中 ADC 为一个 逐次逼近(SAADC) 模拟数字转化器

  • 8/10/12 位分辨率,采用过采样能够到达 14 位分辨率。
  • 多达 8 个输入通道: 单端输入时有 1 个通道,2 个通道组成差分输入。 单端和差分输入时能够装备成扫描形式。
  • 满量程输入规模为 0 和 VDD
  • 能够经过软件触发采样使命发动采样,也能够运用低功耗 32.768KHz 的 RTC 守时器或更精确的 1/16MHz 守时器经过 PPI 来触发采样使命。
  • NRF52832 的 SAADC 支持 单次形式和扫描形式: 单次的收集形式只运用一个收集通道。 扫描形式是依照次序采样一系列通道。Sample delay between channels is tack + tconv which may vary between channels according to user configuration of tack.
  • 经过 EasyDMA 能够直接将采样结果保存到 RAM。
  • 无需外部守时器即可完结接连采样。
  • 可装备通道输入负载电阻。
  • 具备采样值门限检测功用。

1.2 采样形式

NRF52832 的 ADC 有 16 个通道(其间 8个正端输入通道P ,和 8个负端输入N),因此信号的采样形式可分为单端输入和差分输入。

NRF52832学习笔记(7)——ADC接口使用
默认状态下,ADC 的装备形式为 单端输入 (即 CH[n].CONFIG 寄存器中的 MODE = 0),此时芯片内部将 ADC 负极输入短接到地。

差分输入 (即 CH[n].CONFIG 寄存器中的 MODE = 1),则是把负极经过负向端输入,经过核算两端的差值来换算出采样结果。

运用单端形式时,是将内部地和外部待测电压的参阅地假设为一样来考虑的,但是地弹噪声会导致 ADC 产生误差,假如这个误差超过咱们能接受的规模,建议运用差分输入。

在参阅源上,相对于 nRF51822,nRF52832 取消了外部参阅源,只能运用内部参阅源。

ADC 输出的采样取决与 CH[n].CONFIG 和 RESOLUTION 寄存器装备的参数,采样结果核算公式如下:

NRF52832学习笔记(7)——ADC接口使用

其间:

  • V(P):ADC 输入正极。
  • V(N):ADC 输入负极。
  • GAIN:CH[n].CONFIG 寄存器中设置的增益 GAIN(1/6、1/5、1/4、1/3、1/2、1、2、4)。
  • REFERENCE:参阅电压(两种方法:一个是内部参阅电压0.6V,另一个是VDD/4为参阅电压)。
  • RESOLUTION:采样精度(8/10/12,采用过采样能够到达 14 位)。
  • m:假如 ADC 装备为单端形式,m = 0,假如 ADC 装备为差分形式,m = 1。

1.3 作业形式

SAADC 有三种作业形式:单次转化、接连转化和扫描形式。

1.3.1 单次转化

装备ADC的寄存器CH[n].PSELP、CH[n].PSELN 和 CH[n].CONFIG 使得ADC作业于单次形式。触发采样使命后,ADC 开端采样输入电压,采样时间经过 CH[n].CONFIG.TACQ 装备。EVENTS_DONE 事情表明晰一次采样的完结。 在没有过采样产生的情况下 EVENTS_RESULTDONE 事情等同于 EVENTS_DONE 事情,留意在实践采样数据经过EasyDMA 被保存到 RAM 之前,这两个事情都会产生。

1.3.2 接连转化

能够经过下面两种方法完结接连形式:

  • 运用 ADC 内部守时器完结守时采样,ADC 有一个 SAMPLERATE 寄存器,该寄存器能够装备为 Timer,装备 SAMPLERATE 的比较值 SAMPLERATE.CC 即可完结守时采样。这种方法下,触发一次采样使命即可对一切使能的通道进行采样。
  • 运用 nRF52832 的通用守时器守时经过 PPI 触发采样,完结接连采样。这种方法不能算是 ADC 本身的接连采样功用,由于它是凭借了其他外设完结的接连采样。  采样速率由 SAMPLERATE.CC 操控,即守时时间长,采样速率慢,守时时间短,采样速率快,需求留意的是,运用接连采样形式的时分,采样速率应契合下面的公式:

NRF52832学习笔记(7)——ADC接口使用

运用 SAMPLERATE 的 Timer 完结的接连采样不能和扫描形式结合运用,接连采样形式下只能运用一个通道。 EVENTS_DONE 事情表明晰一次采样的完结,接连采样形式下,在没有过采样产生的情况下 EVENTS_RESULTDONE 事情等同于 EVENTS_DONE 事情,留意在实践采样数据经过 EasyDMA 被保存到 RAM 之前,这两个事情都会产生。

1.3.3 接连转化

当咱们使能一个 ADC 通道,ADC 作业于单次形式,当使能的通道数量大于 1 个,ADC进入扫描形式。 扫描形式下,采样一切通道花费的总时间如下式:

NRF52832学习笔记(7)——ADC接口使用

EVENTS_DONE 事情表明晰一次采样的完结,扫描形式下,在没有过采样产生的情况下EVENTS_RESULTDONE 事情等同于 EVENTS_DONE 事情,留意在实践采样数据经过EasyDMA 被保存到 RAM 之前,这两个事情都会产生。

二、硬件连接

功用口 引脚
AIN0 P2
AIN1 P3
AIN2 P4
AIN3 P5
AIN4 P28
AIN5 P29
AIN6 P30
AIN7 P31

NRF52832学习笔记(7)——ADC接口使用

三、移植文件(单次采样)

留意:以下出现缺失common.h文件过错,去除即可。uint8改为uint8_t或unsigned char或自己宏界说

链接:pan.baidu.com/s/1rsa6nkGu… 提取码:vpyt

board_adc.cboard_adc.h 两个文件参加工程的Application文件夹下

NRF52832学习笔记(7)——ADC接口使用
NRF52832学习笔记(7)——ADC接口使用

3.1board_adc.c

/*********************************************************************
 * 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****************************************************/

3.2 board_adc.h

#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_ */

四、API调用

需包含头文件 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装备

点击 sdk_config.h 文件

NRF52832学习笔记(7)——ADC接口使用

选择 Configuration Wizard

NRF52832学习笔记(7)——ADC接口使用

nRF_Drivers 中勾选SAADC相关选项

NRF52832学习笔记(7)——ADC接口使用

六、运用比如

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