10. ADC采集
10.1 什么是ADC
ADC 全称为模拟-数字转换器,是一种用于将模拟信号转换为数字信号的模拟数字转换器。我们知道,模拟信号是连续的,其取值可以在一定范围内任意变化,如声音、光信号等。而数字信号则是离散的二进制信号,如计算机中的数据0和1,仅能取有限的值。 ADC的工作原理是将模拟信号通过采样转换为离散的数字信号,然后再通过量化、编码等处理,最终得到对应的数字表示。ADC采样的频率越高,得到的数字信号就越接近原来的模拟信号,也就是保真度越高,但是需要更多的资源和计算功耗。 ADC 通常用于从外部模拟传感器中读取模拟信号,并将其转换为数字信号供嵌入式系统或计算机进行处理,例如测量温度、湿度、压力等物理量。
10.2 MSPM0L系列的ADC介绍
MSPM0L1306采用的是逐次逼近型的12位ADC,它有 10 个多路复用通道可以转换。10个外部通道,都对应单片机的某个引脚,这个引脚不是固定的,详情请参考引脚图或者数据手册。
各种通道的 A/D 转换可以配置成 单次、序列转换 模式。
单次转换模式: 每次进行一次ADC转换后,ADC会自动停止,并将结果存储在ADC数据寄存器中。
重复单次转换模式: 当ADC完成一次转换后,它会自动启动另一次转换,持续的进行转换,直到外部触发或者软件触发的方式停止连续转换。
多通道顺序单次转换模式: 用于对多个输入通道进行依次转换。在该模式下,ADC会根据配置的通道采集顺序,对多个通道进行单次采样并转换。
多通道顺序重复转换模式: 用于对多个输入通道进行依次重复转换。在该模式下,ADC会根据配置的通道采集顺序,对多个通道进行重复采样并转换。
10.3 ADC基本参数
分辨率: 表示ADC转换器的输出精度,通常以位数(bit)表示,比如8位、10位、12位等,位数越高,精度越高。MSPM0L1306支持8、10、12位的分辨率。
采样率: 表示ADC对模拟输入信号进行采样的速率,通常以每秒采样次数(samples per second,SPS)表示,也称为转换速率,表示ADC能够进行多少次模拟到数字的转换。MSPM0G3507的SPS为4Msps。
电压基准: ADC的电压基准是用于与模拟输入信号进行比较,从而实现模拟信号到数字信号的转换的一个参考电压。这个基准电压的准确性和稳定性对ADC的转换精度有着决定性的影响。而MSPM0G3507可以支持软件选择三种基准:(1)1.4V 和 2.5V 的可配置内部专用 ADC 基准电压 (VREF)(2)MCU 电源电压 (VDD) (3)通过 VREF+和 VREF- 引脚为 ADC 提供外部基准。如未配置电压基准则默认使用MCU电源电压作为ADC电压基准。
采样范围: 指ADC可以采集到的模拟输入信号的电压范围,范围见下:
其中VREF- 为设置的电压基准负,通常为0V。VREF+ 为电压基准正,根据软件的配置确定范围。
10.4 基本原理
MSPM0L1306采用的是逐次逼近型的ADC,逐次逼近型ADC是一种常见的ADC工作原理,它的思想是通过比较模拟信号与参考电压之间的大小关系来逐步逼近输入信号的数字表示。在逐次逼近型ADC中,输入信号和参考电压被加入一个差分放大器中,产生一个差分电压。然后,这个差分电压被输入到一个逐步逼近的数字量化器中,该量化器以逐步递减的方式将其与一系列参考电压进行比较。具体来说,在每个逼近阶段,量化器将输入信号与一个中间电压点进行比较,将该电压点上方或下方的参考电压作为下一个逼近阶段的参考电压。这个过程一直持续到量化器逼近到最终的数字输出值为止。
10.5 ADC优点
- 数字信号具有良好的抗干扰性。数字信号是由一系列离散的数字表示,因此可以抵抗模拟信号受到的各种干扰,如噪声、漂移等。
- 方便数字信号的存储、处理和传输。由于数字信号是离散的,因此它们可以轻松存储在计算机内存或其他数字设备中,方便进行处理和传输。
- 具有可编程性。现代的ADC出现了很多可编程的功能,例如可编程增益、采样率和滤波器等,可以根据不同的应用场景进行优化。
- 适用性广泛。ADC被广泛应用于工业、通信、医疗、电子测量、音频、视频等领域,可转换各种不同类型的模拟信号,包括电压、电流、声音、光信号等。
10.6 ADC应用
ADC的应用非常广泛。例如,我们可以用ADC将传感器的模拟信号转换为数字信号,然后通过计算机进行处理和分析;ADC在音频处理中也起着重要的作用,将模拟声音信号转换为数字信号,并接下来进行数字信号处理;无线电通信中的信号调制也需要使用ADC等。总的来说,ADC在现代电子工程中非常重要,是数字信号处理和控制技术的关键部分。
10.7 ADC的配置
本案例将以采集3.3V和GND的电压作为实验案例。通过ADC采集其电压变化,实现判断电压情况。
10.7.1 开启SYSCONFIG配置工具
在Keil中打开SDK中的空白工程 empty。
在Keil的主界面打开empty.syscfg文件,在empty.syscfg文件打开的情况下,再打开SYSCONFIG的GUI界面。
在sysconfig中,左侧可以选择MCU的外设,我们找到并点击ADC12选项卡,在ADC12中点击ADD,就可以添加ADC外设。
10.7.2 ADC参数的配置
这里选择的配置是使用32Mhz的ADC频率,采样频率为4MHz,单次重复转换模式,通过软件触发ADC开始采样,数据格式为二进制的右对齐方式。
MSPM0G3507的ADC支持各种数据对齐方式以适应不同的应用场景。常见的数据对齐方式包括左对齐和右对齐。在右对齐模式下,ADC的数据在转换结束后被右对齐到最低位,不足的位数在高位填充0。右对齐模式允许实现在没有精度的损失下更好的动态范围。
在左对齐模式下,ADC的数据被左对齐到最高位,不足的位数在低位填充0。左对齐模式可以提高分辨率,但会导致动态范围降低。
我们配置为右对齐方式,这样我们可以直接将转换完成的数据进行运算。
10.7.3 配置ADC采集通道
配置ADC的分辨率
开启ADC采集完成的中断。当ADC采集完成时触发中断,我们就进行数据处理。
将以上配置保存,然后到Keil中编译更新。
10.8 ADC采集电压实验
在empty.c(要main函数的文件)中编写如下代码,然后将代码烧录到开发板中运行。
#include "ti_msp_dl_config.h"
#include "stdio.h"
volatile unsigned int delay_times = 0;
volatile bool gCheckADC; //ADC采集成功标志位
void delay_ms(unsigned int ms); //搭配滴答定时器的精准毫秒级延时
unsigned int adc_getValue(void);//读取ADC的数据
/******************************串口重定向***************************************/
#if !defined(__MICROLIB)
//不使用微库的话就需要添加下面的函数
#if (__ARMCLIB_VERSION <= 6000000)
//如果编译器是AC5 就定义下面这个结构体
struct __FILE
{
int handle;
};
#endif
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
#endif
//printf函数重定义
int fputc(int ch, FILE *stream)
{
//当串口0忙的时候等待,不忙的时候再发送传进来的字符
while( DL_UART_isBusy(UART_0_INST) == true );
DL_UART_Main_transmitData(UART_0_INST, ch);
return ch;
}
/*********************************************************************/
int main(void)
{
unsigned int adc_value = 0;
unsigned int voltage_value = 0;
SYSCFG_DL_init();
//清除串口中断标志
NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);
//开启串口中断
NVIC_EnableIRQ(UART_0_INST_INT_IRQN);
//开启ADC中断
NVIC_EnableIRQ(ADC_VOLTAGE_INST_INT_IRQN);
printf("adc Demo start\r\n");
while (1)
{
//获取ADC数据
adc_value = adc_getValue();
printf("adc value:%d\r\n", adc_value);
//将ADC采集的数据换算为电压
voltage_value = (int)((adc_value/4095.0*3.3)*100);
printf("voltage value:%d.%d\r\n",
voltage_value/100,
voltage_value/10%10 );
delay_ms(1000);
}
}
//延时函数
void delay_ms(unsigned int ms)
{
delay_times = ms;
while( delay_times != 0 );
}
//读取ADC的数据
unsigned int adc_getValue(void)
{
unsigned int gAdcResult = 0;
//软件触发ADC开始转换
DL_ADC12_startConversion(ADC_VOLTAGE_INST);
//如果当前状态为正在转换中则等待转换结束
while (false == gCheckADC) {
__WFE();
}
//获取数据
gAdcResult = DL_ADC12_getMemResult(ADC_VOLTAGE_INST, ADC_VOLTAGE_ADCMEM_ADC_CH0);
//清除标志位
gCheckADC = false;
return gAdcResult;
}
//滴答定时器的中断服务函数
void SysTick_Handler(void)
{
if( delay_times != 0 )
{
delay_times--;
}
}
//ADC中断服务函数
void ADC_VOLTAGE_INST_IRQHandler(void)
{
//查询并清除ADC中断
switch (DL_ADC12_getPendingInterrupt(ADC_VOLTAGE_INST))
{
//检查是否完成数据采集
case DL_ADC12_IIDX_MEM0_RESULT_LOADED:
gCheckADC = true;//将标志位置1
break;
default:
break;
}
}
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
实验结果:向PA27引脚输出2.6V的电压,ADC数据采集结果:
⚠警告
使用printf时不能使用%f,会无法下载甚至内部关键寄存器被篡改导致2级锁死无法修复;