LED音乐频谱之输入数据处理

转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/38023431

通过前面的介绍我们知道,声音信号要通过AD转换,变成我们能够处理的数字信号,然后再交给FFT进行处理。

一.ADC转换

1.设置引脚

void GPIO_Init()					 	// GPIO口的初始化
{
	P1M1  = B(00000011);				  //设置P1口模式
	P1M0  = B(00000000);				  //设置P1口模式	只有1.0和1.1为开漏,用于AD
	P1    = B(00000011);
	P1ASF = B(00000011);				  //将P10,P11的IO设置为模拟输入功能;
}

2.ADC初始化

void ADC_Init()							// 集成ADC的初始化(官方函数)
{
    ADC_RES = 0;                   		//Clear previous result
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL;  //ADC_SPEEDLL,每次转换需要420个时钟周期。420*0.04=16.8us
    Delay(2);                      		//ADC power-on and delay
}

3.ADC转换

uchar GetADCResult(uchar ch)		   	// 进行AD转换(官方函数)
{
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ch | ADC_START;
    _nop_();                        //Must wait before inquiry
    _nop_();
    _nop_();
    _nop_();
    while (!(ADC_CONTR & ADC_FLAG));//Wait complete flag
    ADC_CONTR &= ~ADC_FLAG;         //Close ADC
    return ADC_RES;                 //Return ADC result
}

二.FFT

FFT(Fast Fourier Transformation),即为快速傅氏变换,是离散傅氏变换的快速算法,它是根据离散傅氏变换的奇、偶、虚、实等特性,对离散傅立叶变换的算法进行改进获得的。

1.实现的根本:时域到频域

从这张图我们清晰的看到,左面的就是我们输入的信号,右面就是输出到点阵上面的信号。中间的转换过程由FFT实现。

2.算法实现

float code iw[32]=
{
	1.000,0.000,  /* 0.9952,-0.0980,*/   0.9808,-0.1951,  /*0.9569,-0.2903,*/  0.9239,-0.3827,  /*0.8819,-0.4714,*/  0.8315,-0.5556,
  /*0.7730,-0.6344,*/ 0.7071,-0.7071,  /* 0.6344,-0.7730,*/  0.5556,-0.8315, /* 0.4714,-0.8819,*/  0.3827,-0.9239, /*0.2903,-0.9569, */
	0.1951,-0.9808,   /*0.0980,-0.9952,*/  0.0,-1.0000,      /*-0.0980,-0.9952,*/    -0.1951,-0.9808,  /* -0.2903,0.9569,*/   -0.3827,-0.9239,
	/*-0.4714,-0.8819, */  -0.5556,-0.8315, /* -0.6344,-0.7730,  */ -0.7071,-0.7071,   /* -0.7730,-0.6344, */  -0.8315,-0.5556, /*  -0.8819,-0.4714, */
	-0.9239,-0.3827, /*   -0.9569,-0.2903,*/ -0.9808,-0.1951,  /*-0.9952,-0.0980	*/
};	 //偶数cos值 奇数sin值。       复常数码表 。历经几个小时终于尼玛明白了。。原因 就是计算器还要调弧度和角度模式。。好了这这就是将1的sin和cos值按照角度平均分然后得到的sin和cos值即为上表。目的是为了节约计算量。第二个cos(π/32)记住计算器弧度和角度的设置

void ee(struct compx b1,uchar b2)			 //复数乘法
{
	temp.real=b1.real*iw[2*b2]-b1.imag*iw[2*b2+1];
	temp.imag=b1.real*iw[2*b2+1]+b1.imag*iw[2*b2];
} 

uint mypow(uchar  nbottom,uchar  ntop)		 //乘方函数
{
    uint  result=1;
    uchar t;
    for(t=0;t<ntop;t++)result*=nbottom;
    return result;
}

void fft(struct compx *xin,uchar data N)	 //快速傅立叶变换
{
	uchar data  fftnum,i,j,k,l,m,n,disbuff,dispos,dissec;
	data struct compx t;
	fftnum=N;                         //傅立叶变换点数
	for(m=1;(fftnum=fftnum/2)!=1;m++);//求得M的值 			N=64 所以M=6 也就是分解为6级
	for(k=0;k<=N-1;k++)               //码位倒置
	{
		n=k;
		j=0;
		for(i=m;i>0;i--)             //倒置				  例如001变为100
		{
			j=j+((n%2)<<(i-1));		  //二进制除二取余的反过程。就得到了倒置十进制数
			n=n/2;					  //倒置过程中处位和末位都不变即0和63都不变。此例中1对应32
		}
		if(k<j){t=xin[1+j];xin[1+j]=xin[1+k];xin[1+k]=t;}//交换数据	    ,	因为从0开始,所以进行加1处理。例如xin[2]和xin[33]交换。即1与32交换
	}  												  //到此FFT码位倒置结束
	for(l=1;l<=m;l++)                //FFT运算		    l为级数
	{
		disbuff=mypow(2,l);          //求得碟间距离		 求得碟间距离	(乘方函数 pow(X,y)就是计算X的Y次方)
		dispos=disbuff/2;            //求得碟形两点之间的距离
		for(j=1;j<=dispos;j++)		 //每个蝶形群运算的次数        当N等于64时每次进行蝶形运算次数l=1时为1*32 l=2时为 2* 16	 总之每次进行蝶形运算数不变都为N/2	 只不过相乘的两个数的间隔每级都加大
			for(i=j;i<N;i=i+disbuff) //遍历M级所有的碟形
			{
				dissec=i+dispos;     //求得第二点的位置
				ee(xin[dissec],(uint)(j-1)*(uint)N/disbuff);//复数乘法
				t=temp;
				xin[dissec].real=xin[i].real-t.real;
				xin[dissec].imag=xin[i].imag-t.imag;
				xin[i].real=xin[i].real+t.real;
				xin[i].imag=xin[i].imag+t.imag;
			}
	}
} 

虽然注释的比较完善,但我还需要进行几点说明:

a.FFT的产生

我们想通过单片机处理信号,信号必须是离散的和有限长度的数据才能被处理。所以我们只能用DFT(离散傅里叶变换),DFT虽然能够处理信号,但是运算比较复杂,所以根据它的一些性质提出了FFT。

b.蝶形算法

在这里希望读者感兴趣的话找一本信号分析与处理的数来对照着分析,我写的注释比较完善,相信你能够理解,其中的注释部分,是我在用64点采样的时候做的,32点的原理是一样的。我在这里简单说下思路。

1.码位倒置

2.基32的FFT蝶形算法

c.采样点数的选取

这里网上大家通常看到的用单片机一般都采用64个点的。高级一点的STM32的程序用128个点的,而我选择了32个点。这里其实并没有明确要求大家采样用多少个点。但是通常都是2的N次方,因为计算会方便。再来说说我做的,由于由于FFT结果的对称性,通常只使用前N/2个采样点的结果。所以我用32个点采样,刚刚好能满足。采样频率我选择的是1KHZ,由于我一共有16列所以,每个列的频率其实已经固定了。所以加大采样的点数只是增加了计算而已,还有就是增加了我这16个点的选择机会。我测试过采用64个点,发现会有闪屏的现象,就是偶尔你会发现点阵上没有灯亮和刚开机的时候一样,后来分析原因就是运算的时间过长,使得画面无法连续显示。

d.FFT的采样时间

理论的情况:根据香农采样定力我们知道:为了不失真地恢复模拟信号,采样频率应该不小于模拟信号频谱中最高频率的2倍。人耳理论能听到声音范围20---2kHZ,但是实际上超过1kHZ人耳已经很难分辨了,所以我选择了500us定时,也就是2kHZ的采样频率。但是很遗憾结果是失真的,但是失真的结果是可以接受的。

真实的情况:我曾最后拿着音频频谱和分析音频的软件相对比,发现你采样时2KHZ还是4KHZ结果几乎是差不多的。我试着再次减小频率,发现还是差不多。最后分析整个程序我才发现其中的问题。那就是由于单片机性能还是有限处理这种FFT计算还是有些费力。所以造成的运算的时间比较长。这就使得无论你怎样调节定时器,只要超过了某个值,其结果都是一样的,这也是这个设计的一个缺陷之处。

void Timer0() interrupt 1			//我用的24M晶振,ADC采样频率即为采样周期	 2KH	 由于执行语句过长所以即使频率加大也没有仍会失真
{
	TH0 = 0xd1;
    TL0 = 0x20;      

	fft_sign = 1;

}	

e.显示的BUG

显示的时候小伙伴可能会看到第一列的灯始终是亮着的,因为这个是结果产生的直流分量,当时调程序的时候,由于C语言能力有限,没能够弄掉,有点遗憾。

时间: 2024-11-07 08:19:48

LED音乐频谱之输入数据处理的相关文章

LED音乐频谱之输出数据处理

转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/38023539 一.PWM调节 1.初始化 void DACInit() { CCON=0; //PAC初始化 CL=0; // PAC16位计数器低8位 CH=0; // PAC16位计数器高8位 CMOD=0x00; //选择 系统时钟/12为计数脉冲,则PWM的频率f=sysclk/256/12 CCAP0H=0X80; //占空比控制,0x80为百分之50 100000

LED音乐频谱之概述

点击打开链接       转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/37929733 这个LED音乐频谱是我在学51单片机的时候做的一个小项目.今天再看此代码仍有非常多不规范之处,但因为硬件已经送人,没法继续调试,希望小伙伴们引以为戒.写此文主要是想让大家知道当中的原理,知道怎么做固然重要,更重要的是知道为什么要这样做. 一.效果展示 有可能有的人第一次听说过这个东西,但实际上这个东西非常早就有人做出来了. 为了让小伙伴

LED音乐频谱之综述

转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/37929733 这个LED音乐频谱是我在学51单片机的时候做的一个小项目.今天再看此代码仍有很多不规范之处,但由于硬件已经送人,没法继续调试,希望小伙伴们引以为戒.写此文主要是想让大家知道其中的原理,知道怎么做固然重要,更重要的是知道为什么要这样做. 一.效果展示 有可能有的人第一次听说过这个东西,但实际上这个东西很早就有人做出来了.为了让小伙伴们能有一个感性的认识先发下我的成

LED音乐频谱之点阵

转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/37967455 一.硬件 这里的LED选择直插的雾面LED,亮度可以还不失美观.注意每行要加上限流电阻.74HC138(三八译码器)作为列选,每行都连着74HC595(移位寄存器)实现串行输入,并行输出. 二.软件 led.h #ifndef __Led_H #define __Led_H #include "STC12C5A.h" #define uint unsi

Android 音乐频谱实现

最近由于需要实现音乐频谱,所以今天就为大家普及一下.关于音乐频谱你需要了解数字信号处理的知识,尤其是FFT的知识.简单说就是把时域上连续的信号(波形)强度转换成离散的频域信号(频谱).我理解波形就是信号的强度,或者说音响设备的输出的功率,功率高,音量就大.但是歌曲的曲调是不会变的,因为频谱是不会变的. 频谱反映的是这个这个音乐在某个连续时间段内,声音的震动频率.不知道理解的对不对. 本文的音乐频谱实现是仿照Android Api Demo 里的一个例子实现的,需要Android 2.3及以上系统

TensorFlow多线程输入数据处理框架(四)——输入数据处理框架

参考书 <TensorFlow:实战Google深度学习框架>(第2版) 输入数据处理的整个流程. #!/usr/bin/env python # -*- coding: UTF-8 -*- # coding=utf-8 """ @author: Li Tian @contact: [email protected] @software: pycharm @file: sample_data_deal.py @time: 2019/2/8 20:30 @desc:

TensorFlow多线程输入数据处理框架(三)——组合训练数据

参考书 <TensorFlow:实战Google深度学习框架>(第2版) 通过TensorFlow提供的tf.train.batch和tf.train.shuffle_batch函数来将单个的样例组织成batch的形式输出. #!/usr/bin/env python # -*- coding: UTF-8 -*- # coding=utf-8 """ @author: Li Tian @contact: [email protected] @software:

CI框架 -- 核心文件 之 Input.php(输入数据处理文件)

class CI_Input { //用户ip地址 protected $ip_address = FALSE; //用户浏览器地址 protected $user_agent = FALSE; //允许get方式提交数据 protected $_allow_get_array = TRUE; //新行央视标记 protected $_standardize_newlines = TRUE; //xss攻击过滤 protected $_enable_xss = FALSE; //csrf攻击过滤

HMM 模型输入数据处理的优雅做法 来自实际项目

实际项目我是这样做的: def mining_ue_procedures_behavior(seq, lengths, imsi_list): print("seq 3:", seq[:3], "lengths 3:", lengths[:3]) # model.fit(seq, lengths) fitter = LabelEncoder().fit(seq) import sys n_components=[5, 10, 20, 30][int(sys.argv