51单片机模拟音乐详解

2019-04-15 17:49发布

想用蜂鸣器模拟出音乐,就需要先把乐谱转换为十六进制数,简单看来音乐就是高低不一,长短不一声的音间隔不同时间的排列组合,所以乐谱改编成十六进制就是三个要素:音符即DO,RE,MI,FA,SO,LA,SI这七个不同音符,音高即高中低三种音,节拍即音符之间的间隔时长. 所以基本思路是用根据这三要素定时器产生音频脉冲,不同音符对应频率如下表: 音符 低 中 高 DO (C) 262 523 1046 RE (D) 294 587 1175 MI (E) 330 659 1318 FA (F) 349 698 1397 SO (G) 392 784 1568 LA (A) 440 880 1760 SI (B) 494 988 1967   每个音符使用1个字节,字节的高4位代表音符的高低,低4位代表音符的节拍,下表为节拍码的对照。但如果1拍为0.4秒,1/4拍是0.1秒,只要设定延迟时间就可求得节拍的时间。假设1/4节拍为1DELAY,则1拍应为4DELAY,以此类推。所以只要求得1/4拍的DELAY时间,其余的节拍就是它的倍数,如下表为1/41/8节拍的时间设定。 曲调 1/4拍的延迟时间 1/8拍的延迟时间 4/4 125ms 62ms 3/4 187ms 94ms 2/4 250ms 125ms   下面直接用程序举例(世上只有妈妈好,两只老虎)介绍下基本的编程方法: #include    #define uchar unsigned char sbit      beep=P3^6;   //定义蜂鸣器输出端口 sbit  s1 = P3^5; //定义一个按键以免刚烧写程序就开始响,可以不用 uchar timer0h,timer0l,time;      //  数据表  (音符,音高,节拍)以 code uchar sszymmh[]={ //   世上只有妈妈好 6,2,3,     5,2,1,      3,2,2,    5,2,2,    1,3,2,    6,2,1,  5,2,1, //                  6,2,4,     3,2,2,      5,2,1,    6,2,1,    5,2,2,    3,2,2,    1,2,1, //                  6,1,1,     5,2,1,      3,2,1,    2,2,4,    2,2,3,    3,2,1,    5,2,2, //                  5,2,1,     6,2,1,      3,2,2,    2,2,2,    1,2,4,    5,2,3,    3,2,1, //                  2,2,1,     1,2,1,      6,1,1,    1,2,1,    5,1,6,    0,0,0      //以免刚开始看不清楚,可以用简谱对比看下                           // 两只老虎 1,2,2,2,2,2,3,2,2,1,2,2, 1,2,2,2,2,2,3,2,2,1,2,2, 3,2,2,4,2,2,5,2,4,                        3,2,2,4,2,2,5,2,4, 5,2,1,6,2,1,5,2,1,4,2,1,3,2,2,1,2,2,    5,2,1,6,2,1,5,2,1,4,2,1,3,2,2,1,2,2, 2,2,2,5,1,2,1,2,4,    2,2,2,5,1,2,1,2,4      };                   /*频率计算: T = 65536 - 1/Fr/2/MC     T:  要算得的定时器初值 Fr:  各音阶对应的频率 MC: 一个机器周期所需的时间 ,11.0592MHz的晶振对应的机器周期为1.085μs EX:低音Do对应的频率为262,T = 65536 - 1/2/1.085/262*(10^6)=63777,对应十六进制数为0xF921,分别写进TH0,TL0 */  // 音阶频率表 高八位(计时器初值) code uchar FREQH[]={0xF2,0xF3,0xF5,0xF5,0xF6,0xF7,0xF8,                     0xF9,0xF9,0xFA,0xFA,0xFB,0xFB,0xFC, 0xFC,0xFC,0xFD,0xFD,0xFD,0xFD,0xFE,                     0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFF,} ;                          // 音阶频率表 低八位(计时器初值) code uchar FREQL[]={0x42,0xC1,0x17,0xB6,0xD0,0xD1,0xB6,                     0x21,0xE1,0x8C,0xD8,0x68,0xE9,0x5B, 0x8F,0xEE,0x44, 0x6B,0xB4,0xF4,0x2D,                     0x47,0x77,0xA2,0xB6,0xDA,0xFA,0x16,}; void delay(uchar t)   // 延时函数  t = 1表示四分之一拍,如果要用到八分之一拍可以改为 t2<2000,类推 { uchar t1; unsigned long t2; for(t1=0;t1音乐处理函数 { TH0=timer0h; TL0=timer0l; TR0=1; delay(time);                        }   void main(void) { uchar k,i; TMOD=1; //CT0定时工作方式1 EA=1; //中断全开 ET0=1; //IE=0x82 //CPU开中断,CT0开中断 while(1) { i=0;   while(i<100)   //音乐数组长度 ,唱完从头再来   {               k=sszymmh[i]+7*sszymmh[i+1]-1; timer0h=FREQH[k]; timer0l=FREQL[k]; time=sszymmh[i+2]; i=i+3; if(s1 == 0) { song(); } } } } void t0int() interrupt 1 //定时器0中断函数 { TR0=0; beep=!beep; TH0=timer0h; TL0=timer0l; TR0=1; }