stm32 web 参数_正点原子【STM32-F407探索者】第五十二章 DSP 测试实验

1)资料下载:点击资料即可下载

2)对正点原子Linux感兴趣的同学可以加群讨论:935446741

3)关注正点原子公众号,获取最新资料更新

85790e13d163b9dfad0d4cf7a92c318b.png

上一章,我们在 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 所示:

0265750073a3f5b772e19422de7bcc70.png
图 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 所示:

5f7f05307dcd196bc32cb93f63e81f6c.png
图 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 参考资料

→STM32F4xxHAL 库 文件夹里面,解压该文件,即可找到 ST 提供的 DSP 库,详细路径为:

光 盘

→8

STM32

参 考 资 料

→STM32F4xxHAL

→STM32F4xx_DSP_StdPeriph_Lib_V1.4.0→Libraries→CMSIS→

DSP_Lib,该文件夹下目录结构如图 52.1.1.3 所示:

5078b4b4e4af046db1c35f62e56ae803.png
图 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 参考资料→STM32F4xxHAL

库→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_cortexM4l_math.lib (Cortex-M4 小端模式)

⑦ arm_cortexM4bf_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 所示:

1d19b141a1a501027b235e90b087ba80.png
图 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 所示:

eba1bfd7b0bfa112e01038090abc20a7.png
图 52.1.2.2 添加.lib 文件

这样,添加文件就结束了(就添加了一个.lib 文件)。

2, 添加头文件包含路径

添加好.lib 文件后,我们要添加头文件包含路径,将第一步拷贝的 Include 文件夹和,加入

头文件包含路径,如图 52.1.2.3 所示:

53fb731146f11b38aadc3dd55b0ccd41.png
图 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

添加方法:点击

2d4d389a483e12a2c3c934365bfaf480.png

→C/C++选项卡,然后在 Define 里面进行设置,如图 52.1.2.4 所示:

f1a9c303a6643231892271a520304e91.png
图 52.1.2.4 DSP 库支持全局宏定义设置

这里,两个宏之间用“,”隔开。并且,上面的全局宏里面,我们没有添加__FPU_USED,

因为这个宏定义在 Target 选项卡设置 Code Generation 的时候(上一章有介绍),选择了:Use FPU

(如果没有设置 Use FPU,则必须设置!!),故 MDK 会自动添加这个全局宏,因此不需要我们

手动添加了。同时__FPU_PRESENT 全局宏我们 FPU 实验已经讲解,这个宏定义在 stm32f4xx.h

头文件里面已经定义。 这样,在 Define 处 要 输 入 的 所 有 宏 为 :

USE_HAL_DRIVER,STM32F407xx,ARM_MATH_CM4,__CC_ARM,ARM_MATH_MATRIX_CH

ECK,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;

HAL_Init(); //初始化 HAL 库

Stm32_Clock_Init(336,8,2,7); //设置时钟,168Mhz

delay_init(168); //初始化延时函数

uart_init(115200); //初始化 USART

LED_Init(); //初始化 LED

KEY_Init(); //初始化按键

LCD_Init(); //初始化 LCD FSMC 接口

TIM3_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,"2016/5/6");

LCD_ShowString(30,150,200,16,16," No DSP runtime:"); //显示提示信息

LCD_ShowString(30,190,200,16,16,"Use DSP runtime:"); //显示提示信息

POINT_COLOR=BLUE; //设置字体为蓝色

while(1)

{

//不使用 DSP 优化

__HAL_TIM_SET_COUNTER(&TIM3_Handler,0);//重设 TIM3 定时器的计数器值

timeout=0;

res=sin_cos_test(PI/6,200000,0);

time=__HAL_TIM_GET_COUNTER(&TIM3_Handler)+(u32)timeout*65536;

sprintf((char*)buf,"%0.1fmsrn",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 优化

__HAL_TIM_SET_COUNTER(&TIM3_Handler,0);//重设 TIM3 定时器的计数器值

timeout=0;

res=sin_cos_test(PI/6,200000,1);

time=__HAL_TIM_GET_COUNTER(&TIM3_Handler)+(u32)timeout*65536;

sprintf((char*)buf,"%0.1fmsrn",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;

HAL_Init(); //初始化 HAL 库

Stm32_Clock_Init(336,8,2,7); //设置时钟,168Mhz

delay_init(168); //初始化延时函数

uart_init(115200); //初始化 USART

LED_Init(); //初始化 LED

KEY_Init(); //初始化按键

LCD_Init(); //初始化 LCD FSMC 接口

TIM3_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,"2016/5/6");

LCD_ShowString(30,150,200,16,16," No DSP runtime:"); //显示提示信息

LCD_ShowString(30,190,200,16,16,"Use DSP runtime:"); //显示提示信息

POINT_COLOR=BLUE; //设置字体为蓝色

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.3fmsrn",time/1000);

LCD_ShowString(30+12*8,160,100,16,16,buf); //显示运行时间

arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH);//取模得幅值

printf("rn%d point FFT runtime:%0.3fmsrn",FFT_LENGTH,time/1000);

printf("FFT Result:rn");

for(i=0;i<FFT_LENGTH;i++)

{

printf("fft_outputbuf[%d]:%frn",i,fft_outputbuf[i]);

}

}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 所示:

fcc1f462f4b91e1ef624681541329759.png
图 52.4.1 使用 DSP 库和不使用 DSP 库的基础数学函数速度对比

从图中可以看出,使用 DSP 库的基础数学函数计算所用时间比不使用 DSP 库的短,使用

STM32F4 的 DSP 库,速度上面比传统的实现方式提升了约 17%。

对于实验 47_2 DSP FFT 测试,下载后,屏幕显示提示信息,然后我们按下 KEY0 就可以

看到 FFT 运算所耗时间,如图 52.4.2 所示:

6766afdac28b2c9f89a0f0b74c152f5e.png
图 52.4.2 FFT 测试界面

可以看到,STM32F4 采用基 4 法计算 1024 个浮点数的 FFT,只用了 0.584ms,速度是相当

快的了。同时,可以在串口看到 FFT 变换取模后的各频点模值,如图 52.4.3 所示:

acc86c9ac94c8be3001fe3075668b79a.png
图 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 库的其他测试实例,大家可以自行研究下,我们

这里就不再介绍了。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值