利用定时器定时翻转蜂鸣器的beep引脚,产生方波使蜂鸣器发生,所以根据定时器不同的定时产生不同的方波,就可以改变蜂鸣器的音调(与改变led灯亮度类似)。
void time0() interrupt 1
{
TH0=timeh; //赋值,改变定时
TL0=timel;
beep=~beep; //翻转产生方波
}
用二维数组music[n][m]来存储乐谱,方便歌曲的切换和简化代码,m要开大点,要比最长的乐谱大一点。将定时器0设置最高优先等级。
电子音乐代码(还未优化):
#include <STC15F2K60S2.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
sbit beep=P3^4;
sbit key1=P3^2;
sbit key2=P3^3;
uchar timeh,timel,jindu=0,gequ=0;
bit flag;
uint MusicCnt=3; //歌曲总数
uchar code music[3][500] =
{ /*0x15,“1”代表低八音,“5”代表so*/
{ /*0x21,“2”代表中八音,“1”代表do*/
0x15,0x20,0x21,0x10, /*0x31,“3”代表高八音,“1”代表do*/
0x22,0x10,0x23,0x15, /*上面是音符的记录方法,每个音符后跟的是节拍*/
0x24,0x05,0x23,0x10, /*本代码还未优化节拍,可以利用STC-ISP生成精准延时函数,优化节拍*/
0x21,0x10,0x22,0x20,
0x21,0x10,0x16,0x10,
0x21,0x40,0x15,0x20,
0x21,0x10,0x22,0x10,
0x23,0x10,0x23,0x05,
0x24,0x05,0x25,0x10,
0x21,0x10,0x24,0x15,
0x23,0x05,0x25,0x10,
0x22,0x05,0x23,0x05,
0x23,0x05,0x22,0x05,
0x22,0x30,0x23,0x20,
0x25,0x10,0x31,0x10,
0x27,0x15,0x26,0x05,
0x26,0x20,0x25,0x10,
0x25,0x05,0x26,0x05,
0x27,0x10,0x26,0x05,
0x25,0x05,0x23,0x40,
0x24,0x15,0x24,0x05,
0x25,0x10,0x26,0x10,
0x25,0x10,0x24,0x05,
0x23,0x05,0x22,0x20,
0x17,0x10,0x17,0x05,
0x16,0x05,0x15,0x10,
0x16,0x10,0x21,0x40,
0x00,0x00
},
{
0x21,0x05,0x21,0x05,
0x21,0x05,0x23,0x05,
0x25,0x05,0x25,0x05,
0x25,0x05,0x25,0x05,
0x26,0x05,0x26,0x05,
0x26,0x05,0x31,0x05,
0x25,0x20,0x24,0x05,
0x24,0x05,0x24,0x05,
0x26,0x05,0x23,0x05,
0x23,0x05,0x23,0x05,
0x23,0x05,0x22,0x05,
0x22,0x05,0x22,0x05,
0x22,0x05,0x25,0x15,
0x25,0x05,0x21,0x05,
0x21,0x05,0x21,0x05,
0x23,0x05,0x25,0x05,
0x25,0x05,0x25,0x05,
0x25,0x05,0x26,0x05,
0x26,0x05,0x26,0x05,
0x31,0x05,0x25,0x20,
0x24,0x05,0x24,0x05,
0x24,0x05,0x26,0x05,
0x21,0x05,0x21,0x05,
0x21,0x05,0x23,0x05,
0x25,0x05,0x25,0x05,
0x25,0x05,0x25,0x05,
0x26,0x05,0x26,0x05,
0x26,0x05,0x31,0x05,
0x25,0x20,0x24,0x05,
0x24,0x05,0x24,0x05,
0x23,0x03,0x23,0x03,
0x23,0x03,0x23,0x03,
0x23,0x05,0x23,0x05,
0x22,0x05,0x22,0x05,
0x22,0x05,0x23,0x05,
0x21,0x20,0x00,0x00
},
{
0x16,0x05,0x17,0x05,
0x21,0x15,0x17,0x05,
0x21,0x10,0x23,0x10,
0x17,0x30,0x23,0x10,
0x16,0x15,0x15,0x05,
0x16,0x10,0x21,0x10,
0x15,0x30,0x13,0x10,
0x14,0x15,0x13,0x05,
0x14,0x05,0x21,0x05,
0x21,0x10,0x13,0x30,
0x21,0x10,0x17,0x10,
0x14,0x05,0x14,0x10,
0x17,0x10,0x17,0x20,
0xff,
0x16,0x05,0x17,0x05,
0x21,0x15,0x17,0x05,
0x21,0x10,0x23,0x10,
0x17,0x30,0x13,0x05,
0x13,0x05,0x16,0x15,
0x15,0x05,0x16,0x10,
0x21,0x10,0x15,0x30,
0x13,0x10,0x14,0x10,
0x21,0x05,0x17,0x05,
0x17,0x10,0x21,0x10,
0x22,0x10,0x23,0x05,
0x21,0x20,0xff,
0x21,0x05,0x17,0x05,
0x16,0x10,0x17,0x10,
0x15,0x10,0x16,0x20,
0xff,
0x21,0x05,0x22,0x05,
0x23,0x15,0x22,0x10,
0x23,0x10,0x24,0x10,
0x25,0x05,0x22,0x30,
0x15,0x10,0x22,0x03,
0x21,0x03,0x17,0x03,
0x21,0x03,0x21,0x10,
0x21,0x05,0x22,0x10,
0x23,0x05,0x23,0x40,
0x16,0x05,0x17,0x05,
0x21,0x10,0x17,0x05,
0x21,0x05,0x22,0x10,
0x21,0x15,0x15,0x05,
0x15,0x20,0x24,0x10,
0x23,0x10,0x22,0x10,
0x21,0x10,0x23,0x30,
0x16,0x05,0x17,0x05,
0x21,0x15,0x17,0x05,
0x21,0x10,0x23,0x10,
0x17,0x30,0x13,0x10,
0x16,0x15,0x15,0x05,
0x16,0x10,0x21,0x10,
0x15,0x30,0x13,0x10,
0x14,0x10,0x21,0x05,
0x17,0x05,0x17,0x10,
0x21,0x10,0x22,0x10,
0x23,0x05,0x21,0x05,
0x21,0x20,0x21,0x05,
0x17,0x05,0x16,0x10,
0x17,0x10,0x15,0x10,
0x16,0x40,0x00,0x00
}
};
uchar code quzi[] = /*每个音对应的定时器重装值,左边一列为高位,右边一列为低位*/
{
0xf8,0x8c,//低1
0xf9,0x5b,
0xfa,0x15,//低3
0xfa,0x67,
0xfb,0x04,
0xfb,0x90,
0xfc,0x0c,
0xfc,0x44,
0xfc,0xac,
0xfd,0x09,
0xfd,0x34,
0xfd,0x82,
0xfd,0xc8,
0xfe,0x06,
0xfe,0x22,
0xfe,0x56,
0xfe,0x6e,
0xfe,0x9a,
0xfe,0xc1,
0xfe,0xe4,
0xff,0x03
};
uint ss[3]={180,300,200};//因为还未优化节拍,所以引入ss数组调节节拍
void delay(unsigned int x)
{
uint i,j;
for(i=x;i>0;i--)
for(j=124;j>0;j--);
}
void Delay5ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void Delay100ms(uint x) //@11.0592MHz
{
unsigned char i, j, k;
while(x--)
{
_nop_();
_nop_();
i = 5;
j = 52;
k = 195;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
}
uchar quyin(uchar tem) //取音调对应重装值位置
{
uchar qudiao,jp,weizhi;
qudiao=tem/16;
jp=tem%16;
if(qudiao==1)
qudiao=0;
else if(qudiao==2)
qudiao=14;
else if(qudiao==3)
qudiao=28;
weizhi=qudiao+(jp-1)*2;
return weizhi;
}
void playmusic()
{
uchar p,m,tem;
while(1)
{
if(flag==1)
{
if(gequ==MusicCnt)gequ=0;//歌单播放完,切回第一首
p=music[gequ][jindu];
if(p==0x00) //0x00,歌曲结束标志
{
jindu=0;
gequ++;
Delay100ms(20); //延时2s后,播放下一首
break;
}
else if(p==0xff) //0xff,休止符标志,延时100ms
{
jindu=jindu+1;
Delay100ms(1);
TR0=0;
break;
}
else
{
tem=quyin(music[gequ][jindu]);//取音符的音调对应重装值的位置
timeh=quzi[tem]; //取重装值
timel=quzi[tem+1];
jindu++;
TH0=timeh; //重装
TL0=timel;
m=music[gequ][jindu]; //取节拍
jindu++;
}
TR0=1; //开启定时器
delay(m*ss[gequ]);//延时完成节拍,可以用精准的延时函数进行优化
TR0=0; //关闭定时器
beep=0; //保护蜂鸣器
}
else
while(flag!=1);
}
}
void init()
{
P0M0=0xff;
P0M1=0x00;
P2M0=0x08;
P2M1=0x00;
P3M0=0x10;
P3M1=0x00;
TMOD=0x01;
TH0=0xD8;
TL0=0xEF;
IE=0x87;
IP=0x02;
TR0=0;
beep=0;
}
void main()
{
init();
P0=0x00;
key1=1;
key2=1;
flag=0;
while(1)
{
playmusic();
}
}
void time0() interrupt 1
{
TH0=timeh;
TL0=timel;
beep=~beep;
}
void ex1() interrupt 0
{
Delay5ms();
if(key1==0)
{
while(!key1);
Delay5ms();
flag=~flag;
}
}
void ex2() interrupt 2
{
Delay5ms();
if(key2==0)
{
while(!key2);
Delay5ms();
jindu=0;
gequ++;
if(gequ==MusicCnt)
gequ=0;
}
}
也可以引入其他功能,比如调节歌曲进度,加减jindu就行(注意:进度的边界)。
(若有不足和错误,还请各位斧正)