记录ARM DSP 库下 FFT 的使用。
实数 FFT 的使用
实数 FFT 对应的函数为 arm_rfft_fast_f32,对于实数 FFT,输入信号无需按“实部、虚部、实部、虚部、、、”的顺序存储数据。
arm_rfft_fast_f32 函数的形参如下所示。arm_rfft_fast_f32 函数只支持 32、64、128、256、512、1024、2048 和 4096 个点的 FFT 计算。需要注意的是,arm_rfft_fast_f32 函数的输入是一个实序列,输出的是一个虚序列,输出的点数是输入点数的一半。假设输入的数据量为 1024 个点,因为是实序列,所以输入的字节总数为 1024 * 4 = 4096 个字节;输出的数据量为 512 个点,又因为是虚序列,所以输出的字节总数为 512 * 2 * 4 = 1024 个字节。综上,arm_rfft_fast_f32 函数输入输出的字节总数是一样的。
void arm_rfft_fast_f32(
arm_rfft_fast_instance_f32 * S, //实数 FFT 控制结构体
float32_t * p, //实序列数据输入(此函数会更改输入缓冲区的内容)
float32_t * pOut, //结果输出,输出的是一个虚序列,按 "实部、虚部、实部、虚部、、、" 的顺序存储数据,输入输出的数据量相同
uint8_t ifftFlag) //0-FFT 正变换,1-FFT 逆变换
因为 arm_rfft_fast_f32 函数的输出是一个虚序列,所以需要通过 arm_cmplx_mag_f32 函数求解模值,arm_cmplx_mag_f32 函数的形参如下所示。
void arm_cmplx_mag_f32(
const float32_t * pSrc, //输入,是一个虚序列,按"实部、虚部、实部、虚部、、、" 的顺序存储数据
float32_t * pDst, //输出,是一个实序列,字节总数为输入序列的一半
uint32_t numSamples) //点数
实数 FFT 的使用如下所示。实数 FFT 变换之前需要先用 arm_rfft_fast_init_f32 函数初始化 FFT 控制结构体,告诉 DSP 库 FFT 变换的点数。
#include "arm_math.h"
//FFT 点数
#define FFT_LENGTH 1024
//主函数
void main(void)
{
static float32_t s_arrFFTInput[FFT_LENGTH] = {0}; //FFT 输入缓冲区
static float32_t s_arrFFTOutput[FFT_LENGTH] = {0}; //FFT 输出缓冲区
static float32_t s_arrResult[FFT_LENGTH] = {0}; //FFT 模值
static arm_rfft_fast_instance_f32 s_structRfft; //FFT 控制结构体
//临时变量
u16 adc;
u32 waveCnt, i, pos;
float max;
//波形计数清零
waveCnt = 0;
//主循环
while(1)
{
//采集波形信号
adc = GetWaveADC();
s_arrFFTInput[waveCnt++] = adc;
//采集够了数据量
if(waveCnt >= FFT_LENGTH)
{
//FFT计算
//注意:arm_rfft_fast_f32 函数的输出是一个复数序列,按照 "实部、虚部、实部、虚部、、、" 的顺序存储数据,一共输出 FFT_LENGTH/2 个实数
//所以 arm_cmplx_mag_f32 函数的输出只有前 FFT_LENGTH/2 个点是有意义的
arm_rfft_fast_init_f32(&s_structRfft, FFT_LENGTH); //初始化结构体中的参数
arm_rfft_fast_f32(&s_structRfft, s_arrFFTInput, s_arrFFTOutput, 0); //1024点实序列快速FFT
arm_cmplx_mag_f32(s_arrFFTOutput, s_arrResult, FFT_LENGTH / 2); //求解模值
//计算波形频率
max = 0; pos = 0;
for(i = 1; i < FFT_LENGTH / 2; i++)
{
if(s_arrResult[i] > max)
{
max = s_arrResult[i];
pos = i;
}
}
printf("rate: %.1fHz\r\n", (double)pos * 1000.0 / (double)FFT_LENGTH);
//清空波形计数
waveCnt = 0;
}
//延时1ms
DelayNms(1);
}
}
频率计算方法:最高点所在下标 x 采样率 / FFT 点数。采样率单位是 Hz。
复数 FFT 的使用
复数 FFT 对应的函数为 arm_cfft_f32,只支持 16、32、64、128、256、512、1024、2048 和 4096 个点的 FFT 计算,对于复数 FFT,输入信号必须按“实部、虚部、实部、虚部、、、”的顺序存储数据。arm_cfft_f32 函数的形参如下所示。
void arm_cfft_f32(
const arm_cfft_instance_f32 * S, //复数 FFT 控制结构体
float32_t * p1, //数据输入、结果输出,输入和输出均是虚序列,必须按“实部、虚部、实部、虚部、、、”的顺序存储数据
uint8_t ifftFlag, //0-FFT 正变换,1-FFT 逆变换
uint8_t bitReverseFlag) //0-禁用输出位翻转,1-使能输出位翻转,一般选 0
复数 FFT 的使用如下所示。
#include "arm_math.h"
#include "arm_const_structs.h"
//FFT 点数
#define FFT_LENGTH 1024
//主函数
void main(void)
{
static float32_t s_arrFFTInput[FFT_LENGTH * 2] = {0}; //FFT 输入缓冲区
static float32_t s_arrResult[FFT_LENGTH * 2] = {0}; //FFT 模值
//临时变量
u16 adc;
u32 waveCnt, i, pos;
float max;
//波形计数清零
waveCnt = 0;
//主循环
while(1)
{
//采集波形信号
adc = GetWaveADC(); //获取 ADC 输入
s_arrFFTInput[(2 * waveCnt) + 0] = adc; //实部
s_arrFFTInput[(2 * waveCnt) + 1] = 0; //虚部
waveCnt++; //计数,只需计数到数组大小的一半
//采集够了数据量
if(waveCnt >= FFT_LENGTH)
{
//虚数序列快速FFT,arm_cfft_sR_f32_len1024 为 "arm_const_structs.h" 提供的配置变量,包含头文件后,直接调用即可
arm_cfft_f32(&arm_cfft_sR_f32_len1024, s_arrFFTInput, 0, 1);
//求解模值
arm_cmplx_mag_f32(s_arrFFTInput, s_arrResult, FFT_LENGTH);
//计算波形频率
max = 0; pos = 0;
for(i = 1; i < FFT_LENGTH / 2; i++)
{
if(s_arrResult[i] > max)
{
max = s_arrResult[i];
pos = i;
}
}
printf("rate: %.1fHz\r\n", (double)pos * 1000.0 / (double)FFT_LENGTH);
//清空波形计数
waveCnt = 0;
}
//延时1ms
DelayNms(1);
}
}