0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

MCU如何实现FFT音乐频谱显示

恩智浦MCU加油站 来源:恩智浦MCU加油站 作者:恩智浦MCU加油站 2022-09-08 09:07 次阅读

最近在做一个有趣的小项目,其中有一小部分的内容的是使用FFT做音乐频谱显示。于是就有了下面这个音乐频谱显示的低成本方案,话不多说看看低成本MCU如何实现FFT音乐频谱显示吧。

音频采集硬件电路

音频采集的硬件电路比较简单,主要的器件就是麦克风和LM358运放。

6709c17e-2f0e-11ed-ba43-dac502259ad0.png

图中电路R5可调电阻的作用是来调节运放的增益。R4的作用的是给运放一个VDD*R4/(R3+R4) 的直流偏置,这里加直流偏置是由于ADC只能采集正电压值,为了不丢失负电压的音频信号,给信号整体加了一个直流偏置。

但是这个图还有一个小问题,运放的输出端加了一个电容C2,C2会把直流偏置给隔掉。在设计时,这个电容可以去掉。

下图是按照上图搭建的音频采集电路的输出信号,图中波动信号是施加的外部音频,是我们需要做音乐频谱显示需要的信号。该信号有一个2.3v的直流偏置,在后续处理时需要减去这个偏置。

673ca6e8-2f0e-11ed-ba43-dac502259ad0.png

CTimer+ADC+DMA 音频信号采集

为了呼应标题,我们选择的MCU是LPC845,这是NXP的一款低成本的MCU。考虑到我们平常听的音乐频率大都低于5kHz,在软件设计时设置ADC采样频率为10kHz。不要问为什么,问就是采样定理。

LPC845的ADC有8个触发源,我们使用CTiimer match3来触发ADC,将寄存器SEQA_CTRL的bit 14:12设置为0x5。CTimer match 3的输出频率为10kHz。

为了确保我们采集数据的实时性,DMA建议配置成双buffer模式,以防止采样的数据被覆盖掉。

FFT音频信号处理

在DMA搬运ADC采样值时,使用了双buffer来搬,ADC采样值需要减去一个2.3V的直流偏置。Samples[]数组用于FFT计算。

    //Calculate the FFT input buffer
   if(g_DmaTransferDoneFlag_A == true)
   {
   for (i=0; i<128; i++)
   {
  Samples[i] =(int16_t)(((g_AdcConvResult_A[i] & 0xfff0) >> 4) - 2979);//substract the 2.3v offset in the Amplifier output
    }
  g_DmaTransferDoneFlag_A = false;

    }
   else if(g_DmaTransferDoneFlag_B == true)
   {
   for (i=0; i<128; i++)
   {
  Samples[i] =(int16_t)(((g_AdcConvResult_B[i] & 0xfff0) >> 4) - 2979);//substract the 2.3v offset in the Amplifier output
   }
   g_DmaTransferDoneFlag_B = false;
   }

根据FFT算法的原理,在进行FFT计算之前,还需要将ADC的采样值Samples[]乘上一个窗函数,这里我们使用的汉宁窗函数,由于篇幅限制,具体原理可以去查看FFT算法相关的资料

    //If 'Window' isn't rectangular, apply window
    if(Window == Triangular){
        //Apply a triangular window to the data.
        for(Cnt = 0; Cnt>L2Len;
            else Samples[Cnt] = ((int32_t)Samples[Cnt]*((Len/2)-Cnt))>>L2Len;
        }
    }
    else if(Window == Hann){
        //Use the cosine window wavetable to apply a Hann windowing function to the samples
        for(Cnt = 0; Cnt>L2Len;
            Samples[Cnt] = ((int32_t)Samples[Cnt]*(int32_t)CosWindow[Index])>>(CWBD);
        }
    }

前面说了这么多,FFT算法才是实现音乐频谱显示的关键部分(其实上边每一步都缺一不可)。

我在网上找了好多FFT算法的资料,大家在做频谱显示时,用到最多的就是CMSIS DSP的算法库。于是乎,采用CMSIS DSP的库貌似是首选。

但是不用不知道,一用才发现,由于CMSIS DSP的库使用的是查表的方式,我的64K Flash的LPC845轻轻松松就被撑爆了。没办法,只能改用其他方案。经过不懈的查阅资料,在GitHub找到一份FFT算法的代码,这个代码写的非常简洁,而且用起来很好用,感谢发布者pyrohaz,下面是FFT代码的一部分。

/*
  FIX_MPY() - fixed-point multiplication & scaling.
  Substitute inline assembly for hardware-specific
  optimization suited to a particluar DSP processor.
  Scaling ensures that result remains 16-bit.
*/
inline short FIX_MPY(short a, short b)
{
    /* shift right one less bit (i.e. 15-1) */
    int c = ((int)a * (int)b) >> 14;
    /* last bit shifted out = rounding-bit */
    b = c & 0x01;
    /* last shift + rounding bit */
    a = (c >> 1) + b;
    return a;
}

fix_fft(short fr[], short fi[], short m, short inverse)函数,FFT计算函数

int fix_fft(short fr[], short fi[], short m, short inverse)
{
    int mr, nn, i, j, l, k, istep, n, scale, shift;
    short qr, qi, tr, ti, wr, wi;

    n = 1 << m;

    /* max FFT size = N_WAVE */
    if (n > N_WAVE)
        return -1;

    mr = 0;
    nn = n - 1;
    scale = 0;

    /* decimation in time - re-order data */
    for (m=1; m<=nn; ++m) {
        l = n;
        do {
            l >>= 1;
        } while (mr+l > nn);
        mr = (mr & (l-1)) + l;

        if (mr <= m)
            continue;
        tr = fr[m];
        fr[m] = fr[mr];
        fr[mr] = tr;
        ti = fi[m];
        fi[m] = fi[mr];
        fi[mr] = ti;
    }       

接 fix_fft(short fr[], short fi[], short m, short inverse)函数

    l = 1;
    k = LOG2_N_WAVE-1;
    while (l < n) {
        if (inverse) {
            /* variable scaling, depending upon data */
            shift = 0;
            for (i=0; i 16383 || m > 16383) {
                    shift = 1;
                    break;
                }
            }
            if (shift)
                ++scale;
        } else {
            /*
                fixed scaling, for proper normalization --
                there will be log2(n) passes, so this results
                in an overall factor of 1/n, distributed to
                maximize arithmetic accuracy.
            */
            shift = 1;
        }

接fix_fftr(short f[], int m, int inverse)函数

        /*
            it may not be obvious, but the shift will be
            performed on each data point exactly once,
            during this pass.
        */
        istep = l << 1;
        for (m=0; m>= 1;
                wi >>= 1;
            }
            for (i=m; i>= 1;
                    qi >>= 1;
                }
                fr[j] = qr - tr;
                fi[j] = qi - ti;
                fr[i] = qr + tr;
                fi[i] = qi + ti;
            }
        }
        --k;
        l = istep;
    }
    return scale;
}

/*
  fix_fftr() - forward/inverse FFT on array of real numbers.
  Real FFT/iFFT using half-size complex FFT by distributing
  even/odd samples into real/imaginary arrays respectively.
  In order to save data space (i.e. to avoid two arrays, one
  for real, one for imaginary samples), we proceed in the
  following two steps: a) samples are rearranged in the real
  array so that all even samples are in places 0-(N/2-1) and
  all imaginary samples in places (N/2)-(N-1), and b) fix_fft
  is called with fr and fi pointing to index 0 and index N/2
  respectively in the original array. The above guarantees
  that fix_fft "sees" consecutive real samples as alternating
  real and imaginary samples in the complex array.
*/
int fix_fftr(short f[], int m, int inverse)
{
    int i, N = 1<<(m-1), scale = 0;
    short tt, *fr=f, *fi=&f[N];

    if (inverse)
        scale = fix_fft(fi, fr, m-1, inverse);
    for (i=1; i

int fix_fft(short fr[], short fi[], short m, short inverse) 是FFT算法的计算函数,fr[]是ADC采集到信号值的实部,fi[]是ADC采集到信号值的虚部。经过fix_fft函数处理之后,fr[]是FFT计算所得实部,fi[]是计算所得的虚部。

我们最终要显示的音乐频谱其实是FFT频域中音频的幅值,幅值的计算是实部的平方+虚部的平方开根号。下面是具体的幅值计算部分的代码,每一个幅值点对应OLED的一列像素点。

    //Calculate the magnitude
    for(Cnt = 0; Cnt>ColumnFilter; //calculate the DB
            }
            else{
                Col[Index] += (BufSum-Col[Index])>>ColumnFilter;  //calculate the amplitude
            }

            //Limit maximum column value
            if(Col[Index] >= YPix-9) Col[Index] = YPix-10;

            IndO = Index;
            BufSum = 0;
        }
    }

审核编辑:彭静
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • mcu
    mcu
    +关注

    关注

    146

    文章

    17123

    浏览量

    350973
  • 硬件电路
    +关注

    关注

    39

    文章

    243

    浏览量

    29216
  • 音乐频谱
    +关注

    关注

    1

    文章

    13

    浏览量

    8936

原文标题:如何用低成本MCU实现音乐频谱显示

文章出处:【微信号:NXP_SMART_HARDWARE,微信公众号:恩智浦MCU加油站】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    基于MCUFFT音乐频谱显示方案

    最近在做一个有趣的小项目,其中有一小部分的内容的是使用FFT音乐频谱显示。于是就有了下面这个音乐频谱
    发表于 09-08 09:05 1734次阅读

    前几天做的一个音乐fft频谱显示效果

    本帖最后由 wishriver1 于 2024-9-10 11:53 编辑 前段时间做的频谱显示fft显示, 利用fft原理
    发表于 11-10 19:09

    单片机制作音乐频谱显示

    见他们好多人都用单片机作的音乐频谱显示器,不知道如何做到的,记得学习数字信号处理的时候须要用到好多FFT啊之类的算法,这里也会用到吗?如何编程实现
    发表于 08-25 22:09

    怎样实现根据音乐跳动的频谱显示

    最近在网上看见好多人做的可以跟着音乐的节奏跳动的频谱显示,主要就是FFT用51单片机怎么实现?求大神帮助,谢谢{:soso_e149:}
    发表于 09-16 11:42

    基于SLH89F5162的单片机智能音乐频谱播放器

    是采用32点的基2-FFT快速傅里叶算法,然后通过两块8*8点阵显示频谱。 功能与使用说明 该作品可以实现音乐播放以及
    发表于 10-31 14:29

    FFT音乐频谱资料

    在阿莫电子论坛看到两个音乐频谱的视频,看起来很漂亮,是下面显示频谱,上面显示波形的,但能力有限,我也做了个频柱的,感觉效果差很多!网上找到的
    发表于 08-21 16:50

    STM32F103+FFT+OLED25664的音乐频谱制作资料分享

    前言:之前,笔者设计并制作了一个简单的FFT音乐频谱,并且分享了制作过程。本文是在原来的基础上做了一些修改。效果演示:https://www.bilibili.com/video
    发表于 02-09 06:24

    基于RT-Thread+RA6M4的FFT音乐频谱显示器制作方案

    1、基于RT-Thread+RA6M4的FFT音乐频谱显示器制作本项目制作了一个FFT音乐
    发表于 07-25 12:46

    基于Matlab的系统信号FFT频谱分析与显示pdf

    基于Matlab的系统信号FFT频谱分析与显示
    发表于 06-19 14:55 63次下载

    基于MATLAB系统的信号FFT频谱分析与显示

    基于MATLAB系统的信号FFT频谱分析与显示:给出一种用MATLAB系统实现信号频谱分析与显示
    发表于 02-08 12:38 101次下载

    STM32F103+FFT+OLED25664的音乐频谱制作分享(继续干货)

    STM32F103+FFT+OLED25664的音乐频谱制作分享(继续干货)
    发表于 12-05 18:06 58次下载
    STM32F103+<b class='flag-5'>FFT</b>+OLED25664的<b class='flag-5'>音乐</b><b class='flag-5'>频谱</b>制作分享(继续干货)

    stm32音乐频谱OLED屏显示

    基于STM32F103和FFT变换的音乐频谱显示
    发表于 06-17 14:52 52次下载

    基于STM32单片机的音乐频谱显示器设计

    系统设计主要涉及的有作为核心进行计算的STM32单片机的主控电路设计、实现蓝牙电磁数据传输的MH-M18蓝牙音频采集电路的设计、负责提供音乐切换控制信号的按键状态采集电路、对FFT变换计算所得
    的头像 发表于 08-22 15:47 6189次阅读
    基于STM32单片机的<b class='flag-5'>音乐</b><b class='flag-5'>频谱</b><b class='flag-5'>显示</b>器设计

    如何用低成本MCU实现音乐频谱显示

    如何用低成本MCU实现音乐频谱显示
    的头像 发表于 10-18 17:10 879次阅读
    如何用低成本<b class='flag-5'>MCU</b><b class='flag-5'>实现</b><b class='flag-5'>音乐</b><b class='flag-5'>频谱</b><b class='flag-5'>显示</b>

    什么是实时频谱分析仪呢?傅里叶变换(FFT)如何实现频谱测量?

    什么是实时频谱分析仪呢?傅里叶变换(FFT)如何实现频谱测量? 实时频谱分析仪是一种用于测量信号频谱
    的头像 发表于 01-19 15:50 2973次阅读