L4 ADC 模数转换基础
本课目标
理解连续电压如何变为 12 位数字量,能配置采样通道、完成校准与轮询采样,并用 Stellaris-Nexus 的 PA5 / ADC_IN5 电位器验证结果。
一、ADC是什么
1 ADC与DAC
ADC(Analog to Digital Converter,模拟->数字信号转换器)。
与此相对应的还有DAC(Digital to Analog Converter,数字->模拟信号转换器)。
2 模拟信号与数字信号
模拟信号:自然界中,连续变化的信号,时间幅度都连续,它可以在一个范围内取无限多个值。

从电压角度看:电压0-3.3V,可能是0.2V、1.23V....无数种可能取值。
数字信号:计算机中离散变化的信号,时间和幅度只取有限的状态。

从电压角度看:单片机中,0V→表示0,3.3V→表示1
为了能让计算机处理自然界中的信号,我们要将模拟信号转换为数字信号,这个转换工具就是ADC。
二、STM32F103C8T6中的ADC转换原理
ADC1和ADC2,12位逐次逼近型ADC。
1 采样深度
采样深度:用多少位二进制数表示转换结果。可理解为精度、分辨率。

一般单片机都是10位或12位,也有一些更好的是14位或16位,位数越多,转换精度越好。
如

2 逐次逼近型
(P.S.插播一条,通信原理里的PCM编码差不多也是这么个算法)
| ADC 类型 | 工作原理 | 优点 | 缺点 | 应用 |
|---|---|---|---|---|
| 逐次逼近型 (SAR) | 逐次逼近算法,每次比较输入电压与内部基准电压,逐步逼近输入电压 | 速度适中,功耗低,分辨率高(8-18 位) | — | 数据采集系统、传感器接口 |
| 内存型 (Flash) | 使用并行比较器同时比较输入电压和一组参考电压 | 转换速度非常快(纳秒级),分辨率有限(4-8 位) | 电路复杂,成本高,功耗大 | 高速数据采集、视频信号处理 |
| 积分型 (Integrating) | 对输入信号进行积分,然后比较积分结果与参考电压 | 高精度,适合低频信号 | 转换速度慢 | 精密测量仪器,如数字电压表 |
| Σ-Δ (Sigma-Delta) | 利用超采样和数字滤波,通过调制和积分实现高分辨率转换 | 非常高的分辨率(16-24 位),噪声性能好 | 转换速度慢,延迟高 | 高精度音频设备、精密测量仪器 |
| 流水线 (Pipeline) | 将转换过程分为多个阶段,每个阶段逐步提高分辨率 | 中高速度和高分辨率,转换速度比逐次逼近型快,分辨率高于闪存型 | 电路复杂,延迟较 | 通信系统、高性能数据采集 |
| 双积分型 (Dual Slope) | 先对输入信号积分一定时间,然后对基准电压反向积分,比较结果 | 高精度,对电源噪声和信号噪声不敏感 | 转换速度慢 | 高精度测量设备,如高精度数字万用表 |
用天平理解“逐次逼近型 12 位 ADC”
假设:
你有一个双盘天平
你要称一个石头的重量
这块石头重量在 0 ~ 4095 克之间
你最多只能放 12 次砝码(最大的砝码是
2^11 = 2048 g)
(因为 2¹² = 4096,这就是 12 位)
称重规则
每次你需要知道:
石头重 ? 还是砝码重 ?
也就是说:
左边下沉 → 石头重
右边下沉 → 砝码重
你只能得到“大小关系”。
第一步:放最大砝码
你先放一个 2048g 砝码(总范围的一半)。
问:
石头比 2048g 重吗?
如果石头更重 → 保留 2048g
如果石头更轻 → 拿走 2048g
这一步就确定了 最高位是 1 还是 0
第二步:放 1024g
在刚才的基础上:
再放一个 1024g 砝码。
再看天平:
如果石头还是更重 → 保留
如果变成砝码重 → 换一个小的
这一步确定第二位
接下来依次放:
512g
256g
128g
64g
32g
16g
8g
4g
2g
1g
一共 12 个砝码。
最终效果
经过 12 次比较,
你就知道每个砝码该不该保留。
这些保留下来的砝码总重量,
就是石头的重量。
二进制怎么来的?
比如最后你保留了:
2048 + 512 + 256 + 128 + 32 + 16 + 8那就表示:
1011 0111 1000(二进制)这就是 ADC 输出的 12 位数字。
和 ADC 对应关系
| 天平 | ADC内部 |
|---|---|
| 石头 | 输入的模拟电压 |
| 砝码 | 二进制数 |
| 看哪边下沉 | 比较器 |
| 决定保留砝码 | 逐次逼近寄存器 |

为什么叫“逐次逼近”?
因为它:
✔ 从最大砝码开始 ✔ 每次试探一个砝码 ✔ 一位一位确定 ✔ 一步步逼近真实值
逐次逼近型 12 位 ADC 就像用 12 个不同重量的砝码,通过天平比较,一步步确定物体的真实重量。
怎么转换回标准电压?
假设上面这个二进制数是由模拟电压转换来的
1011 0111 1000(二进制)十进制:
= 2¹¹ + 2⁹ + 2⁸ + 2⁶ + 2⁵ + 2⁴ + 2³
= 2048 + 512 + 256 + 64 + 32 + 16 + 8
= 2936(10)乘分辨率:
(2936 / 4095)* 3.3V = 2936 * (3.3V / 4095) = 2.37V3 采样保持电路
逐次逼近转换不是瞬间完成的,它需要多个时钟周期。
如果在转换期间输入电压仍在变化:
比较器每次比较的基准会不同
转换结果会失真
因此必须:
先采样固定电压 → 再进行转换。

3.1采样过程(Sampling)
当 ADC 进入采样阶段时:
模拟开关闭合
输入信号与采样电容连通
此时:
电容电压会向输入电压变化
直到两者相等(或非常接近)
采样结束时,电容上的电压等于输入电压在该时刻的值。
3.2保持过程(Hold)
当采样时间结束:
模拟开关断开
电容与输入信号断开
此时:
电容电压不再跟随输入变化
该电压保持为采样瞬间的值
这个电压被送入比较器,用于后续逐次逼近转换。
4 转换过程总结
参考电压:3.3V
4位ADC(图中 b3~b0)
分辨率:
(3.3V−0)÷15=0.22V
- 当前采样电压:2.21V
(1)采样保持完成
采样阶段结束后:
电容电压 = 2.21V
开关断开
此电压送入比较器正端
后续逐次逼近过程中,这个 2.21V 不再变化。
(2)逐次逼近开始(从最高位开始)
采用“先试最高位”的方式。
第一步:确定 b3(最高位)
b3 对应权重:
2^3 = 8
对应电压:
8×0.22V=1.76V
试探:
输出 = 1.76V
比较:
2.21V > 1.76V
结论:
b3 = 1 保留 1.76V
第二步:确定 b2
b2 权重:
2^2 = 4
对应电压:
4×0.22V=0.88V
试探:
当前已保留 1.76V
加入 0.88V:
1.76+0.88=2.64V
比较:
2.21V < 2.64V
结论:
b2 = 0 舍弃 0.88V
当前仍为 1.76V
第三步:确定 b1
b1 权重:
2^1 = 2
对应电压:
2×0.22V=0.44V
试探:
1.76+0.44=2.20V
比较:
2.21V > 2.20V
结论:
b1 = 1 保留 0.44V
当前总电压:
2.20V
第四步:确定 b0
b0 权重:
2^0 = 1
对应电压:
0.22V
试探:
2.20+0.22=2.42V
比较:
2.21V < 2.42V
结论:
b0 = 0
(3)最终结果
四位结果:
b3 b2 b1 b0=1 0 1 0
即:
1010(二进制)
转换为十进制:
8+2=10
对应模拟电压:
10×0.22V=2.20V
5 采样时间与转换时间
采样时间:开关闭合的时间长度
转换时间:对采样点进行转换所消耗的时间
5.1 ADC的时钟频率
一般ADC时钟不超过14MHz(ADC的工作原理决定了它在高频率采样时,其分辨率会有所下降,不考虑精度时也可以大于14MHz。)

5.2转换时间

1周期 (cycle)= 1 / 12MHz = 0.083us
转换时间 = 12cycle + 0.5cycle = 12.5cycle(12位逐次逼近型)
5.3采样时间

采样时间公式(了解)
Ts > (RAIN + RADC) × CADC × ln(2^(N+2)) × fADC(cubemx)

1.5cycle 更快速
7.5cycle 更精确
三、STM32F103C8T6中的ADC工作原理

1 ADC的多路复用
多个输入共用一个 ADC 内核
这就是多路复用。
假设我们要采 PA0、PA1、PA2 三个通道:
软件选择通道0 模拟开关闭合通道0 进行一次采样 + 转换
软件切换到通道1 断开通道0 接通通道1 再次采样 + 转换
软件切换到通道2 重复上述过程
注意
(1)不是同时转换
虽然有很多输入通道,
但只有一个 ADC 内核,
因此:
实际是“轮流转换”
(2)每次切换通道都会重新采样
切换通道后:
采样电容会重新连接到新的输入
重新充电
再开始逐次逼近
2 常规序列与注入序列
2.1常规序列


2.2注入序列
通道更少、每个通道有单独的结果寄存器,优先级更高
2.3工作原理总结

区别与理解:
→不使用序列的方式:
一次只转换一个通道 通道切换由软件控制 转换顺序完全由程序决定 结构简单
→使用序列:
提前定义好一组通道顺序,让 ADC 自动按顺序依次转换。
四、软件编程
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef *hADC);作用:启动常规序列
闭合外部触发的开关
如果选择的是软件触发,则通过编程方式发送一个脉冲
HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef *hADC, uint32_t Timeout);
uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef *hADC);
现象:
拧动电位器,电压采样将实时变化↓
