26. ADC——电压采集¶
26.1. ADC简介¶
ADC 即模拟-数字转换器(Analog-to-digital converter), 是一种用于将连续的模拟信号转换为离散的数字信号的器件。 就比如我们可以将我们生活中的温度、压力、声音这样的模拟信号通过 ADC 转化为可以通过单片机处理的数字信号。
RA6M5 有2个ADC单元,每个ADC单元有12位、10位、8位读取数据的格式可以选择,在单元0上有13个ADC通道,而在单元1上有16个ADC通道。 ADC单元具有三种扫描方式分别为:单次描模式、连续扫描模式和分组扫描模式, 对于RA6M5来说ADC单元具有强大的功能,具体我们可以通过ADC特性和ADC的结构框图来分析每个部分的功能。
RA4M2 有1个ADC单元,ADC单元也有12位、10位、8位读取数据的格式可以选择,有13个ADC通道。 RA2L1 有1个ADC单元,ADC单元仅有12位读取数据的格式可以选择,有19个ADC通道。
ADC模块特性
以启明6M5开发板的为例,RA6M5 的ADC特性如下:
2 个ADC转换单元。
支持高达 26 个通道。 其中有三组通道(AN000 & AN100、AN001 & AN101 和 AN002 & AN102)分别共用相同的引脚, 因此通道 AN000 和 AN100 不可同时使用,AN001 和 AN101、AN002 和 AN102 这两组也同理。
支持内部温度传感器,可以检测芯片运行温度;支持测量内部参考电压。
逐次逼近型ADC,支持的分辨率:12-bit, 10-bit, 8-bit。
转换时间很短:0.4 μs/每通道(这是在使用 12-bit ADC、时钟 PCLKC (ADCLK) 等于 50 MHz 的条件下)。
PCLKA 是 RA6M5 ADC 外设模块的时钟,用于驱动外设模块的工作; 而 PCLKC (ADCLK) 是用于 A/D 转换的时钟,它们的频率需要保持一定的比例。 PCLKA 与 PCLKC (ADCLK) 的时钟频率比可以设置为:1:1, 2:1, 4:1, 8:1, 1:2, 1:4。
可启用的 A/D 数据存储缓冲区是一个环形缓冲区,由16个缓冲组成,用于顺序存储 A/D 转换后的数据。
更多详细具体的特性请读者参考芯片的硬件手册。
需要注意的是,RA4M2 与 RA6M5 的 ADC 外设基本上没多大差别,而 RA2L1 的 ADC 特性差别稍微大一点。 三者 ADC 特性的简单对比如下表所示:
特性 |
RA6M5/RA4M2 |
RA2L1 |
---|---|---|
ADC转换单元 (逐次逼近型) |
RA6M5:2个 RA4M2:1个 |
1个 |
输入通道 |
RA6M5:26个 (少数通道连接到相同引脚) RA4M2:13个 |
19个 |
支持分辨率 |
12-bit, 10-bit, 8-bit |
12-bit |
AD转换时间 |
0.4 μs/每通道 |
正常转换模式:0.7 µs/每通道 快速转换模式:0.67 µs/每通道 |
AD转换时钟 |
ADC外设模块时钟:PCLKA AD转换时钟:PCLKC (ADCLK) |
ADC外设模块时钟:PCLKB AD转换时钟:PCLKD (ADCLK) |
操作模式 |
|
|
ADC 的相关引脚或信号及其功能用途如下表所示:
引脚 |
功能 |
---|---|
AVCC0 |
作为ADC12和DAC12的模拟电源输入引脚 |
AVSS0 |
作为ADC12和DAC12的模拟电源接地引脚 |
VREFH0 |
作为ADC12单元 0 的参考高电压引脚 |
VREFL0 |
作为ADC12单元 0 的参考低电压引脚 |
VREFH |
作为ADC12单元 1 的参考高电压引脚 |
VREFL |
作为ADC12单元 1 的参考低电压引脚 |
AN0xx |
ADC12单元0的模拟输入通道(xx为通道号) |
AN1xx |
ADC12单元1的模拟输入通道(xx为通道号) |
26.2. ADC的结构框图¶
RA6M5 有两个ADC单元,即 ADC Unit 0 和 ADC Unit 1;而 RA4M2 和 RA2L1 仅有一个ADC单元。 实际上,RA4M2 的ADC单元与 RA6M5 的 ADC Unit 0 是基本一致的,RA2L1 的ADC单元也仅有很少的区别。 下面我们将以 RA6M5 的两个ADC单元为例来讲解ADC模块的结构框图。
RA6M5 的 ADC Unit 0 的结构框图如下图所示:
RA6M5 的 ADC Unit 1 的结构框图如下图所示:
对比以上 ADC 单元0和单元1的结构框图, 可以发现两者除了参考电压引脚不同以外,其他结构几乎完全相同。
26.2.1. 单元0和单元1的独立参考电压¶
见图中标注 ① 处。
ADC0 和 ADC1 的参考电压其实是独立的,这一点需要注意一下:
ADC0 的参考电压为 VREFH0 和 VREFL0,而
ADC1 的参考电压为 VREFH 和 VREFL。
RA6M5 的两个 ADC 单元每个单元都可以设置为不同的参考电压, VREFL0、VREFH0 是 ADC0 参考电压和参考地;VREFL、VREFH 是 ADC1 参考电压和参考地。 用户在硬件设计时可以为这两个不同的 ADC 单元设置不同的参考电压。
电压输入范围
ADC0 通道的输入电压范围为:VREFL0 ≤ VIN ≤ VREFH0;
ADC1 通道的输入电压范围为:VREFL ≤ VIN ≤ VREFH。
我们在设计原理图的时候一般把 AVSS0 和 VREFL0/VREFL 接地, 把 AVCC0 和 VREFH0/VREFH 经过一个磁珠进行滤波之后接到3V3,因而可得到 ADC 的输入电压范围为:0~3.3V。 如果我们想让输入的电压范围变宽,可以测试负电压或者更高的正电压,我们可以在外部加一个电压调理电路, 把需要转换的电压转换到0~3.3V的范围,这样就可以使用ADC进行测量了。
26.2.2. 单元0和单元1的输入通道¶
见图中标注 ② 处。
ADC 的模拟输入通道:
AN0xx 是 ADC0 的模拟输入通道(xx为通道号);
AN1xx 是 ADC1 的模拟输入通道(xx为通道号)。
具体的 ADC 通道如下:
ADC0 通道:AN000 ~ AN010, AN012, AN013(共13个通道)
ADC1 通道:AN100 ~ AN102, AN116 ~ AN128(共16个通道)
总共29个ADC通道(实际可同时使用的为26个通道)
从上图中,我们还可以看到: ADC0 与 ADC1 之间有三组通道,即 AN000 和 AN100、AN001 和 AN101、AN002 和 AN102 分别共用了相同的物理引脚, 因此通道 AN000 和 AN100 不可同时使用,AN001 和 AN101、AN002 和 AN102 这两组也是一样的,不可同时使用。
26.2.3. 模拟多路转换器和采样保持器¶
见图中标注 ③ 处。
模拟多路转换器用于对需要转换的ADC通道进行选择, 从上图可以发现,使用 ADC 除了可以对一般通道(AN0xx 或 AN1xx)的电压进行测量以外, 我们还可以选择对内部参考电压、内部温度传感器输出等信号进行测量。
采样保持器具有对输入的信号进行采样和保持的功能。
26.2.4. AD转换器、AD数据和控制寄存器¶
见图中标注 ④ 处。
ADC0 和 ADC1 均为逐次逼近型的AD转换器。 ADC 控制寄存器用于通过控制电路控制 ADC 外设模块的各项功能。 这部分了解即可,读者不必深究。
对于 RA6M5 和 RA4M2 来说,用户可设置 ADC 外设的 AD 转换精度为 12-bit, 10-bit, 8-bit, 相应的,AD 转换结束后保存到数据寄存器的结果对应为 12-bit, 10-bit, 8-bit 数据。 对于 RA2L1,其 ADC 外设仅支持 AD 转换精度为 12-bit。
用户还可以设置 ADC 数据寄存器的保存格式为左对齐或右对齐。如下图所示。
26.2.5. 控制电路以及触发输入和输出信号¶
见图中标注 ⑤ 处。
中断请求信号
事件输出到ELC信号
同步触发信号
异步触发信号
中断源
在 AD 扫描结束后,会产生一个 AD 扫描结束中断请求信号和 ELC 信号。 用户还可以设置激活 DTC 启动数据传输,将 ADC 转换的结果快速传输至指定的位置。
触发源
用于触发启动 AD 转换的触发源包含以下的三种类型:
软件触发
来自事件链接控制器(ELC)的同步触发
通过外部触发引脚,以及 ADTRG0 (unit 0) 和 ADTRG1 (unit 1)
下图为我们可以在FSP里选择的模式:
26.3. ADC扫描模式¶
在ADC单元0中有多达13个模拟输入通道,而在ADC单元1中有多达16个模拟输入通道, 两个ADC单元加起来总共就拥有了29个ADC通道,但是实际上ADC单元0和ADC单元1分别有3个通道只能连接到相同的引脚, 所以实际上能同时使用的通道数量为26个,加上还有内部的温度传感器输出等特殊通道。
这么多的ADC通道我们该如何使用它们呢?在这里我们引入“扫描模式”的概念, 在使用ADC外设时,我们必须首先配置其扫描模式,使得ADC按照特定扫描模式的方式进行ADC转换。
ADC 外设有三种扫描模式,分别为:单次扫描模式、连续扫描模式和组扫描模式。 在 FSP 配置器中,用户可以为 ADC 模块设置为三种扫描模式当中的其中一种,如下图所示。
单次扫描模式:在单次扫描下,每一次触发将扫描一个或多个指定通道。
连续扫描模式:在连续扫描下,首先需要一次触发,然后一个或多个指定的通道会被重复连续进行扫描, 直到软件调用函数 R_ADC_ScanStop() 停止扫描。
分组扫描模式:将所选择的模拟输入通道分为A组和B组,然后按组对所选择的模拟输入通道进行一次A/D转换。 A、B组可独立选择扫描启动条件,可独立启动A、B组的A/D转换。
在每种模式中,模拟通道按通道数的升序进行转换,然后扫描温度传感器和电压传感器(如果它们也被勾选了的话)。 每一种扫描模式都有着它的优点和缺点,但具体使用什么模式进行ADC转换,就需要通过我们的项目的需求需要什么样的效果来决定。
26.3.1. 单次扫描模式¶
在单次扫描模式转换期间,我们可以通过ADST为来判断ADC是否处在工作状态,在ADC转换的期间ADST为将一直保持为1,当所有选定通道的ADST转换完成时,将自动设置为0。然后ADC将进入一个等待状态。
当ADCSR.ADST位通过软件触发器、同步触发器输入(ELC)和异步触发器输入被置1的时候,ADC转换开始。对在ADANSA0和ADANSA1寄存器中选择的ANn通道进行A/D转换,从编号最小的n的通道开始。
每当单个信道的A/D转换完成时,A/D转换结果都被存储在关联的A/D数据寄存器(ADDRy)中。
当所有选定通道的A/D转换完成时,将生成一个ADC12i_ADI(i = 0,1)中断请求。
26.3.2. 连续扫描模式¶
在连续扫描模式下,对指定信道的模拟输入重复执行A/D转换。 这里的ADCSR.ADST位不会自动清除,只要ADCSR.ADST位保持1时就会一直的重复步骤2、步骤3、步骤4,直到ADCSR.ADST位通过软件被置0时ADC单元转换才会停止,之后ADC单元进入等待状态。
当ADCSR.ADST位通过软件触发器、同步触发器输入(ELC)和异步触发器输入被置1的时候,ADC转换开始。对在ADANSA0和ADANSA1寄存器中选择的ANn通道进行A/D转换,从编号最小的n的通道开始。
每当单个信道的A/D转换完成时,A/D转换结果都被存储在关联的A/D数据寄存器(ADDRy)中。
当所有选定通道的A/D转换完成时,将生成一个ADC12i_ADI(i = 0,1)中断请求。
对在ADANSA0和ADANSA1寄存器中选择的ANn通道进行A/D转换,从编号最小的n的通道开始。
26.3.3. 组扫描模式¶
在分组扫描模式下,应用程序将通道分配给两个组:组 A 和组 B 当中的一个。 可以为这些组分配不同的启动触发器,分别选择A、B两组的开始扫描条件,当接收到该组的指定 ELC 启动触发器时,转换开始。 并且组 A 可以设置为优先于组 B,当 A 组优先于 B 组时,A 组触发器将暂停正在进行的 B 组扫描。
我们以ELC为例子:使用GPT作为A组的触发源,并使用A组作为B组的触发源。
当ELC0上的GPT触发ELC_ADC(A组)时,A组的ADC开始转换。
当组A扫描完成时,将生成一个ADC12i_ADI(i = 0,1)中断。
B组的扫描由ELC_ADC(A组)开始。
当B组扫描完成时,如果ADCSR.GBADIE位为1时将生成一个ADC12i_GBADI(i = 0,1)中断。
26.4. ADC转换时间¶
26.4.1. ADC时钟¶
当使用 ADC 时,ADC 转换时钟(ADCLK)必须至少为1MHz。 并且,在使用 ADC 时很多 RA MCU 一般也有 PCLK 比率限制。
RA6M5 和 RA4M2 的 AD 转换时钟是由 PCLKC 经过分频产生, PCLKA 和 PCLKC (ADCLK) 的分频比可以设置为 1:1, 2:1, 4:1, 8:1, 1:2, 1:4。 当使用 50 MHz 时钟的时候 12-bit AD转换时间为 0.4 μs。
RA2L1 的 AD 转换时钟是由 PCLKD 经过分频产生, PCLKB 和 PCLKD (ADCLK) 的分频比可以设置为 1:1, 1:2, 1:4。 在正常转换模式下,当使用 64 MHz 时钟的时候 12-bit AD转换时间为 0.7 μs; 在快速转换模式下,当使用 48 MHz 时钟的时候 12-bit AD转换时间为 0.67 μs。
26.4.2. 采样时间¶
扫描转换时间(tSCAN)包括:扫描开始时间(tD)、断开检测辅助处理时间(tDIS)*1、自诊断A/D转换处理时间(tDIAG和tDSD)*2、A/D转换处理时间(tCONV)、扫描结束时间(tED)。
A/D转换处理时间(tCONV)由输入采样时间(tSPL)和逐次逼近转换时间(tSAM)组成。采样时间(tSPL)用于在A/D转换器中对采样和保持电路充电。 如果由于模拟输入信号源的高阻抗而没有足够的采样时间,或者如果A/ D转换时钟(ADCLK)很慢,可以使用ADSSTRn寄存器来调整采样时间。
由逐次逼近(tSAM)转换的时间如下
12位精度需要13个ADCLK
状态10位精度需要11个ADCLK
状态8位精度需要9个ADCLK
选择通道数为n的单次描模式下的扫描转换时间(tSCAN)可确定为:
连续扫描模式下第一个周期的扫描转换时间为单次扫描减去tED的tSCAN。连续扫描模式下第二次及后续周期的扫描转换时间固定如下:
26.5. 电压值转换¶
模拟电压经过ADC转换后,是一个12位的数字值,如果通过串口以16进制打印出来的话,可读性比较差, 那么有时候我们就需要把数字电压转换成模拟电压,也可以跟实际的模拟电压(用万用表测)对比,看看转换是否准确。
一般在设计硬件原理图的时候会把ADC的输入电压范围设定在:0~3.3V。 若设置ADC的分辨率是12位的,那么12位满量程对应的电压就是3.3V,12位满量程对应的数字值是:2^12,数值0对应的就是0V。 假设转换后的数值为 X,X 对应的模拟电压为 Y,那么会有这么一个等式成立:
因此在ADC转换完成之后,我们可以调用 FSP 库函数 R_ADC_Read(), 从 ADC 的数据寄存器里读出上述等式中 X 的值,从而再经过计算得出对应的电压值。
26.6. 实验:电位器电压采集¶
26.6.1. 硬件设计¶
野火启明6M5开发板的 ADC 电位器电路图如下图所示。
野火启明4M2开发板的 ADC 电位器电路图如下图所示。
野火启明2L1开发板的 ADC 电位器电路图如下图所示。
可以看到,三块开发板板载的电位器都是连接到 P000 引脚, P000 引脚可以连接到MCU内部的 ADC 外设,从而对电位器输入的模拟信号进行采集。
电位器可调端ADC引脚 |
P000 |
26.6.2. 软件设计¶
26.6.2.1. 新建工程¶
- 对于 e2 studio 开发环境:
拷贝一份我们之前的 e2s 工程模板 “19_UART_Receive_Send”, 然后将工程文件夹重命名为 “25_ADC”,最后再将它导入到我们的 e2 studio 工作空间中。
- 对于 Keil 开发环境:
拷贝一份我们之前的 Keil 工程模板 “19_UART_Receive_Send”, 然后将工程文件夹重命名为 “25_ADC”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。
工程新建好之后,在工程根目录的 “src” 文件夹下面新建 “adc” 文件夹, 再进入该文件夹里面新建源文件和头文件:“bsp_adc.c” 和 “bsp_adc.h”。 工程文件结构如下。
25_ADC
├─ ......
└─ src
├─ led
│ ├─ bsp_led.c
│ └─ bsp_led.h
├─ debug_uart
│ ├─ bsp_debug_uart.c
│ └─ bsp_debug_uart.h
├─ adc
│ ├─ bsp_adc.c
│ └─ bsp_adc.h
└─ hal_entry.c
26.6.2.2. FSP配置¶
打开该工程的 FSP 配置界面进行配置。
首先依次点击 “Stacks” -> “Pins” -> “Peripherals” -> “ADC0” 来配置通道 AN000 对应的引脚为 P000。 如下图所示。
然后依次点击 “Stacks” -> “New Stack” -> “Analog” -> “ADC (r_adc)” 来配置ADC模块。 如下图所示。
ADC 的属性配置:
ADC属性 |
描述 |
---|---|
General > Name |
模块实例名 |
General > Unit |
指定要使用的ADC单元 |
General > Resolution |
指定转换分辨率 |
General > Alignment |
指定转换结果对齐方式 |
General > Clear after read |
读取转换结果后自动清除 |
General > Mode |
模式
|
General > Double-trigger |
双触发 |
Input > Channel Scan Mask (channel availability varies by MCU) |
该通道位掩码用于使能 ADC 通道; 若是在组模式下,则是用于指定哪些通道归属于扫描组 A。 这里要勾选 “Channel 0”,图上由于篇幅关系没有标出来 |
Interrupts > Normal/Group A Trigger |
正常模式下的或组模式下组A的触发类型。 这里按照默认,选择软件触发 |
Interrupts > Callback |
中断回调函数。设置为 adc_callback |
Interrupts > Scan End Interrupt Priority |
扫描完成中断优先级 |
Extra > ADC Ring Buffer |
ADC环形缓冲区 |
配置完成之后可以按下快捷键“Ctrl + S”保存, 最后点右上角的 “Generate Project Content” 按钮,让软件自动生成配置代码即可。
26.6.2.3. ADC初始化函数¶
void ADC_Init(void)
{
fsp_err_t err;
err = R_ADC_Open(&g_adc0_ctrl, &g_adc0_cfg);
err = R_ADC_ScanCfg(&g_adc0_ctrl, &g_adc0_channel_cfg);
assert(FSP_SUCCESS == err);
}
R_ADC_Open()为整个外设设置操作模式、触发源、中断优先级和配置。如果启用了中断,该函数将注册一个回调函数指针,以便在扫描完成时通知用户。
R_ADC_ScanCfg()配置ADC扫描参数。通道特定设置是在这个函数中设置的。
26.6.2.4. ADC中断回调函数¶
//ADC转换完成标志位
volatile bool scan_complete_flag = false;
void adc_callback(adc_callback_args_t * p_args)
{
FSP_PARAMETER_NOT_USED(p_args);
scan_complete_flag = true;
}
在FSP配置页面注册回调函数以及优先级,我们就可以使用来自ADC的中断回调函数了。
注解
我们通过ADC的中断回调函数来判断ADC是否转换完成。 我们需要定义了一个布尔类型的数据scan_complete_flag来当做ADC读取完成的标志位。 当没有转换完成的时候scan_complete_flag的值一直为false,单ADC触发中断的时候将scan_complete_flag的值变为true。
26.6.2.5. 如果未启用中断¶
如果未启用中断,则可使用R_ADC_StatusGet() API 用于轮询 ADC 以确定扫描何时完成。读取 API 函数用于访问转换后的 ADC 结果。这适用于支持校准的MCU的普通扫描和校准扫描。
26.6.2.6. ADC读取转换结果函数¶
ADC读取思路,我们在这里调用R_ADC_ScanStart触发相应的adc通道转换,当ADC转换完成之后会将scan_complete_flag标志位变为true。 当我们判断到标志位变为true后我们使用R_ADC_Read()或R_ADC_Read32()读取转换完成的数值。
/* 进行ADC采集,读取ADC数据并转换结果 */
double Read_ADC_Voltage_Value(void)
{
uint16_t adc_data;
double a0;
(void)R_ADC_ScanStart(&g_adc0_ctrl);
while (!scan_complete_flag) //等待转换完成标志
{
;
}
scan_complete_flag = false; //重新清除标志位
/* 读取通道0数据 */
R_ADC_Read(&g_adc0_ctrl, ADC_CHANNEL_0, &adc_data);
/* ADC原始数据转换为电压值(ADC参考电压为3.3V) */
a0 = (double)(adc_data*3.3/4095);
return a0;
}
R_ADC_ScanStart()启动软件扫描或启用扫描的硬件触发器,具体取决于触发器在R_ADC_Open调用中的配置方式。如果该单元被配置为ELC或外部硬件触发,那么该功能允许触发信号到达ADC单元。该函数不能控制触发器本身的生成。如果该单元被配置为软件触发,则该功能启动软件触发扫描。
R_ADC_Read()从单通道或传感器寄存器读取转换结果,返回的数据为uint16_t型。
R_ADC_Read32()从单通道或传感器寄存器读取转换结果,返回的数据为uint32_t型。
26.6.2.7. hal_entry入口函数¶
void hal_entry(void)
{
LED_Init(); // LED 初始化
Debug_UART4_Init(); // SCI4 UART 调试串口初始化
/* ADC 初始化 */
ADC_Init();
printf("这是一个读取电位器ADC电压转换值的例程\r\n");
printf("打开串口助手查看ADC转换结果,旋钮电位器,可以看到ADC值在一定范围之内发生变化\r\n");
printf("开始读取ADC转换值:\r\n");
while(1)
{
printf("a0 = %f\r\n", Read_ADC_Voltage_Value());
R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS); //大概0.5秒钟读取一次
LED1_TOGGLE;
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
26.6.3. 下载验证¶
用USB TYPE-C线连接开发板“USB TO UART”接口跟电脑,在电脑端打开串口调试助手,把编译好的程序下载到开发板。 在串口调试助手可看到从 ADC 引脚读出的模拟电压数值。