DSP28335实现FFT的工程实现方法
- 1、FFT的工程应用痛点分析
- 2、解决方案
- 3、代码实现
- 4、实际测试与效果分析
- 5、一些新的想法
- 感谢支持,如果这篇文章对你有帮助请您留下宝贵一票,这也将是我持续输出的动力。
1、FFT的工程应用痛点分析
在DSP28335中要实现FFT,不仅需要对FFT的每个步骤熟悉,还需要考虑在现有控制架构下FFT是否影响正常的控制算法运行。在FFT的工程应用的痛点就是它的蝶形运算的循环。要实现工程应用就要解决掉这个魂环对于控制器的影响。那有什么方法可以克服这个问题呢?
2、解决方案
FFT的算法可分解为三步骤,第一步位反转排序,第二步蝶形运算,第三步是构建输出。其中位反转排序是单循环结构,蝶形运算是三级for循环结构,输出构建是单循环结构。为了将所有循环消除,将FFT的每个层级循环嵌入至控制算法的背景循环中。这样FFT的计算实时性将受到了影响,但对于控制算法的影响将降低到了最小。对于控制算法的影响就是每个控制周期多增加了几条执行语句。按照DSP150MHz的执行周期,增加的这几条指令对于控制算法和通讯只增加了2us不到。
3、代码实现
参考"FFT的完整DSP实现“进行了二次开发,代码见。
取消所有for循环的代码如下:
fft.c文件如下:
/*
* xl_fft.c
*
* Created on: 2023-5-12
* Author: xkl
*
* TEST OK
*/
#include "xkfft.h"
#include "math.h"
#include "string.h"
/*
* FFT Algorithm
* === Inputs ===
* x : complex numbers
* N : nodes of FFT. @N should be power of 2, that is 2^(*)
*/
FFT_T sFFT;
void FFtInit(FFT_T *v)
{
memset((void *)&v->In[0], 0, sizeof(FFT_T));
v->SampleEn = 1;
}
int fft(FFT_T *v, int N)
{
TYPE_FFT *x;
static int i,j,l,k,ip;
static int M = 0;
static int le,le2;
static TYPE_FFT_E sR,sI,tR,tI,uR,uI;
x = v->In;
M = (int)(log(N) / log(2));
/*
* bit reversal sorting
*/
switch(v->StepNum)
{
case 0:
l = N / 2;
j = l;
i=1;
v->StepNum = 1;
break;
case 1:
//BitReverse(x,x,N,M);
if(i <= N-2)
{
if (i < j)
{
tR = x[j].real;
tI = x[j].imag;
x[j].real = x[i].real;
x[j].imag = x[i].imag;
x[i].real = tR;
x[i].imag = tI;
}
k = l;
while (k <= j) {
j = j - k;
k = k / 2;
}
j = j + k;
i++;
}
else
{
v->StepNum = 2;
l = 1;
}
break;
case 2:
// FFT 核心开始
/* loop for ceil{log2(N)} */
if(l <= M)
{
le = (int)pow(2,l);
le2 = (int)(le / 2);
uR = 1;
uI = 0;
sR = cos(XK_FFT_PI / le2);
sI = -sin(XK_FFT_PI / le2);
v->StepNum = 3;
// 跳转至第二层循环
j=1;
}
else
{
v->StepNum = 5;
}
break;
case 3:
/* loop for each sub DFT */
if(j<=le2)
{
i=j-1;
// 跳转至第三层循环
v->StepNum = 4;
}
else
{
v->StepNum = 2;
l++;
}
break;
case 4:
/* loop for each butterfly */
if(i<=N-1)
{
ip = i + le2;
tR = x[ip].real * uR - x[ip].imag * uI;
tI = x[ip].real * uI + x[ip].imag * uR;
x[ip].real = x[i].real - tR;
x[ip].imag = x[i].imag - tI;
x[i].real += tR;
x[i].imag += tI;
i+=le;
}
else
{
tR = uR;
uR = tR * sR - uI * sI;
uI = tR * sI + uI *sR;
j++;
v->StepNum = 3;
}
break;
case 5:
MakeOutput(v,N);
v->StepNum = 6;
break;
case 6:
/*谐波含量计算*/
v->StepNum = 0;
v->SampleEn = 1;
break;
default:
break;
}
return 0;
}
/*
* Inverse FFT Algorithm
*/
int ifft(FFT_T *v, int N)
{
int k = 0;
TYPE_FFT *x;
x = v->In;
for (k=0; k<=N-1; k++) {
x[k].imag = -x[k].imag;
}
fft(v, N); /* using FFT */
for (k=0; k<=N-1; k++) {
x[k].real = x[k].real / N;
x[k].imag = -x[k].imag / N;
}
return 0;
}
void MakeInput(FFT_T *v, float N)
{
int i;
for ( i=0;i<N;i++ )
{
v->In[i].real = sin(XK_FFT_PI*2.0*50.5*i/N)+0.5*sin(XK_FFT_PI*2.0*250.5*i/N);
v->In[i].imag = 0.0f;
}
}
fft.h文件如下:
/*
* XK_fft.h
*
* Created on: 2023-5-13
* Author: xkl
*/
#ifndef _XK_FFT_H
#define _XK_FFT_H
#define TYPE_FFT_E float /* Type is the same with COMPLEX member */
#ifndef XK_FFT_PI
#define XK_FFT_PI (3.14159265f)
#endif
#ifndef SAMPLE_NODES
#define SAMPLE_NODES (1024)
#endif
typedef struct {
float real;
float imag;
} COMPLEX;
typedef COMPLEX TYPE_FFT; /* Define COMPLEX in Config.h */
typedef struct
{
TYPE_FFT In[SAMPLE_NODES];
TYPE_FFT_E Out[SAMPLE_NODES/2];
unsigned int SampleEn; //采样使能
unsigned int SampleCnt; //采样计数
unsigned int StepNum;
float Thd;
}FFT_T;
extern void FFtInit(FFT_T *v);
extern int fft(FFT_T *v, int N);
extern int ifft(FFT_T *v, int N);
extern void MakeOutput(FFT_T *v,float N);
extern void MakeInput(FFT_T *v, float N);
extern void ThdCacl(FFT_T *v, float N,float FreBase);
extern FFT_T sFFT;
#endif /* XK_FFT_H_ */
定时器中断的数据采集代码如下:
/*
* Task6.c
*
* Created on: 2023年5月13日
* Author: Administrator
*/
#include "Task.h"
//-----------------------------------------------
/*
*/
//-----------------------------------------------
#ifndef TASK6_PERIOD
#define TASK6_PERIOD (1/1000.0)
#endif
//-----------------------------------------------
float Task6Time1 = 0;
//-----------------------------------------------
interrupt void Task6(void)
{
long long TimerS;
long long TimerE;
float signal= 0;
//static float TimeDly = 0;
//===========================================
IER |= MINT13; // 中断优先级设定
EINT;
//-------------------------------------------
TimerS = CpuTimer1.RegsAddr->TIM.all;
Tsc = 0.001;
//===========================================
signal = 100*sin(XK_FFT_PI*2.0*50.0*sFFT.SampleCnt*Tsc)+5*sin(XK_FFT_PI*2.0*250.0*sFFT.SampleCnt*Tsc);
if(sFFT.SampleEn == 1)
{
sFFT.In[sFFT.SampleCnt].real = signal;
sFFT.In[sFFT.SampleCnt].imag = 0.0f;
sFFT.SampleCnt++;
if(sFFT.SampleCnt == SAMPLE_NODES)
{
sFFT.SampleEn = 0;
sFFT.SampleCnt = 0;
}
}
//===========================================
TimerE = CpuTimer1.RegsAddr->TIM.all;
if(TimerE > TimerS)
{
Task6Time1 = ((float)(150000 - TimerE + TimerS ))/150.0;
if(Task6Time1 < 0)
{
}
}
else
{
Task6Time1 = ((float)(TimerS - TimerE ))/150.0;
}
}
背景循环的执行代码如下:
/************************************************
* File: Main.c
* Created on: 2023年5月13日
* Author: xkl
* Project: DSP_28335_FFT
**********************************************/
// 系统头文件
#include "DSP2833x_Device.h"
#include "DSP2833x_GlobalPrototypes.h"
#include "DSP2833x_Examples.h"
//-----------------------------------------------
// 用户头文件
#include "Task.h"
//-----------------------------------------------
#include "stdlib.h"
#include "stdio.h"
//-----------------------------------------------
long long TimerS = 0;
long long TimerE = 0;
float fftTime1 = 0;
//-----------------------------------------------
// while loop 时间测试
//-----------------------------------------------
void main (void)
{
//===========================================
// 系统初始化
InitSysCtrl();
MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);
InitFlash();
//===========================================
// 初始化 IO
//===========================================
// 禁止中断 初始化 中断向量表
// 禁止全局中断
DINT;
//初始化PIE 控制寄存器
InitPieCtrl();
//禁止 CPU 中断,清除 中断标志
IER = 0x0000;
IFR = 0x0000;
// 初始化中断向量表
InitPieVectTable();
//用户中断函数 映射
EALLOW;
// 定时器 1 函数指初始化
PieVectTable.XINT13 = &Task6;
EDIS;
//------------------------------------------
// 初始化外设
// 初始化定时器
InitCpuTimers();
//-------------------------------------------
// 配置定时器
ConfigCpuTimer(&CpuTimer1, 150, 1000);
//===========================================
// 使能 CPU 中断
IER |= M_INT13; //CpuTimer1 中断使能
//===========================================
// 使能全局中断
EINT;
ERTM;
//===========================================
// 用户 循环
while(1)
{
if(1 == sDataSample.u16SensorCorrectFlag)
{
FFtInit(&sFFT);
sDataSample.u16SensorCorrectFlag = 0;
CpuTimer1Regs.TCR.bit.TSS = 0;
}
TimerS = CpuTimer1.RegsAddr->TIM.all;
if(sFFT.SampleEn == 0)
{
fft(&sFFT,SAMPLE_NODES);
}
TimerE = CpuTimer1.RegsAddr->TIM.all;
fftTime1 = ((float)(TimerS - TimerE ))/150.0;
}
}
4、实际测试与效果分析
直接使用中的代码进行测试,总共FFT执行一次需要9470us。使用改进后的代码,每次执行仅需0.2us。从获取结果的时间维度上看,一次性解算完毕需要9470us,而将解算过程分解到每个背景循环中,解算量是不变的,仅仅是背景循环的次数增多。而对于控制器,它最需要的就是控制周期越短越好。所以这个方法实现FFT的工程应用即保证了FFT解算的结果,而且还对实时控制影响降低到了微乎其微的地步。
5、一些新的想法
对于含有控制器的DSP来实现FFT,FFT的结果反映了控制的效果,FFT的结果不是控制的目标。因此,为了保证控制效果的可观测,在不使用谐波测量仪器的前提下,直接使用控制器的一些资源,计算出控制目标的谐波含量还是一个不错的方案。这不仅节省了测量仪器的费用,而且还节省了一些隐形的人力成本。
对于采用FPGA+DSP的控制器架构实现FFT来看,利用FPGA的特性,使用FPGA实现高精度采样,DSP仅完成FFT的解算也是一个不错的方案,这将解决DSP控制中断与高速数据采样中断的冲突,使控制器资源最大化。