基于STM32F407实现的信号发生与采集分析系统

演示视频已上传至Bilibili视频平台:https://www.bilibili.com/video/BV1wUiRYxE8z


一、系统功能与整体架构设计

系统实现功能

(1)单片机在按键控制下,产生1kHz的正弦波或方波;

(2)单片机能够采集波形,并且显示;

(3)单片机能够分析采集波形的频谱,并且显示频谱与基波频率。

整体架构设计图

系统主页与按键对应功能简介

每次启动系统都会默认直接进入该主页面:

(1)蓝色部分的文字为系统名称与作者姓名,这会在后续的每个功能页面中都有显示;

(2)黑色部分的文字为各按键对应的功能介绍。

正如主页的功能介绍栏所示:

(1)按下KEY0:PA4引脚开始持续输出1kHz的正弦波信号,并在屏幕上实时显示从PA5引脚采集到的输入信号波形;

(2)按下KEY1:PA4引脚开始持续输出1kHz的方波信号,并在屏幕上实时显示从PA5引脚采集到的输入信号波形;

(3)按下KEY2:在屏幕上实时显示从PA5引脚采集到的输入信号的频谱分析结果(幅值谱,频率范围为0~1000Hz);

(3)按下KEY3(KEY_UP):在屏幕上实时显示从PA5引脚采集到的输入信号的频谱分析结果(幅值谱,频率范围为0~8000Hz)。


二、各部分功能实现

1、1kHz正弦波与方波的产生

模块功能架构设计

在实际单片机编程实现时,导入并调用DSP库加速信号数组(正弦波)的计算,并通过时钟TIM6(分频)控制DMA的数据搬运过程,并设置DAC数模转换将搬运后的信号数字数据在PA4引脚以模拟信号形式输出。

模块功能实现依据

为使用单片机产生指定频率的波形,需要根据上述架构设置对应的参数,基本的设置逻辑如下:

(1)首先,这里使用定时器TIM6来控制DMA搬移数据的过程,在CubeMX中已预先设置其时钟频率为84MHz;

(2)在生成信号数组时,C语言程序中设定数组长度为1024(与后续采集一致,为4的整数次幂以便于进行快速傅里叶变换FFT);

(3)事实上,对于信号数组长度N、定时器频率fT与信号基波频率f而言存在如下关系式:f = fT / N,这意味着以输出基波频率f = 1kH的信号为例,经过时钟分频后的定时器频率fT是可以直接确定的,进而可以确定分频倍数(时钟频率/分频后定时器频率)。

经过计算,当分频倍数设置为82时(实际单片机控制程序中为两次分频,取第一次分频倍数为41、第二次分频倍数为2即二分频),输出的信号基波频率f约为1000(由于数组长度为1024,在分频倍数必须取整的情况下,基波频率无法精准等于1000Hz,实际约为1000.38Hz)。

在MATLAB中,可以编写简单的测试程序模拟这一过程:

1
2
3
4
5
6
7
8
TIM6_Frequency = 84000000; %DAC_DMA时钟TIM6频率
DAC_DMA_Divide1 = 41; %DAC_DMA时钟一次分频
DAC_DMA_Divide2 = 2; %DAC_DMA时钟二次分频
DAC_DMA_Frequency = TIM6_Frequency / (DAC_DMA_Divide1 * DAC_DMA_Divide2); %分频后时钟频率

N = 1024; %数组长度与采样点数

f = DAC_DMA_Frequency / N; %产生信号频率(期望值1000)

模块功能实现效果

启动系统后按压按键KEY_0启动正弦波生成,将示波器的通道正极与信号输出引脚PA4连接,示波器的通道负极与单片机的地GND连接,可在示波器上显示出如下波形:

可以看到输出的波形形状为标准的正弦波,输出电平范围为03.3V(对应生成的正弦信号数组振幅为2048、偏置为2047即数据点范围位于04095),均值为1.6V,且周期约为1kHz(示波器显示1.00045kHz;一个周期大致占据五格、每格代表200us即一个周期为1ms)。

按压按键KEY_1切换为生成方波,可在示波器上显示出如下波形:

可以看到输出的波形形状为标准的方波(占空比50%),输出低电平为0V、高电平为3.3V(对应生成的方波信号数组前一半值为0、后一半值为4095),均值为1.6V,且周期约为1kHz(示波器显示1.00043kHz;一个周期大致占据五格、每格代表200us即一个周期为1ms)。

2、波形信号的采集与显示

模块功能架构设计

在实际单片机编程实现时,通过定时器控制从PA5引脚读入模拟信号,通过ADC模数转换为数字数组并通过DMA搬运将其存入长度为1024(为4的整数次幂以便于进行快速傅里叶变换FFT)的数组中,存满一次数组即中断一次DMA搬运并将该数组数据(即采集波形)显示在显示屏上,短暂延迟(控制屏幕刷新速度合适)后进行新一轮的信号采集、搬运与波形显示。

模块功能实现依据

为使用单片机采集信号数据并以合适的形式将波形显示在显示屏上,需要根据上述架构设置对应的参数,基本的设置逻辑如下:

(1)首先,控制ADC1的定时器在CubeMX中已预先设置其时钟频率为84MHz,但根据相关手册与文档,硬件上对于分频后的ADC实际频率有限制,不能高于30MHz,在这样的条件下一般取四分频(仅分频一次,以对应结构体参数hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4实现),即分频后定时器频率为21MHz;

(2)其次,根据相关手册与文档,完成一次采样至少会花费12个时钟周期,为调控实际采样频率通常还可以设置额外的时钟周期(库函数限制只能为特定的几个值),即实际的采样频率Fs应为:分频后定时器频率(21MHz)/一次采样花费的时钟周期数(12+额外设置的时钟周期);

(3)事实上,要想控制屏幕上显示的波形不过于松散/密集,需要控制一次采样(填满数组,DMA中断)内包含的信号周期数量,这可以通过将信号产生的定时器频率fT除以采样频率Fs得到;

(4)另一方面还需要注意为使得采集到的波形没有失真(频域混叠)现象,要求采样频率Fs与待采集波形频率f满足:Fs≥2f。

经过计算与测试,当额外设置的时钟周期设置为112时(sConfig.SamplingTime = ADC_SAMPLETIME_112CYCLES),一次采样中包含(屏幕上显示)的信号周期约为6,这样的显示效果较为合理;同时此时的采样频率Fs约为42683Hz,远大于待采集波形频率f = 1000Hz的两倍,不会发生频谱混叠。

在MATLAB中,可以编写简单的测试程序模拟这一过程(以正弦信号为例):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ADC_Timer_Frequency = 84000000; %ADC时钟频率
%硬件限制:要求ADC实际时钟频率不能超过30MHz
ADC_Divide = 4; %取四分频,分完后达到21MHz满足要求
ADC_Frequency = ADC_Timer_Frequency / ADC_Divide; %分频后ADC时钟频率

%完成一次采样需要多个时钟周期
Collect1 = 12; %固定消耗12次循环,无法更改
Collect2 = 112; %可设置额外消耗循环数以调整采样频率
Fs = ADC_Frequency / (Collect1 + Collect2); %ADC采样频率

Cycle = DAC_DMA_Frequency / Fs; %一次采样采出多少个周期

A = 2047; %幅值
B = 2048; %直流偏置分量
t = 0 : 1 / Fs : (N - 1) / Fs;
x = A * sin(2 * pi * f * t) + B;

% 绘制原始信号
figure;
subplot(2,1,1);
plot(t, x);
title('正弦波信号');
xlabel('时间 (秒)');
ylabel('幅值');

运行该MATLAB程序,绘制出一次采样采集到的波形如下图所示:

模块功能实现效果

启动系统后按压按键KEY_0启动正弦波信号的生成与采集,将信号输出引脚PA4与信号输入引脚PA5连接,屏幕上显示采集波形效果如下:

按压按键KEY_1切换为生成方波信号并采集,屏幕上显示采集波形效果如下:

图上的横坐标单位为ms;可以看到屏幕上显示的即为6个周期的信号波形,这与MATLAB的模拟计算结果是完全一致的,且波形无失真。

3、采集波形信号的频谱分析

模块功能实现依据

在频谱分析与频谱图显示方面,有如下要点需要注意:

(1)首先,频谱分析依赖于对于信号的傅里叶变换,在数字信号层面对于离散的数据点则需要采用离散傅里叶变换,但这样的变换计算速度往往很感人,因此需要利用其快速算法,即快速傅里叶变换FFT,MATLAB可直接调用fft函数实现,单片机编程中在DSP库中也有相应的函数可以实现完全相同的过程,但要求信号数组的长度应为4的整数次幂,故先前均选取1024作为发生与采集信号的数组长度;

(2)其次,经过FFT变换后会得到一个长度相同(1024)的新数组,其中每一个数字的下标index对应的实际频率应为index*Fs/1024,这意味着如果直接将整个FFT变换结果数组作为频谱图显示到屏幕上,横坐标的跨度实际上为Fs≈42683Hz,为使得频谱图更加直观,需要限制绘制频谱图的频率范围,并对应控制绘制数组中的部分数据;

(3)事实上,FFT变换结果的数组中各数值并不是期望的对应频率的幅值,还需要除以数组长度1024(单片机程序中对于起始点只需要除以一半的数组长度即512)才可得到正确的幅值。

由于涉及到信号的基波频率检测以及方波的频谱分析,在KEY_2和KEY_UP按键分别设置了频谱频率范围为01000Hz与08000Hz两种模式。在MATLAB中,可以编写简单的测试程序模拟频谱分析过程并在0~8000Hz的频段上展示频谱:

1
2
3
4
5
6
7
8
9
f_range = linspace(0, Fs, N);%频域横坐标,注意奈奎斯特采样定理,最大原信号最大频率不超过采样频率的一半
xk = fft(x) / N; %用fft得出离散傅里叶变换

% 计算并绘制频谱
subplot(2,1,2);
plot(f_range(1:50),abs(xk(1:50)));%画双侧频谱幅度图
title('正弦波频谱');
xlabel('频率 (Hz)');
ylabel('幅度');

运行该MATLAB程序,绘制出一次采样采集到的波形如下图所示(以正弦信号为例):

可以看到该信号具有直流分量(频率为0)以及1000Hz除的正弦分量,两者幅值均为2048(与产生波形时的一致)。

除此之外,在单片机编程中,为寻找并在屏幕上打印出信号的基波频率,还需要在显示频率波形的同时完成对于除直流分量外最高幅值对应频率的计算(数组返回最大值对应下表,算法较简单在此省略实现过程)。

模块功能实现效果

启动系统后按压按键KEY_0启动1000Hz正弦波信号的生成与采集,将信号输出引脚PA4与信号输入引脚PA5连接,并按压按键KEY_2可启动短频段0~1000Hz的频谱显示如下:

按压按键KEY_UP可启动长频段0~8000Hz的频谱显示如下:

可以看到此时只有直流分量和1000Hz的正弦分量两个尖峰,与MATLAB模拟计算结果一致。

按压按键KEY_1,切换为1000Hz方波波信号的生成与采集,将信号输出引脚PA4与信号输入引脚PA5连接,并按压按键KEY_2可启动短频段0~1000Hz的频谱显示如下:

按压按键KEY_UP可启动长频段0~8000Hz的频谱显示如下:

可以看到此时在01000Hz频段只有直流分量和1000Hz的正弦分量两个尖峰,但在08000Hz频段,由于方波实质上是不同频率的正弦信号的叠加,所以频谱会在基波的奇数倍(1、3、5……)处也有尖峰,但尖峰的幅值会远小于基波1000Hz处,且倍数越大幅值越小,这使得按照先前的算法也能识别出基波频率约为1000Hz。

4、补充测试

由于还需要对于基波频率在0~1000Hz范围内的任意输入信号进行频谱分析,经过调试后,当输入信号频率为200Hz时,为使得显示波形合理,将ADC环节设置的额外时钟周期由112调整至480,结果如下所示:

200Hz正弦波:

时域:

频域:

短频段(0~1000Hz):

长频段(0~8000Hz):

200Hz方波:

时域:

频域:

短频段(0~1000Hz):

长频段(0~8000Hz):

在ADC环节额外时钟周期设置为480的情况下,可以计算得出,对于频率为1000Hz的信号,一次采样(即屏幕内显示)包含21个周期(正好为整数),结果如下所示:

1000Hz正弦波:

时域:

频域:

长频段(0~8000Hz):

1000Hz方波:

时域:

频域:

长频段(0~8000Hz):

可以看到此时虽然时域上波形显示更加狭窄密集,但是频域上尖峰的变化过程也有了迅速的提升,且测得的基波频率也更加精准。


三、总结

通过本次项目实践,不仅在实验中进一步加深了对于数字信号的产生、采集与频谱分析处理过程的理解,特别是通过期望发生信号频率去计算定时器分频系数、采样频率的计算过程以及FFT计算与频谱图像绘制的过程;而且也增加了对于STM32F407单片机开发的实战经验,在巩固了引脚GPIO与时钟配置相关内容的同时,又对于DMA内存搬运及其中断以及DAC数模转换输出与ADC模数转换输入等功能模块有了更深刻的认识,包括定时器对于这些过程的调控也涉及到相关频率的计算,所有模块的配置之间都有着密切的联系。

基于STM32F407实现的信号发生与采集分析系统

http://asgard-tim.github.io/2024/12/05/document/

作者

Tim

发布于

2024-12-05

更新于

2025-03-01

许可协议

评论