我是靠谱客的博主 清新口红,这篇文章主要介绍NRF52832学习笔记(7)——ADC接口使用一、简介二、硬件连接三、移植文件(单次采样)四、API调用五、SDK配置六、使用例子,现在分享给大家,希望可以做个参考。

一、简介

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),因此信号的采样模式可分为单端输入和差分输入。

默认状态下,ADC 的配置模式为 单端输入 (即 CH[n].CONFIG 寄存器中的 MODE = 0),此时芯片内部将 ADC 负极输入短接到地。

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

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

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

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

其中:

  • V§: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 控制,即定时时间长,采样速率慢,定时时间短,采样速率快,需要注意的是,使用连续采样模式的时候,采样速率应符合下面的公式:

使用 SAMPLERATE 的 Timer 实现的连续采样不能和扫描模式结合使用,连续采样模式下只能使用一个通道。
EVENTS_DONE 事件表明了一次采样的完成,连续采样模式下,在没有过采样发生的情况下 EVENTS_RESULTDONE 事件等同于 EVENTS_DONE 事件,注意在实际采样数据通过 EasyDMA 被保存到 RAM 之前,这两个事件都会产生。

1.3.3 连续转换

当我们使能一个 ADC 通道,ADC 工作于单次模式,当使能的通道数量大于 1 个,ADC进入扫描模式。
扫描模式下,采样所有通道花费的总时间如下式:

EVENTS_DONE 事件表明了一次采样的完成,扫描模式下,在没有过采样发生的情况下EVENTS_RESULTDONE 事件等同于 EVENTS_DONE 事件,注意在实际采样数据通过EasyDMA 被保存到 RAM 之前,这两个事件都会产生。

二、硬件连接

功能口引脚
AIN0P2
AIN1P3
AIN2P4
AIN3P5
AIN4P28
AIN5P29
AIN6P30
AIN7P31

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

注意:以下出现缺失common.h文件错误,去除即可。uint8改为uint8_t或unsigned char或自己宏定义
链接:https://pan.baidu.com/s/1rsa6nkGuU1QgEsQ0i1PGZQ 提取码:vpyt
board_adc.cboard_adc.h 两个文件加入工程的Application文件夹下

3.1board_adc.c

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/********************************************************************* * 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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#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 文件

选择 Configuration Wizard

nRF_Drivers 中勾选SAADC相关选项

六、使用例子

1)添加头文件

复制代码
1
2
#include "board_adc.h"

2)添加初始化代码(SDK15.3 中 ble_peripheral 的 ble_app_template 工程 main() 函数中)
加入 ADC_Init() 并在初始化后调用 ADC_Disable 进入低功耗,在需要用ADC时调用 ADC_Enable 开启ADC

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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进入低功耗

复制代码
1
2
3
4
5
6
7
8
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值进行电压值转换

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/** @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

最后

以上就是清新口红最近收集整理的关于NRF52832学习笔记(7)——ADC接口使用一、简介二、硬件连接三、移植文件(单次采样)四、API调用五、SDK配置六、使用例子的全部内容,更多相关NRF52832学习笔记(7)——ADC接口使用一、简介二、硬件连接三、移植文件(单次采样)四、API调用五、SDK配置六、使用例子内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(191)

评论列表共有 0 条评论

立即
投稿
返回
顶部