一、ADC 基础概念

核心重点

  1. ADC 本质:连续的模拟信号→离散的数字信号的转换,是嵌入式系统采集外部传感器(温度、电压、电流)的核心接口。
  2. 两大核心特性
    • 量程:ADC 能采集的电压范围(如 IMX6ULL 是 0~3.3V)。
    • 分辨率:数字量的精度(如 12 位 = 2^12 = 4096 级,最小分辨电压 = 3.3V/4095 ≈ 0.8mV)。
  3. 常见类型:逐次逼近型(IMX6ULL 采用)、Σ-Δ 型(高精度测量)、流水线型(高速采集)。

二、逐次逼近型 ADC

核心原理

逐次逼近型 ADC 核心是 “二分查找”:

  1. ADC 内部有 DAC(数模转换器)和比较器。
  2. 从最高位(MSB)到最低位(LSB)依次试探:
    • 若 DAC 输出 < 输入电压 → 保留该位。
    • 若 DAC 输出 > 输入电压 → 清零该位。
  3. 经过 n 次试探(n 为分辨率),得到最终数字量。

关键流程

初始化 → 校准 → 启动转换 → 等待完成 → 读取结果。

关键概念解析

2.1 量程

  • 定义:ADC 输入电压的有效范围,IMX6ULL ADC 单极性输入为 0~3.3V
  • 注意:输入电压不能超过量程,否则会导致数据饱和(全为 0xFFF)或损坏芯片。

2.2 分辨率

  • 定义:ADC 能分辨的最小电压变化,公式:最小分辨电压=满量程数字值量程​=212−13.3V​≈0.806mV
  • IMX6ULL 支持:8/10/12 位分辨率,12 位为最高精度,博客重点讲解 12 位配置。

2.3 精度

  • 区别:精度 ≠ 分辨率!精度包含误差(偏移误差、增益误差、噪声误差)。
  • 校准作用:IMX6ULL 的 ADC 内置自校准功能,能消除偏移 / 增益误差,提升精度(博客代码核心实现校准)。

2.4 实际应用选择指南

应用场景 分辨率选择 关键要求
普通电压采集(电池电压) 12 位 满足精度,配置简单
高精度传感器(温湿度) 12 位 + 校准 需多次采样取平均
高速数据采集 10/8 位 降低转换时间,提升采样率

三、IMX6ULL 中的 ADC 模块

3.1 硬件配置

(1)引脚复用

IMX6ULL 的 ADC 引脚需配置为ADC 功能(而非 GPIO),以 ADC1_IN1 为例:

  • 引脚:GPIO1_IO01 → 复用为 ADC1_IN1(ALT1 功能)。
  • 配置函数:IOMUXC_SetPinMux/IOMUXC_SetPinConfig(与你现有 I2C 代码风格一致)。
(2)时钟配置
  • IMX6ULL ADC 时钟源来自 peripheral clock(默认 66MHz)。
  • 时钟分频:通过 CFG 寄存器配置,推荐分频系数 8(时钟 8.25MHz,符合 ADC 时钟要求)。

3.2 关键寄存器配置(核心)

寄存器名称 位定义 功能 实战配置
控制寄存器(ADCx_HC0) AIEN(bit7) 转换完成中断使能 0 = 禁用中断(查询模式)
ADCH(bit[4:0]) 采样通道选择 0b00001 = 选择 ADC1_IN1 通道
状态寄存器(ADCx_HS) COCO0(bit0) 通道 0 转换完成标志 等待该位为 1,确认转换结束
数据寄存器(ADCx_R0) CDATA(bit[11:0]) 12 位 ADC 转换结果存储 仅读取低 12 位,屏蔽高位无效数据
配置寄存器(ADCx_CFG) MODE(bit[4:2]) ADC 分辨率选择 0b010 = 12 位分辨率
ADICLK(bit[6:5]) ADC 时钟源选择 0b11 = 异步时钟源
CLK_DIV(bit[1:0]) 输入时钟分频系数 0b11 = 8 分频(适配 ADC 时钟要求)
通用控制寄存器(ADCx_GC) CAL(bit7) ADC 自校准启动 1 = 启动内部自校准流程
ASYNC_CLK_EN(bit6) 异步时钟使能 1 = 使能 ADC 异步时钟
SW_START(bit0) 软件触发转换 1 = 软件启动 ADC 转换
通用状态寄存器(ADCx_GS) CALF(bit1) 自校准失败标志 0 = 校准成功;失败时写 1 清零标志

四、ADC驱动代码实现

4.1 ADC 初始化函数 adc_init

功能:引脚配置 → 寄存器配置 → ADC 自校准

int adc_init(ADC_Type * base)
{
    // 1. 配置引脚为 ADC 功能
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO01_GPIO1_IO01, 1);
    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01, 0x10b0);

    // 2. 关闭通道0,防止配置时误触发转换
    base->HC[0] &= ~(1 << 0);

    // 3. 配置分辨率+时钟分频:12位 + 8分频
    base->CFG = (0x2 << 2) | (0x3 << 0);

    // 4. 选择软件触发转换
    base->GC = (0x01 << 0);

    // 5. 清除校准失败标志
    base->GS |= (0x01 << 1);

    // 6. 启动自校准
    base->GC |= (0x01 << 7);

    // 7. 等待校准完成
    while(base->GC & (1 << 7));

    // 8. 判断校准是否成功
    if(base->GS & (1 << 1))
        return -1;

    return 0;
}
  • 引脚配置

    • GPIO1_IO01 配置为 ALT1 → 复用为 ADC1_IN1 通道
    • 0x10b0 为 ADC 输入推荐电气配置
  • HC [0] 关闭通道先清空通道使能位,保证配置安全。

  • CFG 寄存器配置

    • bit4~bit2 = 0x212 位分辨率
    • bit1~bit0 = 0x38 分频时钟
  • GC 寄存器配置

    • bit0 = 1软件触发转换
    • bit7 = 1启动 ADC 自校准
  • GS 状态寄存器

    • bit1 为校准失败标志
    • 校准后若为 1 → 初始化失败

4.2 ADC 读取函数 adc_read

功能:启动转换 → 等待完成 → 读取 12 位结果

unsigned short adc_read(ADC_Type * base)
{
    unsigned short adc_value = 0;

    // 1. 选择ADC通道(0x1f为内部通道,可改为外部通道)
    base->HC[0] |= (0x1f << 0);

    // 2. 启动转换
    base->HC[0] = (0x1 << 0);

    // 3. 等待转换完成标志
    while(!(base->HS & (1 << 0)));

    // 4. 读取低12位有效数据
    adc_value = base->R[0] & 0xfff;

    return adc_value;
}
  • HC [0] 通道选择0x1f:内部温度传感器通道若使用外部 ADC1_IN1,改为 0x01

  • 启动转换HC[0] 的 bit0 写 1 → 软件启动转换

  • HS 状态寄存器bit0 = 1 表示 转换完成循环等待直到该位置 1

  • R [0] 结果寄存器只取低 12 位有效数据 → 0xfff 屏蔽高位结果范围:0 ~ 4095

4.3 main.c函数

int main(void)
{
    // 定义变量存储ADC原始数据
    unsigned short data = 0;

    // ==================== 1. 系统基础资源初始化 ====================
    system_irq_init();   // 初始化中断控制器
    clk_init();          // 初始化系统时钟
    led_init();          // LED初始化(可选,用于状态指示)
    beep_init();         // 蜂鸣器初始化(可选)
    key_irq_init(gpio1_io18_handler); // 按键中断初始化
    gpt1_init();         // 定时器初始化
    uart_init(UART1);    // 串口初始化(用于打印结果)
    i2c_init(I2C1);      // I2C初始化(根据需求可选)

    // ==================== 2. ADC初始化 ====================
    int flag = adc_init(ADC1);

    // ==================== 3. 循环采样、打印电压 ====================
    while(1)
    {
        // 若ADC初始化成功,才执行采样
        if(!flag)
        {
            // 读取ADC原始数据(0~4095)
            data = adc_read(ADC1);

            // 计算公式:实际电压 = 原始数据 * 330 / 4095
            // 330 = 3.3V * 100,用于保留两位小数
            unsigned int adc_value = data * 330 / (4096 - 1);

            // 串口打印:原始数据 + 实际电压(保留两位小数)
            printf("data = 0x%x  value = %d.%02d\r\n", data, adc_value / 100, adc_value % 100);

            // 每隔3秒采样一次
            delay_ms(3000);
        }
    }

    return 0;
}

运行结果:程序下载到开发板后,串口助手会输出传感器的实时电压约为 3.3V

五、学习总结

ADC 配置完整思路流程

  1. 引脚配置:将 GPIO1_IO01 复用为 ADC 功能,并配置电气属性。
  2. 关闭通道:清空 HC [0] 启动位,防止配置时误触发转换。
  3. 核心参数配置:通过 CFG 寄存器设置 12 位分辨率 + 8 分频时钟。
  4. 触发模式配置:通过 GC 寄存器配置为软件触发转换。
  5. 校准准备:清空 GS 寄存器的校准失败标志位。
  6. 启动自校准:置位 GC 寄存器校准位,启动 ADC 内部校准。
  7. 等待校准完成:循环等待校准位硬件自动清零。
  8. 校准结果判断:检查校准失败标志,判断初始化是否成功。
  9. 通道选择:读取时配置 HC [0] 选择 ADC 采集通道。
  10. 启动转换:软件置位启动位,开始 AD 转换。
  11. 等待完成:查询 HS 寄存器状态,等待转换结束。
  12. 读取结果:从 R [0] 寄存器读取低 12 位有效采样数据。

ADC 配置先引脚、再时钟分辨率、必须校准,采集时选通道、软启动、等完成、读数据,核心就是寄存器的正确配置与状态判断。

ADC 学习重点总结

  1. 核心原理:ADC 完成模拟电压到数字量的转换,i.MX6ULL 采用 12 位逐次逼近型结构。
  2. 关键步骤引脚复用 → 寄存器配置 → 自动校准 → 软件触发 → 结果读取
  3. 寄存器核心:CFG(分辨率 / 时钟)、GC(触发 / 校准)、HC [0](通道 / 启动)、HS(完成标志)、R [0](数据)。
  4. 必备操作:ADC 必须执行自校准才能保证采集精度。
  5. 采集流程:选择通道 → 启动转换 → 等待完成 → 读取有效数据。
  6. 实战要点:12 位 ADC 结果范围 0~4095,对应 0~3.3V 电压,可通过公式换算实际电压。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐