先理清核心概念:ADC = 模拟数字转换器;涉及时钟、GPIO、ADC 内核、采样时序、触发方式、数据读取五大块。

前置前提:ADC 依赖时钟,必须先打开 ADC 外设时钟!

完整标准配置流程(推荐代码执行顺序)

1. 配置 ADC 输入 GPIO 引脚

  1. 打开 GPIO 端口时钟
  2. 将对应引脚设置为 模拟输入模式(Analog)
    • 禁止上拉 / 下拉电阻!上下拉会引入直流电流,干扰模拟电压
    • 关闭引脚数字输入缓冲(很多芯片模拟模式自动关闭)

重点:模拟信号线尽量远离高速数字信号线,减少干扰;必要时外部加 RC 滤波。

2. 使能 ADC 外设时钟 + 配置 ADC 内核时钟

  1. RCC 打开 ADC 时钟门控
  2. 设置 ADC 预分频,得到 ADC 工作时钟 ADCCLK ⚠️ 硬性约束:ADCCLK 不能超过芯片手册规定最大时钟! 例:很多 12bit ADC 最大 36MHz、部分 24MHz,超频会造成采样不准、噪声变大。

区分两个容易混淆时钟:

  • APB 总线时钟:寄存器读写用
  • ADCCLK:ADC 转换内核工作时钟

3. ADC 基础内核参数配置

  1. 选择 ADC 分辨率:6/8/10/12/16bit
  2. 数据对齐:左对齐 / 右对齐(决定读取数据时怎么处理)
  3. 开启 / 关闭 ADC 连续转换模式、扫描模式
    • 单次转换:触发一次,转换一次,停止
    • 连续转换:转换完成立刻自动开始下一轮
    • 扫描模式:多路通道依次轮流转换(多通道采集必备)
  4. 设置采样周期(采样保持时间 SMP)

    非常关键!外部信号源阻抗大,必须加长采样周期,否则采样值偏低、波动大。每个通道可以独立配置采样时间。

4. 通道配置

  1. 选择 ADC 通道编号(IN0/IN1…)绑定对应引脚
  2. 多通道扫描模式下:配置通道序列、转换顺序、序列长度

5. 选择转换触发方式(二选一)

方式 A:软件触发(最简单)

软件写寄存器直接启动转换。适合低速采集。

方式 B:硬件触发(定时器触发 / 外部引脚触发)

常用:TIM 定时触发 ADC,实现周期性等间隔采样(信号采集、示波器类场景首选)

  1. 选择触发源(TIMx_CH、外部 EXTI 等)
  2. 设置触发极性:上升沿 / 下降沿触发

6. 中断 / DMA 配置(按需选配)

方案 1:查询模式(最简单,不推荐高频采样)

转换完成后轮询 EOC 转换完成标志位读取数据。

方案 2:ADC 中断模式
  1. 使能转换完成中断 / EOS 序列结束中断
  2. 配置 NVIC 中断优先级
  3. 在中断服务函数读取 ADC 数据
方案 3:DMA 搬运(多通道、高速采集首选)
  1. 打开 DMA 时钟
  2. 配置 DMA 方向:外设→内存
  3. 设置 DMA 地址:ADC_DR 寄存器地址 + 内存缓存地址
  4. 循环模式 / 普通模式、数据宽度
  5. 开启 DMA 请求,ADC 转换完成自动把数据搬运到内存,无需 CPU 干预

7. 使能 ADC 模块

所有参数配置完成后,打开 ADC 电源 / 使能 ADC

注意:部分芯片 ADC 使能后需要等待稳定时间。

8. 启动转换,采集数据

  • 软件触发:置位 SWSTART 启动
  • 硬件触发:等待定时器触发信号自动转换

9. 读取转换结果 + 可选后处理

  1. 等待转换完成标志
  2. 读取 ADC 数据寄存器 DR
  3. 根据参考电压,将原始 AD 值换算成实际电压:

\(V_{in} = \frac{AD_{val}}{2^{res}} \times V_{ref}\)

10. 可选高级配置

  1. 内部参考电压、温度传感器、VBAT 通道开启(需要单独使能内部通道)
  2. ADC 看门狗(模拟阈值监测,高于 / 低于阈值产生中断)
  3. 过采样(Oversample):多次采样平均,提高有效分辨率、降低噪声
  4. 差分输入配置(部分支持差分 ADC,抑制共模干扰)
  5. ADC 校准!【重中之重】

STM32/GD32 等绝大多数 MCU:上电后必须执行 ADC 自校准,消除偏移误差,否则零点不准。


最简流程一句话总结

配置 GPIO 模拟输入 → 开启 ADC 时钟 + 设置 ADC 预分频时钟 → 配置分辨率、对齐、采样周期、单通道 / 扫描模式 → 选择触发方式 →(按需配置 DMA / 中断)→(执行 ADC 自校准)→ 使能 ADC → 启动转换 → 读取 AD 数值。

高频踩坑清单

  1. GPIO 没设成模拟输入、开启上下拉 → 数值漂移不准
  2. ADCCLK 超过规格上限 → 噪声巨大
  3. 采样周期太短,信号源阻抗高 → AD 读数偏低
  4. 忘记 ADC 自校准 → 零点偏移
  5. 多通道没开启扫描模式,只会采集第一个通道
  6. DMA 没开启、地址配置错误,读不到数据
  7. 使用内部通道(温度传感器、VREFINT)忘记使能内部开关
  8. 频繁开关 ADC,每次开启建议重新校准
  9. 参考电压不稳定,没有滤波电容,采样波动大

对照实例(STM32F1/F4 典型流程)

\(V_{in} = \frac{AD\_val}{4096} \times V_{ref}\) ❌ 缺点:几乎没有缺点,常规项目默认选右对齐。

2. 左对齐(Left-aligned)

有效数据靠最高位摆放,低位补 0 12bit 数据向左挪 4 位,末尾填充 4 个 0

plaintext

bit15 ~ bit4        | bit3 ~ bit0
1010 1011 1100      | 0    0    0    0

寄存器读到的值:0xABC0

左对齐怎么还原真实 AD 值?

需要右移 4 位

c

运行

uint16_t raw = ADC->DR;
uint16_t adc_val = raw >> 4;

3. 左对齐存在的意义(什么时候用?)

核心场景:想直接把 ADC 高有效位当做 16bit DAC/PWM 输入,或者方便有符号差分 ADC 运算

场景 1:差分 ADC(有符号采样)

很多差分 ADC 输出带符号(最高位是符号位) 左对齐时,符号位正好落在 16bit 寄存器 bit15 位置,符合标准有符号 int16 格式:

直接强制转换成 int16_t 就能做正负电压运算,非常方便。 这是左对齐最大用途!

场景 2:直接截取高几位使用

如果你只关心高 8 位精度: 左对齐模式下直接读取 DR 的高字节,不需要移位运算。

4. 一张对比表(12bit ADC)

表格

项目 右对齐 左对齐
采样值 0xABC 在 DR 中 0x0ABC 0xABC0
获取真实 AD 值 直接读取,无需移位 val >> 4
单端普通电压采集 ⭐推荐首选 不推荐,多一次移位
差分有符号 ADC 采集 不方便 ⭐适合
数据格式 无符号自然存放 高位对齐,适配 int16

5. 高频踩坑点

极简建议

拓展延伸:如果是 16bit ADC

寄存器正好填满,左右对齐没有区别;只有有效位 < 寄存器宽度时,对齐设置才有意义。

  1. GPIO 配置为模拟输入
  2. RCC 开启 ADC 时钟,配置 ADC 预分频
  3. 设置采样时间 SMPx
  4. 选择通道、扫描模式(多通道)
  5. 选择软件触发 / TIM 触发
  6. 开启 DMA(可选)
  7. 执行 ADC 校准
  8. 使能 ADC
  9. 软件启动转换
  10. 查询 / 中断 / DMA 获取 AD 值
  11. ADC 左对齐 VS 右对齐(通俗易懂 + 实例)

    先建立前提: ADC 输出原始采样值是 N bit(常见 12bit),但是ADC 数据寄存器 DR 一般是 16bit 宽度。 12bit 的有效数据,放在 16bit 寄存器里,就产生两种摆放方式:右对齐 / 左对齐

    约定例子:12bit ADC,采样得到数值 = 0xABC(二进制 1010 1011 1100) DR 寄存器:16bit(bit15 ~ bit0)

    1. 右对齐(Right-aligned,绝大多数人首选)

    有效数据靠最低位摆放,高位补 0

    plaintext

    bit15 bit14 ... bit12 | bit11 ~ bit0
      0     0      0      | 1010 1011 1100
    

    寄存器读到的值:0x0ABC

    ✅ 优点:

  12. 读到的值直接就是原始 ADC 数值,不用移位;
  13. 电压计算公式直接套用:
  14. 配置左对齐,但代码仍然直接拿 DR 的值代入电压公式 → 数值偏大 16 倍,电压计算完全错误!
  15. 代码写死按照右对齐解析,实际寄存器配置成左对齐,数据全部异常。
  16. 单端采集、普通电压检测:一律右对齐
  17. 差分 ADC、需要正负信号、使用 int16 处理采样数据:选择左对齐
Logo

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

更多推荐