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 ++;
				}

    }
}


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