MQ-2传感器数据飘得厉害?手把手教你用STM32的ADC+DMA+TIM定时采集,实现稳定滤波(基于HAL库)
·
MQ-2传感器数据飘得厉害?手把手教你用STM32的ADC+DMA+TIM定时采集,实现稳定滤波(基于HAL库)
烟雾检测系统的稳定性往往取决于传感器数据的可靠性。MQ-2作为常见的半导体气敏元件,其输出信号容易受环境温度、供电波动等因素影响,导致原始ADC值出现明显抖动。本文将分享一套基于STM32 HAL库的工程级解决方案,通过硬件定时触发+DMA传输+软件滤波的组合拳,实现低CPU占用率的高稳定性数据采集。
1. 系统架构设计思路
传统轮询式ADC采集存在两个致命缺陷:一是MCU需要持续等待转换完成,造成资源浪费;二是单次采样难以消除噪声干扰。我们采用的方案核心在于三模块协同工作:
- TIM定时器:以精确的时间间隔触发ADC转换(例如100ms)
- DMA控制器:自动搬运ADC数据到内存缓冲区,完全解放CPU
- 多级滤波算法:对原始数据进行滑动平均+中值滤波处理
这种架构下,CPU仅在需要读取最终结果时介入,其余时间可处理其他任务。实测显示,相比传统轮询方式,CPU占用率可从70%降至不足5%。
2. 硬件环境搭建
2.1 元器件清单
| 组件 | 型号/参数 | 备注 |
|---|---|---|
| 主控芯片 | STM32F103C8T6 | 蓝色pill开发板 |
| 传感器 | MQ-2 | 需预热3分钟 |
| 电阻 | 10kΩ | 分压电路使用 |
| 电容 | 0.1μF | 电源滤波 |
2.2 电路连接要点
- 供电部分:
- 开发板5V输出接MQ-2的VCC引脚
- 共地连接必须可靠
- 信号调理:
// 分压电路计算(MQ-2电阻随浓度变化) float voltage = adc_value * 3.3f / 4095; float rs_air = (5.0f - voltage) / voltage * RL; // RL=10kΩ - ADC接口:
- 推荐使用PA0-PA7等ADC1通道
- 走线尽量远离数字信号线
3. STM32CubeMX配置
3.1 时钟树设置
- 主频设为72MHz
- APB2分频保持1:1(ADC时钟上限14MHz)
3.2 外设参数
/* ADC1 Configuration */
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
/* TIM3 Configuration */
htim3.Instance = TIM3;
htim3.Init.Prescaler = 7200-1; // 10kHz
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 1000-1; // 100ms触发
/* DMA Configuration */
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
关键提示:TIM3的TRGO信号需要设置为Update Event触发,CubeMX中在TIM3参数页的Trigger Output选项里设置。
4. 滤波算法实现
4.1 滑动窗口滤波
#define SAMPLE_SIZE 10
uint16_t adc_buffer[SAMPLE_SIZE];
float moving_average_filter() {
static uint8_t index = 0;
static uint32_t sum = 0;
sum -= adc_buffer[index];
sum += adc_buffer[index] = HAL_ADC_GetValue(&hadc1);
index = (index + 1) % SAMPLE_SIZE;
return (float)sum / SAMPLE_SIZE;
}
4.2 中值滤波优化
int compare(const void *a, const void *b) {
return (*(uint16_t*)a - *(uint16_t*)b);
}
uint16_t median_filter(uint16_t *buf, uint8_t size) {
uint16_t temp[size];
memcpy(temp, buf, size*sizeof(uint16_t));
qsort(temp, size, sizeof(uint16_t), compare);
return temp[size/2];
}
两种滤波方式可组合使用:先对DMA缓冲区的原始数据做中值滤波,再对连续多个中值结果进行滑动平均。
5. 完整工程代码框架
5.1 主循环结构
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_TIM3_Init();
HAL_TIM_Base_Start(&htim3);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, SAMPLE_SIZE);
while (1) {
if(data_ready_flag) {
float filtered = post_process_data();
printf("Filtered: %.2f\n", filtered);
data_ready_flag = 0;
}
// 其他任务...
}
}
5.2 DMA完成回调
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
data_ready_flag = 1;
// 可在此处添加临界区保护
}
6. 性能优化技巧
-
采样时序调整:
- 加热阶段提高采样率(1Hz)
- 稳定后降低至0.1Hz
-
动态阈值算法:
#define ALPHA 0.1f float dynamic_threshold(float new_sample) { static float baseline = 0; baseline = ALPHA * new_sample + (1-ALPHA) * baseline; return baseline * 1.5f; // 安全系数 } -
低功耗模式:
- 在采样间隔期间进入STOP模式
- 通过TIM唤醒
实际项目中,这套方案将MQ-2的数据波动幅度从±15%降低到±3%以内。需要注意的是,传感器预热期间的数据应丢弃,且每隔2-3个月需要进行校准。
更多推荐

所有评论(0)