什么是ADC
ADC是一种用于将模拟信号转换为数字信号的模拟数字转换器。我们知道,模拟信号是连续的,其取值可以在一定范围内任意变化。而数字信号则是离散的,仅能取有限的值。ADC的工作原理是将模拟信号通过采样转换为离散的数字信号,然后再通过量化、编码等处理,最终得到对应的数字表示。ADC采样的频率越高,得到的数字信号就越接近原来的模拟信号,也就是保真度越高,但是需要更多的资源和计算功耗。
地奇星 的ADC介绍
R7FA6E2BB3CNE 它搭载了一个模数转换器(ADC),其中A/D转换精度支持从12位,10位,8位转换中选择,从而可以优化权衡在产生数字值的速度和分辨率之间。支持以下工作模式:单扫描模式、连续扫描模式与 组扫描模式。
ADC的基础参数
分辨率:表示ADC转换器的输出精度,通常以位数(bit)表示,地奇星开发板支持8位、10位、12位,其位数越高,精度越高。
采样率:表示ADC对模拟输入信号进行采样的速率,通常以每秒采样次数(samples per second,SPS)表示,也称为转换速率,表示ADC能够进行多少次模拟到数字的转换。
采样范围:指ADC可以采集到的模拟输入信号的电压范围,范围为:VREFH0 ≥ ADC ≥ VREFL0 。
注:其中VREFL0为V(SSA)等于地0V,VREFH0 等于V(DDA),而立创·地奇星开发板在原理图设计的时候VDDA接入了3.3V 。
ADC的基本原理
ADC(模数转换器)是一种电子设备,用于将连续的模拟信号转换为对应的数字信号。这种转换过程涉及到几个基本原理:
采样:ADC首先对模拟信号进行采样。这意味着它以固定的时间间隔测量输入信号的值。采样频率决定了系统对输入信号的精度和响应速度。
保持:在进行采样后,信号的值被"保持"在一个电容器或者某种存储设备中,以便在转换期间保持不变。这可以防止因为信号在转换期间发生变化而引起的误差。
量化:采样后的模拟信号值需要被量化成数字形式。这个过程涉及将连续的模拟信号分成离散的数值级别,通常使用二进制表示。ADC的分辨率决定了它能够提供的数字级别数量,也就是其精度。
编码:量化后的信号被编码成数字形式,通常以二进制的形式表示。这些数字可以由微处理器或者其他数字电路进行处理和存储。
输出:最后,编码后的数字信号可以被传输到数字系统中进行处理,比如微处理器、FPGA等。
这些基本原理构成了ADC的工作方式,使得它能够将来自传感器、信号源或者其他模拟设备的信号转换为数字形式,以便数字系统进行处理、分析和存储。
ADC的优点
数字信号具有良好的抗干扰性。数字信号是由一系列离散的数字表示,因此可以抵抗模拟信号受到的各种干扰,如噪声、漂移等。
方便数字信号的存储、处理和传输。由于数字信号是离散的,因此它们可以轻松存储在计算机内存或其他数字设备中,方便进行处理和传输。
具有可编程性。现代的ADC出现了很多可编程的功能,例如可编程增益、采样率和滤波器等,可以根据不同的应用场景进行优化。
适用性广泛。ADC被广泛应用于工业、通信、医疗、电子测量、音频、视频等领域,可转换各种不同类型的模拟信号,包括电压、电流、声音、光信号等。
ADC 特性
ADC 说明
注:这里需要注意的一点的是地奇星开发板的ADC转换时间是使用时钟源PCLKC其支持最大的频率为:50MHz,AD 转换时间为 0.52us 每通道。其外设模块时钟PCLKA和A/D转换时钟PCLKC (ADCLK)可以通过以下方式设置分频比:PCLKA与PCLKC (ADCLK)频率比= 1:1、2:1、4:1、8:1、1:2、1:4
ADC I/O Pins
引脚名称 | 输入/输出 | 功能 |
---|---|---|
AVCC0 | 输入 | 模拟模块电源引脚(不使用ADC12/DAC12时连接至VCC) |
AVSS0 | 输入 | 模拟模块电源接地引脚(不使用ADC12/DAC12时连接至VSS) |
VREFH0 | 输入 | 模拟参考电压电源引脚 |
VREFL0 | 输入 | 模拟参考接地引脚 |
AN000至AN002、AN007、AN011至AN013、AN016 | 输入 | 模拟输入引脚0至2、7、11至13、16 |
ADTRG0 | 输入 | 启动A/D转换的外部触发输入引脚 |
ADC 采集
注:我们这里的工程是复制的串口工程上继续开发的。
首先打开 e2 studio 软件新建工程后 ,在 FSP Configuration -> Pins -> Peripherals -> Analog:ADC -> ADC0 接下来我们需要配置 Operation Mode ,选择 Custom 。如下图所示:
接下来,添加ADC的模块, New Stack -> Analog -> ADC(r_adc),如下图所示:
接下来我们选择 g_adc0_ADC 模块,然后点击属性,然后放大,需要修改的内容,如下图所示:
这里需要说明一下工作模式,如下图所示:
- 单次扫描模式:在单次扫描下,每一次触发将扫描一个或者多个。
- 连续扫描模式:先触发一次,之后设定的通道会不停地重复扫描,一直到用软件里的 R_ADC_ScanStop () 函数喊停才会结束。
- 分组扫描模式:把选好的模拟输入通道分成 A、B 两组,分别对两组通道进行 A/D 转换。A 组和 B 组能各自决定什么时候开始扫描,可以单独启动各自的转换。
中断源,如下图所示:
用于触发ADC转换的方法主要为三种类型:
- 软件触发。
- 事件链接控制器(ELC)的同步触发
- 外部触发引脚。
其中模式依次为:
- 外部触发
- 软件触发
- ADC0比较匹配触发
- ADC0比较不匹配触发
- ADC0扫描结束触发
- 端口1-4事件触发
- SCI4 AM(Address match event) 地址匹配事件触发
- 接收数据错误触发
- 接收数据满事件触发
- 发送数据结束事件触发
- 发送完数据事件触发
注:
- General:
- Uint:adc的号数。
- Resolution:ADC的采样位数。
- Alignment:ADC采样结果对齐方式。
- Clear after read:读取ADC采样值后是否清空对应寄存器值
- Mode:ADC采样模式(Single Scan:单次扫描 Continuous Scan:连续扫描 Group Scan:分组扫描)
- Double-triger :adc双触发源使能。
- input:
- Channel Scan Mask:在普通模式转换通道,在组模式,指定A组通道。
- Group B Scan Mask:在组模式,指定B组通道。
- Interrupts:
- Normal/Group A Trigger:通道A的触发(软件、外部引脚、ELC信号触发)
- Group B Trigger:通道B的触发(软件、外部引脚、ELC信号触发)
- Group Priority:选择A组是否可以中端B组采样
- Callback:中断回调函数
- Scan End Interrupt Priority 扫描采样完成中断优先级
- Scan End Group B Interrupt Pririty:B组扫描完成中断优先级
- Windows Compare A/B Interrupt Pririty:A/B组电压比较中断优先级
设置完成后,按下 Ctrl + S 进行保存,然后点击 Generate Project Content 进行工程生成。
程序编写
然后再 src 下创建一个 adc 的文件夹,在里面创建 bsp_adc.c 与 bsp_adc.h 两个文件,创建一个 Applay 的文件夹,在里面创建 app.c 与 app.h 两个文件,如下图所示:
#include "Apply\app.h"
#include "uart\bsp_uart.h"
#include "adc\bsp_adc.h"
void Run(void)
{
Debug_UART0_Init();
ADC_Init();
printf("欢迎使用立创·地奇星RA6E2开发板\r\n");
printf("接下来开始 ADC 实验:\r\n");
while(1)
{
printf("ADC0=%f\r\n",ADC_read_Value());
R_BSP_SoftwareDelay(1,BSP_DELAY_UNITS_SECONDS);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef __APP_H
#define __APP_H
#include "hal_data.h"
#include <stdio.h>
void Run(void);
#endif
2
3
4
5
6
7
8
9
#include "bsp_adc.h"
#include "r_adc_api.h"
#include "hal_data.h"
#include <stdio.h>
volatile bool adc_flag = false;
void ADC_Init(void)
{
fsp_err_t err;
// 打开ADC设备
err = R_ADC_Open(&g_adc0_ctrl, &g_adc0_cfg);
if (FSP_SUCCESS != err) {
printf("ADC初始化失败! \n");
return;
}
// 配置ADC扫描
err = R_ADC_ScanCfg(&g_adc0_ctrl, &g_adc0_channel_cfg);
if (FSP_SUCCESS != err) {
printf("ADC扫描配置失败! \n");
R_ADC_Close(&g_adc0_ctrl); // 关闭已打开的ADC
return;
}
printf("ADC初始化成功!\n");
}
void adc_callback(adc_callback_args_t *p_args)
{
FSP_PARAMETER_NOT_USED(p_args);
adc_flag = true;
}
double ADC_read_Value(void)
{
uint16_t adc_data = 0;
double temperature = 0.0F;
// 启动ADC扫描
fsp_err_t err = R_ADC_ScanStart(&g_adc0_ctrl);
if (FSP_SUCCESS != err) {
printf("ADC扫描启动失败! \n");
return 9999; // 返回错误值
}
// 等待ADC转换完成
while (!adc_flag);
adc_flag = false; // 清除标志位
// 读取ADC数据
err = R_ADC_Read(&g_adc0_ctrl, ADC_CHANNEL_0, &adc_data);
if (FSP_SUCCESS != err) {
printf("ADC数据读取失败!\n");
return 9999; // 返回错误值
}
// 计算温度值
temperature = (adc_data * 3.3)/ 4096;
return temperature;
}
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
#ifndef __BSP_ADC_H_
#define __BSP_ADC_H_
#include "hal_data.h"
#include "stdio.h"
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
void ADC_Init(void);
double ADC_read_Value(void);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "hal_data.h"
#include "Apply\app.h"
FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER
/*******************************************************************************************************************//**
* main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
* is called by main() when no RTOS is used.
**********************************************************************************************************************/
void hal_entry(void)
{
/* TODO: add your own code here */
Run();
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ADC 实验结果
我这边稳压电源提供是一个 1V 的电压,这里采集出来的范围也接近 1V ,这是正常的,因为器件与稳压电源都有误差。
TSN 实验
在配置程序之前我们需要查看一下他的一个ADC转换为温度的公式,部分截图(第1699页)如下图所示:
这里只需要满足 T=(Vs-V1)/slope +T1 这个公式即可。
注:
- Vs:温度传感器测温时输出的电压(V)
- T1:在某一点实验测量的温度(°C)
- V1:经温度传感器测量后输出电压T1 (V)
- T2:第二点的实验测量温度(°C)
- V2:温度传感器测量T2时输出的电压(V)
- 斜率:温度传感器的温度梯度(V /°C),斜率= (V2 - V1) / (T2 - t1)
由于这里需要算一个 Slope 斜率,我们继续往下找可以找到一个 TSN 特征表:如下所示:
这里展示每 4mV为1度,至此我们的斜率就得到了,至此我们还有两个参数需要去获取,V1与T1,接着查看第1699页,如下图所示:
这里我们可以看到转换公式所需的参数,接下来我们该去配置软件(注:这里是在ADC的采集实验上进行修改的)。我们选择 g_adc0_ADC 模块,然后点击属性,然后放大,如下图所示:
设置完成后,按下 Ctrl + S 进行保存,然后点击 Generate Project Content 进行工程生成。
程序编写
这里我只展示修改的程序。
#include "bsp_adc.h"
#include "r_adc_api.h"
#include "hal_data.h"
#include <stdio.h>
volatile bool adc_flag = false;
void ADC_Init(void)
{
fsp_err_t err;
// 打开ADC设备
err = R_ADC_Open(&g_adc0_ctrl, &g_adc0_cfg);
if (FSP_SUCCESS != err) {
printf("ADC初始化失败! \n");
return;
}
// 配置ADC扫描
err = R_ADC_ScanCfg(&g_adc0_ctrl, &g_adc0_channel_cfg);
if (FSP_SUCCESS != err) {
printf("ADC扫描配置失败! \n");
R_ADC_Close(&g_adc0_ctrl); // 关闭已打开的ADC
return;
}
printf("ADC初始化成功!\n");
}
void adc_callback(adc_callback_args_t *p_args)
{
FSP_PARAMETER_NOT_USED(p_args);
adc_flag = true;
}
double ADC_read_Value(void)
{
uint16_t adc_data = 0;
int32_t CAL127 = 0;
adc_info_t Adc_Temp;
double slope = 0.004F;
double temperature = 0.0F;
// 启动ADC扫描
fsp_err_t err = R_ADC_ScanStart(&g_adc0_ctrl);
if (FSP_SUCCESS != err) {
printf("ADC扫描启动失败! \n");
return 9999; // 返回错误值
}
// 等待ADC转换完成
while (!adc_flag);
adc_flag = false; // 清除标志位
// 读取ADC数据
err = R_ADC_Read(&g_adc0_ctrl, ADC_CHANNEL_TEMPERATURE, &adc_data);
if (FSP_SUCCESS != err) {
printf("ADC数据读取失败!\n");
return 9999; // 返回错误值
}
// 获取校准数据
err = R_ADC_InfoGet(&g_adc0_ctrl, &Adc_Temp);
if (FSP_SUCCESS != err) {
printf("ADC校准数据获取失败!\n");
return 9999; // 返回错误值
}
//获取温度
CAL127 = (int32_t) Adc_Temp.calibration_data;
// 计算温度值
temperature = ((adc_data - CAL127) * 3.3 / 4096) / slope + 127;
return temperature;
}
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