单片机:实现数字滤波
1. 项目背景与目标
数字滤波是处理信号噪声和提高信号质量的重要技术,广泛应用于各种信号处理场景,如温度传感器、压力传感器、音频处理等。在嵌入式系统中,数字滤波主要用于对采集到的模拟信号进行去噪或平滑处理,以便提取有效信息。
常见的数字滤波方法包括:
- 平均滤波:通过计算多个数据点的平均值来消除噪声。
- 中值滤波:通过选择数据窗口中的中位数来过滤异常值。
- 低通滤波:对高频信号进行衰减,仅保留低频信号。
本项目将实现一种简单的平均滤波算法,使用STM32单片机对传感器数据进行滤波处理。
2. 硬件设计
2.1 硬件组件
- 单片机:例如STM32系列单片机,内置ADC模块。
- 模拟信号源:例如温度传感器(如LM35)、光敏电阻等。
- 显示设备(可选):LCD或串口显示用于展示滤波后的数据。
2.2 硬件连接
- 模拟信号源:连接到STM32的ADC输入引脚(例如PA0、PA1等)。
- 显示设备:如LCD、数码管或通过串口输出结果。
3. 数字滤波原理
本项目使用的是平均滤波方法。其基本思想是通过对信号序列进行平滑处理,消除高频噪声。具体做法是对信号进行一个滑动窗口操作,计算该窗口内信号的平均值,并将其作为输出。
假设采样信号序列为x[n]
,其滤波结果y[n]
为:
其中,M
是滑动窗口的大小,通常为3、5、7等。
4. 软件设计
4.1 设计思路
- 配置ADC读取模拟信号。
- 对读取到的ADC值进行平均滤波处理。
- 将滤波后的结果通过串口或显示设备输出。
4.2 代码实现
#include "stm32f4xx_hal.h"
#include <stdio.h>
#define ADC_CHANNEL ADC_CHANNEL_0 // 选择通道0(PA0)
// ADC句柄
ADC_HandleTypeDef hadc1;
// 滤波窗口大小
#define FILTER_WINDOW_SIZE 5
// 滤波数据缓冲区
uint32_t adc_buffer[FILTER_WINDOW_SIZE] = {0};
uint8_t filter_index = 0;
// 初始化ADC
void ADC_Init(void) {
__HAL_RCC_ADC1_CLK_ENABLE(); // 启用ADC1的时钟
// 配置ADC输入引脚
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE(); // 启用GPIOA时钟
// 配置PA0为模拟输入模式
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置ADC参数
hadc1.Instance = ADC1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B; // 12位分辨率
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; // 单通道模式
hadc1.Init.ContinuousConvMode = DISABLE; // 非连续转换模式
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1; // 外部触发模式
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 数据对齐方式:右对齐
hadc1.Init.NbrOfConversion = 1; // 进行单次转换
hadc1.Init.DMAContinuousRequests = DISABLE; // 不使用DMA
// 初始化ADC
HAL_ADC_Init(&hadc1);
// 配置ADC通道(选择通道0,PA0)
ADC_ChannelConfig();
}
// 配置ADC通道
void ADC_ChannelConfig(void) {
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL; // 设置通道
sConfig.Rank = ADC_REGULAR_RANK_1; // 第1通道
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES; // 设置采样时间
sConfig.Offset = 0;
// 配置ADC通道
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}
// 获取ADC值
uint32_t ADC_Read(void) {
// 启动ADC转换
HAL_ADC_Start(&hadc1);
// 等待ADC转换完成
if (HAL_ADC_PollForConversion(&hadc1, 1000) == HAL_OK) {
// 获取ADC值
return HAL_ADC_GetValue(&hadc1);
}
return 0; // 如果转换失败,返回0
}
// 计算滤波后的结果
uint32_t Filter_Average(void) {
uint32_t sum = 0;
for (uint8_t i = 0; i < FILTER_WINDOW_SIZE; i++) {
sum += adc_buffer[i];
}
return sum / FILTER_WINDOW_SIZE;
}
// 更新滤波缓冲区
void Filter_Update(uint32_t new_value) {
adc_buffer[filter_index] = new_value;
filter_index = (filter_index + 1) % FILTER_WINDOW_SIZE; // 环形缓冲区
}
// 主函数
int main(void) {
HAL_Init(); // 初始化HAL库
ADC_Init(); // 初始化ADC
// 主循环
while (1) {
uint32_t adc_value = ADC_Read(); // 获取ADC采样值
// 更新滤波器数据
Filter_Update(adc_value);
// 获取滤波后的值
uint32_t filtered_value = Filter_Average();
// 输出ADC值和滤波后的值(通过串口或其他方式)
printf("Raw ADC Value: %lu, Filtered Value: %lu\n", adc_value, filtered_value);
HAL_Delay(500); // 每隔500ms进行一次采样
}
}
5. 代码解释
-
ADC初始化:
- 在
ADC_Init()
函数中,初始化了ADC模块并配置了PA0引脚为模拟输入。配置了ADC为12位分辨率,单通道模式,并且设置了适当的采样时间。
- 在
-
滤波窗口和缓冲区:
- 使用一个大小为
FILTER_WINDOW_SIZE
(在本例中为5)的环形缓冲区adc_buffer[]
来存储最近的几个ADC采样值。 filter_index
用于控制缓冲区的写入位置,确保新的数据会覆盖旧的数据,保持缓冲区的最新数据。
- 使用一个大小为
-
平均滤波:
Filter_Average()
函数计算缓冲区中所有值的平均值,即对采样值进行平滑处理。这里的FILTER_WINDOW_SIZE
定义了滤波窗口的大小,通常选择3、5、7等小值,来平衡滤波效果与实时性。
-
更新滤波缓冲区:
Filter_Update()
函数将新采样值添加到缓冲区,并更新filter_index
,实现环形缓冲区的效果。
-
主循环:
- 在主循环中,通过
ADC_Read()
函数获取ADC值,然后通过Filter_Update()
函数更新缓冲区,最后使用Filter_Average()
获得滤波后的结果。 - 结果通过
printf()
输出,可以使用串口调试工具观察原始ADC值和滤波后的值。
- 在主循环中,通过
6. 滤波效果
假设输入信号存在一定噪声,未经滤波的ADC值可能会呈现波动。通过平均滤波后,输出的值会更加平滑,噪声得到有效抑制。例如,在一个连续变化的模拟信号中,经过滤波后的信号曲线将更加平滑,减少了高频噪声的影响。
7. 优化与扩展
-
其他滤波方法:
- 除了简单的平均滤波外,还可以使用其他滤波方法,如中值滤波、低通滤波等,这些方法能更好地应对不同类型的噪声。
-
滤波窗口动态调整:
- 根据实际需要,可以动态调整滤波窗口的大小,较大的窗口可以消除更多噪声,但会延迟响应。
-
实时性能优化:
- 对于高频信号的实时处理,使用硬件DMA(直接存储器访问)进行ADC数据采样并自动存储至缓冲区,从而减少CPU的负担,提高系统效率。
-
低功耗优化:
- 对于电池供电的嵌入式设备,滤波过程中可以结合低功耗模式,减少功耗,延长电池使用寿命。
8. 总结
本项目展示了如何在STM32单片机上实现一个简单的数字滤波功能,采用平均滤波算法对ADC采样的信号进行噪声抑制。通过环形缓冲区存储最近的采样值,并通过平均滤波计算平滑后的结果,能够有效降低信号中的高频噪声。这种方法适用于各种需要平滑信号的应用,如传感器信号处理、音频信号处理等。