STM32F4 FFT 音乐频谱 不要太easy!

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hduxiejun/article/details/46287341

      在数字信号处理当中,我们最常用的就是卷积和离散傅里叶变换(DFT)。可以证明卷积、相关、谱分析、滤波等都可以转化为离散傅里叶变换来实现。大学学了信号和系统、数字信号处理对于傅里叶变换有了更深的理解。

下面介绍一种超级简单的方法做一个用FFT做一个音乐频谱仪。过程中参考了正点原子的STM32F4教程,在此,要特别感谢正点原子。

先看下效果:

  • 系统采用基于ARM-Cortex M4核的STM32F407ZGT6单片机,调用官方DSP库进行4096点FFT运算,然后实时显示音乐信号的频谱。

下面将重点介绍核心函数和算法:

1、 第一个函数是

arm_status arm_cfft_radix4_init_f32(arm_cfft_radix4_instance_f32 * S,

                 uint16_t fftLen,uint8_t ifftFlag,uint8_t bitReverseFlag)

      在使用时,这个函数用于初始化 傅里叶变换运算的相关配置。fftLen用于确定FFT变换的长度,其默认有5种不同的设置,分别是(16/64/256/1024/4096)。本系统采用4096点傅里叶变换,故将此设置为4096。ifftFlag这个参数为0或者是1,为0时是傅里叶变换,为1时为傅里叶反变换,系统采用傅里叶变换,固将其配置为0;bitReverseFlag的取值为0或者1,为1时是按位取反,系统配置为1。最后这些参数存储在一个结构体指针S里面。

2、第二个函数是

void arm_cfft_radix4_f32(const arm_cfft_radix4_instance_f32 * S,float32_t * pSrc)

    本系统在计算基-4浮点傅里叶变换运算时就是利用这个函数,通过指针pSrc传入采集到的输入信号数据(实部和虚部形式)。这里要特别注意,输入信号应以复数的形式传入,实部放在奇数位,虚部放在偶数位。进行4096点FFT运算,则pSrc指向4096个复数,实际应用中,系统通过A/D采样,这样偶数位全部为0,奇数位为A/D转换器采集到的电压值。同时FFT变换后的数据也存放在pSrc里面,pSrc必须不小于fftLen的长度。另外,S结构体指针参数是由先该函数设置好,然后传入该函数的。

3、第三个函数是

void arm_cmplx_mag_f32(float32_t * pSrc,float32_t * pDst,uint32_t numSamples)

      系统通过这个函数来计算复数模值,可以对第二个函数* pSrc指向的输出结果进行傅里叶变换进行取模操作。pSrc指向第二个函数傅里叶变换的结果,为复数输入数组指针;pDst为输出数组指针,存储取模后的值;numSamples是进行取模的点数,系统进行4096点的傅里叶变换,在此配置为4096即可。

      为了验证算的可靠性,我们可以通过模拟生成输入序列的方式进行验证。

for(i=0;i<Length;i++)
{
   Inputbuf[2*i]=1+
  1*arm_sin_f32(2*PI*10*i/Fs)+
  3*arm_sin_f32(2*PI*40*i/Fs)+
  5*arm_sin_f32(2*PI*80*i/Fs);
  Inputbuf[2*i+1]=0;
}
  arm_cfft_radix4_init_f32(&Base4fft,Length,0,1);
  arm_cfft_radix4_f32(&Base4fft,Inputdata);
  arm_cmplx_mag_f32(Inputdata,Outputdata,Length);

     上段代码就是模拟生成一个序列。原始信号由三个正弦信号和一个直流信号叠加而成,频率分别是10Hz、40Hz和80Hz,峰值分别为1、3、5,直流分量为1。 通过测试,可以发现,系统计算4096点的傅里叶变换需要的时间仅仅只要2.71ms,可见其速度是非常快。在傅里叶变换的输出中,我们可以发现只有在Outputbuf[0]、Outputbuf[1]、Outputbuf[4]、Outputbuf[8]处傅里叶变换的结果分别为4096、2048、6144、10240;Outputbuf[4088]、Outputbuf[4092]、Outputbuf[4095]处的值分别为10240、6144、2048。下面来分析这些结果。

     模拟的采样频率为40960Hz,采样的点数为4096,由数字信号处理知识可知,分辨率为10Hz,也即傅里叶变换计算后的频谱之间的间隔是10Hz。而Inputdata[2*i]=1+1*arm_sin_f32(2*PI*10*i/Fs)+3*arm_sin_f32(2*PI**40*i/Fs)+5*arm_sin_f32(2*PI*80*i/Fs)。可知待测信号由三个正弦信号和一个直流信号叠加而成,频率分别是10Hz、40Hz和80Hz,峰值分别为1、3、5,直流分量为1。

     因为A/D采样后的时序信号为实信号,根据DFT的对称性可知,其傅里叶变换的结果具有共轭反对称性。Outputbuf[1]和Outputbuf[4095],Outputbuf[4]和Outputbuf[4092],Outputbuf[8]和Outputbuf[4088]幅值应该相同,实际情况再次和理论值完全相同。

     根据DFT变换后的性质可知:Outputbuf[0]=4096,其傅里叶变换变换后的模值刚好是原始信号的4096倍,所以值为4096.0;而在其它频率点处,模值应该是幅值的4096/2=2048倍,输出的结果刚好为Outputbuf[1]=4096、Outputbuf[4]=2048、Outputbuf[8]=6144与理论值完全相同,可见傅里叶变换运算的精度极高,运算速度极快。

下面我们用Matlab验证算法的正确性:

Matlab 程序:

close all; %Turn off all the pictures 
clc;      
DC=1;   %The magnitude of the dc signal 
Amp1=1; %The amplitude  of the first signal 
Amp2=3; %The amplitude  of the second signal 
Amp3=5; %The frequency of the third signal 
Fre1=10;%The frequency of the first signal )
Fre2=40;%The frequency of the Second signal 
Fre3=80;%The frequency of the third signal 
Fs=40960; %The sampling frequency
N=4096;   %The sampling points 
t=[0:1/Fs:1]; %Sampling time
S=DC+Amp1*sin(2*pi*Fre1*t)+Amp2*sin(2*pi*Fre2*t)+Amp3*sin(2*pi*Fre3*t); %信号
%Display the original signal
subplot(3,1,1);
plot(S,'b');
grid on;
xlabel('                                                                       Frequency/HZ');
ylabel('Voltage');
title('The original signal');
Y = fft(S,N);    %Do FFT transform 
Module = (abs(Y));   %:Acquire modulus
subplot(3,1,2);
F=([1:N]-1)*Fs/N; 
h = stem(F(1:N/320)/10,Module(1:N/320),'fill','--');
set(get(h,'BaseLine'),'LineStyle',':')
set(h,'MarkerFaceColor','red')
grid on;
xlabel('                                                                       FFTout[i]');
ylabel('FFT module');
title('the module value of FFT');
Module=Module/(N/2);  
Module(1)=Module(1)/2;
F=([1:N]-1)*Fs/N; 
subplot(3,1,3);
h = stem(F(1:N/320),Module(1:N/320),'fill','--');
set(get(h,'BaseLine'),'LineStyle',':')
set(h,'MarkerFaceColor','red')
grid on;
xlabel('                                                                       Frequency /HZ');
ylabel('Voltage');
title('Amplitude - frequency curves');
Matlab运行的结果如下所示:

1 FFT仿真结果

    从Matlab仿真出来的结果和理论的结果完全一样。图一为原始的信号,原始信号由三个正弦信号和一个直流信号叠加而成,频率分别是10Hz、40Hz和80Hz,峰值分别为1、3、5,直流分量为1。图二为输入信号进行FFT运算后的输出结果,可以看出在FFTout[1]、FFTout[4]、FFTout[8]处有值,且值得大小和输入信号的幅度有关。图三为将模值化为实际电压值后的结果,可以看出其分别在频率0Hz、10Hz、40Hz、80Hz处的幅值分别为1、1、3、5。


注意事项:

  • 输入音乐信号可以是电脑、iPad、手机等音频信号,但是不可接直接将音频信号送入STM32的IO口,负的电平可能直接烧坏STM32的内部AD,因此需要做适当的信号调理电路,对输入的音乐信号进行放大、电平抬升等。




猜你喜欢

转载自blog.csdn.net/hduxiejun/article/details/46287341