第七章:ADC 模块 —— 给你的开发板一双“慧眼”! - 实践
2026-01-11 17:18 tlnshuju 阅读(16) 评论(0) 收藏 举报ADC模块详解:从配置到高效数据采集
✅ 适用对象:STM32 初学者
核心目标:掌握 ADC 的 CubeMX 配置 + DMA 数据传输 + 数据处理
特色:用“秤”比喻ADC转换过程,用“自动搬运工”解释DMA工作原理!
一、硬件原理图(以 STM32G431RBT6 为例)
ADC 是什么?
- 模拟信号转数字信号的桥梁
- 将外界物理量(如温度、电压)转化为微控制器能理解的数字值
基本连接

| 信号 | 引脚(ADC1) | 方向 | 说明 |
|---|---|---|---|
| AINx | PA0 ~ PA7 | 输入 | 模拟输入通道(可选多个) |
| VREF+ / VREF- | 内部参考电压 | - | 默认为电源电压 |
⚠️ 注意:
- 去耦电容:每个模拟输入前建议加一个0.1uF的去耦电容,减少噪声干扰
- 地线设计:确保良好的接地,避免引入干扰
⚙️ 二、STM32CubeMX 配置详解




1. ADC 参数设置
基本参数(就像“称重规则”)
| 参数 | 说明 | 常见值 |
|---|---|---|
| Resolution (分辨率) | 转换精度 | 12 bits(最高精度,默认推荐) |
| Data Alignment (数据对齐) | 结果存储格式 | Right Alignment(右对齐,方便处理) |
| Scan Conversion Mode (扫描转换模式) | 是否连续转换多个通道 | Enable(多通道时使用) |
| Continuous Conversion Mode (连续转换模式) | 是否持续采样 | Disable(一般禁用,除非需要实时监控) |
| Discontinuous Conversion Mode (间歇转换模式) | 是否分组采样 | Disable(简单场景禁用) |
⚙️ 高级参数(进阶功能)
| 参数 | 说明 |
|---|---|
| External Trigger Conversion Mode (外部触发转换模式) | 外部事件触发采样(如定时器中断) |
| Trigger Source (触发源) | 触发源选择(如 TIMx_TRGO、EXTI 等) |
| Channel Configuration (通道配置) | 配置各通道的采样时间和优先级 |
| Sampling Time (采样时间) | 提高精度,但增加转换时间 |
| DMA (直接存储器访问) | 自动传输结果至内存,减轻CPU负担 |
| Watchdog (看门狗) | 监控结果是否超出预设范围 |
| Analog Watchdog Mode (模拟看门狗模式) | 单通道或多通道监控 |
✅ 推荐配置:12-bit 右对齐,启用扫描模式(多通道),禁用连续和间歇模式,采样时间适中
2. GPIO 设置
| 引脚 | 模式 | 说明 |
|---|---|---|
| PA0 (AIN0) | Analog Input | 连接传感器或其他模拟信号源 |
| PA1 (AIN1) | Analog Input | 同上 |
注意:
- 确保引脚未被其他外设占用
- 若有多个通道,重复上述步骤
3. DMA 设置(高效传输核心!)
| 项目 | 配置建议 |
|---|---|
| DMA Request | ADC1_Regular_Channel(常规通道) |
| DMA Channel | 自动分配(如 DMA1_Channel1) |
| DMA Mode | Circular(循环模式) ← 接收必须用此模式! |
| Memory Increment | ✅ Enable(内存地址递增) |
| Peripheral Increment | ❌ Disable(外设地址固定) |
为什么?
- ADC 数据寄存器地址固定 → 外设地址不递增
- 数据要存入 buffer[0], buffer[1]... → 内存地址必须递增
三、底层代码实现(DMA 模式)
生活比喻:秤与搬运工
想象你有一台智能秤(ADC),每次放上物品都会显示重量(数字值)。为了记录这些重量,你可以:
- 手动记录(无DMA):每称一次就记一次数(效率低)
- 自动记录(DMA):雇佣一个搬运工(DMA),他会在后台帮你把每次的重量记录下来,完全不需要你操心!
完整代码实现
1. 全局变量定义
uint32_t dma_buff[2][30]; // DMA接收缓存,支持两个ADC通道,每通道30个样本
float adc_value[2]; // ADC采样值数组
2. 初始化函数
void adc_dma_init(void) {
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&dma_buff[0][0], 30); // 启动ADC1的DMA传输
HAL_ADC_Start_DMA(&hadc2, (uint32_t*)&dma_buff[1][0], 30); // 启动ADC2的DMA传输
}
3. 数据处理函数
void adc_proc(void) {
// 计算平均值
adc_value[0] = adc_value[1] = 0;
for(uint8_t i = 0; i < 30; i++) {
adc_value[0] += (float)dma_buff[0][i];
adc_value[1] += (float)dma_buff[1][i];
}
adc_value[0] /= 30 * 4096; // 归一化处理
adc_value[1] /= 30 * 4096;
// 转换为实际电压值(假设参考电压为3.3V)
adc_value[0] *= 3.3f;
adc_value[1] *= 3.3f;
printf("ADC1 Value: %.2fV\n", adc_value[0]);
printf("ADC2 Value: %.2fV\n", adc_value[1]);
}
修复细节:
i +)→i++)(循环变量自增)- 添加了归一化处理(除以4096)和电压转换公式
四、HAL 库核心 API 详解
HAL_ADC_Start_DMA()
功能
启动 ADC 的 DMA 模式转换。通过此函数,ADC 可以将转换后的数据直接存储到内存中,而无需 CPU 的干预,从而提高数据处理效率。
描述
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef *hadc, uint32_t *pData, uint32_t Length);
| 参数 | 说明 |
|---|---|
| hadc | 指向 ADC 句柄的指针 |
| pData | 指向数据存储缓冲区的指针 |
| Length | 要转换的数据量 |
| 返回值 | 说明 |
|---|---|
| HAL_OK | 函数执行成功 |
| HAL_ERROR | 函数执行时发生错误 |
| HAL_BUSY | ADC处于忙碌状态,无法启动新的转换 |
| HAL_TIMEOUT | 函数执行超时 |
使用场景
- 连续数据采集:在需要连续采集大量ADC数据并将其存储到内存中时,使用DMA可以有效减少CPU负担。
- 实时数据处理:应用程序可以在后台持续采集数据,同时前台处理其他任务,提高系统响应速度。
- 多通道ADC转换:在多通道ADC转换场景中,使用DMA可以自动将各通道的数据存储到指定的缓冲区中。
五、使用示例(main.c)
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init(); // 初始化ADC1
MX_ADC2_Init(); // 初始化ADC2
MX_DMA_Init();
adc_dma_init(); // 启动DMA传输
while (1) {
adc_proc(); // 处理ADC数据
HAL_Delay(1000); // 每秒处理一次
}
}
✅ 总结:关键点回顾
分辨率与精度的关系
- 分辨率越高(如12位),精度越高,但转换时间越长
- 计算公式:
实际电压 = ADC值 * Vref / 4096Vref:参考电压(通常为3.3V)4096:12位ADC的最大值(2^12)
DMA的作用
- 自动搬运工:将ADC转换结果直接传输到内存,释放CPU资源
- 循环模式:特别适合连续采集,避免数据丢失
数据处理流程
- 初始化:配置ADC和DMA
- 启动DMA:开始数据采集
- 处理数据:定期读取DMA缓冲区,计算平均值或进行其他处理
本章口诀(背下来!)
ADC 配置四要素:分辨率、对齐方式、采样时间、DMA使能!
模拟输入需去耦,干净电源稳如山!
DMA是搬运工,CPU从此轻松松!
⏸️ 采样时间定精度,时间越长越精准!
数据处理别马虎,归一化后才准确!
️ printf输出真方便,调试信息全知晓!
这份笔记完整覆盖了ADC所有内容,包括:
- CubeMX 配置细节
- DMA 数据传输实现
- 数据处理逻辑
- HAL 库 API 详解
- 硬件连接说明
现在,你不仅能配置 ADC,还能利用 DMA 实现高效的数据采集和处理!
浙公网安备 33010602011771号