华大半导体HC32F4A0笔记(五),使用CMSIS-DSP库进行FFT运算

本文介绍如何在HC32F4A0微控制器上启用FPU功能并利用DSP库进行快速傅里叶变换(FFT)。内容包括配置FPU、生成正弦波采样序列、执行Q15定点FFT及计算结果的幅值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、开启FPU功能

点这个麻将牌四筒,展开CMSIS,把DSP勾了。
在这里插入图片描述
在这里插入图片描述
点开后
在这里插入图片描述

然后点这个锤子在这里插入图片描述

在这里插入图片描述
No Auto Includes的勾不要打,让它自动include,因为CMSIS-DSP库在KEIL的安装目录中已经存在了,工程里面不需要另外添加这些库文件,自动include会帮你找到它们。

在Target标签页选上这个Use Single Precision,有些版本上面显示的是use FPU
在这里插入图片描述
打开hc32f4a0.h文件,搜索__FPU_PRESENT,确定其在宏定义处定义为1
在这里插入图片描述
至此FPU功能就开启完毕了,我们可以使用官方库函数进行多种数学和信号运算,这些官方函数在实现这些高级运算时调用了FPU的指令,这样运算速度大幅提升!
在这里插入图片描述

当我们使用FFT时,需要引入两个头文件,由于允许Auto Includes,KEIL MDK会自动在它的安装目录里面找到它们

#include "arm_math.h"
#include "arm_const_structs.h"

在这里插入图片描述

二、使用库函数q15_t arm_sin_q15(q15_t x);生成正弦采样序列

在使用外部信号源经AD采样获取真实信号继而FFT之前,我们先模拟一个正弦波采样序列来试验测试FFT功能。

根据实际使用需求,我们模拟一个50hz的正弦信号,采样率为1600Hz,即每个周波32采样,共采样1024个点,每个点都使用Q15定点数 (-1 ~ 0.9999695)。

Fs = 1600 Hz (采样率)
N = 1024个点 (采样数)
n = 0,1,2,… N-1 (采样点序列)
t = 0,1/Fs,2/Fs,…(N-1)/Fs (采样时刻序列)
x = sin(2π x 50 x t) (采样信号序列)

#define TEST_LENGTH_SAMPLES 1024 
static q15_t testInput_q15_50hz[TEST_LENGTH_SAMPLES];
uint32_t fftSize = TEST_LENGTH_SAMPLES ; 
uint32_t Fs = 1600; 

数学上 y = sin(x) 的定义域是是(-∞,+∞),但是DSP库中的q15_t arm_sin_q15(q15_t x);的参数数值范围是[0,2^15),对应于弧度[0,2π)。

所以我们需要按照每周波32采样的采样率通过取余运算适配这个定义域

	for(uint16_t n=0; n<fftSize; n++)
	{
		j = n % 32;
 		testInput_q15_50hz[n] = arm_sin_q15(k*j);
	}
    systickStart = SysTick->VAL;

其中k为:

	k = 32768 * 50 / Fs;

这样我们就配置好了50Hz正弦波在1600Hz采样率下采集1024个点的信号采样序列。
在这里插入图片描述

三、Q15定点FFT运算

参照DSP库函数说明

  /**
   * @brief Instance structure for the Q15 RFFT/RIFFT function.
   */
  typedef struct
  {
    uint32_t fftLenReal;                      /**< length of the real FFT. */
    uint8_t ifftFlagR;                        /**< flag that selects forward (ifftFlagR=0) or inverse (ifftFlagR=1) transform. */
    uint8_t bitReverseFlagR;                  /**< flag that enables (bitReverseFlagR=1) or disables (bitReverseFlagR=0) bit reversal of output. */
    uint32_t twidCoefRModifier;               /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */
    q15_t *pTwiddleAReal;                     /**< points to the real twiddle factor table. */
    q15_t *pTwiddleBReal;                     /**< points to the imag twiddle factor table. */
    const arm_cfft_instance_q15 *pCfft;       /**< points to the complex FFT instance. */
  } arm_rfft_instance_q15;

  arm_status arm_rfft_init_q15(
  arm_rfft_instance_q15 * S,
  uint32_t fftLenReal,
  uint32_t ifftFlagR,
  uint32_t bitReverseFlag);

  void arm_rfft_q15(
  const arm_rfft_instance_q15 * S,
  q15_t * pSrc,
  q15_t * pDst);

调用该函数运算FFT:

	static q15_t testOutput_q15_50hz[TEST_LENGTH_SAMPLES];
	arm_rfft_instance_q15 S;
	uint32_t ifftFlag = 0; 
	uint32_t doBitReverse = 1; 
	/* 初始化结构体S */
 	(void)arm_rfft_init_q15(&S, fftSize, ifftFlag, doBitReverse);
 	/* 实数FFT Q15运算 */
 	arm_rfft_q15(&S, testInput_q15_50hz, testOutput_q15_50hz);

查阅资料得知其结果是Q5格式。

安富莱电子

根据公式x = (float)xq * 2-Q可以换算成浮点数

	static float32_t testOutput_f32_50hz[TEST_LENGTH_SAMPLES];
	for(uint16_t n = 0; n < fftSize; n++)
	{
		testOutput_f32_50hz[n] = (float32_t)testOutput_q15_50hz[n]/32;
	}

其运算结果为:
在这里插入图片描述
这是一个复数,偶数项为实部,其后的奇数项为虚部。这个复数为-0.03125-512j

再调用DSP库函数计算其幅值,其实由于实部很小,估算可知其幅值为512。

	static float32_t testOutput[TEST_LENGTH_SAMPLES];
 	arm_cmplx_mag_f32(testOutput_f32_50hz, testOutput, fftSize); 

求得其结果为:
在这里插入图片描述
由于本例是1600hz采样1024点,所以FFT后其频域响应在32 * 1600 / 1024 = 50Hz处出现极大值。
幅值计算公式为:An * 2 / N,即512 * 2 / 1024 = 1。
即得到正弦频率为50Hz,增益为1。结果与输入信号的相符。

四、实数Q15 1024FFT的运行时间

HC32F4AO主频设在200MHz,使用Systick记录Q15 1024FFT加上转换为浮点数最后算得幅值的总时间。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
运行耗时在900us左右。再分开查看
在这里插入图片描述
FFT耗时565us,1024个Q5定点复数转成浮点型耗时85us,算幅值耗时203us。这段时间内应该发生了几次中断,所以实际的耗时应该要比它更短。

由于实际需求中不一定需要关注全频域的幅频响应,所以算幅值的203us可以得到相当的节省。

<think>好的,用户想知道如何在Keil MDK中配置和使用CMSIS-DSP进行信号处理。首先,我需要回忆一下相关的步骤。根据之前看过的引用,特别是引用[3]和[4],华大半导体HC32F4A0笔记和STM32-DSP使用可能有帮助。 首先,用户可能已经安装了Keil,但需要确认是否安装了CMSIS Pack。引用[2]提到CMSIS-DSP可以通过CMSIS Pack集成,所以用户需要确保在Keil的Pack Installer里安装了CMSIS-DSP。这一步很关键,因为文件通常在这里添加。 接下来,创建工程时,用户需要使用STM32CubeMX配置硬件,特别是如果用到外设比如ADC或者DMA,这可能影响信号处理的效率。例如,FFT运算可能需要从ADC读取数据,所以正确的引脚和时钟配置是必要的。 然后,添加DSP到工程中。引用[3]提到两种方法:手动添加源文件或者以lib形式添加。用户可能需要根据项目需求选择。如果选择lib,需要指定包含路径,这样编译器能找到头文件。而手动添加可能需要复制源文件到项目目录,这样更灵活但可能麻烦。 配置工程选项时,包含路径必须正确。引用[4]提到不要勾选“No Auto Includes”,让Keil自动包含文件,这样可以避免路径错误。此外,Cortex-M4有浮点单元,所以需要确保在Keil中启用FPU选项,否则浮点运算会慢或者出错。 编写代码部分,用户需要包含DSP的头文件,比如arm_math.h。然后初始化数据结构,比如FFT实例,准备输入输出缓冲区。执行函数后处理数据,比如计算FFT,并应用窗函数或其他后处理步骤。 验证部分,可能用已知信号测试FFT输出是否正确,比如正弦波的峰值频率是否正确。如果结果不对,可能需要检查配置,比如FFT长度是否正确,数据是否对齐,或者是否有足够的栈空间,因为DSP函数可能使用较大栈。 用户可能遇到的问题包括头文件找不到,这时候要检查包含路径;链接错误,可能没添加文件或没选择正确的微控制器型号;或者运算结果错误,需要检查FPU是否启用,数据格式是否正确。 参考引用中的例子,比如HC32F4A0FFT运算,用户可能需要按照类似步骤,确保工程设置正确,特别是自动包含和路径。此外,Python封装(引用[2])可能对测试有用,但用户现在可能更关注Keil环境。 总结步骤应该是:安装Pack、配置工程、添加、设置路径和FPU、编写代码、测试。需要一步步引导用户,避免常见的配置错误,确保所有设置正确。</think>### 如何在Keil MDK中配置和使用CMSIS-DSP进行信号处理 #### **1. 环境准备** - **安装Keil MDK**:确保已安装最新版本的Keil MDK(如v5.37+),并激活对应芯片的软件包(如STM32系列)。 - **安装CMSIS-Pack**:通过Keil的**Pack Installer**(菜单栏 `Pack -> Install`)搜索并安装 `ARM.CMSIS-DSP` 包[^2][^3]。 #### **2. 创建工程并配置硬件** - **使用STM32CubeMX生成工程**: 1. 在CubeMX中选择目标芯片型号(如STM32F4系列支持Cortex-M4的DSP指令)。 2. 配置时钟、外设(如ADC用于信号采集,DMA用于数据传输)。 3. 生成Keil工程时勾选**CMSIS-DSP**的集成选项[^3]。 #### **3. 添加CMSIS-DSP到工程** - **方法一:通过文件(.Lib)添加**: 1. 在Keil工程中右键点击项目 -> `Add Existing Files...`,导航至CMSIS-DSP路径(通常位于 `Keil_v5/ARM/PACK/ARM/CMSIS-DSP/<版本>/Lib/ARM`)。 2. 根据芯片架构选择文件(如Cortex-M4选择 `arm_cortexM4lf_math.lib`,`lf` 表示Little-Endian + FPU)[^3][^4]。 - **方法二:手动添加源文件**: 1. 将CMSIS-DSP源码(如 `Source/TransformFunctions/arm_cfft_f32.c`)复制到工程目录。 2. 在Keil工程中手动添加所需源文件,适用于需要定制化修改的场景。 #### **4. 配置工程选项** - **包含头文件路径**: 1. 右键工程 -> `Options for Target -> C/C++`,在 `Include Paths` 中添加CMSIS-DSP头文件路径(如 `ARM.CMSIS-DSP/Include`)[^4]。 - **启用FPU**: 1. 在 `Options for Target -> Target` 中勾选 `Use Single Precision`(针对Cortex-M4/M7的浮点单元)[^3]。 - **链接器配置**: 1. 在 `Options for Target -> Linker` 中勾选 `Use Memory Layout from Target Dialog`,确保栈/堆空间足够(DSP函数可能需较大栈空间)。 #### **5. 编写信号处理代码示例(FFT)** ```c #include "arm_math.h" // CMSIS-DSP核心头文件 #define FFT_LENGTH 1024 // 定义FFT实例和缓冲区 arm_cfft_instance_f32 fft_inst; float32_t input[FFT_LENGTH * 2]; // 实部+虚部交替存储 float32_t output[FFT_LENGTH]; void main() { // 初始化FFT配置(复数FFT,长度1024) arm_cfft_init_f32(&fft_inst, FFT_LENGTH); // 填充输入数据(例如从ADC读取的信号) for (int i = 0; i < FFT_LENGTH; i++) { input[2*i] = 0.5 * arm_sin_f32(2 * PI * 1000 * i / FFT_LENGTH); // 1kHz正弦波 input[2*i + 1] = 0; // 虚部初始化为0 } // 执行FFT arm_cfft_f32(&fft_inst, input, 0, 1); // 计算幅度谱 arm_cmplx_mag_f32(input, output, FFT_LENGTH); } ``` #### **6. 编译与调试** - **编译**:确保无报错(常见问题:头文件路径错误或未正确链接)。 - **调试**:使用Keil调试器观察 `output` 数组,验证FFT结果是否符合预期(如1kHz信号对应频点幅值)。 #### **7. 信号处理扩展应用** - **滤波器设计**:使用 `arm_fir_init_f32()` 和 `arm_fir_f32()` 实现FIR滤波。 - **卷积/相关运算**:调用 `arm_conv_f32()` 或 `arm_correlate_f32()` 函数。 - **统计分析**:利用 `arm_mean_f32()` 和 `arm_std_f32()` 计算信号均值与标准差[^1]。 --- ### **常见问题与解决** 1. **报错:未定义符号 `arm_cfft_f32`** - **原因**:未正确链接文件或FPU未启用。 - **解决**:检查文件路径和FPU配置,确保选择支持浮点的(如 `arm_cortexM4lf_math.lib`)。 2. **FFT结果异常** - **原因**:输入数据未对齐或FFT长度不匹配。 - **解决**:确保输入数组长度为 `2*FFT_LENGTH`(复数),且内存地址对齐到4字节边界。 3. **性能优化** - **启用DSP指令**:在编译器选项中添加 `-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard` 以启用硬件FPU加速[^3]。 ---
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值