> 技术文档 > 【STM32 实战】ADC 全解析:从模拟信号采集到数据处理的完整指南_stm32 adc采集电路

【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 采集中遇到过最棘手的干扰问题是什么?欢迎在评论区讨论! 🔌