ADC模数转换详解
笔记 / 嵌入式
引言
温度、湿度、压力、光线、声音这些物理量都是连续变化的模拟量,单片机只能处理 0 和 1 的数字量。ADC(模数转换器)负责把模拟量转换成数字量,是单片机采集这类信号的入口。下面按基本概念、核心参数、工作模式、HAL 配置示例、DMA 采集和常见问题依次展开。
一、ADC 基本概念
ADC(Analog to Digital Converter)将连续的模拟信号转换为离散的数字信号。转换过程包含四个步骤:
| 步骤 | 说明 |
|---|---|
| 采样(Sampling) | 以固定时间间隔获取模拟信号的瞬时值 |
| 保持(Hold) | 用电容保持采样值,防止转换期间信号变化 |
| 量化(Quantization) | 将连续的模拟值划分为离散的数字级别 |
| 编码(Encoding) | 将量化结果以二进制形式输出 |
二、核心参数详解
1. 分辨率(Resolution)
- STM32 F1/F4 系列:典型 12 位分辨率,输出范围 0 ~ 4095(2¹² - 1)
- STM32 H7 系列:最高 16 位分辨率,过采样可达 26 位
LSB(最低有效位)计算公式:
LSB = Vref+ / 2^分辨率位数
以 12 位分辨率、参考电压 3.3V 为例:
LSB = 3.3V / 4096 ≈ 0.806mV
实际电压 = ADC转换值 × (3.3 / 4096)
2. 采样率(Sampling Rate)
| 关键参数 | F1 系列典型值 | 说明 |
|---|---|---|
| ADC 时钟 | 最高14 MHz | 超过会降低精度 |
| 常用分频配置 | PCLK2 的 8 分频 = 9 MHz | ADCCLK 必须 ≤ 14MHz |
| 最短采样周期 | 1.5 个 ADCCLK 周期 | 可编程范围:1.5 ~ 239.5 周期 |
总转换时间公式:
Tconv = 采样时间 + 12.5 个周期(逐次逼近固定时间)
最快转换时间(F1):Tconv = (1.5 + 12.5) × (1/12MHz) ≈ 1.17 μs
3. ADC 类型
STM32 采用逐次逼近型(SAR)ADC,在精度和速度之间取得良好平衡。逐次逼近的工作方式类似二分查找:从最高位开始,每次比较后把候选范围缩小一半,逐位确定转换结果。
三、工作模式
转换模式
| 模式 | 说明 |
|---|---|
| 单次转换 | 触发一次,执行一次转换后停止 |
| 连续转换 | 一次触发后,不断循环转换 |
| 扫描模式 | 按预设顺序轮流采集多个通道 |
| 间断模式 | 分组间断地进行多通道转换 |
通道组
| 通道组 | 通道数 | 特点 |
|---|---|---|
| 规则通道组(Regular) | 最多 16 路 | 正常运行,结果存于单个数据寄存器 |
| 注入通道组(Injected) | 最多 4 路 | 类似中断,可打断规则通道转换 |
触发方式
- 软件触发:代码直接启动转换
- 外部事件触发:定时器、EXTI 外部中断等
四、HAL 库配置示例
单通道单次转换
// 1. 使能时钟
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
// 2. GPIO 配置为模拟输入
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 3. ADC 初始化
ADC_HandleTypeDef hadc1;
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
HAL_ADC_Init(&hadc1);
// 4. 配置通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// 5. 读取转换值
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 10);
uint16_t adc_value = HAL_ADC_GetValue(&hadc1);
float voltage = adc_value * (3.3f / 4096);
五、DMA 模式:多通道与高速采集
对于多通道或高速持续采集,推荐使用 DMA 循环传输模式,由 DMA 自动将 ADC 数据搬移到内存数组,无需 CPU 频繁干预:
// DMA 关键配置
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; // ADC数据寄存器
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adc_buffer; // 内存数组
DMA_InitStructure.DMA_BufferSize = 通道数量;
这样 ADC 持续采集,DMA 自动搬运,CPU 只需在需要时读取 adc_buffer 即可。
六、常见问题与注意事项
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 外部阻抗过大导致误差 | 分压电阻过大(MΩ 级),与内部 50kΩ 阻抗分压 | 使用 ≤10kΩ 分压电阻,或加运放做阻抗变换 |
| 多通道串扰 | 采样电容在通道切换时残留电荷 | 增大通道间采样延时 |
| 引脚浮空 | 外部无输入时引脚悬空 | 加下拉电阻 |
| 扫描模式数据覆盖 | 规则组只有一个数据寄存器 | 及时读取或使用 DMA |
| ADC 时钟超频 | ADC 时钟 >14MHz | 正确配置分频因子 |
七、STM32 各系列 ADC 参数速查
| 指标 | STM32F1 | STM32F4 | STM32H7 |
|---|---|---|---|
| 分辨率 | 12 bit | 12 bit | 16 bit |
| ADC 时钟 | 最高14 MHz | 36 MHz | 36 MHz |
| 最快转换时间 | ≈ 1 μs | ≈ 0.5 μs | 更快 |
| 参考电压 | 3.3V | 3.3V | 2.5V / 3.3V |
| LSB(12bit@3.3V) | 0.806 mV | 0.806 mV | 约 0.05 mV |
总结
ADC 把连续变化的模拟量转换成离散的数字量,中间经过采样、保持、量化和编码四个步骤。分辨率决定能分辨的最小电压变化,采样时间和 ADC 时钟决定转换速度,多通道或高速采集则交给 DMA 循环搬运。这几个参数之间的取舍,是设计采样电路时要先确定的部分。