> 技术文档 > 单片机:实现数字滤波(完整源码)_单片机fir数字滤波器

单片机:实现数字滤波(完整源码)_单片机fir数字滤波器


单片机实现数字滤波技术详解

作者:Katie

目录

  1. 项目简介

  2. 数字滤波理论基础
    2.1 数字滤波器的应用场景
    2.2 FIR滤波器与IIR滤波器
    2.3 滤波器设计中的关键参数

  3. 系统设计与实现思路
    3.1 项目整体架构
    3.2 数字滤波算法的实现流程

  4. 详细代码实现
    4.1 完整代码(集成版)
    4.2 代码中关键部分详细注释

  5. 代码解读

  6. 系统调试与测试

  7. 项目总结与心得

  8. 参考资料与扩展阅读


1. 项目简介

在嵌入式系统中,传感器采集的数据往往伴随着噪声,而数字滤波技术能有效抑制干扰,改善数据的稳定性和精度。本文项目基于单片机实现数字滤波,主要通过预先设计好的数字滤波器算法(例如FIR滤波器)对采集的信号进行处理,滤除高频噪声,提取有用信号。整个方案适用于温度、压力、光照等传感器数据的实时处理,也为音频信号处理和其他实时控制提供参考。

本项目采用简单的FIR滤波器作为示例,利用查找表存储滤波器系数,通过卷积运算实现滤波。项目内容不仅涉及数字滤波算法的数学原理,还详细介绍了单片机上实现数字滤波的硬件、软件设计和代码实现。


2. 数字滤波理论基础

2.1 数字滤波器的应用场景

数字滤波器在嵌入式系统中主要用于:

  • 噪声抑制:滤除传感器采集数据中的高频噪声;

  • 信号平滑:对信号进行平滑处理,提取有效趋势;

  • 频谱分离:在多通道数据处理中,区分不同频段的信号。

其优势在于无需模拟电路即可实现灵活、可编程的信号处理。

2.2 FIR滤波器与IIR滤波器

数字滤波器主要有两大类:

  • FIR(有限冲激响应)滤波器
    FIR滤波器具有线性相位、固有稳定性等特点,其输出由有限个输入样本和滤波系数的加权求和得到。设计上较为简单,适合对相位要求严格的场合。

  • IIR(无限冲激响应)滤波器
    IIR滤波器结构上类似模拟滤波器,具有较高的滤波效率,但稳定性和相位特性需要额外注意。其递归结构决定了输出不仅与当前输入有关,也与之前的输出有关。

本项目采用FIR滤波器作为示例,由于其计算过程简单、稳定性好,易于在单片机上实现。

2.3 滤波器设计中的关键参数

在FIR滤波器设计中,关键参数包括:

  • 滤波器阶数:决定滤波器的长度和滤波性能,阶数越高滤波精度越好,但计算量也随之增大。

  • 截止频率:决定滤波器允许通过信号的频率范围。

  • 窗函数:用于设计滤波器系数的窗函数(如汉明窗、黑曼窗),影响滤波器的旁瓣衰减和主瓣宽度。

设计时需权衡滤波效果与单片机资源限制,选取合适的阶数和系数。


3. 系统设计与实现思路

3.1 项目整体架构

本系统整体设计包括以下几个模块:

  • 数据采集模块
    采集模拟信号(例如温度、压力等),通过外部ADC或单片机内置ADC转换为数字信号。

  • 数字滤波模块
    利用FIR滤波算法对采集的数字信号进行滤波处理,采用查找表存储滤波器系数,通过卷积计算实现滤波。

  • 控制与显示模块
    根据滤波后数据进行处理(例如控制显示、调节控制参数等),也可通过串口输出结果以便调试。

  • 系统时钟与定时器模块
    保证数据采集与滤波处理的定时性和实时性。

3.2 数字滤波算法的实现流程

FIR滤波器的实现主要步骤如下:

  1. 滤波器系数设计
    根据所需截止频率和滤波器阶数,通过设计工具或手工计算获得一组滤波器系数,并存储在数组中。

  2. 数据采集
    定时采集传感器的原始数据,存入缓冲区。

  3. 卷积计算
    对每个采样数据点,利用当前以及之前的若干数据与滤波器系数进行乘积求和,得到滤波后的输出。

  4. 输出与处理
    将滤波结果输出或用于后续控制。

数据处理流程示意图如下:

┌─────────────────────┐│ 数据采集(ADC转换) │└───────────┬─────────┘ │ ▼┌─────────────────────┐│ 滤波器系数查找表 │└───────────┬─────────┘ │ ▼┌─────────────────────┐│ 卷积运算(FIR滤波) │└───────────┬─────────┘ │ ▼┌─────────────────────┐│ 滤波后数据输出与控制 │└─────────────────────┘

4. 详细代码实现

下面给出完整的代码示例,基于C语言实现。代码主要实现了一个简单的FIR滤波器,用于对采样数据进行低通滤波。代码中假设采样数据通过某种方式获得(例如模拟信号转换为数字数据),并使用定时器中断进行定时采样与滤波计算。所有代码均附有详细注释。

4.1 完整代码(集成版)

/* * 单片机实现数字滤波(FIR滤波器)示例 * 作者:Katie * 代码日期:2025-03-28 * * 本程序演示了如何在单片机上实现一个简单的FIR数字滤波器, * 用于对采集的模拟信号进行低通滤波,去除高频噪声。 * * 硬件说明: * - 假设系统通过外部ADC获得采样数据(本示例中用模拟数据替代)。 * - 定时器中断用于定时触发采样和滤波计算。 * - 滤波器系数采用预先设计好的低通FIR滤波器系数。 * - 滤波后的数据可用于显示、控制或通过串口输出调试。 */#include #include // -------------------- 宏定义 --------------------#define FOSC 12000000UL // 系统时钟12MHz#define TIMER0_RELOAD (256 - (FOSC/12/1000)) // 定时器0重载值,1ms中断#define FILTER_ORDER 8 // FIR滤波器阶数(滤波器长度为FILTER_ORDER+1)#define SAMPLE_BUFFER_SIZE (FILTER_ORDER+1) // 采样数据缓冲区大小// -------------------- 全局变量 --------------------// 模拟采样数据缓冲区,用于存储最新的采样数据(先进先出)volatile int sampleBuffer[SAMPLE_BUFFER_SIZE] = {0};// 滤波器系数数组(低通滤波器示例,系数可通过设计工具获得)// 此处给出一组示例系数,总和约为1(注意实际设计时需归一化)const float firCoeffs[SAMPLE_BUFFER_SIZE] = { 0.05, 0.1, 0.15, 0.2, 0.2, 0.15, 0.1, 0.05, 0.0};// 滤波器输出结果volatile int filteredValue = 0;// 采样数据模拟变量(在实际应用中,由ADC转换获得)volatile int adcValue = 0;// 定时器中断中用作采样周期计数器volatile unsigned int sampleCounter = 0;// -------------------- 函数原型声明 --------------------void SystemInit(void);void Timer0_Init(void);void Delay_ms(unsigned int ms);int ReadADC(void); // 模拟ADC读取函数void FIR_Filter(void); // FIR滤波器处理函数void UpdateSampleBuffer(int sample); // 更新采样缓冲区void OutputFilteredValue(int value); // 输出滤波后的值(如串口或LCD显示)// -------------------- 主函数 --------------------void main(void){ SystemInit(); // 系统初始化 Timer0_Init(); // 定时器0初始化(1ms中断) EA = 1; // 全局中断使能 while(1) { // 主循环中可以执行其它任务 // 此处为节能等待或执行数据处理、显示等 Delay_ms(10); }}// -------------------- 系统初始化 --------------------void SystemInit(void){ // 配置I/O口 // 此处可配置显示或串口输出端口,示例中略}// -------------------- 定时器0初始化 --------------------void Timer0_Init(void){ TMOD &= 0xF0; // 清除定时器0控制位 TMOD |= 0x01; // 定时器0模式1(16位定时器) TH0 = TIMER0_RELOAD; TL0 = TIMER0_RELOAD; ET0 = 1; // 允许定时器0中断 TR0 = 1; // 启动定时器0}// -------------------- 定时器0中断服务函数 --------------------void Timer0_ISR(void) interrupt 1{ // 重装定时器初值,确保1ms中断周期 TH0 = TIMER0_RELOAD; TL0 = TIMER0_RELOAD; // 每1ms中断,累加采样周期计数 sampleCounter++; // 模拟ADC采样,假设每10ms采样一次 if(sampleCounter >= 10) { sampleCounter = 0; adcValue = ReadADC(); // 读取模拟采样值 UpdateSampleBuffer(adcValue); // 更新采样缓冲区 FIR_Filter();  // 进行FIR滤波运算 OutputFilteredValue(filteredValue); // 输出滤波后的数据 }}// -------------------- ReadADC函数 --------------------/* * 模拟读取ADC转换值 * 在实际系统中,此函数应调用ADC接口读取数据 */int ReadADC(void){ // 此处返回模拟数据,可根据需要改为实际ADC读取代码 static int simValue = 0; simValue = (simValue + 1) % 1024; // 模拟0~1023范围内变化 return simValue;}// -------------------- UpdateSampleBuffer函数 --------------------/* * 更新采样缓冲区,将新采样数据插入数组末尾,其它数据整体左移 */void UpdateSampleBuffer(int sample){ int i; // 将数组中旧数据整体左移 for(i = 0; i < SAMPLE_BUFFER_SIZE - 1; i++) { sampleBuffer[i] = sampleBuffer[i + 1]; } // 将最新数据放入数组最后一个位置 sampleBuffer[SAMPLE_BUFFER_SIZE - 1] = sample;}// -------------------- FIR_Filter函数 --------------------/* * 利用FIR滤波器对采样缓冲区内的数据进行滤波运算 * 滤波器输出为加权求和结果,结果存入全局变量filteredValue */void FIR_Filter(void){ int i; float sum = 0; // 对采样缓冲区数据与滤波器系数进行卷积求和 for(i = 0; i < SAMPLE_BUFFER_SIZE; i++) { sum += firCoeffs[i] * sampleBuffer[i]; } // 将滤波结果转换为整数输出 filteredValue = (int)sum;}// -------------------- OutputFilteredValue函数 --------------------/* * 输出滤波后的数据 * 该函数可扩展为通过串口、LCD或LED显示数据 * 本示例中仅为占位函数,调试时可观察全局变量filteredValue */void OutputFilteredValue(int value){ // 例如,可利用调试器或串口输出滤波结果 // 此处暂不做具体实现}// -------------------- Delay_ms函数 --------------------/* * 简单的毫秒级延时函数,基于12MHz系统时钟 */void Delay_ms(unsigned int ms){ unsigned int i, j; for(i = 0; i < ms; i++) for(j = 0; j < 120; j++);}

4.2 代码中关键部分详细注释

  1. 采样数据获取与更新

    • 函数ReadADC模拟了ADC转换过程,在实际系统中可替换为真实的ADC接口调用。

    • 函数UpdateSampleBuffer实现采样数据缓冲区的更新,将最新采样值插入数组末尾,并将旧数据依次左移,为FIR滤波运算提供最新的输入序列。

  2. FIR滤波器运算

    • 数组firCoeffs存储预先设计好的FIR滤波器系数,这里给出一组示例低通滤波器系数(实际设计时可根据需要调整)。

    • FIR_Filter函数对采样缓冲区内的数据进行卷积运算,计算加权求和得到滤波输出,并保存到全局变量filteredValue中。

  3. 定时器中断与采样触发

    • 定时器0中断(每1ms触发)中,利用全局变量sampleCounter控制每10ms采样一次数据,确保系统采样频率和滤波计算的实时性。

    • 在中断服务程序中完成ADC读取、缓冲区更新、滤波运算和数据输出,保证数字滤波模块能按预期周期工作。

  4. 输出与扩展

    • 函数OutputFilteredValue为数据输出接口,可根据实际需求扩展为串口、LCD显示或其它通信方式,实时反馈滤波结果。


5. 代码解读

本文代码主要实现了一个简单的FIR数字滤波器,其关键模块包括:

  • 数据采集与缓冲
    通过模拟ADC采样获得数据,并利用缓冲区保存最近的若干个采样点,为后续的卷积运算提供数据基础。

  • FIR滤波运算
    利用预设的滤波器系数数组,对采样数据进行卷积求和,从而实现低通滤波效果,滤除高频噪声。

  • 定时器中断调度
    采用定时器0中断每1ms触发一次采样与滤波计算,确保系统按照固定采样率运行,同时保证数字滤波的实时性。

该实现方法结构清晰、代码模块化,便于后续扩展为更高阶的滤波器设计或者结合其他数据处理算法。


6. 系统调试与测试

在调试和测试过程中,可参考以下步骤:

  1. ADC采样验证

    • 确认ReadADC函数(或实际ADC接口)能正确采集到传感器数据,数值范围符合预期。

  2. 滤波器系数校验

    • 检查firCoeffs数组中的系数是否正确,并验证滤波器在仿真环境下能达到所需低通特性。

  3. 定时器中断与采样周期

    • 利用示波器或调试器验证定时器中断是否按1ms触发,以及每10ms是否进行一次采样与滤波计算。

  4. 滤波效果测试

    • 对比原始数据与滤波后数据,观察高频噪声是否被有效滤除,输出数据是否平滑稳定。

  5. 系统稳定性测试

    • 长时间运行测试系统,观察滤波结果输出是否连续、准确,并结合实际应用场景调整滤波器参数。


7. 项目总结与心得

项目总结

本项目成功在单片机上实现了数字滤波技术,主要成果如下:

  • 利用FIR滤波器实现了对传感器数据的低通滤波,改善了噪声干扰问题;

  • 采用定时器中断进行定时采样与滤波计算,保证了系统的实时性;

  • 代码结构清晰,模块划分明确,为后续扩展更高阶滤波算法或结合其他数据处理方法打下了坚实基础。

项目心得

  1. 数字滤波器在嵌入式系统中的应用
    通过本项目,深入理解了数字滤波算法在信号处理中的重要作用,尤其在噪声抑制和数据平滑方面有着广泛应用。

  2. FIR滤波器设计与实现
    FIR滤波器实现简单、稳定性好,但需要权衡阶数与单片机资源。合理设计滤波器系数和采样策略,是取得良好滤波效果的关键。

  3. 实时性与模块化设计
    利用定时器中断实现实时采样与数据处理,保证了系统响应速度。代码模块化设计便于调试、维护与后续功能扩展。


8. 参考资料与扩展阅读

  1. 《数字信号处理基础》——详细讲解FIR和IIR滤波器的理论与设计方法。

  2. 《嵌入式系统设计与实践》——涵盖定时器中断、ADC采样与数字滤波技术在嵌入式系统中的应用。

  3. 网络论坛和博客(如CSDN、51单片机论坛)上关于数字滤波器设计与实现的实例,为本项目提供了丰富的实践参考。


结语

本文从理论基础到系统设计,再到完整代码实现与调试测试,详细介绍了如何在单片机上实现数字滤波技术。通过本项目,工程师不仅能掌握FIR滤波器的设计和实现方法,还能了解如何利用定时器中断和数据缓冲机制实现实时信号处理。