51单片机模拟音乐详解

来源:互联网 发布:知喻经济发展讲座 编辑:程序博客网 时间:2024/05/11 14:35

想用蜂鸣器模拟出音乐,就需要先把乐谱转换为十六进制数,简单看来音乐就是高低不一,长短不一声的音间隔不同时间的排列组合,所以乐谱改编成十六进制就是三个要素:音符即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 <reg52.h>   

#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<t;t1++)

{

for(t2=0;t2<4000;t2++);

}

TR0=0;

}

void song()  //  音乐处理函数

{

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;

}