IMX6ULL<ARM架构>——ADC 模数转换器
ADC 是嵌入式系统中连接模拟世界和数字世界的关键组件。在 ARM 架构的 IMX6ULL 处理器中,ADC 模块通过寄存器配置和驱动代码实现,可以方便地与各种传感器配合使用。掌握 ADC 的使用,对于开发基于 ARM 架构的嵌入式系统至关重要。在物联网、工业控制、智能家居等应用中,ADC 都能发挥重要作用,帮助我们获取和处理各种物理世界的信息。
一、ADC 基础概念
- ADC (Analog-to-Digital Converter):模拟到数字转换器。它是一种电子设备或模块,用于将连续变化的模拟信号转换为离散的数字信号,以便数字系统(如微处理器、微控制器等)能够对其进行处理和分析
- 模拟信号:物理世界中连续变化的物理量(如温度、光线、压力等)
- 数字信号:离散的、不连续的信号(计算机处理的0和1)
- 传感器:一种能够探测、感知外部环境信息(如温度、光线、压力、运动等),并将这些信息转换成电信号(通常是电压或电流)的装置或器件
ADC 工作流程:现实世界物理量 → 传感器 → 模拟电压 → ADC → 数字信号 → 数字系统
二、逐次逼近型 ADC
ADC有多种实现方式,其中逐次逼近型ADC在速度和精度间取得了良好平衡:
- 速度:比双积分型 ADC 快得多
- 精度:比 Flash 型 ADC 高得多
逐次逼近型 ADC 的核心是一个比较器(Comparator)和一套 “逐次逼近寄存器(SAR)” 逻辑。它的转换过程像一个 “猜数字游戏”:通过不断折半参考电压,逐步确定输入电压对应的二进制每一位(MSB到LSB)
下图以8位ADC为例,展示了将3.8V模拟电压(参考电压5V)转换为数字信号的过程:

- 初始化:将 8 位数字量的最高位设为 1(其余为 0),对应电压为 5V/2 = 2.5V,与输入模拟电压(如 3.8V)比较;
- 逐步逼近:
- 2.5V < 3.8V → 保留最高位 1,次高位设为 1,对应电压2.5V + 1.25V = 3.75V;
- 3.75V < 3.8V → 保留次高位 1,第三位设为 1,对应电压3.75V + 0.625V = 4.375V;
- 4.375V > 3.8V → 第三位设为 0,第四位设为 1,对应电压3.75V + 0.3125V = 4.0625V;
- 重复此过程,直到 8 位全部判定完毕
3.输出结果:最终 8 位数字量为11000100(十六进制 0xC2,十进制 194),换算为实际电压:
- 实际电压 = (采样值 / 2^ADC位数) × 参考电压
- 代入得:194 / 256 × 5V = 3.7890625V,与输入 3.8V 高度逼近。
三、关键概念解析
2.1 量程
- 定义:ADC能够测量的输入电压范围
- 决定因素:参考电压
- 注意事项:输入电压超过量程会导致失真(削顶)或损坏器件
2.2 分辨率
- 定义:指ADC的位数
- 常见位数:8位ADC(256级)、10位ADC(1024级)、12位ADC(4096级)、16位ADC(65536级)
- 计算:12位ADC的分辨率 = 3.3V / 4096 ≈ 0.8mV
2.3 精度
- 定义:与测量结果的绝对准确性相关
- 关键指标:偏移误差、增益误差、INL(积分非线性)、DNL(微分非线性)
- 重要提示:高精度ADC价格更高,需根据实际需求选择
2.4 实际应用选择指南
- 量程选择:确保信号电压范围在ADC量程内
- 信号太小 → 需先用运放放大
- 信号太大 → 需用电阻分压
- 分辨率选择:根据需要的测量精度选择位数
- 例:锂电池电压(3.0V-4.2V),12位ADC(4096级)足以满足需求
3.精度考量:对绝对准确性要求高的场合,需研究数据手册中的精度指标
四、IMX6ULL 中的 ADC 模块
4.1 硬件配置
| 特性 | 参数 |
|---|---|
| 分辨率 | 12位(0~4095) |
| 通道数 | 10路(ADC1_IN0 ~ ADC1_IN9) |
| 参考电压 | 外部引脚ADC_VREFH(通常接3.3V) |
| 时钟源 | 支持IPG时钟、异步时钟(ADACK) |
| 附加功能 | 硬件平均、自动校准、比较器、DMA支持 |
4.2 关键寄存器配置
| 寄存器名称 | 核心功能 | 实战配置(12 位、通道 1) |
|---|---|---|
| 控制寄存器(ADCx_HC0) | 中断使能、采样通道选择 | AIEN=0(禁用中断)、ADCH=1(选择 ADC1_IN1) |
| 状态寄存器(ADCx_HS) | 转换完成标志位(COCO0) | 等待 COCO0=1(表示转换完成) |
| 数据寄存器(ADCx_R0) | 存储采样结果 | 读取低 12 位(CDATA [0~11]),屏蔽高位无效数据 |
| 配置寄存器(ADCx_CFG) | 分辨率、时钟源、采样时间配置 | MODE=10(12 位)、ADICLK=11(异步时钟) |
| 通用控制寄存器(ADCx_GC) | 校准启动、时钟使能 | CAL=1(启动校准)、ASYNC_CLK_EN=1(时钟使能) |
| 通用状态寄存器(ADCx_GS) | 校准失败标志(CALF) | CALF=0(校准成功),失败需写1清零 |
五、驱动代码实现
5.1 自动校准函数
校准是消除器件固有误差、提升采样精度的关键步骤,必须在初始化时执行:
int adc1_calibration(void)
{
ADC1->GS |= (1 << 1); // 清零CALF校准失败标志(写1清零)
ADC1->GC |= (1 << 7); // 置位CAL位,启动校准
while ((ADC1->GC & (1 << 7)) != 0); // 等待校准完成(CAL位自动清零)
if ((ADC1->GS & (1 << 1)) != 0)
{
return -1; // 校准失败
} else {
return 0; // 校准成功
}
}
关键逻辑:校准过程中需等待 CAL 位清零,校准失败后需重新初始化,避免带误差采样。
5.2 初始化函数
初始化流程包含引脚配置→寄存器配置→校准三大核心步骤:
void adc1_init(void)
{
// 1. 引脚配置:GPIO1_IO01复用为ADC1_IN1,禁用上拉
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO01_GPIO1_IO01, 1); // 复用为ADC模式
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01,
IOMUXC_SW_PAD_CTL_PAD_PKE(1) | // 使能引脚保持器
IOMUXC_SW_PAD_CTL_PAD_PUE(0)); // 禁用上拉(ADC推荐配置)
// 2. 寄存器配置:12位分辨率、异步时钟
ADC1->HC[0] &= ~(1 << 7); // 禁用转换完成中断
ADC1->CFG = 0; // 清空配置寄存器
ADC1->CFG |= (2 << 2); // MODE=10 → 12位分辨率
ADC1->CFG |= (3 << 0); // ADICLK=11 → 异步时钟(ADACK)
ADC1->GC = 0; // 清空通用控制寄存器
ADC1->GC |= (1 << 0); // 使能异步时钟输出
// 3. 启动校准并打印结果
printf(adc1_calibration() == 0 ? "Calibration OK\n" : "Calibration Failed\n");
}
5.3 采样与电压转换函数
- get_adc_value:触发采样并读取 12 位数字量;
- get_adc_volt:根据核心公式将数字量转换为实际电压。
// 读取ADC采样值(12位,0~4095)
unsigned short get_adc_value(void)
{
ADC1->HC[0] = 0; // 清空通道选择
ADC1->HC[0] |= (1 << 0); // 选择通道1,触发单次采样
while ((ADC1->HS & (1 << 0)) == 0); // 等待转换完成(COCO0=1)
return ADC1->R[0] & 0xFFF; // 读取低12位有效数据
}
// 转换为实际电压(参考电压3.3V)
float get_adc_volt(void)
{
return get_adc_value() * 3.3 / 4096; // 核心公式
}
5.4 主函数集成测试
main.c 中初始化系统资源后,循环采样并打印电压值,直观验证 ADC 功能:
int main(void)
{
system_interrupt_init(); // 系统中断初始化
clock_init(); // 时钟初始化
beep_init(); led_init(); // 其他外设初始化
adc1_init(); // ADC初始化
float adc_volt = 0;
while (1)
{
adc_volt = get_adc_volt(); // 读取电压
// 格式化打印(保留3位小数)
int m = (int)(adc_volt * 1000) / 1000;
int n = (int)(adc_volt * 1000) % 1000;
printf("ADC Voltage: %d.%03dV\n", m, n);
delay_ms(10); // 10ms采样间隔
}
return 0;
}
运行结果:串口将持续输出类似 ADC Voltage: 1.234V 的信息,反映传感器的实时电压变化。
六、滤波算法选型与实现
实际应用中,采样信号易受电源纹波、电磁干扰影响,导致数据波动。文档与图片中提到多种滤波算法,以下结合场景选型并实现优化。
6.1 滤波算法对比与选型
| 滤波算法 | 核心原理 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 均值滤波 | 多次采样取平均值 | 静态信号(如温度、光线) | 实现简单、抑制随机噪声 | 响应慢、不适合动态信号 |
| 卡尔曼滤波 | 基于概率模型动态修正误差 | 动态信号(如运动物体速度、电流) | 响应快、精度高 | 公式复杂、参数难调 |
| 低通滤波 | 过滤高频噪声、保留低频信号 | 电源纹波干扰严重的场景 | 平滑效果好 | 高频信号失真 |
| 高通滤波 | 过滤低频噪声、保留高频信号 | 缓慢漂移的信号(如压力传感器) | 抑制漂移 | 静态信号误差大 |
6.2 均值滤波优化实现
在 get_adc_value 基础上扩展,采样 10 次取平均值,抑制随机噪声:
// 均值滤波:10次采样取平均
unsigned short get_adc_value_avg(void)
{
unsigned int sum = 0;
for (int i = 0; i < 10; i++)
{
ADC1->HC[0] = 0;
ADC1->HC[0] |= (1 << 0);
while ((ADC1->HS & (1 << 0)) == 0);
sum += ADC1->R[0] & 0xFFF;
}
return sum / 10; // 返回平均值,降低波动
}
七、总结
ADC 是嵌入式系统中连接模拟世界和数字世界的关键组件。在 ARM 架构的 IMX6ULL 处理器中,ADC 模块通过寄存器配置和驱动代码实现,可以方便地与各种传感器配合使用。
掌握 ADC 的使用,对于开发基于 ARM 架构的嵌入式系统至关重要。在物联网、工业控制、智能家居等应用中,ADC 都能发挥重要作用,帮助我们获取和处理各种物理世界的信息。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐
所有评论(0)