最近在做一个有趣的小项目,其中有一小部分的内容的是使用fft做音乐频谱显示。于是就有了下面这个音乐频谱显示的低成本方案,话不多说,看看低成本mcu如何实现fft音乐频谱显示吧。
♦音频采集硬件电路
音频采集的硬件电路比较简单,主要的器件就是麦克风和lm358运放。
图中电路r5可调电阻的作用是来调节运放的增益。r4的作用的是给运放一个vdd*r4/(r3+r4) 的直流偏置,这里加直流偏置是由于adc只能采集正电压值,为了不丢失负电压的音频信号,给信号整体加了一个直流偏置。
但是这个图还有一个小问题,运放的输出端加了一个电容c2,c2会把直流偏置给隔掉。在设计时,这个电容可以去掉。
下图是按照上图搭建的音频采集电路的输出信号,图中波动信号是施加的外部音频,是我们需要做音乐频谱显示需要的信号。该信号有一个2.3v的直流偏置,在后续处理时需要减去这个偏置。
♦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> 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> 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 = 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 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 (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
>columnfilter; //calculate the amplitude } //limit maximum column value if(col[index] >= ypix-9) col[index] = ypix-10; indo = index; bufsum = 0; } }
来源:恩智浦mcu加油站
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理
iPhone8什么时候上市:iPhone8全面深度解析,7大部分来揭秘!你想知道的iPhone8都在这
学习探头选型指南方案,如何选好探头呢?
AI智能:无感布控,高效助力智慧执法
网络分析仪8720D维修屏幕花白最新惠普案例
Erecoin项目将创建一个去中心化区块链技术支持平台
如何用低成本MCU实现音乐频谱显示
赋能储能大电芯智造 光大激光切叠一体机“进阶术”
中国拥有led芯片领域自主知识产权吗
逆导晶闸管(RCT)特性分析
大陆半导体产业掘起,今年半导体设备采购金额首度挤下台湾
PCB工厂如何控制PCB板的品质
两会:中国移动董事长杨杰建议强化5G顶层设计 建立健全数据要素市场体系
Dimetix激光测距传感器解决压力隧道的距离/形变监测的问题
JFrog Artifactory—高性能软件制品管理仓库
CW32 电容式触摸按键设计指南
合力泰高频高速基板提高智能终端的信号传输速度
如何布置大型视频会议
遍地开花,势如破竹!美国打压华为恐怕为时已晚
AD转换接口实验
如何获得最新《2020智能驾驶激光雷达行业蓝皮书》