1. 程式人生 > 其它 >【STM32H7的DSP教程】第49章 STM32H7的自適應濾波器實現,無需Matlab生成係數(支援實時濾波)

【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 初學者重要提示

  1. ARM DSP庫提供了LMS最小均方自適應濾波和歸一化最小均方自適應濾波器,推薦使用歸一化方式,因為歸一化方法的步長更容易設定。
  2. 自適應濾波器的濾波因數步長設定比較考究,詳見本章教程第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生成係數(支援實時濾波)

實驗目的:

  1. 學習LMS最小均方濾波器。

實驗內容:

  1. 啟動一個自動重灌軟體定時器,每100ms翻轉一次LED2。
  2. 按下按鍵K1,列印測試波形1和濾波後的波形資料。
  3. 按下按鍵K2,列印測試波形2和濾波後的波形資料。
  4. 按下按鍵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生成係數(支援實時濾波)

實驗目的:

  1. 學習LMS最小均方濾波器。

實驗內容:

  1. 啟動一個自動重灌軟體定時器,每100ms翻轉一次LED2。
  2. 按下按鍵K1,列印測試波形1和濾波後的波形資料。
  3. 按下按鍵K2,列印測試波形2和濾波後的波形資料。
  4. 按下按鍵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