ARM DSP 库 FFT 的使用
ARM DSP 库 FFT 的使用

ARM DSP 库 FFT 的使用

记录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);
  }
}