基於HAL庫的STM32的DSP庫詳解(附FFT應用)

1 . 建立工程,生成程式碼時選擇包含所有庫。
 
2. 打開 option for target 選擇 Target 標籤,在code generatio中,將floating point hardware 選擇 USE Single Precision。
 
3.  打開 option for target 選擇 C/C++ 標籤

在define後添加:__TARGET_FPU_VFP,ARM_MATH_MATRIX_CHECK,ARM_MATH_ROUNDING,ARM_MATH_CM4,__CC_ARM,由於使用HAL的庫,所以前面有USE_HAL_DRIVER,STM32F429xx的全局宏定義,如果使用的不是HAL庫,而是使用韌體庫的話,一般會有韌體庫的一個全局宏定義USE_STDPERIPH_DRIVER和STM32F4XXxx在裡面。所以當前只需要添加
__TARGET_FPU_VFP,
ARM_MATH_MATRIX_CHECK,
ARM_MATH_ROUNDING,
ARM_MATH_CM4,
__CC_ARM
注意中間用英文逗號分開。其中ARM_MATH_MATRIX_CHECK是庫函數的參數檢查開關。ARM_MATH_ROUNDING這個是庫函數在運算是是否開啟四捨五入的功能,可以根據實際需要進行配置。ARM_MATH_CM4這個就非常重要,必須要配置進去,否則在編譯之後,會默認使用math.h的庫函數,而不會用到硬體的FPU的。__CC_ARM是不同編譯器的編譯配置宏定義,__CC_ARM就是代表MDK開發環境。
 4. 添加浮點庫(.lib)文件到工程(或者添加源碼庫文件)。

如果用的是 uv4,打開 C:\Keil\ARM\CMSIS\Lib\ARM 目錄,複製「arm_cortexM4lf_math.lib」文件到你的工程下,並加入工程。
如果用的是 uv5,打開 C:\Keil_v5\ARM\Pack\ARM\CMSIS\4.2.0\CMSIS\Lib\ARM 目錄,複製「arm_cortexM4lf_math.lib」文件到工程下,並加入工程,4.2.0是庫的版本,每個版本可能不一樣。
也可以從官方下載韌體庫程式包中複製也行,  \STM32F4xx_DSP_StdPeriph_Lib\Libraries\CMSIS\Lib\ARM
如果查看DSP庫的各個函數的原型,可以添加源碼庫文件, 則當前工程目錄下的Drivers\CMSIS\DSP_Lib\Source目錄可以查看,當然安裝目錄下也有該庫源文件。
BasicMathFunctions
基本數學函數:提供浮點數的各種基本運算函數,如向量加減乘除等運算。 
CommonTables
arm_common_tables.c文件提供位翻轉或相關參數表。
ComplexMathFunctions
複雜數學功能,如向量處理,求模運算的。
ControllerFunctions
控制功能函數。包括正弦餘弦,PID電機控制,矢量Clarke變換,矢量Clarke逆變換等。
FastMathFunctions
快速數學功能函數。提供了一種快速的近似正弦,餘弦和平方根等相比CMSIS計算庫要快的數學函數。
FilteringFunctions
濾波函數功能,主要為FIR和LMS(最小均方根)等濾波函數。 
MatrixFunctions
矩陣處理函數。包括矩陣加法、矩陣初始化、矩陣反、矩陣乘法、矩陣規模、矩陣減法、矩陣轉置等函數。
StatisticsFunctions
統計功能函數。如求平均值、最大值、最小值、計算均方根RMS、計算方差/標準差等。
SupportFunctions
支援功能函數,如數據拷貝,Q格式和浮點格式相互轉換,Q任意格式相互轉換。
TransformFunctions
變換功能。包括複數FFT(CFFT)/複數FFT逆運算(CIFFT)、實數FFT(RFFT)/實數FFT逆運算(RIFFT)、和DCT(離散餘弦變換)和配套的初始化函數。
相關函數說明以及示例也可在安裝路徑Keil_v5\ARM\PACK\ARM\CMSIS\4.5.0\CMSIS\Documentation\DSP\html下獲得,自行查閱。
5. DSP庫的FFT變換示例。
在工程的main.c文件中添加如下程式碼
說明:取樣頻率為1024Hz,fft點數為1024點,則頻率解析度,計算公式:解析度=取樣頻率/取樣點數 = 1Hz,此時能看到0Hz,1Hz,2Hz,3Hz,…..512Hz的頻率分量。
FFTOutput這個數組中,下標0對應的元素就是0Hz(也就是直流分量)的幅度,下標1對應的就是1Hz的幅度,下標2對應2Hz的幅度……,依次類推。
此點非常重要,例如取樣率為2048Hz,那麼頻率解析度為2Hz,那麼能看到0Hz,2Hz,4Hz,6Hz,…..1024Hz的頻率分量
FFTOutput這個數組中,下標0對應的元素就是0Hz(也就是直流分量)的幅度,下標1對應的就是2Hz的幅度,下標2對應4Hz的幅度……,依次類推。此時去看1Hz,3Hz,則會出現誤差比較大的情況。
#include "arm_math.h"                  //添加頭文件
#define  FFT_LENGTH        1024        //FFT長度,默認是1024點FFT
#define SAMPLE_FREQ 1024 //取樣頻率
float fft_inputbuf[FFT_LENGTH*2];      //FFT輸入輸出數組,此數組為arm_cfft_radix4_f32的輸入輸出數組,前一個元素為實部,後一個為虛部,每兩個元素代表一個點.
float fft_outputbuf[FFT_LENGTH];    //arm_cmplx_mag_f32()幅度輸出數組
arm_cfft_radix4_instance_f32 scfft; //fft變換的初始化參數

在主函數進入while(1)之前添加如下程式碼

說明:arm_sin_f32函數生成取樣點,取樣訊號為DC訊號,100Hz,150Hz訊號的疊加,此時解析度為1Hz,剛好能夠看到DC, 100Hz,150Hz頻率分量的幅度,分別對應fft幅度輸出數組的下標0,100,150

arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);//初始化scfft結構體,設定FFT相關參數
for(int i=0;i<FFT_LENGTH;i++)//生成訊號序列
{
fft_inputbuf[2*i]=15 + 10*arm_sin_f32(2*PI*i*100/SAMPLE_FREQ) + \
5.5*arm_sin_f32(2*PI*i*150/SAMPLE_FREQ); //生成實部
fft_inputbuf[2*i+1]=0;//虛部全部為0
}
arm_cfft_radix4_f32(&scfft,fft_inputbuf); //FFT計算(基4(即fft長度22*n),FFT長度只能為64,256,1024,4096目前測試過這幾個長度,)
arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH); //把運算結果複數求模得幅值

DC分量值為15360.001,則對應的直流分量為15360.001/FFT點數 = 15360.001/1024=15

100Hz分量值為5119.99658,則對應的100Hz幅度為5119.99658*2/FFT點數 = 5119.99658*2/1024=10

150Hz分量值為2815.99976,則對應的150Hz幅度為2815.99976*2/FFT點數 = 2815.99976*2/1024=5.5

其餘的頻率的幅度近似為0。

若要查看某一頻率分量的實部與虛部,則可以查看fft_inputbuf數組。下標對應乘以2,因為實部和虛部存在。

當把取樣頻率改為2048Hz,生成訊號頻率其中一路改為151Hz,讀者可以自行測試該頻率分量的幅度,肯定會存在誤差。