最早我實現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 ++;
}
}
}