【STM32HF429的DSP教程】第40章 STM32F429的FIR带阻滤波器实现(支持逐个数据的实时滤波)
完整版教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547
第40章?????? STM32F429的FIR帶阻濾波器實現(支持逐個數據的實時濾波)
本本章節講解FIR帶阻濾波器實現。
目錄
40.1 初學者重要提示
40.2 帶阻濾波器介紹
40.3 FIR濾波器介紹
40.4 Matlab工具箱filterDesinger生成帶阻濾波器C頭文件
40.5 FIR帶通濾波器設計
40.5.1??????? 函數arm_fir_init_f32
40.5.2 函數arm_fir_f32
40.5.3 filterDesigner獲取帶阻濾波器系數
40.5.4 帶阻濾波器實現
40.6 實驗例程說明(MDK)
40.7 實驗例程說明(IAR)
40.8 總結
40.1 初學者重要提示
?1、 本章節提供的帶阻濾波器支持實時濾波,每次可以濾波一個數據,也可以多個數據,不限制大小。但要注意以下兩點:
- ? 所有數據是在同一個采樣率下依次采集的數據。
- ? 每次過濾數據個數一旦固定下來,運行中不可再修改。
2、? FIR濾波器的群延遲是一個重要的知識點,詳情在本教程第41章有詳細說明。
40.2 帶阻濾波器介紹
減弱一個范圍內的頻率信號通過,讓范圍之外的頻率信號通過。比如混合信號含有50Hz + 200Hz + 400Hz信號,我們可通過帶通濾波器,讓50Hz + 400Hz信號通過,而阻止200Hz信號通過。
40.3 FIR濾波器介紹
ARM官方提供的FIR庫支持Q7,Q15,Q31和浮點四種數據類型。其中Q15和Q31提供了快速算法版本。
FIR濾波器的基本算法是一種乘法-累加(MAC)運行,輸出表達式如下:
y[n] = b[0] * x[n] + b[1] * x[n-1] + b[2] * x[n-2] + ...+ b[numTaps-1] * x[n-numTaps+1]
結構圖如下:
?
這種網絡結構就是在35.2.1小節所講的直接型結構。
40.4 Matlab工具箱filterDesinger生成帶阻濾波器C頭文件
下面我們講解下如何通過filterDesigner工具生成C頭文件,也就是生成濾波器系數。首先在matlab的命窗口輸入filterDesigner就能打開這個工具箱:
filterDesigner界面打開效果如下:
FIR濾波器的低通,高通,帶通,帶阻濾波的設置會在后面逐個講解,這里重點介紹設置后相應參數后如何生成濾波器系數。參數設置好以后點擊如下按鈕:
點擊Design Filter按鈕以后就生成了所需的濾波器系數,生成濾波器系數以后點擊filterDesigner界面上的菜單Targets->Generate C header ,打開后顯示如下界面:
然后點擊Generate,生成如下界面:
再點擊保存,并打開fdatool.h文件,可以看到生成的系數:
/** Filter Coefficients (C Source) generated by the Filter Design and Analysis Tool* Generated by MATLAB(R) 9.4 and Signal Processing Toolbox 8.0.* Generated on: 20-Jul-2021 12:19:30*//** Discrete-Time FIR Filter (real)* -------------------------------* Filter Structure : Direct-Form FIR* Filter Length : 51* Stable : Yes* Linear Phase : Yes (Type 1)*//* General type conversion for MATLAB generated C-code */ #include "tmwtypes.h" /* * Expected path to tmwtypes.h * D:\Program Files\MATLAB\R2018a\extern\include\tmwtypes.h */ /** Warning - Filter coefficients were truncated to fit specified data type. * The resulting response may not match generated theoretical response.* Use the Filter Design & Analysis Tool to design accurate* single-precision filter coefficients.*/ const int BL = 51; const real32_T B[51] = {-0.0009190982091, -0.00271769613,-0.002486952813, 0.003661438357, 0.0136509249,0.01735116541, 0.00766530633,-0.006554719061,-0.007696784101, 0.006105459295,0.01387391612,0.0003508617228, -0.01690892503,-0.008905642666, 0.01744112931,0.02074504457, -0.0122964941, -0.03424086422,-0.001034529647, 0.04779030383,0.02736303769, -0.05937951803, -0.08230702579, 0.06718690693, 0.3100151718,0.4300478697, 0.3100151718, 0.06718690693, -0.08230702579, -0.05937951803,0.02736303769, 0.04779030383,-0.001034529647, -0.03424086422, -0.0122964941,0.02074504457, 0.01744112931,-0.008905642666, -0.01690892503,0.0003508617228,0.01387391612, 0.006105459295,-0.007696784101,-0.006554719061, 0.00766530633,0.01735116541, 0.0136509249, 0.003661438357,-0.002486952813, -0.00271769613,-0.0009190982091 };上面數組B[51]中的數據就是濾波器系數。下面小節講解如何使用filterDesigner配置FIR低通,高通,帶通和帶阻濾波。關于Filter Designer的其它用法,大家可以在matlab命令窗口中輸入help filterDesigner打開幫助文檔進行學習。
?
40.5 FIR帶通濾波器設計
本章使用的FIR濾波器函數是arm_fir_f32。使用此函數可以設計FIR低通,高通,帶通和帶阻
濾波器。
40.5.1??????? 函數arm_fir_init_f32
函數原型:
void arm_fir_init_f32(arm_fir_instance_f32 * S,uint16_t numTaps,const float32_t * pCoeffs,float32_t * pState,uint32_t blockSize);函數描述:
這個函數用于FIR初始化。
函數參數:
- ? 第1個參數是arm_fir_instance_f32類型結構體變量。
- ? 第2個參數是濾波器系數的個數。
- ? 第3個參數是濾波器系數地址。
- ? 第4個參數是緩沖狀態地址。
- ? 第5個參數是每次處理的數據個數,最小可以每次處理1個數據,最大可以每次全部處理完。
注意事項:
結構體arm_fir_instance_f32的定義如下(在文件arm_math.h文件):
typedef struct{uint16_t numTaps; /**< number of filter coefficients in the filter. */ float32_t *pState; /**< points to the state variable array. The array is of length */numTaps+blockSize-1. float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */} arm_fir_instance_f32;1、參數pCoeffs指向濾波因數,濾波因數數組長度為numTaps。但要注意pCoeffs指向的濾波因數應該按照如下的逆序進行排列:
{b[numTaps-1], ?b[numTaps-2], ?b[N-2], ?..., ?b[1], ?b[0]}?
但滿足線性相位特性的FIR濾波器具有奇對稱或者偶對稱的系數,偶對稱時逆序排列還是他本身。
2、pState指向狀態變量數組,這個數組用于函數內部計算數據的緩存。
3、blockSize 這個參數的大小沒有特殊要求,最小可以每次處理1個數據,最大可以每次全部處理完。
40.5.2 函數arm_fir_f32
函數原型:
void arm_fir_f32( const arm_fir_instance_f32 * S, const float32_t * pSrc, float32_t * pDst, uint32_t blockSize)函數描述:
這個函數用于FIR濾波。
函數參數:
- ? 第1個參數是arm_fir_instance_f32類型結構體變量。
- ? 第2個參數是源數據地址。
- ? 第3個參數是濾波后的數據地址。
- ? 第4個參數是每次調用處理的數據個數,最小可以每次處理1個數據,最大可以每次全部處理完。
40.5.3 filterDesigner獲取帶阻濾波器系數
設計一個如下的例子:
信號由50Hz正弦波和200Hz正弦波組成,采樣率1Kbps,現設計一個帶阻濾波器,截止頻率125Hz和300Hz,采樣1024個數據,采用函數fir1進行設計(注意這個函數是基于窗口的方法設計FIR濾波,默認是hamming窗),濾波器階數設置為28。filterDesigner的配置如下:
配置好帶阻濾波器后,具體濾波器系數的生成大家參考本章第4小節的方法即可。
40.5.4 帶阻濾波器實現
通過工具箱filterDesigner獲得低通濾波器系數后在開發板上運行函數arm_fir_f32 來測試帶阻濾波器的效果。
#define TEST_LENGTH_SAMPLES 1024 /* 采樣點數 */ #define BLOCK_SIZE 1 /* 調用一次arm_fir_f32處理的采樣點個數 */ #define NUM_TAPS 29 /* 濾波器系數個數 */uint32_t blockSize = BLOCK_SIZE; uint32_t numBlocks = TEST_LENGTH_SAMPLES/BLOCK_SIZE; /* 需要調用arm_fir_f32的次數 */static float32_t testInput_f32_50Hz_200Hz[TEST_LENGTH_SAMPLES]; /* 采樣點 */ static float32_t testOutput[TEST_LENGTH_SAMPLES]; /* 濾波后的輸出 */ static float32_t firStateF32[BLOCK_SIZE + NUM_TAPS - 1]; /* 狀態緩存,大小numTaps + blockSize - 1*//* 帶阻濾波器系數 通過fadtool獲取*/ const float32_t firCoeffs32BS[NUM_TAPS] = {-0.003560454352f, -0.0002683042258f, 0.001964005642f, -0.001277366537f, 0.008085897192f,0.02002927102f, -0.01026879996f, -0.03190089762f, -0.001673383522f, -0.0334023945f,-0.06278027594f, 0.1240097657f, 0.2419839799f, -0.07700803876f, 0.6521340013f,-0.07700803876f, 0.2419839799f, 0.1240097657f, -0.06278027594f, -0.0334023945f,-0.001673383522f, -0.03190089762f, -0.01026879996f, 0.02002927102f, 0.008085897192f,-0.001277366537f, 0.001964005642f, -0.0002683042258f, -0.003560454352f };/* ********************************************************************************************************* * 函 數 名: arm_fir_f32_lp * 功能說明: 調用函數arm_fir_f32_lp實現低通濾波器 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ static void arm_fir_f32_lp(void) {uint32_t i;arm_fir_instance_f32 S;float32_t *inputF32, *outputF32;/* 初始化輸入輸出緩存指針 */inputF32 = &testInput_f32_50Hz_200Hz[0];outputF32 = &testOutput[0];/* 初始化結構體S */arm_fir_init_f32(&S, NUM_TAPS, (float32_t *)&firCoeffs32BS[0], &firStateF32[0], blockSize);/* 實現FIR濾波,這里每次處理1個點 */for(i=0; i < numBlocks; i++){arm_fir_f32(&S, inputF32 + (i * blockSize), outputF32 + (i * blockSize), blockSize);}/* 打印濾波后結果 */for(i=0; i<TEST_LENGTH_SAMPLES; i++){printf("%f, %f\r\n", testOutput[i], inputF32[i]);}}運行如上函數可以通過串口打印出函數arm_fir_f32濾波后的波形數據,下面通過Matlab繪制波形來對比Matlab計算的結果和ARM官方庫計算的結果。
對比前需要先將串口打印出的一組數據加載到Matlab中, arm_fir_f32的計算結果起名sampledata,加載方法在前面的教程中已經講解過,這里不做贅述了。Matlab中運行的代碼如下:
%**************************************************************************************** % FIR帶阻濾波器設計 %*************************************************************************************** fs=1000; %設置采樣頻率 1K N=1024; %采樣點數 n=0:N-1; t=n/fs; %時間序列 f=n*fs/N; %頻率序列x=sin(2*pi*50*t)+sin(2*pi*200*t); %50Hz和200Hz正弦波混合 b=fir1(28, [125/500 300/500], 'stop'); %獲得濾波器系數,截止頻率125Hz和300,帶阻濾波。 y=filter(b, 1, x); %獲得濾波后的波形 subplot(211); plot(t, y); title('Matlab FIR濾波后的實際波形'); grid on;subplot(212); plot(t, sampledata); %繪制ARM官方庫濾波后的波形。 title('ARM官方庫濾波后的實際波形'); grid on;Matlab運行結果如下:
從上面的波形對比來看,matlab和函數arm_fir_f32計算的結果基本是一致的。為了更好的說明濾波效果,下面從頻域的角度來說明這個問題,Matlab上面運行如下代碼:
%**************************************************************************************** % FIR帶阻濾波器設計 %*************************************************************************************** fs=1000; %設置采樣頻率 1K N=1024; %采樣點數 n=0:N-1; t=n/fs; %時間序列 f=n*fs/N; %頻率序列x=sin(2*pi*50*t)+sin(2*pi*200*t); %50Hz和200Hz正弦波混合 subplot(221); plot(t, x); %繪制信號x的波形 xlabel('時間'); ylabel('幅值'); title('原始信號'); grid on;subplot(222); y=fft(x, N); %對信號x做FFT plot(f,abs(y)); xlabel('頻率/Hz'); ylabel('振幅'); title('原始信號FFT'); grid on;y3=fft(sampledata, N); %經過FIR濾波器后得到的信號做FFT subplot(223); plot(f,abs(y3)); xlabel('頻率/Hz'); ylabel('振幅'); title('濾波后信號FFT'); grid on;b=fir1(28, [125/500 300/500], 'stop'); %獲得濾波器系數,截止頻率125Hz和300Hz,帶阻濾波。 [H,F]=freqz(b,1,160); %通過fir1設計的FIR系統的頻率響應 subplot(224); plot(F/pi,abs(H)); %繪制幅頻響應 xlabel('歸一化頻率'); title(['Order=',int2str(28)]); grid on;Matlab顯示效果如下:
上面波形變換前的FFT和變換后FFT可以看出,200Hz的正弦波基本被濾除。
40.6 實驗例程說明(MDK)
配套例子:
V6-228_FIR帶阻濾波器設計(支持逐個數據的實時濾波)
實驗目的:
實驗內容:
使用AC6注意事項
特別注意附件章節C的問題
上電后串口打印的信息:
波特率 115200,數據位 8,奇偶校驗位無,停止位 1。
RTT方式打印信息:
程序設計:
? 系統棧大小分配:
? 硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器并初始化一些全局變量。只需要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) {/* STM32F429 HAL 庫初始化,此時系統用的還是F429自帶的16MHz,HSI時鐘:- 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。- 設置NVIC優先級分組為4。*/HAL_Init();/* 配置系統時鐘到168MHz- 切換使用HSE。- 此函數會更新全局變量SystemCoreClock,并重新配置HAL_InitTick。*/SystemClock_Config();/* Event Recorder:- 可用于代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。- 默認不開啟,如果要使能此選項,務必看V5開發板用戶手冊第8章*/ #if Enable_EventRecorder == 1 /* 初始化EventRecorder并開啟 */EventRecorderInitialize(EventRecordAll, 1U);EventRecorderStart(); #endifbsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */bsp_InitTimer(); /* 初始化滴答定時器 */bsp_InitUart(); /* 初始化串口 */bsp_InitExtIO(); /* 初始化擴展IO */bsp_InitLed(); /* 初始化LED */ }? 主功能:
主程序實現如下操作:
- ? 啟動一個自動重裝軟件定時器,每100ms翻轉一次LED2。
- ? 按下按鍵K1,打印原始波形數據和濾波后的波形數據。
40.7 實驗例程說明(IAR)
配套例子:
V6-228_FIR帶阻濾波器設計(支持逐個數據的實時濾波)
實驗目的:
實驗內容:
上電后串口打印的信息:
波特率 115200,數據位 8,奇偶校驗位無,停止位 1。
RTT方式打印信息:
程序設計:
? 系統棧大小分配:
? 硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器并初始化一些全局變量。只需要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) {/* STM32F429 HAL 庫初始化,此時系統用的還是F429自帶的16MHz,HSI時鐘:- 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。- 設置NVIC優先級分組為4。*/HAL_Init();/* 配置系統時鐘到168MHz- 切換使用HSE。- 此函數會更新全局變量SystemCoreClock,并重新配置HAL_InitTick。*/SystemClock_Config();/* Event Recorder:- 可用于代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。- 默認不開啟,如果要使能此選項,務必看V5開發板用戶手冊第8章*/ #if Enable_EventRecorder == 1 /* 初始化EventRecorder并開啟 */EventRecorderInitialize(EventRecordAll, 1U);EventRecorderStart(); #endifbsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */bsp_InitTimer(); /* 初始化滴答定時器 */bsp_InitUart(); /* 初始化串口 */bsp_InitExtIO(); /* 初始化擴展IO */bsp_InitLed(); /* 初始化LED */ }? 主功能:
主程序實現如下操作:
- ? 啟動一個自動重裝軟件定時器,每100ms翻轉一次LED2。
- ? 按下按鍵K1,打印原始波形數據和濾波后的波形數據。
40.8 總結
本章節主要講解了FIR濾波器的帶阻實現,同時一定要注意線性相位FIR濾波器的群延遲問題,詳見本教程的第41章。
總結
以上是生活随笔為你收集整理的【STM32HF429的DSP教程】第40章 STM32F429的FIR带阻滤波器实现(支持逐个数据的实时滤波)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【硬见小百科】PCB多层板各层含义与设计
- 下一篇: Altium Designer——PCB