第五十二章 DSP测试实验
1.硬件平台:正点原子探索者STM32F407开发板 2.软件平台:MDK5.1 3.固件库版本:V1.4.0
上一章,我们在
ALIENTEK探索者
STM32F4开发板上测试了
STM32F4的硬件
FPU。
STM32F4除了集成硬件
FPU外,还支持多种
DSP指令集。同时
ST还提供了一整套
DSP库方便我们工程中开发应用。
本章,我们将指导大家入门
STM32F4的
DSP,手把手教大家搭建
DSP库测试环境,同时通过对
DSP库中的几个基本数学功能函数和
FFT快速傅里叶变换函数的测试,让大家对
STM32F4的
DSP库有个基本的了解。本章分为如下几个部分:
52.1 DSP简介与环境搭建
52.2 硬件设计
52.3 软件设计
52.4 下载验证
52.1 DSP简介与环境搭建
本节将分两个部分:
1,
STM32F4 DSP简介;
2,
DSP库运行环境搭建
52.1.1 STM32F4 DSP简介
STM32F4采用
Cortex-M4内核,相比
Cortex-M3系列除了内置硬件
FPU单元,在数字信号处理方面还增加了
DSP指令集,支持诸如单周期乘加指令(
MAC),优化的单指令多数据指令(
SIMD),饱和算数等多种数字信号处理指令集。相比
Cortex-M3,
Cortex-M4在数字信号处理能力方面得到了大大的提升。
Cortex-M4执行所有的
DSP指令集都可以在单周期内完成,而
Cortex-M3需要多个指令和多个周期才能完成同样的功能。
接下来我们来看看
Cortex-M4的两个
DSP指令:
MAC指令(
32位乘法累加)和
SIMD指令。
32位乘法累加(
MAC)单元包括新的指令集,能够在单周期内完成一个
32×
32+ 64à
64 的操作或两个
16×
16 的操作,其计算能力,如表
52.1.1.1所示:
图
52.1.1.1 32位乘法累加(
MAC)单元的计算能力
Cortex-M4支持
SIMD指令集,这在
Cortex-M3/M0系列是不可用的。上述表中的指令,有的属于
SIMD指令。与硬件乘法器一起工作(
MAC),使所有这些指令都能在单个周期内执行。受益于
SIMD指令的支持,
Cortex-M4处理器能在单周期内完成高达
32×
32+64à
64的运算,为其他任务释放处理器的带宽,而不是被乘法和加法消耗运算资源。
比如一个比较复杂的运算:两个
16×
16乘法加上一个
32位加法,如图
52.1.1.2所示:
图
52.1.1.2 SUM运算过程
以上图片所示的运算,即:
SUM = SUM +(
A* C)
+(
B *D),在
STM32F4上面,可以被编译成由一条单周期指令完成。
上面我们简单的介绍了
Cortex-M4的
DSP指令,接下来我们来介绍一下
STM32F4的
DSP库。
STM32F4的
DSP库源码和测试实例在
ST提供的标准库:
stm32f4_dsp_stdperiph_lib.zip里面就有(该文件可以在:
http://www.st.com/web/en/catalog/tools/FM147/CL1794/SC961/SS1743/P
F257901下载,文件名:
STSW-STM32065),该文件在:光盘à
8,
STM32参考资料à
STM32F4xx固件库 文件夹里面,解压该文件,即可找到
ST提供的
DSP库,详细路径为:光盘à
8,
STM32参考资料à
STM32F4xx固件库à
STM32F4xx_DSP_StdPeriph_Lib_V1.4.0à
Librariesà
CMSISà
DSP_Lib,该文件夹下目录结构如图
52.1.1.3所示:
图
52.1.1.3 DSP_Lib目录结构
DSP_Lib源码包的
Source文件夹是所有
DSP库的源码,
Examples文件夹是相对应的一些测试实例。这些测试实例都是带
main函数的,也就是拿到工程中可以直接使用。接下来我们一一讲解一下
Source源码文件夹下面的子文件夹包含的
DSP库的功能。
BasicMathFunctions
基本数学函数:提供浮点数的各种基本运算函数,如向量加减乘除等运算。
CommonTables
arm_common_tables.c文件提供位翻转或相关参数表。
ComplexMathFunctions
复杂数学功能,如向量处理,求模运算的。
ControllerFunctions
控制功能函数。包括正弦余弦,
PID电机控制,矢量
Clarke变换,矢量
Clarke逆变换等。
FastMathFunctions
快速数学功能函数。提供了一种快速的近似正弦,余弦和平方根等相比
CMSIS计算库要快的数学函数。
FilteringFunctions
滤波函数功能,主要为
FIR和
LMS(最小均方根)等滤波函数。
MatrixFunctions
矩阵处理函数。包括矩阵加法、矩阵初始化、矩阵反、矩阵乘法、矩阵规模、矩阵减法、矩阵转置等函数。
StatisticsFunctions
统计功能函数。如求平均值、最大值、最小值、计算均方根
RMS、计算方差
/标准差等。
SupportFunctions
支持功能函数,如数据拷贝,
Q格式和浮点格式相互转换,
Q任意格式相互转换。
TransformFunctions
变换功能。包括复数
FFT(
CFFT)
/复数
FFT逆运算(
CIFFT)、实数
FFT(
RFFT)
/实数
FFT逆运算(
RIFFT)、和
DCT(离散余弦变换)和配套的初始化函数。
所有这些
DSP库代码合在一起是比较多的,因此,
ST为我们提了
.lib格式的文件,方便使用。这些
.lib文件就是由
Source文件夹下的源码编译生成的,如果想看某个函数的源码,大家可以在
Source文件夹下面查找。
.lib格式文件路径:光盘à
8,
STM32参考资料à
STM32F4xx固件库à
STM32F4xx_DSP_StdPeriph_Lib_V1.4.0à
Librariesà
CMSISà
Libà
ARM ,总共有
8个
.lib文件,如下:
①
arm_cortexM0b_math.lib
(Cortex-M0大端模式
)
②
arm_cortexM0l_math.lib
(Cortex-M0小端模式
)
③
arm_cortexM3b_math.lib
(Cortex-M3大端模式
)
④
arm_cortexM3l_math.lib
(Cortex-M3小端模式
)
⑤
arm_cortexM4b_math.lib
(Cortex-M4大端模式
)
⑥
arm_cortexM4bf_math.lib
(Cortex-M4小端模式
)
⑦
arm_cortexM4l_math.lib (浮点
Cortex-M4大端模式
)
⑧
arm_cortexM4lf_math.lib (浮点
Cortex-M4小端模式
)
我们得根据所用
MCU内核类型以及端模式来选择符合要求的
.lib文件,本章我们所用的
STM32F4属于
CortexM4F内核,小端模式,应选择:
arm_cortexM4lf_math.lib(浮点
Cortex-M4小端模式
)。
对于
DSP_Lib的子文件夹
Examples下面存放的文件,是
ST官方提供的一些
DSP测试代码,提供简短的测试程序,方便上手,有兴趣的朋友可以根据需要自行测试。
52.1.2 DSP库运行环境搭建
本节我们将讲解怎么搭建
DSP库运行环境,只要运行环境搭建好了,使用
DSP库里面的函数来做相关处理就非常简单了。本节,我们将以上一章例程(实验
46_1)为基础,搭建
DSP运行环境。
在
MDK里面搭建
STM32F4的
DSP运行环境
(使用
.lib方式
)是很简单的,分为
3个步骤:
1, 添加文件。
首先,我们在例程工程目录下新建:
DSP_LIB文件夹,存放我们将要添加的文件:
arm_cortexM4lf_math.lib和相关头文件,如图
52.1.2.1所示:
图
52.1.2.1 DSP_LIB文件夹添加文件
其中
arm_cortexM4lf_math.lib的由来,在
52.1.1节已经介绍过了。
Include文件夹,则是直接拷贝:
STM32F4xx_DSP_StdPeriph_Lib_V1.4.0à
Librariesà
CMSISà
Include 这个
Include文件夹,里面包含了我们可能要用到的相关头文件。
然后,打开工程,新建
DSP_LIB分组,并将
arm_cortexM4lf_math.lib添加到工程里面,如图
52.1.2.2所示:
图
52.1.2.2 添加
.lib文件
这样,添加文件就结束了(就添加了一个
.lib文件)。
2, 添加头文件包含路径
添加好
.lib文件后,我们要添加头文件包含路径,将第一步拷贝的
Include文件夹和
DSP_LIB文件夹,加入头文件包含路径,如图
52.1.2.3所示:
图
52.1.2.3 添加相关头文件包含路径
3, 添加全局宏定义
最后,为了使用
DSP库的所有功能,我们还需要添加几个全局宏定义:
1,
__FPU_USED
2,
__FPU_PRESENT
3,
ARM_MATH_CM4
4,
__CC_ARM
5,
ARM_MATH_MATRIX_CHECK
6,
ARM_MATH_ROUNDING
添加方法:点击
à
C/C++选项卡,然后在
Define里面进行设置,如图
52.1.2.4所示:
图
52.1.2.4 DSP库支持全局宏定义设置
这里,两个宏之间用“
,”隔开。并且,上面的全局宏里面,我们没有添加
__FPU_USED,因为这个宏定义在
Target选项卡设置
Code
Generation的时候(上一章有介绍),选择了:
Use FPU(如果没有设置
Use FPU,则必须设置!!),故
MDK会自动添加这个全局宏,因此不需要我们手动添加了。同时
__FPU_PRESENT全局宏我们
FPU实验已经讲解,这个宏定义在
stm32f4xx.h头文件里面已经定义。这样,在
Define处要输入的所有宏为:
STM32F40_41xxx,USE_STDPERIPH_DRIVER,ARM_MATH_CM4,__CC_ARM,ARM_MATH_MATRIX_CHECK,ARM_MATH_ROUNDING 共
6个。
至此,
STM32F4的
DSP库运行环境就搭建完成了。
特别注意,为了方便调试,本章例程我们将
MDK的优化设置为
-O0优化,以得到最好的调试效果。
52.2 硬件设计
本例程包含2个源码:
实验47_1 DSP BasicMath测试和
实验47_2 DSP FFT测试,他们除了main.c里面内容不一样外,其他源码完全一模一样(包括MDK配置)。
实验47_1 DSP BasicMath测试 实验功能简介:测试STM32F4的DSP库基础数学函数:arm_cos_f32和arm_sin_f32和标准库基础数学函数:cosf和sinf的速度差别,并在LCD屏幕上面显示两者计算所用时间,DS0用于提示程序正在运行。
实验47_2 DSP FFT测试 实验功能简介:测试STM32F4的DSP 库的FFT函数,程序运行后,自动生成1024点测试序列,然后,每当KEY0按下后,调用DSP库的FFT算法(基4法)执行FFT运算,在LCD屏幕上面显示运算时间,同时将FFT结果输出到串口,DS0用于提示程序正在运行。
本实验用到的资源如下:
1,指示灯
DS0
2,
KEY0按键
3,串口
4,
TFTLCD模块
这些前面都已介绍过。
52.3 软件设计
本章代码,分成两个工程:
1,实验
47_1 DSP BasicMath测试;
2,实验
47_2 DSP FFT测试,接下来我们分别介绍。
52.3.1 DSP BasicMath测试
这是我们使用
STM32F4的
DSP库进行基础数学函数测试的一个例程。使用大家耳熟能详的公式进行计算:
sin(x)2+cos(x)2=1
这里我们用到的就是
sin和
cos函数,不过实现方式不同。
MDK的标准库(
math.h)提供我们:
sin、
cos、
sinf和
cosf等
4个函数,带
f的表示单精度浮点型运算,即
float型,而不带
f的表示双精度浮点型,即
double。
STM32F4的
DSP库,则提供我们另外两个函数:
arm_sin_f32和
arm_cos_f32(
注意:需要添加:arm_math.h头文件才可使用!!!),这两个函数也是单精度浮点型的,用法同
sinf和
cosf一模一样。
本例程就是测试:
arm_sin_f32&
arm_cos_f32 同
sinf&cosf的速度差别。
因为
52.1.2节已经搭建好
DSP库运行环境了,所以我们这里直接只需要修改
main.c里面的代码即可,
main.c代码如下:
#include "math.h"
#include "arm_math.h"
#define DELTA 0.000001f //误差值
//sin cos测试
angle:起始角度
times:运算次数
//mode:0,不使用
DSP库
;1,使用
DSP库
//返回值:
0,成功
;0XFF,出错
u8 sin_cos_test(float angle,u32 times,u8 mode)
{
float
sinx,cosx;
float
result;
u32
i=0;
if(mode==0)
{
for(i=0;i<times;i++)
{
cosx=cosf(angle); //不使用
DSP优化的
sin,
cos函数
sinx=sinf(angle);
result=sinx*sinx+cosx*cosx;
//计算结果应该等于
1
result=fabsf(result-1.0f); //对比与
1的差值
if(result>DELTA)return
0XFF;//判断失败
angle+=0.001f; //角度自增
}
}else
{
for(i=0;i<times;i++)
{
cosx=arm_cos_f32(angle); //使用
DSP优化的
sin,
cos函数
sinx=arm_sin_f32(angle);
result=sinx*sinx+cosx*cosx;
//计算结果应该等于
1
result=fabsf(result-1.0f); //对比与
1的差值
if(result>DELTA)return
0XFF;//判断失败
angle+=0.001f; //角度自增
}
}
return
0;//任务完成
}
u8 timeout;//定时器溢出次数
int main(void)
{
float
time;
u8
buf[50]; u8 res;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组
2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为
115200
LED_Init(); //初始化
LED
KEY_Init(); //初始化按键
LCD_Init(); //初始化
LCD
TIM3_Int_Init(65535,8400-1);//10Khz计数频率
,最大计时
6.5秒超出
POINT_COLOR=RED;
LCD_ShowString(30,50,200,16,16,"Explorer
STM32F4");
LCD_ShowString(30,70,200,16,16,"DSP
BasicMath TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2014/7/2");
LCD_ShowString(30,150,200,16,16,"
No DSP runtime:"); //显示提示信息
LCD_ShowString(30,190,200,16,16,"Use
DSP runtime:"); //显示提示信息
POINT_COLOR=BLUE; //设置字体为蓝 {MOD}
while(1)
{
LCD_Fill(30+16*8,150,lcddev.width-1,60,WHITE); //清除原来现实
//不使用
DSP优化
TIM_SetCounter(TIM3,0);//重设
TIM3定时器的计数器值
timeout=0;
res=sin_cos_test(PI/6,200000,0);
time=TIM_GetCounter(TIM3)
+(u32)timeout*65536;
sprintf((char*)buf,"%0.1fms
",time/10);
if(res==0)LCD_ShowString(30+16*8,150,100,16,16,buf); //显示运行时间
else
LCD_ShowString(30+16*8,150,100,16,16,"error!
"); //显示当前运行情况
//使用
DSP优化
TIM_SetCounter(TIM3,0);//重设
TIM3定时器的计数器值
timeout=0;
res=sin_cos_test(PI/6,200000,1);
time=
TIM_GetCounter(TIM3)+(u32)timeout*65536;
sprintf((char*)buf,"%0.1fms
",time/10);
if(res==0)LCD_ShowString(30+16*8,190,100,16,16,buf); //显示运行时间
else
LCD_ShowString(30+16*8,190,100,16,16,"error!
"); //显示错误
LED0=!LED0;
}
}
这里包括
2个函数:
sin_cos_test和
main函数,
sin_cos_test函数用于根据给定参数,执行
sin(x)2+cos(x)2=1
的计算。计算完后,计算结果同给定的误差值(
DELTA)对比,如果不大于误差值,则认为计算成功,否则计算失败。该函数可以根据给定的模式参数
(mode)来决定使用哪个基础数学函数执行运算,从而得出对比。
main函数则比较简单,这里我们通过定时器
3来统计
sin_cos_test运行时间,从而得出对比数据。主循环里面,每次循环都会两次调用
sin_cos_test函数,首先采用不使用
DSP库方式计算,然后采用使用
DSP库方式计算,并得出两次计算的时间,显示在
LCD上面。
DSP基础数学函数测试的程序设计就讲解到这里。
52.3.1 DSP FFT测试
这是我们使用
STM32F4的
DSP库进行
FFT函数测试的一个例程。
首先,我们简单介绍下
FFT:
FFT即快速傅里叶变换,可以将一个时域信号变换到频域。因为有些信号在时域上是很难看出什么特征的,但是如果变换到频域之后,就很容易看出特征了,这就是很多信号分析采用
FFT变换的原因。另外,
FFT可以将一个信号的频谱提取出来,这在频谱分析方面也是经常用的。简而言之,
FFT就是将一个信号从时域变换到频域方便我们分析处理。
在实际应用中,一般的处理过程是先对一个信号在时域进行采集,比如我们通过
ADC,按照一定大小采样频率
F去采集信号,采集
N个点,那么通过对这
N个点进行
FFT运算,就可以得到这个信号的频谱特性。
这里还涉及到一个采样定理的概念:在进行模拟
/数字信号的转换过程中,当
采样频率F大于信号中最高频率
fmax的
2倍时
(F>2*fmax),采样之后的数字信号完整地保留了原始信号中的信息,采样定理又称
奈奎斯特定理。举个简单的例子:比如我们正常人发声,频率范围一般在
8KHz以内,那么我们要通过采样之后的数据来恢复声音,我们的采样频率必须为
8KHz的
2倍以上,也就是必须大于
16KHz才行。
模拟信号经过
ADC采样之后,就变成了数字信号,采样得到的数字信号,就可以做
FFT变换了。
N个采样点数据,在经过
FFT之后,就可以得到
N个点的
FFT结果。为了方便进行
FFT运算,通常
N取
2的整数次方。
假设采样频率为
F,对一个信号采样,采样点数为
N,那么
FFT之后结果就是一个
N点的复数,每一个点就对应着一个频率点(以基波频率为单位递增),这个点的模值(
sqrt(实部
2+虚部
2))就是该频点频率值下的幅度特性。具体跟原始信号的幅度有什么关系呢?假设原始信号的峰值为
A,那么
FFT的结果的每个点(除了第一个点直流分量之外)的模值就是
A的
N/2倍,而第一个点就是直流分量,它的模值就是直流分量的
N倍。
这里还有个基波频率,也叫频率分辨率的概念,就是如果我们按照
F的采样频率去采集一个信号,一共采集
N个点,那么基波频率(频率分辨率)就是
fk=F/N。这样,第
n个点对应信号频率为:
F*(n-1)/N;其中
n≥
1,当
n=1时为直流分量。
关于
FFT我们就介绍到这。
如果我们要自己实现
FFT算法,对于不懂数字信号处理的朋友来说,是比较难的,不过,
ST提供的
STM32F4
DSP库里面就有
FFT函数给我们调用,因此我们只需要知道如何使用这些函数,就可以迅速的完成
FFT计算,而不需要自己学习数字信号处理,去编写代码了,大大方便了我们的开发。
STM32F4的
DSP库里面,提供了定点和浮点
FFT实现方式,并且有基
4的也有基
2的,大家可以根据需要自由选择实现方式。注意:对于基
4的
FFT输入点数必须是
4n,而基
2的
FFT输入点数则必须是
2n,并且基
4的
FFT算法要比基
2的快。
本章我们将采用
DSP库里面的基
4浮点
FFT算法来实现
FFT变换,并计算每个点的模值,所用到的函数有:
arm_status arm_cfft_radix4_init_f32(
arm_cfft_radix4_instance_f32 * S,
uint16_t
fftLen,uint8_t ifftFlag,uint8_t bitReverseFlag)
void arm_cfft_radix4_f32(const arm_cfft_radix4_instance_f32
* S,float32_t * pSrc)
void arm_cmplx_mag_f32(float32_t * pSrc,float32_t
* pDst,uint32_t numSamples)
第一个函数
arm_cfft_radix4_init_f32,用于初始化
FFT运算相关参数,其中:
fftLen用于指定
FFT长度(
16/64/256/1024/4096),本章设置为
1024;
ifftFlag用于指定是傅里叶变换
(0)还是反傅里叶变换
(1),本章设置为
0;
bitReverseFlag用于设置是否按位取反,本章设置为
1;最后,所有这些参数存储在一个
arm_cfft_radix4_instance_f32结构体指针
S里面。
第二个函数
arm_cfft_radix4_f32就是执行基
4浮点
FFT运算的,
pSrc传入采集到的输入信号数据(实部
+虚部形式),同时
FFT变换后的数据,也按顺序存放在
pSrc里面,
pSrc必须大于等于
2倍
fftLen长度。另外,
S结构体指针参数是先由
arm_cfft_radix4_init_f32函数设置好,然后传入该函数的。
第三个函数
arm_cmplx_mag_f32用于计算复数模值,可以对
FFT变换后的结果数据,执行取模操作。
pSrc为复数输入数组(大小为
2*numSamples)指针,指向
FFT变换后的结果;
pDst为输出数组(大小为
numSamples)指针,存储取模后的值;
numSamples就是总共有多少个数据需要取模。
通过这三个函数,我们便可以完成
FFT计算,并取模值。本节例程(实验
47_2 DSP FFT测试)同样是在
52.1.2节已经搭建好
DSP库运行环境上面修改代码,只需要修改
main.c里面的代码即可,本例程
main.c代码如下:
#include "math.h"
#include "arm_math.h"
#define FFT_LENGTH 1024
//FFT长度,默认是
1024点
FFT
float fft_inputbuf[FFT_LENGTH*2]; //FFT输入数组
float fft_outputbuf[FFT_LENGTH]; //FFT输出数组
u8 timeout;//定时器溢出次数
int main(void)
{
arm_cfft_radix4_instance_f32 scfft;
u8 key,t=0; float time;
u8
buf[50]; u16 i;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组
2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为
115200
LED_Init(); //初始化
LED
KEY_Init(); //初始化按键
LCD_Init(); //初始化
LCD
TIM3_Int_Init(65535,84-1); //1Mhz计数频率
,最大计时
65ms左右超出
POINT_COLOR=RED;
LCD_ShowString(30,50,200,16,16,"Explorer
STM32F4");
LCD_ShowString(30,70,200,16,16,"DSP
FFT TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2014/7/2");
LCD_ShowString(30,130,200,16,16,"KEY0:Run
FFT"); //显示提示信息
LCD_ShowString(30,160,200,16,16,"FFT
runtime:"); //显示
FFT执行时间
POINT_COLOR=BLUE; //设置字体为蓝 {MOD}
arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);//初始化
scfft结构体
,设定
FFT参数
while(1)
{
key=KEY_Scan(0);
if(key==KEY0_PRES)
{
for(i=0;i<FFT_LENGTH;i++)//生成信号序列
{
fft_inputbuf[2*i]=100+
10*arm_sin_f32(2*PI*i/FFT_LENGTH)+
30*arm_sin_f32(2*PI*i*4/FFT_LENGTH)+
50*arm_cos_f32(2*PI*i*8/FFT_LENGTH); //实部
fft_inputbuf[2*i+1]=0;//虚部全部为
0
}
TIM_SetCounter(TIM3,0);//重设
TIM3定时器的计数器值
timeout=0;
arm_cfft_radix4_f32(&scfft,fft_inputbuf); //FFT计算(基
4)
time=
TIM_GetCounter(TIM3)+(u32)timeout*65536; //计算所用时间
sprintf((char*)buf,"%0.3fms
",time/1000);
LCD_ShowString(30+12*8,160,100,16,16,buf); //显示运行时间
arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH);//取模得幅值
printf("
%d
point FFT runtime:%0.3fms
",FFT_LENGTH,time/1000);
printf("FFT
Result:
");
for(i=0;i<FFT_LENGTH;i++)
{
printf("fft_outputbuf[%d]:%f
",i,fft_outputbuf);
}
}else
delay_ms(10);
t++;
if((t%10)==0)LED0=!LED0;
}
}
以上代码只有一个main函数,里面通过我们前面介绍的三个函数:arm_cfft_radix4_init_f32、
arm_cfft_radix4_f32和arm_cmplx_mag_f32来执行FFT变换并取模值。每当按下KEY0就会重新生成一个输入信号序列,并执行一次FFT计算,将arm_cfft_radix4_f32所用时间统计出来,显示在LCD屏幕上面,同时将取模后的模值通过串口打印出来。
这里,我们在程序上生成了一个输入信号序列用于测试,输入信号序列表达式:
fft_inputbuf[2*i]=100+
10*arm_sin_f32(2*PI*i/FFT_LENGTH)+
30*arm_sin_f32(2*PI*i*4/FFT_LENGTH)+
50*arm_cos_f32(2*PI*i*8/FFT_LENGTH); //实部
通过该表达式我们可知,信号的直流分量为100,外加2个正弦信号和一个余弦信号,其幅值分别为10、30和50。
关于输出结果分析,请看52.4节,软件设计我们就介绍到这里。
52.4 下载验证
代码编译成功之后,便可以下载到我们的探索者STM32F4开发板上验证了。
对于实验47_1 DSP BasicMath测试,下载后,可以在屏幕看到两种实现方式的速度差别,如图52.4.1所示:
图52.4.1使用DSP库和不使用DSP库的基础数学函数速度对比
从图中可以看出,使用DSP库的基础数学函数计算所用时间比不使用DSP库的短,使用STM32F4的DSP库,速度上面比传统的实现方式提升了约17%。
对于实验47_2 DSP FFT测试,下载后,屏幕显示提示信息,然后我们按下KEY0就可以看到FFT运算所耗时间,如图52.4.2所示:
图52.4.2
FFT测试界面
可以看到,STM32F4采用基4法计算1024个浮点数的FFT,只用了0.584ms,速度是相当快的了。同时,可以在串口看到FFT变换取模后的各频点模值,如图52.4.3所示:
图52.4.3
FFT变换后个频点模值
查看所有数据,会发现:第0、1、4、8、1016、1020、1023这7个点的值比较大,其他点的值都很小,接下来我们就简单分析一下这些数据。
由于FFT变换后的结果具有对称性,所以,实际上有用的数据,只有前半部分,后半部分和前半部分是对称关系,比如1和1023,4和1020,8和1016等,就是对称关系,因此我们只需要分析前半部分数据即可。这样,就只有第0、1、4、8这四个点,比较大,重点分析。
假设我们采样频率为1024Hz,那么总共采集1024个点,频率分辨率就是1Hz,对应到频谱上面,两个点之间的间隔就是1Hz。因此,上面我们生成的三个叠加信号:10*sin(2*PI*i/1024)+ 30*sin(2*PI*i*4/1024)+50*cos(2*PI*i*8/1024),频率分别是:1Hz、4Hz和8Hz。
对于上述4个值比较大的点,结合52.3.1节的知识,很容易分析得出:第0点,即直流分量,其FFT变换后的模值应该是原始信号幅值的N倍,N=1024,所以值是100*1024=102400,与理论完全一样,然后其他点,模值应该是原始信号幅值的N/2倍,即10*512、30*512、50*512,而我们计算结果是:5119.999023、15360、256000,除了第1个点,稍微有点点误差(说明精度上有损失),其他同理论值完全一致。
DSP测试实验,我们就讲解到这里,DSP库的其他测试实例,大家可以自行研究下,我们这里就不再介绍了。
实验详细手册和源码下载地址:http://www.openedv.com/posts/list/41586.htm
正点原子探索者STM32F407开发板购买地址:http://item.taobao.com/item.htm?id=41855882779
一周热门 更多>