目录
该项目是基于STM32F1的频率测量,采用FFT快速傅里叶变换,配合ADC、DMA、TIM等外设实现频率测量,频率测量偏差小于千分之8。主要运用知识点:
- 1. ADC单通道配置与使用
- 2. 定时器配置及使用
- 3. DMA数据搬运配置与使用
- 4. C语言结构体、数组使用
项目源码文件下载地址:fft_measure V1.1.zip - 蓝奏云
一、快速傅里叶变换(FFT)
快速傅里叶变换(Fast Fourier Transform,简称FFT)是一种高效的算法,用于计算离散傅里叶变换(Discrete Fourier Transform,简称DFT)及其逆变换。傅里叶变换是一种将信号从时域(或空间域)转换到频域的数学方法,反之亦然。这意味着它可以将复杂的波形分解成一系列不同频率的正弦波和余弦波的组合,每个波都有其对应的振幅和相位。这一过程对于信号处理、图像处理、数据分析等领域至关重要。
1.1 工作原理
FFT通过将DFT的计算分解为较小部分的递归算法实现加速。核心思想是利用了傅里叶变换的对称性和周期性,将原问题分解为两个或更多的相似但规模较小的问题,最终通过分治策略合并这些小问题的答案以获得最终结果。特别是,它利用了蝶形运算,减少了乘法和加法的总数,这是FFT效率提升的关键。
1.2 应用
FFT在众多领域有着广泛的应用:
- 信号处理:分析信号的频率组成,滤波,压缩,降噪等。
- 图像处理:图像压缩,边缘检测,纹理分析等。
- 通信:频谱分析,调制解调,信号同步等。
- 音频处理:音乐合成,音调调整,噪声消除等。
- 医学:心电图、脑电图等生物信号的分析。
- 天文学:分析星系光谱,探测宇宙背景辐射等。
1.3 FFT官方支持库
本例中使用STM32提供的DSP库函数进行FFT运算,仅支持F1系列单片机的计算。下载链接地址:STM32_FFT官方库.zip - 蓝奏云。下载后解压文件会得到如下图1所示的源码。

其中可以看到官方提供了两个FFT计算的.s文件,FFT计算点数不同会直接影响到采集的数据精度,因此本文后面采用的是1024点的.s文件用于FFT计算。
然后打开stm32_dsp.h文件可以看到,官方提供了三个计算FFT的函数,如下:
/* Radix-4 complex FFT for STM32, in assembly */
/* 64 points*/
void cr4_fft_64_stm32(void *pssOUT, void *pssIN, u16 Nbin);
/* 256 points */
void cr4_fft_256_stm32(void *pssOUT, void *pssIN, u16 Nbin);
/* 1024 points */
void cr4_fft_1024_stm32(void *pssOUT, void *pssIN, u16 Nbin);
其中每个参数的详细说明如下:
1. *pssOUT: FFT运算后输出的频域数组
2. *pssIN: 输入的时域采样信号数组
3. Nbin: 需要计算的FFT点数。
二、使用外设简介
2.1 ADC外设
模数转换,即Analog-to-Digital Converter,常称ADC,是指将连续变量的模拟信号转换为离散的数字信号的器件。其工作原理涉及对输入的模拟电压进行采样,并将其量化为一定位数的数字值。关键参数包括分辨率(通常以位数表示,如8位、10位、12位等)、转换速度(样本每秒,Sps)、精度、输入通道数量以及是否支持差分输入等。
-
分辨率:决定了ADC能够区分的最小电压变化量,2^n,其中n是位数。例如,一个12位的ADC可以区分2^12=4096个不同的电压等级。
-
转换速率(Conversion Rate/Sampling Rate):单位时间内完成采样并转换的次数,通常以样本每秒(Samples Per Second, Sps)表示。
-
精度:除了分辨率外,还涉及到非线性误差、增益误差和偏置误差等因素,决定了转换结果与真实值的接近程度。
-
输入多路复用:许多ADC支持多个输入通道,通过多路复用器选择要转换的信号。
-
参考电压:决定了ADC能够测量的最大电压范围,通常是ADC的满量程电压。
-
电源和噪声抑制:良好的电源去耦和适当的布局对于减少噪声、提高转换精度至关重要。
其中ADC部分的主要代码初始化如下:
/* ----------------------- ADC结构体初始化 ----------------------- */
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; /* 使用定时器2触发ADC采样 */
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_GPIO_CH, 1, ADC_SampleTime_28Cycles5);
ADC_Cmd(ADC1,ENABLE); /* 使能ADC */
ADC_DMACmd(ADC1, ENABLE);
ADC_ExternalTrigConvCmd(ADC1, ENABLE); /* 外部触发使能 */
2.2 TIM外设
TIM外设,全称为Timer(定时器),是嵌入式系统中常见的外设之一,它主要用于实现定时和计数功能。TIM外设可以实现如下几种功能:
-
定时功能:TIM外设可以基于内部时钟或者外部输入信号进行计数,当计数值达到预设值时,可以产生中断或者DMA请求,从而触发处理器执行特定任务。这使得定时器可以用来实现精确的时间延迟、周期性任务调度等。
-
计数功能:连接到外部输入引脚时,TIM可以作为计数器,记录外部脉冲的数量,适用于测量频率、脉冲宽度等。
-
输出比较:通过比较计数器的值与预设的比较值,可以控制输出引脚的状态,实现PWM(脉宽调制)信号生成,这对于电机控制、LED亮度调节等应用非常有用。
-
输入捕获:捕捉外部输入信号的上升沿或下降沿,可用于测量输入信号的频率、脉冲宽度,实现编码器计数等功能。
-
触发和同步:高级定时器还可以与其他外设(如DAC、ADC)进行同步,用于复杂的控制应用,如在数据采集时精确控制ADC的启动时间。
其中TIM外设部分的初始化代码如下:
/* ----------------------- TIM结构体初始化 ----------------------- */
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = TIM_ARR - 1; /* 自动重装载寄存器周期 */
TIM_TimeBaseStructure.TIM_Prescaler = TIM_PSC - 1; /* 预分频值 */
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; /* 设置时钟分割 */
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; /* TIM向上计数模式 */
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; /* 选择定时器模式:TIM脉冲宽度调制模式1 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; /* 比较输出使能 */
TIM_OCInitStructure.TIM_Pulse = TIM_ARR/2;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; /* 输出极性:TIM输出比较极性低 */
TIM_OC2Init(TIM2, & TIM_OCInitStructure); /* 初始化外设TIM2_CH2 */
TIM_Cmd(TIM2, ENABLE);
3.3 DMA外设
DMA(Direct Memory Access,直接存储器访问)外设是一种专为高效数据传输设计的硬件控制器,它允许外部设备(如传感器、ADC、DAC、网络接口等)和系统内存(RAM)之间直接进行高速数据交换,而无需CPU的持续介入。这一机制显著减轻了CPU的负担,使得CPU可以在数据传输的同时执行其他任务,从而提高了系统的整体性能和响应速度。DMA的主要特点包括:
-
高效传输:DMA能够在不占用CPU资源的情况下,快速传输大量数据,特别适合于大数据量的连续传输,如音频、视频流处理,大容量存储读写等。
-
自动操作:DMA传输由硬件自动完成,一旦配置好DMA控制器(包括源地址、目标地址、传输量和控制参数),数据传输就会在指定的触发条件下自动开始并完成。
-
减少CPU干预:通过DMA,CPU不必参与每次数据的读写操作,仅在传输开始和结束时(或发生错误时)接收中断通知,这样就释放了CPU来执行其他计算密集型任务。
-
多通道支持:现代DMA控制器通常支持多个独立通道,每个通道可以被分配给不同的外设,同时进行数据传输,增加了系统的并行处理能力。
-
优先级管理:DMA控制器能够管理不同通道的优先级,确保关键数据的及时传输,特别是在多个外设同时请求DMA服务时。
-
灵活配置:DMA传输可以配置为单次传输或循环传输,对于需要周期性数据更新的应用(如采样数据的连续采集)非常有用。
-
对齐要求:为了优化传输效率,源和目标地址通常需要按照数据传输宽度(如字节、半字、全字)对齐。
其中DMA部分的主要代码初始化如下:
/* ----------------------- DMA结构体初始化 ----------------------- */
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR); /* ADC1对应地址 */
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adcValue[0]; /* 存储数据数组地址 */
DMA_InitStructure.DMA_DIR = DM