ADC采集
本节介绍
📝本节您将学习ADC(模数转换器)的基本原理与应用,掌握TMS320F28P550的ADC模块功能特性,并通过图形化配置工具(SysConfig)实现电压采集与数据输出。
🏆本章目标
1️⃣理解ADC的工作原理及关键参数(分辨率、电压基准、采样范围等)。
2️⃣熟悉TMS320F28P550的ADC模块架构与功能(如12位分辨率、通道输入、触发模式等)。
3️⃣掌握使用SysConfig工具配置ADC外设(通道选择、参考电压、采样时间等)。
4️⃣实践应用:通过ADC引脚采集外部电压,在CCS的CIO窗口实时输出ADC数值及换算的实际电压值。
什么是ADC
ADC 全称为模拟-数字转换器,是一种用于将模拟信号转换为数字信号的模拟数字转换器。我们知道,模拟信号是连续的,其取值可以在一定范围内任意变化,如声音、光信号等。而数字信号则是离散的二进制信号,如计算机中的数据0和1,仅能取有限的值。
ADC的工作原理是将模拟信号通过采样转换为离散的数字信号,然后再通过量化、编码等处理,最终得到对应的数字表示。ADC采样的频率越高,得到的数字信号就越接近原来的模拟信号,也就是保真度越高,但是需要更多的资源和计算功耗。
ADC 通常用于从外部模拟传感器中读取模拟信号,并将其转换为数字信号供嵌入式系统或计算机进行处理,例如测量温度、湿度、压力等物理量。
基本原理
TMS320F28P550 采用的是逐次逼近型的ADC,逐次逼近型ADC 是一种常见的 ADC 工作原理,它的思想是通过比较模拟信号与参考电压之间的大小关系来逐步逼近输入信号的数字表示。
在 逐次逼近型ADC 中,输入信号和参考电压被加入一个差分放大器中,产生一个差分电压。然后,这个差分电压被输入到一个逐步逼近的数字量化器中,该量化器以逐步递减的方式将其与一系列参考电压进行比较。具体来说,在每个逼近阶段,量化器将输入信号与一个中间电压点进行比较,将该电压点上方或下方的参考电压作为下一个逼近阶段的参考电压。这个过程一直持续到量化器逼近到最终的数字输出值为止。
![]() |
---|
F28P550的ADC
5组ADC,分别是ADC-A\B\C\D\E。具有多达32个通道的输入,虽然有32个通道输入,但是只有 16 个转换通道(SOC) 和 16 个可单独寻址的结果寄存器。
ADC外设均使用的是单端信号模式,即输入信号与GND之间的电压。
以下引脚示意图为开发板引出的引脚所支持的外设功能,其中黄绿色为ADC外设。
![]() |
---|
![]() |
![]() |
采样触发源
- 软件立即启动
- 所有 ePWM :ADCSOC A 或 B
- GPIO XINT2
- CPU 计时器 0/1/2
- ADCINT1/2
- 捕获模式(CEVT1、CEVT2、CEVT3 和 CEVT4)和 APWM 模式(周期匹配、比较匹配或两者)下的 ECAP 事件。
- 对多个 ADC 采用全局软件触发器
ADC基本参数
分辨率: 表示ADC转换器的输出精度,通常以位数(bit)表示,比如8位、10位、12位等,位数越高,精度越高。TMS320F28P550 支持12位的分辨率。
采样率: 表示ADC对模拟输入信号进行采样的速率,通常以每秒采样次数(samples per second,SPS)表示,也称为转换速率,表示ADC能够进行多少次模拟到数字的转换。TMS320F28P550 为 3.9M SPS。
电压基准: ADC的电压基准是用于与模拟输入信号进行比较,从而实现模拟信号到数字信号的转换的一个参考电压。这个基准电压的准确性和稳定性对ADC的转换精度有着决定性的影响。而 TMS320F28P550 可以支持软件选择三种基准:(1)1.65V 和 2.5V 的可配置内部专用 ADC 基准电压 (VREF)(2)MCU 模拟电源电压 (VDDA) (3)通过 VREF+和 VREF- 引脚为 ADC 提供外部基准(External)。如未配置电压基准则默认使用MCU电源电压作为ADC电压基准。
采样范围: 指ADC可以采集到的模拟输入信号的电压范围,范围见下:
VREF- ≤ ADC ≤ VREF+
其中VREF- 为设置的电压基准负,通常为0V。VREF+ 为电压基准正,根据软件的配置确定范围。
案例实验介绍
通过ADC引脚采集外部电压,在CCS(Code Composer Studio)的CIO窗口实时输出ADC数值及换算的实际电压值。
工程创建
打开CCS,创建一个新的基于 F28P55X 的工程。
![]() | ![]() |
---|---|
工程配置
配置工程选项,将我们后面写好的代码烧录到 FLASH
中,并且使用的烧录模式是 cJTAG(1149.7)2-pin
模式。
![]() | ![]() |
---|---|
ADC的配置
ADC时钟最高75MHz,因此需要对输入时钟(SYSCLK)进行2分频得到ADC时钟;
使用 SOC-0 作为转换通道,转换通道中使用 A6采集引脚 作为模拟输入;
触发源使用软件触发;
配置引脚使用 A6引脚;
![]() | ![]() |
---|---|
ePWM常用选项说明(点击展开)
- ADC Instance : 选择使用哪一个 ADCx 外设。
- ADC Clock Prescaler : ADC 时钟源分频,ADC最大输入 75MHz,而时钟的输入是SYSCLK,默认的是150MHz,对其2分频即可得到75MHz。
SOC Configurations
- Enable SOCs : 选择要使能的转换通道,一共16个通道。
- SOC0 Channel : SOC0 的输入信号配置,例如用的是A6引脚,则开启 ADCIN6 信号输入。
- SOC0 Trigger : ADC转换触发方式,默认使用软件触发。
- SOC0 Sample Window [SYSCLK counts] : 设置采样窗口值,根据值转换采样时间。
开启 CIO 功能
CIO 是 CCS 中自带的一个 C语言输入输出操作。想必大家应该知道 C语言 的经典 printf()
,在以前我们想要使用printf输入调试日志,还需要调配串口通信外设功能。而在 CCS 中,只要你开启了堆栈,你就可以使用 CCS 内部的 printf 输出,输出在 CCS 的 CIO 显示框中。关于 CIO 的使用参考 TI 原文:Tips for Using Printf
使用 CIO 的步骤:
导入 stdio.h 文件
include "stdio.h"
每个使用C I/O函数的模块都应该包含
stdio.h
。如果不这样做,可能会使C I/O函数,特别是 printf 失败,而不会由编译器、链接器或调试器产生警告。printf 函数是一个可变函数,在没有有效原型的情况下调用这样的函数会导致未定义的行为。设置堆栈为
0x400
大多数标准的C I/O函数都操作于流。流是指stdout、stdin、stderr或任何用fopen打开的文件。每个流都需要自己的I/O缓冲区,如果用户没有提供,系统会在用户第一次读或写时自动动态地为用户分配一个。该缓冲区的大小为BUFSIZ(在stdio.h中定义),对于所有目标默认为256字节。
对于不使用TI-RTOS的项目:在 项目右键 -> Properties... -> Build -> C2000 Linker -> Basic Options->堆大小(- Heap)输入堆大小,建议为0x400
打开 CIO 显示栏
在 查看 -> Console -> CIO 中打开CIO显示栏。
显示栏在CCS的下方:使用 printf
在项目中直接 printf
使用即可。
相关函数介绍
//*****************************************************************************
//
//! 通过软件强制设置模数转换器中多个SOC(Start-Of-Conversion)标志位为1。
//!
//! \param base 是ADC模块的基地址。
//! \param socMask 指定要通过软件强制的SOC掩码
//!
//! 本函数会强制设置与\e socMask参数指定的SOC相关联的SOCFRC1标志位。
//! 当指定SOC获得优先级后,这将启动一次转换。无论该SOC是否已配置为接受其他特定触发,
//! 都可以使用此软件触发方式。
//! socMask参数的有效值可以是单个ADC_FORCE_SOCx值,也可以是它们的或(OR)组合,
//! 以触发多个SOC。
//!
//! \note 若要触发SOC0、SOC1和SOC2,应传入(ADC_FORCE_SOC0 | ADC_FORCE_SOC1 | ADC_FORCE_SOC2)
//! 作为socMask参数值。
//!
//! \return 无返回值。
//
//*****************************************************************************
static inline void ADC_forceMultipleSOC(uint32_t base, uint16_t socMask);
//*****************************************************************************
//
//! 检测ADC是否处于忙碌状态。
//!
//! \param base 是ADC模块的基地址。
//!
//! 本函数允许调用者判断ADC是否正在忙碌,以及是否可以采样另一个通道。
//!
//! \return 如果ADC正在采样则返回\b true,如果所有采样都已完成则返回\b false。
//
//*****************************************************************************
static inline bool ADC_isBusy(uint32_t base);
//*****************************************************************************
//
//! 读取转换结果。
//!
//! \param resultBase 是ADC结果寄存器的基地址。
//! \param socNumber 是启动转换(SOC)的编号。
//!
//! 本函数返回与传入的\e resultBase基地址和\e socNumber参数对应的SOC转换结果。
//!
//! socNumber参数取值应为\b ADC_SOC_NUMBERX,其中X是0到15的数字,
//! 用于指定要读取哪个SOC的结果。
//!
//! \note 注意确保使用的是结果寄存器的基地址(ADCxRESULT_BASE),
//! 而不是控制寄存器的基地址。
//!
//! \return 返回转换结果值。
//
//*****************************************************************************
static inline uint16_t ADC_readResult(uint32_t resultBase, ADC_SOCNumber socNumber);
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
案例验证
更新主函数代码
更新工程的 empty_driverlib_main.c 文件为以下代码:
#include "driverlib.h"
#include "device.h"
#include "board.h"
#include "c2000ware_libraries.h"
#include "stdio.h"
void main(void)
{
uint16_t myADC0Result0 = 0;
float val = 0.0f;
Device_init();
Device_initGPIO();
Interrupt_initModule();
Interrupt_initVectorTable();
Board_init();
C2000Ware_libraries_init();
EINT;
ERTM;
while(1)
{
// 通过软件触发 SOC0 转换
ADC_forceMultipleSOC(myADC0_BASE, ADC_FORCE_SOC0);
// 等待ADC总线处理完成
while(ADC_isBusy(myADC0_BASE) == true)
{
DEVICE_DELAY_US(1);
}
// 获取 SOC0 通道的转换结果
myADC0Result0 = ADC_readResult(ADCARESULT_BASE, ADC_SOC_NUMBER0);
// 将ADC值换算为实际电压
val = (myADC0Result0 / 4095.0) * 3.3;
// 数值放大100倍取出小数
val = val*100.0;
// CCS的CIO输出数据
printf("myADC0Result0=%d val=%d.%d%d\r\n",myADC0Result0, (int)val/100, (int)val/10%10, (int)val%10);
//延时 100 ms
DEVICE_DELAY_US(100000);
}
}
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
下载器连接
XDS110下载器 | 开发板 |
---|---|
SWD | SWD/TMS |
CLK | CLK/TCK |
GND | GND |
5V | 5V |
代码烧录
GIF 动图
案例现象
使用杜邦线分别测试短接A6和GND引脚,A6和3V3引脚,采集到的电压都正常。CIO输出数据如下:
![]() | ![]() |
---|---|