【STM32系列】利用MATLAB配合ARM-DSP库设计IIR数字滤波器(保姆级教程)

devtools/2025/2/8 7:50:41/

ps.源码放在最后面

设计FIR数字滤波器可以看这里:利用MATLAB配合ARM-DSP库设计FIR数字滤波器(保姆级教程)


设计IIR滤波器

MATLAB配置

设计步骤

首先在命令行窗口输入"filterDesigner",接着就会跳出以下界面,并跟着以下步骤设置:

滤波器幅频响应图像

按下设计滤波器后,接着就可得到以下FIR滤波器及其幅频响应图像:

导出滤波器系数

根据以下步骤,导出MATLAB滤波器的系数(千万不要用"目标"->"生成C头文件"来导出,主要是为了后面操作方便):

最后得到的浮窗是这样的,选择一个合适的位置导出即可。

导出后的系数转换

导出后大概率会出现这样一个MATLAB窗口(可知导出后的文件也可在MATLAB中打开,在打开这类文件的时候,记得选择文件类别为“全部文件”),IIR没FIR那么简单,多了一些步骤:

系数转换

也是像FIR滤波器一样,把这些数据复制给AI,让AI给你整合好就行,这里图片就不放出来了。

STM32部分

DSP库添加

详细请看硬汉哥的这篇文章,讲的十分清晰:ARM DSP源码和库移植方法(MDK5的AC5和AC6)

IIR代码部分

变量参数定义

以下就是需要的变量参数定义,值得注意的是,图中圈起来的两个部分,在IIR滤波器发生变化的时候,即参数改变的时候需要修改的参数(修改部分一看注释中的公式修改;修改部分二看数组中有多少行,根据行数修改;修改部分三根据数组参数变化修改):

主要代码

(打印处i初始等于9是为了显示较好的波形,滤波后的波形在前一段还未完全稳定)
也是千篇一律,主要是对导出的IIR滤波器的系数进行处理,还有修改在定义部分的一些变量值,代码在这里就不多说了。

程序现象

使用串口打印到VOFA+这个软件上

信号频率:4500Hz
采样频率:48000Hz
通带频率:4000Hz
阻带频率:5000Hz
红色:原始信号波形
绿色:滤波后信号波形

 源码

变量定义部分

/*********************** IIR ***********************/
/** 采样频率:48kHz 通带频率:4kHz 阻带频率:5kHz **/
#define IIR_LENGTH  256							/* 采样点数,即要处理的采样数据的总数 */
#define IIR_BLOCK_SIZE 1						/* 调用一次 arm_biquad_cascade_df1_f32 函数处理的采样点个数 */
#define IIR_NUMTAPS_LENGTH  7				/* 2 阶 IIR 滤波的个数【滤波器阶数 = IIR_NUMTAPS_LENGTH * 2,每个 2 阶滤波器有 5 个系数】 */static uint32_t iir_blockSize = IIR_BLOCK_SIZE;								/* 每次处理的数据个数,与 IIR_BLOCK_SIZE 相同 */
static uint32_t iir_numBlocks = IIR_LENGTH / IIR_BLOCK_SIZE;	/* 需要调用 arm_biquad_cascade_df1_f32 函数的次数,通过总采样点数除以每次处理的数据个数得到 */
static float32_t IIR_InputBufer[IIR_LENGTH];									/* 采样点缓存区,用于存储原始的采样数据 */
static float32_t IIR_OutputBufer[IIR_LENGTH];									/* 滤波后的输出缓存区,滤波后输出的数据个数与采样点个数相同 */
static float32_t iir_pState[13 * IIR_NUMTAPS_LENGTH];					/* 状态缓存,大小为 35 * IIR_NUMTAPS_LENGTH,直接 I 型滤波器需要 2N 个延迟器和 2N 个乘法器 */
const float32_t iir_Coeffs32LP[5 * IIR_NUMTAPS_LENGTH] = {		/* IIR_NUMTAPS_LENGTH组二阶滤波系数,每组代表一个 Section二阶滤波器,有5个系数 */1.0f, -1.581251265113648107885069293843116611242f, 1.0f, 1.64464506366406792992052032786887139082f,  -0.931601507332356026935826776025351136923f,1.0f, -1.534184978348168693074171642365399748087f, 1.0f, 1.510438012818041242368849452759604901075f, -0.798208657466401461100247161084553226829f,1.0f, -1.418424762240857006645455840043723583221f, 1.0f, 1.348418230754467206367053222493268549442f, -0.655285433213672163788032776210457086563f,1.0f, -1.169460742425922239462465768156107515097f, 1.0f, 1.151129772069469092699023349268827587366f, -0.493393743131744000329774735291721299291f,1.0f, -0.608329709345672098308455133519601076841f, 1.0f, 0.930270435976758625074012343247886747122f, -0.319126063671286819278805069188820198178f,1.0f, 0.671963013119381002979935146868228912354f,  1.0f, 0.736945396854642664763446191500406712294f, -0.169496181427103626004893044409982394427f,1.0f, 1.0f, 																			 0.0f, 0.328290240600095484246878640988143160939f, -0.0f
};/*【滤波器的核心选择都在这些系数里面,这些系数的生成使用MATLB进行配置】*/
arm_biquad_casd_df1_inst_f32 iir_S;				/* 定义一个结构体变量,用于 IIR 滤波器的初始化 */
float32_t iir_ScaleValue;									/* 定义一个变量,用于存放放缩系数 */
float32_t *iir_inputF32, *iir_outputF32;	/* 定义两个指针变量,分别用于存放滤波器输入和输出缓存的地址 */

主要程序部分

iir_inputF32 = &IIR_InputBufer[0];		/* 初始化输入缓存指针 */
iir_outputF32 = &IIR_OutputBufer[0];	/* 初始化输出缓存指针 */for (uint16_t i = 0; i < IIR_LENGTH; i++) {IIR_InputBufer[i] = (float)ADC_DMA_ConvertedValue[i] * 3.3 / 65536.0;
}memset(iir_pState, 0, sizeof(iir_pState)); /* 初始化前清零状态 */
//滤波器结构体初始化
arm_biquad_cascade_df1_init_f32(&iir_S, 						/* 初始化结构体S【S就相当于滤波器配置参数,对S结构体的各个成员变量完成初始化】*/IIR_NUMTAPS_LENGTH, 									/* 初始化2阶IIR滤波的个数【滤波器阶数=IIR_NUMTAPS_LENGTH*2,每个IIR_NUMTAPS_LENGTH有5个系数】*/(float32_t *)&iir_Coeffs32LP[0], 			/* 初始化S的滤波器系数地址【滤波器的核心选择都在这些系数里面】*/(float32_t *)&iir_pState[0]);					/* 初始化S的计算缓存空间 */
for(uint16_t i = 0; i < iir_numBlocks; i++){arm_biquad_cascade_df1_f32(&iir_S, 									/* 使用滤波器iir_S */iir_inputF32 + (i * iir_blockSize), 	/* 滤波器原始输入数据地址 */iir_outputF32 + (i * iir_blockSize), /* 滤波器滤波后输出数据地址 */iir_blockSize);											/* 每次处理数据点的个数 */
}
//计算放缩系数
iir_ScaleValue = 0.685271189526501567357286148762796074152 *0.617778799034633618880718586297007277608 *0.527648329116606640276643247489118948579 *0.412098486544746456239352028205757960677 *0.279416489886909980011608922723098658025 *0.161885019533814594749898674308496993035 *0.33585487969995225787656067950592841953; 
Set_Current_USART(USART1_IDX);/* 使用串口1 */
for(uint16_t i = 9; i<IIR_LENGTH; i++){//输出原始数据和滤波之后的数据【注意这里需要乘上放缩系数】printf("%d: %f,%f\r\n",i,IIR_InputBufer[i],IIR_OutputBufer[i]*iir_ScaleValue);
}

http://www.ppmy.cn/devtools/157046.html

相关文章

【论文投稿】Python 网络爬虫:探秘网页数据抓取的奇妙世界

目录 前言 一、Python—— 网络爬虫的绝佳拍档 二、网络爬虫基础&#xff1a;揭开神秘面纱 &#xff08;一&#xff09;工作原理&#xff1a;步步为营的数据狩猎 &#xff08;二&#xff09;分类&#xff1a;各显神通的爬虫家族 三、Python 网络爬虫核心库深度剖析 &…

Spring 中的设计模式

控制反转(IoC)和依赖注入(DI) IoC(Inversion of Control,控制反转) 是 Spring 中一个非常非常重要的概念&#xff0c;它不是什么技术&#xff0c;而是一种解耦的设计思想。IoC 的主要目的是借助于“第三方”(Spring 中的 IoC 容器) 实现具有依赖关系的对象之间的解耦(IOC 容器…

Maven 构建命令详解

1. Maven 构建命令概述 Maven 是 Java 生态中最流行的构建工具之一&#xff0c;它提供了一套标准化的构建命令&#xff0c;使得开发者可以轻松管理项目的编译、测试、打包、安装和部署等任务。 本篇文章将深入解析 Maven 中最常用的构建命令&#xff0c;包括&#xff1a; mv…

基于SpringBoot养老院平台系统功能实现五

一、前言介绍&#xff1a; 1.1 项目摘要 随着全球人口老龄化的不断加剧&#xff0c;养老服务需求日益增长。特别是在中国&#xff0c;随着经济的快速发展和人民生活水平的提高&#xff0c;老年人口数量不断增加&#xff0c;对养老服务的质量和效率提出了更高的要求。传统的养…

VIVADO生成DCP和EDF指南

VIVADO生成DCP和EDF 文章目录 VIVADO生成DCP和EDF前言一、DCP封装二、EDF封装 前言 详细步骤就不贴图了&#xff0c;网上一大堆 在Vivado中&#xff0c;常用的三种封装形式有三种&#xff1a; ● IP ● edif ● dcp 在下文之前&#xff0c;先看几个概念 out_of_context&…

基于深度学习的医疗器械分类编码映射系统:设计、实现与优化

一、引言 1.1 研究内容与方法 本研究旨在设计并实现一个基于深度学习的医疗器械分类编码映射系统,以解决医疗器械分类编码管理中存在的问题,提高医疗器械管理的效率和准确性。具体研究内容包括以下几个方面: 系统需求分析:深入研究国内外主流医疗器械编码体系,如中国的 …

stm32小白成长为高手的学习步骤和方法

我们假定大家已经对STM32的书籍或者文档有一定的理解。如不理解&#xff0c;请立即阅读STM32的文档&#xff0c;以获取最基本的知识点。STM32单片机自学教程 这篇博文也是一篇不错的入门教程&#xff0c;初学者可以看看&#xff0c;讲的真心不错。 英文好的同学&#xf…

互联网分布式ID解决方案

业界实现方案 1. 基于UUID 2. 基于DB数据库多种模式(自增主键、segment) 3. 基于Redis 4. 基于ZK、ETCD 5. 基于SnowFlake 6. 美团Leaf(DB-Segment、zkSnowFlake) 7. 百度uid-generator() 基于UUID生成唯一ID UUID生成策略 推荐阅读 DDD领域驱动与微服务架构设计设计模…