STM32 官方DSP的 FFT庫使用

最早我實現FFT是移植的網上搜羅來的FFT 的 C程序代碼,奈何運算效率太低,只好放棄

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "FFT.h"
#include "sample.h"
#define N DEPTH_ADC_MAX



complex x[N + 10];// *W; /*輸入序列,變換核*/
//complex W[N];
int size_x=0;      /*輸入序列的大小,在本程序中僅限2的次冪*/
double PI = 3.1415926535;         /*圓周率*/
void fft(void);     /*快速傅里葉變換*/

void change(void); /*變址*/
void add(complex,complex,complex *);   /*複數加法*/
void mul(complex,complex,complex *);   /*複數乘法*/
void sub(complex,complex,complex *);   /*複數減法*/



complex fft_result(uint16_t index )
{
    if(index >= N)
        index = 0;
    return x[index];
}

int fft_analyze(uint16_t * sample_data,uint16_t sample_cnt)
{
    int i;                             /*輸出結果*/

    //PI=atan(1)*4;
    PI = 3.1415926535;

    if(sample_cnt > N)
        return -1;

    size_x = sample_cnt;



    for(i=0; i<size_x; i++)
    {
        x[i].real = (double)(*( sample_data + i));
        x[i].img = 0;
    }


//	initW();//調用變換核
    fft();//調用快速傅里葉變換

    return 0;
}



/*快速傅里葉變換*/

void fft()
{
    int i=0,j=0,k=0,l=0;
    complex W1;
    int W_Index = 0;
    complex up,down,product;


    change();  //調用變址函數
    for(i=0; i< log(size_x)/log(2) ; i++)      /*一級蝶形運算 stage */
    {
        l=1<<i;
        for(j=0; j<size_x; j+= 2*l )   /*一組蝶形運算 group,每個group的蝶形因子乘數不同*/
        {
            for(k=0; k<l; k++)      /*一個蝶形運算 每個group內的蝶形運算*/
            {
                W_Index = size_x*k/2/l;
                W1.real=cos(2*PI/size_x*W_Index);   //用歐拉公式計算旋轉因子
                W1.img=-1*sin(2*PI/size_x*W_Index);
                mul(x[j+k+l],W1,&product);
                //mul(x[j+k+l],W[size_x*k/2/l],&product);
                add(x[j+k],product,&up);
                sub(x[j+k],product,&down);
                x[j+k]=up;
                x[j+k+l]=down;
            }
        }
    }
}



/*變址計算,將x(n)碼位倒置*/
void change()
{
    complex temp;
    unsigned short i=0,j=0,k=0;
    double t;
    for(i=0; i<size_x; i++)
    {
        k=i;
        j=0;
        t=(log(size_x)/log(2));
        while( (t--)>0 )    //利用按位與以及循環實現碼位顛倒
        {
            j=j<<1;
            j|=(k & 1);
            k=k>>1;
        }
        if(j>i)    //將x(n)的碼位互換
        {
            temp=x[i];
            x[i]=x[j];
            x[j]=temp;
        }
    }
}

void fft_output()
{
    char str[100] = {0};
    uint32_t len = 0;
    complex r;
    uint32_t a;
    for(uint16_t i = 0 ; i < 21 ; i ++) //僅計算1000hz以下的頻率
    {
        r = fft_result(i);
        a = sqrt(r.img * r.img + r.real * r.real);
        if(a > 1000 && len < 80)
        {
            len += sprintf(&str[len],"%uHZ:%u\r\n",i*50,a);
        }

    }
}

void add(complex a,complex b,complex *c)  //複數加法的定義
{
    c->real=a.real+b.real;
    c->img=a.img+b.img;
}


void mul(complex a,complex b,complex *c)  //複數乘法的定義
{
    c->real=a.real*b.real - a.img*b.img;
    c->img=a.real*b.img + a.img*b.real;
}


void sub(complex a,complex b,complex *c)  //複數減法的定義
{
    c->real=a.real-b.real;
    c->img=a.img-b.img;
}



#ifndef _FFT_H
#define _FFT_H

#include "main.h"

//#define OPEN_FFT_TEST


/*定義複數類型*/
typedef struct{
double real;
double img;
}complex;




complex fft_result(uint16_t index );
int fft_analyze(uint16_t * sample_data ,uint16_t sample_cnt);
void fft_output(void);


#endif




後來查閱了資料得知,STM32有官方的DSP庫,裏面有FFT的運算,效率非常高,使用也很方便

在這裏插入圖片描述
可以看到72MHZ的情況下,1024個點位只需要2.138ms的運算時間
但是侷限性也有,只可以運算64/256/1024個點位,多了少了都不行。

官方庫官網已經下載不到了

鏈接:https://pan.baidu.com/s/1CEnYxD0WvetxJQmOtRWGWg
提取碼:d6p9

在這裏插入圖片描述

庫的使用也非常簡單,包含頭文件後,引用這幾個函數就可以了。

/* 64 points*/
void cr4_fft_64_stm32(void *pssOUT, void *pssIN, u16 Nbin);
/* 256 points */
void cr4_fft_256_stm32(void *pssOUT, void *pssIN, u16 Nbin);
/* 1024 points */
void cr4_fft_1024_stm32(void *pssOUT, void *pssIN, u16 Nbin);

下面貼出我的使用代碼做參考

#include "table_fft.h"
#include "sample.h"
#include "stm32_dsp.h"
#include "math.h"
#include "LED.h"
#include "Easy_8266.h"
#include "StringManage.h"

/*
   1.666666666666667us的採樣週期,採樣256個點,採樣頻率爲 600KHZ , 頻率分辨率爲 600KHZ / 256 = 2343.75HZ
*/



/*
假設ADC採樣的聲音數據爲adc_buf[NPT],
FFT運算的輸入數組爲lBufInArray[NPT]。由於FFT計算出來的數據是對稱的,因此通常而言輸出數組取一半的數據,爲lBufOutArray[NPT/2]。除此之外,還定義各次諧波幅值lBufMagArray[NPT/2]。即:
 */
 
 
#define NPT DEPTH_ADC_MAX




long lBufInArray[NPT];     //FFT運算的輸入數組
long lBufOutArray[NPT/2];  //輸出數組取一半的數據
long lBufMagArray[NPT/2];  //各次諧波幅值

//PI2是2π(即6.28318530717959),Fs是採樣頻率44800
#define PI2 6.28318530717959
#define Fs  600000    //  (2343.75 * 256)
static void InitBufInArray_Test()
{
    unsigned short i;
    float fx;
    for(i=0; i<NPT; i++)
    {
        fx = 1500 * sin(PI2 * i * 9375 / Fs) + 2700 * sin(PI2 * i * 18750 / Fs) + 4000 * sin(PI2 * i * 37500 / Fs);
        lBufInArray[i] = ((signed short)fx) << 16;
    }
}

static void GetPowerMag()
{
    signed short lX,lY;
    float X,Y,Mag;
    unsigned short i;
    for(i=0; i < NPT/2; i++)
    {
        lX  = (lBufOutArray[i] << 16) >> 16;
        lY  = (lBufOutArray[i] >> 16);

        //除以32768再乘65536是爲了符合浮點數計算規律
        X = NPT * ((float)lX) / 32768;
        Y = NPT * ((float)lY) / 32768;
        Mag = sqrt(X * X + Y * Y) / NPT;
        if(i == 0)
            lBufMagArray[i] = (unsigned long)(Mag * 32768);
        else
            lBufMagArray[i] = (unsigned long)(Mag * 65536);
    }
}



void FFT_Task()
{
    uint16_t arc_flg = 0;
    uint16_t adc_buf[DEPTH_ADC_MAX] = {0};
    uint16_t size = Get_Adc_Buf(0, adc_buf);
    char tx_buf[256] = {0};
    uint16_t txlen = 0;
    if(size > 0)
    {
        for(uint32_t i=0; i<NPT; i++)
        {
            lBufInArray[i] = adc_buf[i];
            lBufInArray[i] <<= 16;
        }


        //InitBufInArray_Test();

#if NPT == 256
        cr4_fft_256_stm32(lBufOutArray, lBufInArray, NPT);
#endif

#if NPT == 1024
        cr4_fft_1024_stm32(lBufOutArray, lBufInArray, NPT);
#endif

        GetPowerMag();


        txlen = load_string(tx_buf,txlen,"\r\n------------\r\n");
        for(uint32_t i = 1; i<NPT /2; i++)
        {
            if(lBufMagArray[i] > 20 && txlen < 200)
            {
                txlen = load_data(tx_buf, txlen,i,10);
                txlen = load_c(tx_buf,txlen,':');
                txlen = load_data(tx_buf, txlen,(uint32_t)lBufMagArray[i],10);
                txlen = load_string(tx_buf,txlen,"\r\n");
                arc_flg = 1;
            }
        }

				static uint8_t heart_cnt = 0;
        if(arc_flg > 0)
        {
            if(Esp8266_Send_Data((uint8_t*)tx_buf,txlen)  )
            {
                led_on_temp_unit50ms(3);
            }

							
        }
				else
				{
					if(heart_cnt >= 1000 / 50)
					{
					  if(Esp8266_Send_Data((uint8_t*)"-",1) )
						  led_on_temp_unit50ms(1);
						heart_cnt = 0;
					}
					else
						heart_cnt ++;
				}

    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章