
1. 项目背景与核心需求对于刚接触STM32的开发者来说ADC多通道采集是个既基础又关键的技能点。我在实际项目中经常遇到需要同时监测多个模拟信号的场景比如工业控制中的温度、压力、流量三参数采集。传统单通道轮询方式不仅效率低还会丢失关键数据。而使用扫描模式配合DMA传输可以真正做到一劳永逸——配置完成后CPU几乎不用干预就能持续获取稳定的采样数据。本教程要解决的典型场景是通过STM32的ADC同时采集三路PWM信号的平均电压值。这在电机控制、LED调光等应用中非常常见。比如用PWM控制电机转速时我们需要实时监测驱动电压的波动情况。直接测量PWM的瞬时值没有意义必须通过ADC采集多个周期计算平均电压。2. 硬件设计要点2.1 引脚分配策略STM32的ADC通道与GPIO引脚是固定映射关系以STM32F103C8T6为例ADC1_IN0~ADC1_IN15分别对应PA0~PA7/PB0~PB1/PC0~PC5同一个ADC的不同通道可以同时启用但不可跨ADC使用除非使用双ADC模式建议在CubeMX中按以下原则配置优先选择同一GPIO端口的引脚如PA1,PA2,PA3简化布线避免与已用的定时器PWM输出引脚冲突长距离传输时选择具有ADC模拟输入功能的引脚带ADC标注2.2 外围电路设计测量PWM平均电压需要RC低通滤波电路典型参数PWM频率1kHz时 R10kΩ, C1μF → 截止频率1/(2πRC)≈16Hz滤波后的纹波电压应小于ADC的1LSB3.3V/4096≈0.8mV注意直接测量PWM会导致ADC采样值剧烈波动。我曾在一个无人机项目中犯过这个错误导致油门信号检测完全失效。3. 软件配置详解3.1 CubeMX基础配置在Analog标签下启用ADC1勾选需要使用的通道如IN1,IN2,IN3参数设置Resolution: 12BitsScan Conversion Mode: EnabledContinuous Conversion Mode: EnabledDMA Continuous Requests: EnabledNumber Of Conversion: 3在DMA Settings添加DMA通道选择Mode: CircularData Width: Half Word3.2 关键代码实现3.2.1 DMA缓冲区定义#define ADC_CHANNELS 3 uint16_t adcBuffer[ADC_CHANNELS]; // DMA传输目标地址 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { // 每次转换完成自动触发 // 此处可添加数据处理代码 }3.2.2 启动ADC采样HAL_ADC_Start_DMA(hadc1, (uint32_t*)adcBuffer, ADC_CHANNELS);3.2.3 电压计算与滤波float getAverageVoltage(uint8_t channel) { static float sum[ADC_CHANNELS] {0}; static uint16_t count 0; sum[channel] adcBuffer[channel] * 3.3f / 4095; count; if(count 100) { // 每100次采样计算一次平均值 float avg sum[channel] / 100; sum[channel] 0; count 0; return avg; } return -1; // 未完成足够采样 }4. 实测中的典型问题4.1 采样值跳变问题现象即使输入电压稳定ADC读数仍有±3LSB波动 解决方案在ADC引脚添加0.1μF去耦电容适当增加采样时间如239.5周期软件端采用移动平均滤波#define FILTER_SIZE 10 float filterBuffer[FILTER_SIZE]; float movingAverage(float newVal) { static uint8_t index 0; filterBuffer[index] newVal; if(index FILTER_SIZE) index 0; float sum 0; for(uint8_t i0; iFILTER_SIZE; i) { sum filterBuffer[i]; } return sum / FILTER_SIZE; }4.2 多通道间串扰现象通道1的电压变化会影响通道2的读数 根本原因ADC内部采样保持电容放电不完全 解决方法在CubeMX中增加通道间延迟如ADC_TwoSamplingDelay_5Cycles降低采样频率硬件上在信号源端加入电压跟随器5. 性能优化技巧5.1 定时触发采样通过TIM触发ADC可实现精确的采样间隔// CubeMX配置 // TIM2 - Trigger Output Selection - Update Event // ADC1 - External Trigger Source - Timer 2 TRGO HAL_TIM_Base_Start(htim2); // 启动定时器5.2 过采样技术通过16倍过采样可将有效分辨率提升至14位uint32_t sum 0; for(int i0; i16; i) { while(!HAL_ADC_PollForConversion(hadc1, 10)); sum HAL_ADC_GetValue(hadc1); } uint16_t result sum 2; // 除以16后再左移2位6. 完整项目示例以STM32F103测量三路PWM占空比为例硬件连接PWM1 - PA0 (ADC1_IN0) 10kΩ/1μF滤波PWM2 - PA1 (ADC1_IN1) 10kΩ/1μF滤波PWM3 - PA2 (ADC1_IN2) 10kΩ/1μF滤波核心代码逻辑while(1) { float duty1 getAverageVoltage(0) / 3.3 * 100; float duty2 getAverageVoltage(1) / 3.3 * 100; float duty3 getAverageVoltage(2) / 3.3 * 100; printf(Duty: %.1f%%, %.1f%%, %.1f%%\r\n, duty1, duty2, duty3); HAL_Delay(100); }实测发现当PWM频率高于5kHz时需要减小RC时间常数如1kΩ0.1μF但会引入更多噪声。这时可以采用硬件PWM积分电路或者使用STM32的HRTIM配合ADC实现硬件同步采样。