第三讲:ADC单通道转换配置实战
好,咱们今天来点真格的。
前面两讲把ADC的原理和寄存器讲了个七七八八,有朋友私信我说:「老师,能不能直接上手干一个?」
没问题。这一讲,我就带你手把手配置一个单通道,实现最基本的电压采集。
说白了,就是让ADC动起来,采一个值回来。
3.1 硬件连接与引脚确认
动手之前,先看看硬件。
我习惯用TC375 Lite Kit做演示。这块板子上,P10.2引脚默认接到了电位器的中间抽头。你拧电位器,电压就会变。
嗯,这里要注意:不是所有引脚都能当ADC输入。你得查数据手册,确认引脚有没有AN0、AN1这样的标识。
确认好引脚后,记住它的Group和Channel号。比如P10.2对应的是Group 0,Channel 2。
3.2 初始化ADC模块
初始化,说白了就是告诉硬件:「我要用你了,你准备一下。」
我个人习惯分三步走:
- 使能时钟——不给时钟,ADC不干活。
- 配置全局控制寄存器——设定分辨率、转换模式这些大方向。
- 配置具体通道——告诉它采哪个引脚,用什么参考电压。
咱们直接看代码。我用的是iLLD库,这样更直观。
// 第一步:使能ADC时钟
IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
// 第二步:初始化ADC模块
IfxEvadc_Adc adc;
IfxEvadc_Adc_Config adcConfig;
IfxEvadc_Adc_initModuleConfig(&adcConfig, &MODULE_EVADC);
IfxEvadc_Adc_initModule(&adc, &adcConfig);
// 第三步:配置队列和通道
IfxEvadc_Adc_Group group;
IfxEvadc_Adc_GroupConfig groupConfig;
IfxEvadc_Adc_initGroupConfig(&groupConfig, &adc);
// 指定Group 0
groupConfig.groupId = IfxEvadc_GroupId_0;
// 设置参考电压为内部5V基准
groupConfig.reference = IfxEvadc_Reference_Internal;
// 12位分辨率
groupConfig.resolution = IfxEvadc_Resolution_12bit;
IfxEvadc_Adc_initGroup(&group, &groupConfig);
你可能会问:「为什么先关看门狗?」
因为初始化过程中,如果时间太长,看门狗会复位。我刚开始学的时候没关,程序跑着跑着就重启了,查了半天才发现是看门狗在捣乱。
3.3 配置单通道
模块初始化好了,接下来就是配置具体通道。
这里有个关键点:TC3xx的ADC不是你想采就采的,你得把通道加到「请求队列」里。
// 配置通道
IfxEvadc_Adc_Channel channel;
IfxEvadc_Adc_ChannelConfig channelConfig;
IfxEvadc_Adc_initChannelConfig(&channelConfig, &group);
// 指定通道2(对应P10.2)
channelConfig.channelId = IfxEvadc_ChannelId_2;
// 使能该通道
channelConfig.enabled = TRUE;
// 设置结果寄存器编号
channelConfig.resultRegister = IfxEvadc_ResultRegister_0;
IfxEvadc_Adc_initChannel(&channel, &channelConfig);
你看,就这么几行。但有个细节:resultRegister 决定了转换结果存到哪个寄存器里。如果你同时采多个通道,每个通道最好用不同的结果寄存器,免得数据被覆盖。
3.4 启动转换与读取结果
配置完了,该干活了。
启动转换有两种方式:
- 手动触发——你调用一次函数,它转换一次。
- 自动连续转换——配置好后,它自己不停采。
咱们先用手动触发,这样好理解。
// 启动一次转换
IfxEvadc_Adc_startSingleConversion(&group, IfxEvadc_ChannelId_2);
// 等待转换完成
while (!IfxEvadc_Adc_getResult(&channel, &result))
{
// 空等待,或者你可以加个超时保护
}
// 读取结果
uint16 adcValue = result;
这里有个坑:while 循环等待的时候,如果ADC出问题了,程序就卡死了。我建议加个超时机制。
// 带超时的等待
uint32 timeout = 10000;
while (!IfxEvadc_Adc_getResult(&channel, &result) && timeout--)
{
// 等待
}
if (timeout == 0)
{
// 处理超时错误
printf("ADC转换超时!\n");
}
嗯,这样安全多了。
3.5 将原始值转换为电压
ADC采回来的值是个数字,比如12位分辨率下,范围是0~4095。
要换算成实际电压,公式很简单:
电压 = (adcValue / 4095.0) * 参考电压
假设参考电压是5V,adcValue是2048,那电压就是2.5V。
float voltage = (adcValue / 4095.0f) * 5.0f;
printf("ADC原始值: %d, 电压: %.3f V\n", adcValue, voltage);
你想想看,是不是很简单?
3.6 完整代码示例
我把上面的代码拼到一起,就是一个完整的单通道采集程序。
#include "IfxEvadc_Adc.h"
#include "IfxScuWdt.h"
void adc_single_channel_demo(void)
{
// 关看门狗
IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
// 初始化ADC模块
IfxEvadc_Adc adc;
IfxEvadc_Adc_Config adcConfig;
IfxEvadc_Adc_initModuleConfig(&adcConfig, &MODULE_EVADC);
IfxEvadc_Adc_initModule(&adc, &adcConfig);
// 初始化Group 0
IfxEvadc_Adc_Group group;
IfxEvadc_Adc_GroupConfig groupConfig;
IfxEvadc_Adc_initGroupConfig(&groupConfig, &adc);
groupConfig.groupId = IfxEvadc_GroupId_0;
groupConfig.reference = IfxEvadc_Reference_Internal;
groupConfig.resolution = IfxEvadc_Resolution_12bit;
IfxEvadc_Adc_initGroup(&group, &groupConfig);
// 初始化通道2
IfxEvadc_Adc_Channel channel;
IfxEvadc_Adc_ChannelConfig channelConfig;
IfxEvadc_Adc_initChannelConfig(&channelConfig, &group);
channelConfig.channelId = IfxEvadc_ChannelId_2;
channelConfig.enabled = TRUE;
channelConfig.resultRegister = IfxEvadc_ResultRegister_0;
IfxEvadc_Adc_initChannel(&channel, &channelConfig);
// 主循环:不断采集
while (1)
{
uint16 adcValue;
float voltage;
// 启动转换
IfxEvadc_Adc_startSingleConversion(&group, IfxEvadc_ChannelId_2);
// 等待结果(带超时)
uint32 timeout = 10000;
while (!IfxEvadc_Adc_getResult(&channel, &adcValue) && timeout--);
if (timeout)
{
voltage = (adcValue / 4095.0f) * 5.0f;
printf("电压: %.3f V\n", voltage);
}
else
{
printf("转换超时!\n");
}
// 延时一会儿,别采太快
for (uint32 i = 0; i < 1000000; i++);
}
}
这段代码,你直接复制到工程里,改一下引脚配置,就能跑起来。
3.7 常见问题与避坑
最后,我总结几个我踩过的坑:
| 问题 | 原因 | 解决办法 |
|---|---|---|
| 一直读到0 | 引脚没使能ADC功能 | 检查引脚复用表,配置为AN模式 |
| 一直读到4095 | 输入电压超过参考电压 | 用万用表量一下实际电压 |
| 转换结果跳动很大 | 没有加硬件滤波 | 在引脚上加个100nF电容到地 |
| 程序卡死在等待循环 | 看门狗复位或ADC配置错误 | 加超时保护,检查初始化顺序 |
我曾经在一个项目里,ADC读数一直跳,从1000跳到3000。查了两天,最后发现是电源纹波太大。加了个电容,立马稳了。
所以,硬件上的小细节,往往比软件更关键。
3.8 小结
这一讲,咱们从硬件确认开始,到初始化、配置通道、启动转换、读取结果,再到电压换算,完整走了一遍。
你跟着代码敲一遍,应该就能让ADC跑起来了。
下一讲,我会讲多通道同步采集。到时候你会看到,单通道只是开胃菜,多通道才是真正的硬仗。
好,今天就到这儿。有问题欢迎留言,咱们下期见。