【STM32H7的DSP教程】第49章 STM32H7的自適應濾波器實現,無需Matlab生成係數(支援實時濾波)
完整版教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547
第49章 STM32H7的自適應濾波器實現,無需Matlab生成係數(支援實時濾波)
本章節講解LMS最小均方自適應濾波器實現,無需Matlab生成係數,可以自學習。
49.1 初學者重要提示
49.2 自適應濾波器介紹
49.3 LMS最小均方自適應濾波器介紹
49.4 Matlab自適應濾波器實現
49.5 自適應濾波器設計
49.6 實驗例程說明(MDK)
49.7 實驗例程說明(IAR)
49.8 總結
49.1 初學者重要提示
- ARM DSP庫提供了LMS最小均方自適應濾波和歸一化最小均方自適應濾波器,推薦使用歸一化方式,因為歸一化方法的步長更容易設定。
- 自適應濾波器的濾波因數步長設定比較考究,詳見本章教程第5.3小結。
49.2 自適應濾波器介紹
自適應濾波器能夠根據輸入訊號自動調整濾波係數進行數字濾波。作為對比,非自適應濾波器有靜態的濾波器係數,這些靜態係數一起組成傳遞函式。
對於一些應用來說,由於事先並不知道需要進行操作的引數,例如一些噪聲訊號的特性,所以要求使用自適應的係數進行處理。在這種情況下,通常使用自適應濾波器,自適應濾波器使用反饋來調整濾波器係數以及頻率響應。
隨著處理器效能的增強,自適應濾波器的應用越來越常見,時至今日它們已經廣泛地用於手機以及其它通訊裝置、數碼錄影機和數碼照相機以及醫療監測裝置中。
49.3 LMS最小均方介紹
LMS 最小均方自適應濾波器能夠"學習"未知的傳輸特性。LMS濾波器使用梯度下降方法,根據瞬時錯誤訊號更新濾波係數。自適應濾波器常用於通訊系統、均衡器和降噪。
LMS 濾波器由以下兩個部分組成。第一部分是 FIR 濾波器。第二部分是係數更新機制。LMS 濾波器具有兩個輸入訊號。x[n] 是FIR 濾波器輸入,而參考輸入d[n]對應 FIR 濾波器的預期輸出。更新 FIR濾波器係數,以便 FIR濾波器的輸出與參考輸入匹配。濾波器係數更新機制基於 FIR 濾波器輸出和參考輸入之間的差異。當濾波器調整時,"錯誤訊號"e[n]傾向於為零。LMS 處理功能接受輸入和參考輸入訊號,並生成濾波器輸出和錯誤訊號。
輸出訊號 y[n] 由標準 FIR 濾波器計算:
y[n] = b[0] * x[n] + b[1] * x[n-1] + b[2] * x[n-2] + ...+ b[numTaps-1] * x[n-numTaps+1]
誤差訊號等於參考訊號 d[n] 和濾波器輸出之間的差值:
e[n] = d[n] - y[n]。
在計算每個樣本的誤差訊號後,計算濾波器狀態變數的瞬時能量:
E = x[n]^2 + x[n-1]^2 + ... + x[n-numTaps+1]^2。
然後在逐個樣本的基礎上更新濾波器係數 b[k]:
b[k] = b[k] + e[n] * (mu/E) * x[n-k],對於 k=0, 1, ..., numTaps-1
其中 mu 是步長,並且控制係數收斂速度。在函式arm_lms_norm_init_f32中,pCoeffs 指向大小為 numTaps 的濾波器係數陣列。係數按時間倒序儲存:
{b[numTaps-1], b[numTaps-2], b[N-2], ..., b[1], b[0]}
pState 指向一個大小為 numTaps + blockSize - 1 的狀態陣列。 狀態緩衝區中的樣本按順序儲存:
{x[n-numTaps+1], x[n-numTaps], x[n-numTaps-1], x[n-numTaps-2]....x[0], x[1], .. ., x[blockSize-1]}
注意,狀態緩衝區的長度超過了濾波器係數陣列的長度 blockSize-1 個樣本。
49.4 Matlab自適應濾波器實現
首先建立兩個混合訊號,便於更好測試濾波器效果。
混合訊號Mix_Signal_1 = 訊號Signal_Original_1+白噪聲。
混合訊號Mix_Signal_2 = 訊號Signal_Original_2+白噪聲。
Fs = 1000; %取樣率 N = 1000; %取樣點數 n = 0:N-1; t = 0:1/Fs:1-1/Fs; %時間序列 Signal_Original_1 =sin(2*pi*10*t)+sin(2*pi*20*t)+sin(2*pi*30*t); Noise_White_1 = [0.3*randn(1,500), rand(1,500)]; %前500點高斯分部白噪聲,後500點均勻分佈白噪聲 Mix_Signal_1 = Signal_Original_1 + Noise_White_1; %構造的混合訊號 Signal_Original_2 = [zeros(1,100), 20*ones(1,20), -2*ones(1,30), 5*ones(1,80), -5*ones(1,30), 9*ones(1,140), -4*ones(1,40), 3*ones(1,220), 12*ones(1,100), 5*ones(1,20), 25*ones(1,30), 7 *ones(1,190)]; Noise_White_2 = 0.5*randn(1,1000); %高斯白噪聲 Mix_Signal_2 = Signal_Original_2 + Noise_White_2; %構造的混合訊號
濾波程式碼實現如下:
%**************************************************************************************** % % 訊號Mix_Signal_1 和 Mix_Signal_2 分別作自適應濾波 % %*************************************************************************************** %混合訊號 Mix_Signal_1 自適應濾波 N=1000; %輸入訊號抽樣點數N k=100; %時域抽頭LMS演算法濾波器階數 u=0.001; %步長因子 %設定初值 yn_1=zeros(1,N); %output signal yn_1(1:k)=Mix_Signal_1(1:k); %將輸入訊號SignalAddNoise的前k個值作為輸出yn_1的前k個值 w=zeros(1,k); %設定抽頭加權初值 e=zeros(1,N); %誤差訊號 %用LMS演算法迭代濾波 for i=(k+1):N XN=Mix_Signal_1((i-k+1):(i)); yn_1(i)=w*XN'; e(i)=Signal_Original_1(i)-yn_1(i); w=w+2*u*e(i)*XN; end subplot(4,1,1); plot(Mix_Signal_1); %Mix_Signal_1 原始訊號 axis([k+1,1000,-4,4]); title('原始訊號'); subplot(4,1,2); plot(yn_1); %Mix_Signal_1 自適應濾波後訊號 axis([k+1,1000,-4,4]); title('自適應濾波後訊號'); %混合訊號 Mix_Signal_2 自適應濾波 N=1000; %輸入訊號抽樣點數N k=500; %時域抽頭LMS演算法濾波器階數 u=0.000011; %步長因子 %設定初值 yn_1=zeros(1,N); %output signal yn_1(1:k)=Mix_Signal_2(1:k); %將輸入訊號SignalAddNoise的前k個值作為輸出yn_1的前k個值 w=zeros(1,k); %設定抽頭加權初值 e=zeros(1,N); %誤差訊號 %用LMS演算法迭代濾波 for i=(k+1):N XN=Mix_Signal_2((i-k+1):(i)); yn_1(i)=w*XN'; e(i)=Signal_Original_2(i)-yn_1(i); w=w+2*u*e(i)*XN; end subplot(4,1,3); plot(Mix_Signal_2); %Mix_Signal_1 原始訊號 axis([k+1,1000,-10,30]); title('原始訊號'); subplot(4,1,4); plot(yn_1); %Mix_Signal_1 自適應濾波後訊號 axis([k+1,1000,-10,30]); title('自適應濾波後訊號');
Matlab執行效果:
49.5 自適應器設計
自適應濾波器的主要通過下面兩個函式實現,支援逐點實時濾波。
49.5.1 函式arm_lms_norm_init_f32
函式原型:
void arm_lms_norm_init_f32( arm_lms_norm_instance_f32 * S, uint16_t numTaps, float32_t * pCoeffs, float32_t * pState, float32_t mu, uint32_t blockSize);
函式描述:
此函式用於自適應濾波器初始化。
函式引數:
- 第1個引數是arm_lms_norm_instance_f32型別結構體變數。
- 第2個引數是濾波因數個數。
- 第3個引數是濾波因數地址。
- 第4個引數指向狀態變數陣列,這個陣列用於函式內部計算資料的快取。
- 第5個引數用於設定濾波因數更新的步長。
- 第6個引數是每次處理的資料個數,最小可以每次處理1個數據,最大可以每次全部處理完。
49.5.2 函式arm_lms_norm_f32
函式原型:
void arm_lms_norm_f32( arm_lms_norm_instance_f32 * S, const float32_t * pSrc, float32_t * pRef, float32_t * pOut, float32_t * pErr, uint32_t blockSize);
函式描述:
此函式用於自適應濾波器實時濾波。
函式引數:
- 第1個引數是arm_lms_norm_instance_f32型別結構體變數。
- 第2個引數是源資料地址。
- 第3個引數是參考資料地址,需要使用者提供想要逼近的波形效果。
- 第4個引數是輸出資料地址。
- 第5個引數是誤差資料地址。
- 第6個引數是每次處理的資料個數,最小可以每次處理1個數據,最大可以每次全部處理完。
注意事項:
結構體arm_lms_norm_instance_f32的定義如下(在檔案arm_math.h檔案):
typedef struct { uint16_t numTaps; /**< number of 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. */ float32_t mu; /**< step size that control filter coefficient updates. */ float32_t energy; /**< saves previous frame energy. */ float32_t x0; /**< saves previous input sample. */ } arm_lms_norm_instance_f32;
1、引數numTaps用於設定濾波因數個數。
2、pState指向狀態變數陣列,這個陣列用於函式內部計算資料的快取。
3、引數pCoeffs指向濾波因數,濾波因數陣列長度為numTaps。但要注意pCoeffs指向的濾波因數應該按照如下的逆序進行排列:
{b[numTaps-1], b[numTaps-2], b[N-2], ..., b[1], b[0]}
但滿足線性相位特性的FIR濾波器具有奇對稱或者偶對稱的係數,偶對稱時逆序排列還是他本身。
4、引數mu用於設定濾波因數更新的步長。
5、blockSize 這個引數的大小沒有特殊要求,最小可以每次處理1個數據,最大可以每次全部處理完。
49.5.3 濾除200Hz正弦波測試(含不同步長測試,重要)
原始波形200Hz + 50Hz正弦波,濾除200Hz正弦波測試:
/* ********************************************************************************************************* * 函 數 名: arm_lms_f32_test1 * 功能說明: 原始波形200Hz + 50Hz正弦波,濾除200Hz正弦波。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void arm_lms_f32_test1(void) { uint32_t i; float32_t *inputF32, *outputF32, *inputREF, *outputERR; arm_lms_norm_instance_f32 lmsS={0}; for(i=0; i<TEST_LENGTH_SAMPLES; i++) { /* 50Hz正弦波+200Hz正弦波,取樣率1KHz */ testInput_f32_50Hz_200Hz[i] = arm_sin_f32(2*3.1415926f*50*i/1000) + arm_sin_f32(2*3.1415926f*200*i/1000); testInput_f32_REF[i] = arm_sin_f32(2*3.1415926f*50*i/1000); } /* 如果是實時性的濾波,僅需清零一次 */ memset(lmsCoeffs32,0,sizeof(lmsCoeffs32)); memset(lmsStateF32,0,sizeof(lmsStateF32)); /* 初始化輸入輸出快取指標 */ inputF32 = (float32_t *)&testInput_f32_50Hz_200Hz[0]; /* 原始波形 */ outputF32 = (float32_t *)&testOutput[0]; /* 濾波後輸出波形 */ inputREF = (float32_t *)&testInput_f32_REF[0]; /* 參考波形 */ outputERR = (float32_t *)&test_f32_ERR[0]; /* 誤差資料 */ /* 歸一化LMS初始化 */ arm_lms_norm_init_f32 (&lmsS, /* LMS結構體 */ NUM_TAPS, /* 濾波器係數個數 */ (float32_t *)&lmsCoeffs32[0], /* 濾波 */ &lmsStateF32[0], /* 濾波器係數 */ 0.1, /* 步長 */ blockSize); /* 處理的資料個數 */ /* 實現LMS自適應濾波,這裡每次處理1個點 */ for(i=0; i < numBlocks; i++) { arm_lms_norm_f32(&lmsS, /* LMS結構體 */ inputF32 + (i * blockSize), /* 輸入資料 */ inputREF + (i * blockSize), /* 輸出資料 */ outputF32 + (i * blockSize), /* 參考資料 */ outputERR + (i * blockSize), /* 誤差資料 */ blockSize); /* 處理的資料個數 */ } /* 列印濾波後結果 */ for(i=0; i<TEST_LENGTH_SAMPLES; i++) { printf("%f, %f, %f\r\n", testInput_f32_50Hz_200Hz[i], outputF32[i], test_f32_ERR[i]); } }
下面是濾波因數步長調節為0.1時的濾波器效果,淺藍色是原始波形,黃色黃色是濾波後波形,綠色是誤差值:
步長為0.01時效果,可以看到逼近參考波形的速度較慢:
步長為1的效果,逼近參考波形的速度更快,前面的波形由一段陡增。
關於步長,沒有特別好的方式直接鎖定那種步長大小更合適,一般的處理思路是按照10倍關係先鎖定範圍,比如先測試步長為1,0.1,0.001等來測試,然後進一步設定一個合適的值。
49.5.4 濾除白噪聲測試(一)
原始波形由任意波形+ 高斯分佈白噪聲 + 均勻分佈白噪聲組成,濾除高斯分佈白噪聲 + 均勻分佈白噪聲。
/* ********************************************************************************************************* * 函 數 名: arm_lms_f32_test2 * 功能說明: 原始波形由任意波形+ 高斯分佈白噪聲 + 均勻分佈白噪聲組成,濾除高斯分佈白噪聲 + 均勻分佈白噪聲。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void arm_lms_f32_test2(void) { uint32_t i; arm_lms_norm_instance_f32 lmsS; float32_t *inputF32, *outputF32, *inputREF, *outputERR; /* 如果是實時性的濾波,僅需清零一次 */ memset(lmsCoeffs32,0,sizeof(lmsCoeffs32)); memset(lmsStateF32,0,sizeof(lmsStateF32)); /* 初始化輸入輸出快取指標 */ inputF32 = (float32_t *)&MixData[0]; /* 原始波形 */ outputF32 = (float32_t *)&testOutput[0]; /* 濾波後輸出波形 */ inputREF = (float32_t *)&OrigalData[0]; /* 參考波形 */ outputERR = (float32_t *)&test_f32_ERR[0]; /* 誤差資料 */ /* 歸一化LMS初始化 */ arm_lms_norm_init_f32 (&lmsS, /* LMS結構體 */ NUM_TAPS, /* 濾波器係數個數 */ (float32_t *)&lmsCoeffs32[0], /* 濾波 */ &lmsStateF32[0], /* 濾波器係數 */ 0.01, /* 步長 */ blockSize); /* 處理的資料個數 */ /* 實現LMS自適應濾波,這裡每次處理1個點 */ for(i=0; i < numBlocks; i++) { arm_lms_norm_f32(&lmsS, /* LMS結構體 */ inputF32 + (i * blockSize), /* 輸入資料 */ inputREF + (i * blockSize), /* 輸出資料 */ outputF32 + (i * blockSize), /* 參考資料 */ outputERR + (i * blockSize), /* 誤差資料 */ blockSize); /* 處理的資料個數 */ } /* 列印濾波後結果 */ for(i=0; i<TEST_LENGTH_SAMPLES; i++) { printf("%f, %f, %f\r\n", MixData[i], outputF32[i], test_f32_ERR[i]); } }
下面是濾波因數步長調節為0.01時的濾波器效果,淺藍色是原始波形,黃色黃色是濾波後波形,綠色是誤差值:
這個波形做了兩個週期,前1024點和後1024點,後面1024點濾除白噪聲的效果已經比較好,而前1024的點的前半段一直在逼近我們設定的參考波形中。另外從誤差值波形中,我們可以看到原始波形跳變的地方,誤差值也會有一個跳變,然後向0趨近。這是自適應濾波器特性決定的,不斷的調節濾波器係數中。
我們再來看下將濾波因數步長調節為0.1時的效果:
可以看到逼近速度很快,但是逼近效果一般,也就是白噪聲的濾除效果一般。
49.5.5 濾除白噪聲測試(二)
原始波形10Hz正弦波 + 20Hz正弦波 + 30Hz正弦波 + 高斯分佈白噪聲 + 均勻分佈白噪聲。濾除高斯分佈白噪聲 + 均勻分佈白噪聲。
/* ********************************************************************************************************* * 函 數 名: arm_lms_f32_test3 * 功能說明: 10Hz正弦波 + 20Hz正弦波 + 30Hz正弦波 + 高斯分佈白噪聲 + 均勻分佈白噪聲,濾除高斯分佈白噪聲 + 均勻分佈白噪聲 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void arm_lms_f32_test3(void) { uint32_t i; arm_lms_norm_instance_f32 lmsS; float32_t *inputF32, *outputF32, *inputREF, *outputERR; /* 如果是實時性的濾波,僅需清零一次 */ memset(lmsCoeffs32,0,sizeof(lmsCoeffs32)); memset(lmsStateF32,0,sizeof(lmsStateF32)); /* 初始化輸入輸出快取指標 */ inputF32 = (float32_t *)&MixData1[0]; /* 原始波形 */ outputF32 = (float32_t *)&testOutput[0]; /* 濾波後輸出波形 */ inputREF = (float32_t *)&OrigalData1[0]; /* 參考波形 */ outputERR = (float32_t *)&test_f32_ERR[0]; /* 誤差資料 */ /* 歸一化LMS初始化 */ arm_lms_norm_init_f32 (&lmsS, /* LMS結構體 */ NUM_TAPS, /* 濾波器係數個數 */ (float32_t *)&lmsCoeffs32[0], /* 濾波 */ &lmsStateF32[0], /* 濾波器係數 */ 0.1, /* 步長 */ blockSize); /* 處理的資料個數 */ /* 實現LMS自適應濾波,這裡每次處理1個點 */ for(i=0; i < numBlocks; i++) { arm_lms_norm_f32(&lmsS, /* LMS結構體 */ inputF32 + (i * blockSize), /* 輸入資料 */ inputREF + (i * blockSize), /* 輸出資料 */ outputF32 + (i * blockSize), /* 參考資料 */ outputERR + (i * blockSize), /* 誤差資料 */ blockSize); /* 處理的資料個數 */ } /* 列印濾波後結果 */ for(i=0; i<TEST_LENGTH_SAMPLES; i++) { printf("%f, %f, %f\r\n", MixData1[i], outputF32[i], test_f32_ERR[i]); } }
下面是濾波因數步長調節為0.1時的濾波器效果,淺藍色是原始波形,黃色黃色是濾波後波形,綠色是誤差值:
49.6 實驗例程說明(MDK)
配套例子:
V7-234_自適應濾波器實現,無需Matlab生成係數(支援實時濾波)
實驗目的:
- 學習LMS最小均方濾波器。
實驗內容:
- 啟動一個自動重灌軟體定時器,每100ms翻轉一次LED2。
- 按下按鍵K1,列印測試波形1和濾波後的波形資料。
- 按下按鍵K2,列印測試波形2和濾波後的波形資料。
- 按下按鍵K3,列印測試波形3和濾波後的波形資料。
使用AC6注意事項
特別注意附件章節C的問題
上電後串列埠列印的資訊:
波特率 115200,資料位 8,奇偶校驗位無,停止位 1。
RTT方式列印資訊:
程式設計:
系統棧大小分配:
RAM空間用的DTCM:
硬體外設初始化
硬體外設的初始化是在 bsp.c 檔案實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬體裝置。該函式配置CPU暫存器和外設的暫存器並初始化一些全域性變數。只需要呼叫一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* 配置MPU */ MPU_Config(); /* 使能L1 Cache */ CPU_CACHE_Enable(); /* STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鐘: - 呼叫函式HAL_InitTick,初始化滴答時鐘中斷1ms。 - 設定NVIC優先順序分組為4。 */ HAL_Init(); /* 配置系統時鐘到400MHz - 切換使用HSE。 - 此函式會更新全域性變數SystemCoreClock,並重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用於程式碼執行時間測量,MDK5.25及其以上版本才支援,IAR不支援。 - 預設不開啟,如果要使能此選項,務必看V7開發板使用者手冊第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder並開啟 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */ bsp_InitTimer(); /* 初始化滴答定時器 */ bsp_InitUart(); /* 初始化串列埠 */ bsp_InitExtIO(); /* 初始化FMC匯流排74HC574擴充套件IO. 必須在 bsp_InitLed()前執行 */ bsp_InitLed(); /* 初始化LED */ }
MPU配置和Cache配置:
資料Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM),FMC的擴充套件IO區。
/* ********************************************************************************************************* * 函 數 名: MPU_Config * 功能說明: 配置MPU * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void MPU_Config( void ) { MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable(); /* 配置AXI SRAM的MPU屬性為關閉讀Cache和寫Cache */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT _BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT _CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 配置FMC擴充套件IO的MPU屬性為Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /*使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); } /* ********************************************************************************************************* * 函 數 名: CPU_CACHE_Enable * 功能說明: 使能L1 Cache * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void CPU_CACHE_Enable(void) { /* 使能 I-Cache */ SCB_EnableICache(); /* 使能 D-Cache */ SCB_EnableDCache(); }
主功能:
主程式實現如下操作:
- 啟動一個自動重灌軟體定時器,每100ms翻轉一次LED2。
- 按下按鍵K1,列印測試波形1和濾波後的波形資料。
- 按下按鍵K2,列印測試波形2和濾波後的波形資料。
- 按下按鍵K3,列印測試波形3和濾波後的波形資料。
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: c程式入口 * 形 參: 無 * 返 回 值: 錯誤程式碼(無需處理) ********************************************************************************************************* */ int main(void) { uint8_t ucKeyCode; /* 按鍵程式碼 */ uint16_t i; bsp_Init(); /* 硬體初始化 */ PrintfLogo(); /* 列印例程資訊到串列埠1 */ PrintfHelp(); /* 列印操作提示資訊 */ bsp_StartAutoTimer(0, 100); /* 啟動1個100ms的自動重灌的定時器 */ /* 進入主程式迴圈體 */ while (1) { bsp_Idle(); /* 這個函式在bsp.c檔案。使用者可以修改這個函式實現CPU休眠和喂狗 */ if (bsp_CheckTimer(0)) /* 判斷定時器超時時間 */ { /* 每隔100ms 進來一次 */ bsp_LedToggle(2); /* 翻轉LED的狀態 */ } ucKeyCode = bsp_GetKey(); /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { case KEY_DOWN_K1: /* K1鍵按下 */ arm_lms_f32_test1(); break; case KEY_DOWN_K2: /* K2鍵按下 */ arm_lms_f32_test2(); break; case KEY_DOWN_K3: /* K3鍵按下 */ arm_lms_f32_test3(); break; default: /* 其它的鍵值不處理 */ break; } } } }
49.7 實驗例程說明(IAR)
配套例子:
V7-234_自適應濾波器實現,無需Matlab生成係數(支援實時濾波)
實驗目的:
- 學習LMS最小均方濾波器。
實驗內容:
- 啟動一個自動重灌軟體定時器,每100ms翻轉一次LED2。
- 按下按鍵K1,列印測試波形1和濾波後的波形資料。
- 按下按鍵K2,列印測試波形2和濾波後的波形資料。
- 按下按鍵K3,列印測試波形3和濾波後的波形資料。
上電後串列埠列印的資訊:
波特率 115200,資料位 8,奇偶校驗位無,停止位 1。
RTT方式列印資訊:
程式設計:
系統棧大小分配:
RAM空間用的DTCM:
硬體外設初始化
硬體外設的初始化是在 bsp.c 檔案實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬體裝置。該函式配置CPU暫存器和外設的暫存器並初始化一些全域性變數。只需要呼叫一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* 配置MPU */ MPU_Config(); /* 使能L1 Cache */ CPU_CACHE_Enable(); /* STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鐘: - 呼叫函式HAL_InitTick,初始化滴答時鐘中斷1ms。 - 設定NVIC優先順序分組為4。 */ HAL_Init(); /* 配置系統時鐘到400MHz - 切換使用HSE。 - 此函式會更新全域性變數SystemCoreClock,並重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用於程式碼執行時間測量,MDK5.25及其以上版本才支援,IAR不支援。 - 預設不開啟,如果要使能此選項,務必看V7開發板使用者手冊第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder並開啟 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */ bsp_InitTimer(); /* 初始化滴答定時器 */ bsp_InitUart(); /* 初始化串列埠 */ bsp_InitExtIO(); /* 初始化FMC匯流排74HC574擴充套件IO. 必須在 bsp_InitLed()前執行 */ bsp_InitLed(); /* 初始化LED */ }
MPU配置和Cache配置:
資料Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM),FMC的擴充套件IO區。
/* ********************************************************************************************************* * 函 數 名: MPU_Config * 功能說明: 配置MPU * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void MPU_Config( void ) { MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable(); /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 配置FMC擴充套件IO的MPU屬性為Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /*使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); } /* ********************************************************************************************************* * 函 數 名: CPU_CACHE_Enable * 功能說明: 使能L1 Cache * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void CPU_CACHE_Enable(void) { /* 使能 I-Cache */ SCB_EnableICache(); /* 使能 D-Cache */ SCB_EnableDCache(); }
主功能:
主程式實現如下操作:
- 啟動一個自動重灌軟體定時器,每100ms翻轉一次LED2。
- 按下按鍵K1,列印測試波形1和濾波後的波形資料。
- 按下按鍵K2,列印測試波形2和濾波後的波形資料。
- 按下按鍵K3,列印測試波形3和濾波後的波形資料。
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: c程式入口 * 形 參: 無 * 返 回 值: 錯誤程式碼(無需處理) ********************************************************************************************************* */ int main(void) { uint8_t ucKeyCode; /* 按鍵程式碼 */ uint16_t i; bsp_Init(); /* 硬體初始化 */ PrintfLogo(); /* 列印例程資訊到串列埠1 */ PrintfHelp(); /* 列印操作提示資訊 */ bsp_StartAutoTimer(0, 100); /* 啟動1個100ms的自動重灌的定時器 */ /* 進入主程式迴圈體 */ while (1) { bsp_Idle(); /* 這個函式在bsp.c檔案。使用者可以修改這個函式實現CPU休眠和喂狗 */ if (bsp_CheckTimer(0)) /* 判斷定時器超時時間 */ { /* 每隔100ms 進來一次 */ bsp_LedToggle(2); /* 翻轉LED的狀態 */ } ucKeyCode = bsp_GetKey(); /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { case KEY_DOWN_K1: /* K1鍵按下 */ arm_lms_f32_test1(); break; case KEY_DOWN_K2: /* K2鍵按下 */ arm_lms_f32_test2(); break; case KEY_DOWN_K3: /* K3鍵按下 */ arm_lms_f32_test3(); break; default: /* 其它的鍵值不處理 */ break; } } } }
49.8 總結
本章節主要講解了自適應濾波器,功能比較強勁,熟練應用需要多做測試。
微信公眾號:armfly_com 安富萊論壇:www.armbbs.cn 安富萊淘寶:https://armfly.taobao.com