#include <reg51.h>
#include <intrins.h>
#include <string.h>
#define uint unsigned int
#define uchar unsigned char
//音阶键盘对应频率的定时器初始值
uint code Tone_Delay[]={61719,62135,62506,62679,62985,63263,63512,63624,63833,64019,64104,64260,64399,64523,64581,64685};
//歌曲《找朋友》
uchar code Song1[]={11, 12, 11, 12, 11, 12, 11,16,
11, 14, 13, 12, 11, 11, 9,16,
11, 11, 9, 10, 11, 11, 9,16,
8, 10, 9, 8, 7, 8, 7
};
//歌曲《两只老虎》
uchar code Song2[]={7, 8, 9, 7,
7, 8, 9, 7,
9, 10, 11,16,
9, 10, 11,16,
11, 12, 11, 10, 9,16, 7,16,
11, 12, 11, 10, 9,16, 7,16,
8, 4, 7,16,
8, 4, 7
};
//歌曲《新年好》
uchar code Song3[]={7,17, 7,17, 7,16, 4,16,
9,17, 9,17, 9,16, 7,16,
7, 9, 11,16, 11,16, 10, 9, 8,16,
8, 9, 10,16, 10,16,
9, 8, 9,16, 7,16,
7, 9, 8,16, 4,16, 6, 8, 7
};
//歌曲《我和我的祖国》
uchar code Song4[]={11, 12, 11, 10, 9, 8, 7,16, 4,16,
7, 9, 14, 13, 12,16, 9, 11,16,
12, 13, 12, 11, 9, 8,16, 5,16,
6, 5, 4, 11, 7,16, 8, 9,16,
11, 12, 11, 10, 9, 8, 7,16, 4,16,
7, 9, 14, 13, 15,16, 14, 12,16,
14, 13, 12, 11,16,
12, 11, 10, 9,16,
6,16, 5, 4,16, 8, 7
};
//歌曲《天空之城》
uchar code Song5[]={5, 6, 7,18, 6, 7, 9, 6,16,
2, 5,16, 4, 5, 7, 4,16,
2, 3,18, 2, 3, 7, 2,16,
7, 6,16, 3, 3, 6, 6,16,
5, 6, 7,18, 6, 7, 9, 6,16,
2, 2, 5,16, 4, 5, 7, 4,16,
2, 3,16, 7, 6, 7, 8,16, 9, 7,16,
7, 6, 5,16, 6,16, 4, 5
};
//要写入LCD显示器的字符
uchar code table1[]="Song ";
uchar code table2[]="12345";
uchar code table3[]="words:";
uchar code table4[]="0123456789abcdef";
sbit Beed=P2^0; //扬声器
sbit key1=P2^1; //模式选择键
sbit key2=P2^2; //上一曲
sbit key3=P2^3; //播放/暂停键
sbit key4=P2^4; //下一曲
sbit RS=P2^5; //LCD1602指令/数据寄存器选择端
sbit EN=P2^6; //LCD1602使能端
uint Keynum; //按键号,对应频率表中的位置
uint Songnum=1; //歌曲号
uint sign=1; //标址符号,用来判断是否需要调用播放子程序
uint ScanKey();
uint lcd_initial;
uint Tone;
/主程序/
void main()
{
lcd_initial ();//初始化LCD1602
IE=0x82; //开中断
TMOD=0x01; //设置T0为模式1定时器
while(1) //持续运行系统
{
P1=0xF0; //设置P1
if(key1!=0) //模式选择为演奏
{
if(P1!=0xF0) //有键按下
{
ScanKey(); //键扫描
TH0=Tone_Delay[Keynum]/256; //根据按键给T0赋初值
TL0=Tone_Delay[Keynum]%256;
TR0=1; //启动T0
}
else //无键按下
TR0=0; //关闭T0
Delay(200); //延时
sign=1; //令标志信号为1,使播放功能可以正常使用
}
else if(key1==0) //模式选择为播放歌曲
{
if(key2==0) //上一曲
{
Delay(80); //延时消抖
if(key2==0)
{
sign=1; //令标志信号为1,可以在播放之后重新调用播放子程序
Songnum-=1; //歌曲号-1
if(Songnum<1)
Songnum=Songnum+5; //如果歌曲号小于1,则加5,让歌曲可以循环播放
}
}
if(key4==0) //下一曲
{
Delay(80); //延时消抖
if(key4==0)
{
sign=1; //令标志信号为1,可以在播放之后重新调用播放子程序
Songnum+=1; //歌曲号+1
if(Songnum>5)
Songnum=Songnum-5; //如果歌曲号大于5,则减5,让歌曲可以循环播放
}
}
if(key3==0) //播放状态
{
if(sign==1) //标志符号为1,可以调用播放子程序
{
sign=0; //令标志符号为0,防止在播放时持续重新调用播放子程序
PlaySong(Songnum);
}
else //标志符号为0,不调用播放子程序,只启动T0
{
TR0=1;
}
}
else if(key3!=0) //暂停状态
{
TR0=0; //关闭T0
}
}
}
}
//延时
void Delay(uint x)
{
uint i,j;
for(i=0;i<=x;i++)
for(j=0;j<=120;j++);
}
//给LCD1602写指令
void write_com(uchar com)
{
RS=0; //选择指令寄存器
P0=com; //数据送到P0口
Delay(5); //延时,让LCD1602准备接收数据
EN=1;
EN=0; //使能端从1到0,完成数据写入
}
//给LCD1602写数据
void write_date(uchar date)
{
RS=1;
P0=date;
Delay(5);
EN=1;
EN=0;
}
//LCD1602初始化
void lcd_initial ()
{
EN=0;
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);//清屏
Delay(1);
}
//键扫描
void ScanKey()
{
uint temp;
P1=0xF0; //令P1高四位高电平,低四位低电平
Delay(80); //延时消抖
temp=P1^0xF0; //如果有键按下,P1高四位的某位会变成0,通过此式使该位为1,其余位为0
switch(temp)
{
case 16:Keynum=0;break; //按键在第一行
case 32:Keynum=4;break; //按键在第二行
case 64:Keynum=8;break; //按键在第三行
case 128:Keynum=12;break; //按键在第四行
default:return;
}
P1=0x0F; //令P1高四位低电平,低四位高电平
Delay(80); //延时消抖
temp=P1^0x0F; //如果有键按下,P1低四位的某位会变成0,通过此式使该位为1,其余位为0
switch(temp)
{
case 1:Keynum+=0;break; //按键在第一列
case 2:Keynum+=1;break; //按键在第二列
case 4:Keynum+=2;break; //按键在第三列
case 8:Keynum+=3;break; //按键在第四列
default:return;
}
}
//中断
void Tone() interrupt 1
{
TH0=Tone_Delay[Keynum]/256; //通过频率表给T0赋初值
TL0=Tone_Delay[Keynum]%256;
Beed=~Beed; //扬声器处输出翻转
TR0=1; //启动T0
}
//歌曲播放子程序
void PlaySong(Songnum)
{
uint SongLength = 0; //歌曲常长度变量
uint k=0;
uint x;
uchar tap[100]; //定义一个新数组接收歌曲音符信息
switch(Songnum)
{
case 1:memcpy(tap, Song1, sizeof(Song1));SongLength=sizeof(Song1);break; //新数组接收歌曲信息,算得歌曲长度
case 2:memcpy(tap, Song2, sizeof(Song2));SongLength=sizeof(Song2);break;
case 3:memcpy(tap, Song3, sizeof(Song3));SongLength=sizeof(Song3);break;
case 4:memcpy(tap, Song4, sizeof(Song4));SongLength=sizeof(Song4);break;
case 5:memcpy(tap, Song5, sizeof(Song5));SongLength=sizeof(Song5);break;
}
write_com(0x85); //往LCD1602写入歌曲信息
for(x=0;x<5;x++)
{
write_date(table1[x]);
}
write_com(0x8a);
write_date(table2[Songnum-1]);
write_com(0xc0);
for(x=0;x<6;x++)
{
write_date(table3[x]);
}
k=0;
while(k<SongLength) //按照歌曲音符排列信息播放音乐
{
Keynum=tap[k]; //将音符位置赋给Keynum
if(tap[k]==16) //歌曲中音符号码为16,17,18处进行不同延时,达到音乐播放效果
{
ET0=0;
Delay(400);
ET0=1;
}
else if(tap[k]==17)
{
ET0=0;
Delay(10);
ET0=1;
}
else if(tap[k]==18)
{
ET0=0;
Delay(200);
ET0=1;
}
else
{
TH0=Tone_Delay[Keynum]/256; //给T0赋初值
TL0=Tone_Delay[Keynum]%256;
TR0=1; //启动T0
Delay(400); //延时
write_com(0xc7); //往LCD1602写入歌词
write_date(table4[tap[k]]);
}
while(key3!=0) //检测到暂停键时
{
TR0=0; //停止T0
if(key1!=0)
return; //在暂停时,检测到模式转换为演奏,返回主程序,避免一直停留在这里
}
if(key2==0 || key4==0) //按下切换键时
{
TR0=0; //停止T0
return; //返回主程序
}
if(key1!=0) //检测到模式转换为演奏,返回主程序
return;
k+=1; //下一个音符
}
TR0=0; //一首歌播放完毕,关闭T0
Delay(500); //延时
sign=1; //标志符号变为1,可以重新调用播放歌曲子程序
return; //返回主程序
}