DSP2812上FFT调试心得1 事前准备工作FFT运算的代码实现(基本上是所有的程序中)最核心的两个问题就是:数据结构和算法。
由于FFT运算的结果是复数形式,该怎么存储,存储为什么数据格式都要考虑清楚,后面会交代。
FFT的核心程序不建议自己编写,如果你足够厉害的话也可以自己写,主要原因有两个,一计算的精度问题(涉及到数据结构),二是程序的执行效率问题,这些在hello dsp论坛都有讲到。
所以在项目开始之前先从TI官网下载FFT函数库(文件名sprc081),解压并安装。
其次下载一个C28xx定点库说明书(C28X_Fixed_Point_Library_v1_01)拜读一下,上面有FFT库函数的讲解,不要惧怕读英文。
本阶段所需要的文件如下图。
话不多说,现在开始上马,从新建工程开始。
2 新建工程跟往常一样,添加各种文件。
另外为完成FFT运算,需要额外添加以下文件:fft.h,fft.lib和完成FFT运算的asm文件,参见下图高亮显示的头文件和库文件 源文件 由于我用的是复数CFFT 运算,所以源文件里面是cfft32xxx ,如果你用的是实数RFFT32运算,则相应的添加rfft32xxx 。
所有的汇编文件见下图下面就可以编写自己的源文件了。
先说明下我的源文件是干什么的。
我没有用TI 给的例子,里面的位码倒置函数使用了2次,感觉不太对吧,而且加窗函数其实可有可无。
我的是对普通电网的220V电压信号进行采集并进行谐波分析。
具体方法是将正负220V调理到0到3V的交流信号提供给2812的ADC采集,并进行FFT运算。
一个周期(0.02s,50Hz)采集1024个点,由于点数较多,涉及到后面的数据段的分配(CMD文件的配置)等后面再说。
不用我说,这些头文件都要写在程序里吧。
中间的unsigned int flag=0到float harmonics[100]是我自己定义的变量就不需要了。
用#pragma定义了段以后就必须到CMD里面为它分配存储空间。
CMD配置具体参见教科书。
说一下我在这阶段的问题吧。
首先ipcb[2N]得占4K个字(4K*16),mag[N]占2K*16,还有一个潜在的数据段FFTtf(存放旋转因子的,貌似占3N/4个字)最好也得分个2K*16,一共得8K,很明显DSP2812的RAM是存不下的,可能有人会说,2812的RAM不是有18K吗,还存不下?因为2812的RAM都分成不同大小的区了,如果没记错的话H0占8K,不过这些分给程序去了,唯一大点的空间就是L0 SARAM和L1 SARAM各4K,理论上刚刚好。
最开始我把FFTipcb、FFTmag和FFTtf分到这两个区存储,调试出问题了计算结果一直是0,后面仔细翻书发现这两个区是受CSM保护,需要密码才能通过JTAG口读取。
在线调试看不到数据。
段定义到flash中也是一样的受CSM保护,且会降低速度。
初学者还是分配到RAM中便于观察,调试方便。
我的解决办法是在外设接口2区扩展了一个256K*16的RAM,然后将FFTipcb、FFTmag和FFTtf分配到该外扩RAM中去(我用的是试验箱,别人已扩展好)。
RAM扩展电路如下图。
如果你采样数据没这么多,内部RAM完全够用的话,那你就当我上面说的是一堆废话吧。
下面附上我的CMD配置文件和外扩RAM的初始化程序。
CMD在MEMORY PAGE1 下添加:在SECTION中添加:外扩RAM的初始化在DSP28_Xintf()中,其中zone1没必要初始化,看你用的是哪个区就初始化哪个区:上述寄存器配置请看文档。
接下来调试的过程中又出现问题了。
在观察ipcb数组里面的值时提示Memory map prevent read of target memory at address 0x080000.这个问题花了我两天的时间去解决,一直以为是外部RAM没扩展好,或是找程序的原因。
后面发现CCS里面的option->memory map 中的地址映射在0x080000出没有映射成RAM或ROM,而是NONE。
取消Enable memory mapping前面的钩即可观察相应地址的数据。
不好意思,废话太多,请见谅!!!3 调用FFT库函(1)初始化FFT(2)给输入数组ipcb赋值因为ipcb是复数形式,偶数位存实部,奇数位存虚部。
如下图所示,当然也可以通过acq定义的结构体输入,那玩意没研究过就不讲了吧。
这里有话要讲,不然后面就要出错,导致mag里面全零。
请看我注释中有一行voltage_dots[j]=(voltage_dots[j]*2.85/65520.0-1.5)*120110.0/470.0/400.0;最后面的除以400先不要,变成voltage_dots[j]=(voltage_dots[j]*2.85/65520.0-1.5)*120110.0/470.0表示将采样值还原成220√2sin(wt)的形式,即还原后的采样点大小在-311到+311之间。
由于定点FFT运算的输入ipcb是Q31格式(我也不懂没下到详细资料),大致意思是输入给ipcb的数据先转换成-1到1之间,然后再左移31位(<<2^31),所以有ipcb[2*j]=(long)(voltage_dots[j]*2147483648);这条赋值语句(2^31=2147483648)。
输出mag中的值及其与原始信号220√2sin(wt)之间的关系见第4章讲解。
(3)位码倒置和计算4 输入和输出数据的对应关系先废话一下运算结果存放问题:ipcb[]即存放输入数据,又存放运算好的结果,复数形式,即实部、虚部、实部、虚部无限循环下去。
Mag[]中存放的是谐波幅值的平方(别问我为什么存的是平方而不是实际值,开方多耗时间啊)。
(1)频谱幅值问题来了问题1:如果你输入给ipcb的值不是按照先转换成-1到1,再左移31位的话,而是按照论坛上别人说的因为28x系列DSP是定点处理器,而FFT计算涉及到不少浮点计算,TI使用Q 格式来解决这个问题(Q格式说明可参考sprc087_IQmath)。
事实上输入数据采用Q31格式能在避免计算溢出前提下获得最好的计算精度。
对AD采样的数据进行FFT计算,定义计算缓冲区数组:long ipcb[N+2];因为AD结果寄存器是12位的,在数据左对齐的情况下直接左移15位即可:ipcb[ConversionCount] = ((unsigned long)AdcRegs.ADCRESULT0)<<15;那么恭喜你mag中能得到一堆数据,但是mag中的幅值如何对应到原始信号的赋值,不好意思,本教程解答不了(应为作者水平有限,没有琢磨透)如果你是按我说的先除以一个比例系数变成-1到+1,再左移31位的话,那么对应关系将非常明显,我是参考的以下内容,贴出来大家看看(/blogger/post_read.asp?BlogID=991984&PostID=21775125)。
群里有不少同学在讨论如何从FFT库调用后的mag数组中计算所要的结果,其实我看问题的关键都集中在如何看待输入输出数据的Q格式问题。
好些同学认为既然输出是Q30格式,那就将结果直接除2^30,即将mag数组除以1073741824再开方,比如好些同学就直接用与下面类似的代码:for(i=0;i<512;i++){mag[i]= sqrt(mag[i]/1073741824.0);}但是大家都看到,求得的结果mag值几乎都为0。
事实情况是不是这样的呢,或者是不是这样求的,我们可以来试验一下。
因AD采样进的数据受AD参考电压、采样频率精度等影响,为了不让这些误差影响到试验过程和结果,这里采用从MATLAB中仿真计算出一组更理想的且精度更高的采样数值,将其当作外部AD采样的数据,再导入到CCS中让DSP进行FFT计算。
用MATLAB 计算模拟采样点:%%%%%%%%%%%%%%根据实际情况修改下列参数f1 =5; %频率f1a1 = 1; %幅值a1f_sample = 1024; %采样频率N = 2048; %采样点N%采样n = 0:N-1;t = n/f_sample;y= a1*sin(2*pi*f1*t);y_int32=int32(y*2^31); %移位成Q31格式将这组数据导入到CCS中,从CCS图里打开ipcb观察是否导入成功或导入正确。
然后运行与FFT相关的代码,观察mag数组元素值和图形。
从上图可看出计算结果非常漂亮,就是一根完美的脉冲尖峰线,这正好有利于下面的分析(这就是为什么这里不用带有各种干扰误差且影响分析的AD数据而用MATLAB模拟采样数据的原因)。
对结果进行分析。
频率值很好算,mag数组只有mag[10]= 268435451而其余皆为0,故该单频信号的频率值为10* f_sample/ N=5Hz=f1,幅值怎么算?是否直接除2^30?,看看结果:>> 268435451/2^30ans =0.2500说明结果并不是为1,因此求幅值并不能简单地直接除以1073741824再开方,观察这里还有个规律,若是:>> 2*sqrt(268435451/2^30)ans =1.0000开方后还有个两倍的关系,这时才能是真实的幅度值。
我没有仔细研究FFT库里面的汇编程序代码,系数2可能还是Q格式中的移位问题所致。
最近因各种事繁忙,我还没仔细往里面找原因。
现在试验是否可以这样求幅值。
若输入信号的真实幅值(随便写个数)为0.556458,频率还是5Hz,还是按照上面的方法来计算,FFT后的mag[10]= 83119830,则>> 2*sqrt(83119830/2^30)ans =0.55645799150412与真实幅值0.556458对比,上述结果是非常精确的。
其实还可以观察出一些关于FFT输入输出Q格式数据的一些规律。
比如上面的计算,因为移位,输入数据(-1~1之间)放大了2^31倍,计算幅值其实是sqrt(83119830/2^28),若是不放大这么多倍,那如何计算?再随便写个信号幅值数,假定为0.781234,此时移位放大不是2^31而是其他(当然不能移位超出31不然就溢出了,表现在信号上就是信号的上下限被削顶了),假设放大了2^29,将此作为FFT输入,得到mag值>> 2*sqrt(10239579/2^26)ans =0.78123393995944我想仔细的同学也能猜得出来规律了。
其实TI 的这个库输入用Q31格式,是为了得到定点处理器运算下FFT最好的精度,我花了一些时间做计算,做了一个表,从中可以看出这一点。