【STM32 实战】ADC 全解析:从模拟信号采集到数据处理的完整指南_stm32 adc采集电路
一、ADC 是什么?为什么需要它?
在嵌入式系统中,我们常遇到需要处理模拟信号的场景:
- 光敏电阻随光线变化的电压值
- 电位器旋转时的电阻变化量
- 温度传感器输出的电流信号
这些连续变化的模拟量无法直接被 STM32 的数字电路处理,而 **ADC(模数转换器)** 就是搭建 “模拟世界” 与 “数字世界” 的桥梁。
STM32 的 ADC 模块能将 0~3.3V 的模拟电压转换为 12 位数字值(0~4095),精度高、速度快,是传感器数据采集的核心组件。
二、STM32 ADC 核心特性(以 F103 系列为例)
1. 关键参数
- 分辨率:12 位(可识别 3.3V/4096≈0.8mV 的电压变化)。
- 转换速度:最大 1MHz 时钟下,转换时间约 1μs(适合高频采样)。
- 通道数量:最多 16 个外部通道 + 2 个内部通道(温度传感器、参考电压)。
- 工作模式:
- 单次转换:转换一次后停止(适合按键检测等偶发场景)。
- 连续转换:循环采样(适合实时数据监测)。
- 扫描模式:依次转换多个通道(如同时采集温湿度、光照强度)。
2. 硬件连接要点
- 输入范围:0~VREF+(通常接 3.3V,由 VDDA 供电)。
- 抗干扰:模拟信号引脚需接去耦电容(如 100nF),减少高频噪声。
- 引脚复用:如 PA0~PA7 可作为 ADC 通道 0~7,需在 CubeMX 中配置为 “模拟输入” 模式。
三、实战一:单通道电压采集(电位器调节 LED 亮度)
需求分析
- 目标:通过电位器调节输入电压,ADC 采集后转换为数字值,控制 LED 亮度(PWM 实现)。
- 硬件:STM32F103C8T6 开发板、电位器、LED。
- 电路连接:
- 电位器中间引脚接 PA0(ADC 通道 0),两端分别接 3.3V 和 GND。
- LED 接 PC13(PWM 控制,参考前序定时器教程)。
实现步骤(CubeMX + HAL 库)
1. 在 CubeMX 中配置 ADC
① 新建工程:选择 STM32F103C8,启用 ADC1 通道 0(PA0)。
② 配置 ADC1:
- 模式:单次转换,非扫描模式。
- 分辨率:12 位。
- 采样时间:55.5 周期。
- 使能通道 0:在 “Channels” 中勾选 ADC1_IN0。
③ 生成代码:确保勾选 “Generate ADC initialization”。
2. 编写 ADC 采集与 PWM 控制代码
① 在 main.c 中定义变量:
uint16_t adcValue = 0; // 存储ADC转换结果uint16_t pwmValue = 0; // PWM占空比(0~999)
② 初始化 ADC 并启动转换
int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_TIM3_Init(); // 预配置TIM3用于PWM输出 HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 启动PWM while (1) { HAL_ADC_Start(&hadc1); // 启动ADC转换 HAL_ADC_PollForConversion(&hadc1, 100); // 等待转换完成(超时100ms) adcValue = HAL_ADC_GetValue(&hadc1); // 获取转换结果 // 将ADC值(0~4095)映射为PWM占空比(0~999) pwmValue = (uint16_t)(adcValue / 4.096); __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pwmValue); // 更新PWM占空比 HAL_Delay(50); // 控制采样频率 }}
关键公式
- 电压计算:
Voltage = adcValue × 3.3V / 4096
- PWM 映射:将 0~4095 的 ADC 值压缩到 0~999,实现亮度线性调节。
四、实战二:多通道同步采集(温湿度 + 光照强度)
需求分析
- 目标:同时采集 3 路模拟信号(温度、湿度、光照),用 DMA 实现非阻塞传输。
- 硬件:STM32F103C8T6、DHT11(单总线,需 ADC 采集电压)、光敏电阻、热敏电阻。
实现步骤(DMA + 扫描模式)
1. CubeMX 配置
① 启用 ADC1 多通道:勾选 ADC1_IN0(光照)、ADC1_IN1(温度)、ADC1_IN2(湿度)。
② 配置扫描模式:
- 勾选 “Scan Conversion Mode”(扫描模式)。
- 设置通道转换顺序:IN0→IN1→IN2,循环扫描。
③ 启用 DMA: - 选择 DMA 通道 1(ADC1 专用),传输方向:外设到存储器。
- 存储器地址:定义数组
uint16_t adcBuffer[3]
。
2. 代码逻辑(DMA 传输)
uint16_t adcBuffer[3]; // 存储3通道数据int main(void) { HAL_Init(); MX_ADC1_Init(); HAL_ADC_Start_DMA(&hadc1, adcBuffer, 3); // 启动DMA传输,采集3通道数据 while (1) { // 处理数据:adcBuffer[0]=光照,adcBuffer[1]=温度,adcBuffer[2]=湿度 // 例如:通过串口打印数据 printf(\"Light: %d, Temp: %d, Humidity: %d\\r\\n\", adcBuffer[0], adcBuffer[1], adcBuffer[2]); HAL_Delay(1000); }}
优势解析
- DMA 非阻塞:CPU 无需等待 ADC 转换,可同时处理其他任务(如串口通信、按键检测)。
- 扫描模式:自动按顺序采集多通道,避免软件轮询的延迟误差。
五、高级技巧:ADC 校准与抗干扰
1. 硬件校准
- 原因:ADC 内部元件存在制造误差,校准可提高精度。
HAL_ADCEx_Calibration_Start(&hadc1); // 在初始化后调用校准函数
软件滤波(中值滤波)
- 场景:抑制随机噪声(如电机启动时的电压波动)。
- 代码示例:
uint16_t filterBuffer[10]; // 缓存10次采样值uint16_t getFilteredValue() { for (int i=0; i<9; i++) filterBuffer[i] = filterBuffer[i+1]; filterBuffer[9] = HAL_ADC_GetValue(&hadc1); // 排序后取中值 for (int i=0; i<9; i++) { for (int j=i+1; j filterBuffer[j]) { uint16_t temp = filterBuffer[i]; filterBuffer[i] = filterBuffer[j]; filterBuffer[j] = temp; } } } return filterBuffer[5]; // 返回第5大的值}
六、常见问题与解决方案
1. 转换结果始终为 0 或 4095
- 原因:
- 引脚未正确连接(浮空或短路)。
- ADC 时钟未使能(检查 CubeMX 的 RCC 配置)。
- 解决:用万用表测量引脚电压,确保在 0~3.3V 范围内,并重新生成代码。
2. 多通道数据错乱
- 原因:DMA 缓冲区大小与通道数不匹配。
- 解决:DMA 传输数量需等于通道数,如 3 通道则
HAL_ADC_Start_DMA(&hadc1, buffer, 3)
。
3. 精度不足
- 优化:
- 增大采样时间(CubeMX 中设置为 239.5 周期,适合高精度场景)。
- 启用过采样(通过 ADC_CR1 的 OVERSAMPLING_RATIO 配置)。
七、总结:ADC 的应用边界与扩展
通过本文,你已掌握:
- 单通道采集:电位器、传感器等基础应用。
- 多通道扫描 + DMA:高效处理多路模拟信号。
- 抗干扰技巧:校准与滤波提升数据可靠性。
未来方向:
- 结合 DMA 和定时器,实现周期性多通道采样(如工业数据监控)。
- 搭配 DAC(数模转换器),构建闭环控制系统(如自动温控风扇)。
下一篇预告:《STM32 通信实战:USART 串口与物联网模块的互联互通》
(关注我,解锁设备联网技能~)
互动问题:你在 ADC 采集中遇到过最棘手的干扰问题是什么?欢迎在评论区讨论! 🔌