【STM32 系列】利用MATLAB配合ARM-DSP庫設計IIR數字濾波器(保姆級教程)
引言
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_LENGTH2,每個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.rzrgm.cn/Skyrim-sssuuu/p/18774936

浙公網安備 33010602011771號
https://orcid.org/0000-0001-5102-772X