#include <reg51.h>
#include <absacc.h>
#include <stdio.h>
#include <math.h> //头文件
#define uchar unsigned char
#define uint unsigned int //宏定义
uchar STH0; //定时器变量
uchar STL0; //定时器变量
bit FY=0; //模式变量,为0时弹奏模式,为1时播放模式
uchar Song_Index=0,Tone_Index=0;//单首歌曲音符数
uchar k, key; //k:按键数值变量。key:按键的键值(也就是有按键按下时的P0口状态)
sbit SPK=P3^7 ; //定义喇叭的接口
sbit LED1=P3^5;
sbit LED2=P3^4; //定义两个LED的接口
uchar code DSY_CODE[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,//数码管显示的数组(1 2 3 4 5 6 7 8 9)
0x88,0x83,0xc6,0xa1,0x86,0x8e,0x89,0xa3,0x8c,0xc8}; //(A B C D E F H O P N)
unsigned char code num1[]= " Music: ";
uchar code Song[][100]= //内置音乐数组,song【歌曲序号】【音符顺序】
{
// 1 2 3 4 5 6 7
// 8 9 10 11 12 13 14
// 15 16 17 18 19 20 21
{12,10,9,9,10,8,9,10,
12,10,9,9,5,10,11,10,
10,10,14,10,9,8,7,8,
9,10,13,6,8,10,9,6,
8,7,5,6,12,10,9,9,
9,10,8,9,10,12,10,9,
9,9,5,10,11,10,10,10,
14,10,9,8,7,8,9,10,
13,6,8,10,9,6,8,7,
5,6,-1}, //烟花易冷
{13,12,10,12,15,13,
12,13,10,12,13,12,10,8,6,12,
10,9,9,10,12,12,13,10,9,
8,12,10,9,8,6,8,5,-1},//世上只有妈妈好
{10,10,11,10,9,8,9,12,9,9,
8,8,9,8,7,6,7,10,7,7,
6,9,10,9,8,6,5,9,10,9,
8,6,6,9,10,9,8,6,7,8,-1}, //当你孤单你会想起谁
{5,3,5,8,6,8,
5,5,1,2,3,2,1,2,5,
3,5,8,7,6,8,5,5,2,
3,4,0,1,6,8,8,7,6,
7,8,6,7,8,6,6,5,3,
1,2,5,3,5,8,7,6,8,
5,5,2,3,4,0,1,-1}, //送别
{5,6,8,6,6,5,6,5,3,5,
5,6,8,6,6,5,6,5,6,1,
1,2,3,2,2,2,1,2,1,6,
3,2,5,6,8,6,6,5,6,5,
6,1,1,2,3,4,4,5,6,6,
5,6,8,6,8,6,5,5,1,6,
5,5,6,8,3,2,3,1,-1}, //最浪漫的事
{5,9,10,9,10,12,13,12,8,9,
10,13,12,10,12,12,13,15,13,12,
10,12,10,8,9,10,8,6,10,9,
12,9,10,9,10,12,13,12,8,9,
10,13,12,12,13,15,13,
12,10,12,10,8,6,10,9,8,6,
8,9,9,8,-1}, //发如雪
{5,8,9,10,9,10,11,12,12,12,
11,10,9,5,8,9,10,11,12,12,
12,13,12,9,10,8,8,6,9,9,
10,10,8,12,8,12,8,7,8,8,6,
9,9,10,10,12,12,
12,13,12,9,10,8,
5,8,9,10,9,10,11,12,12,12,
11,10,9,5,8,9,10,9,10,11,
12,12,12,9,10,8,8,6,9,9,
10,10,8,12,8,12,12,7,8,-1}, //简单爱
{8,9,10,8,8,9,10,8,10,11,12,10,11,12,
12,13,12,11,10,8,12,13,12,11,10,8,8,5,8,8,5,8,-1}, //两只老虎
{5,5,6,5,8,7,
5,5,6,5,9,8,
5,5,12,10,8,7,6,
11,11,10,8,9,8,-1}, //生日快乐
{6,8,9,10,12,10,8,9,6,8,9,10,
12,12,13,9,10,10,12,13,
12,13,15,14,13,12,13,10,8,9,10,12,8,6,
8,9,10,13,12,10,13,13,
12,11,10,9,10,12,6,8,9,8,9,
10,12,13,15,14,13,12,10,13,-1}, //让我们荡起双桨
{8,8,12,12,13,13,12,
11,11,10,10,9,9,8,
12,12,11,11,10,10,9,
12,12,11,11,10,10,9,
8,8,12,12,13,13,12,
11,11,10,10,9,9,8,-1}, //小星星
{12,10,12,10,12,10,8,9,11,10,9,12,
12,10,12,10,12,10,8,9,11,10,9,8,
9,9,11,11,10,8,12,9,11,10,9,12,
12,10,12,10,12,10,8,9,11,10,9,8,-1}, //粉刷匠
{8,9,10,11,12,12,12,11,10,
11,11,11,10,9,8,10,12,
8,9,10,11,12,12,12,11,10,
11,11,11,10,9,8,10,8,
13,13,13,12,11,12,12,12,11,10,
11,11,11,10,9,8,10,12,
13,13,13,12,11,12,12,12,11,10,
11,11,11,10,9,8,10,8,-1}, //洋娃娃和小熊跳舞
{12,10,12,13,15,16,17,16,15,13,12,15,
17,16,15,12,17,16,15,12,
10,12,13,15,16,17,15,13,15,16,
13,13,14,13,13,17,15,14,15,13,
12,10,12,13,15,16,17,16,15,13,12,15,
17,17,17,17,15,16,16,16,16,12,
12,10,12,13,15,16,17,16,15,13,12,
15,15,-1}, //小红花
{8,9,10,11,12,10,8,15,13,11,12,12,10,
8,9,10,11,12,10,9,8,9,10,9,12,
8,9,10,11,12,10,8,15,13,11,12,10,
8,9,10,11,12,10,9,8,9,10,8,8,
15,13,11,12,12,8,15,13,11,12,10,
8,9,10,11,12,10,9,8,9,10,8,8,
15,13,11,12,12,8,15,13,11,12,10,
8,9,10,11,12,10,9,8,9,10,8,8,-1}, //小红帽
{10,12,16,15,12,11,10,10,10,11,12,
13,12,10,12,16,15,12,11,
10,12,12,13,14,15,15,16,12,12,14,13,12,
10,12,15,13,15,16,15,14,12,10,12,
16,15,12,11,10,12,12,13,14,15,15,-1}, //雪绒花
};
uchar code Len[][100]= //内置音乐对应的节拍(音符持续时间)
{
{1,1,2,4,1,1,1,4,
1,1,2,2,1,1,1,4,
1,1,1,1,2,1,1,1,
1,1,3,1,1,1,1,1,
1,1,1,6,1,1,2,1,
1,1,1,1,4,1,1,2,
1,1,1,1,1,4,1,1,
3,1,2,1,1,1,1,1,
3,1,1,1,1,1,1,2,
2,6,-1},
{3,1,2,2,2,1,
1,4,2,1,1,2,2,1,1,1,
1,4,3,1,2,1,1,2,2,
4,3,1,1,1,1,1,6,-1},
{2,1,1,2,1,1,2,2,2,3,
2,1,1,2,1,1,2,2,2,3,
2,1,1,1,1,2,2,1,1,1,
1,2,2,1,1,1,1,1,1,6,-1},
{2,3,1,3,2,2,
4,2,1,1,2,1,1,4,2,
1,1,2,1,2,2,4,2,1,
1,2,1,4,2,2,4,2,1,
1,4,1,1,1,1,1,1,1,
1,8,2,1,1,2,1,2,2,
4,2,1,1,2,2,4,-1},
{1,1,1,2,1,1,1,1,1,5,
1,1,1,2,1,1,1,1,1,5,
1,1,1,1,1,1,1,1,1,1,
2,3,1,1,1,2,1,1,1,1,
1,5,1,1,1,2,1,1,1,1,
1,2,1,3,1,1,1,4,1,1,
5,1,1,1,1,1,1,6,-1},
{1,1,2,1,1,1,1,3,1,1,
1,1,1,1,3,1,1,2,1,1,
1,1,2,1,1,1,1,1,1,1,
1,1,2,1,1,1,1,3,1,1,
1,1,3,1,1,2,1,
1,1,1,2,1,1,1,1,1,1,
1,1,1,4,-1},
{1,1,1,1,1,1,1,1,1,1,
1,1,3,1,1,1,1,1,1,1,
1,1,2,1,1,2,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,
1,1,2,1,1,2,
1,1,2,1,1,1,1,1,1,1,
1,1,3,1,1,1,1,1,1,1,
1,1,2,1,1,2,1,1,1,1,
1,1,2,1,1,1,1,1,1,-1},
{2,2,2,2,2,2,2,2,2,2,4,2,2,4,
1,1,1,1,2,2,1,1,1,1,2,2,2,2,4,2,2,4,-1},
{1,1,2,2,2,4,
1,1,2,2,2,4,
1,1,2,2,2,2,2,
1,1,2,2,2,4,-1},
{1,1,1,3,1,1,1,2,4,1,1,1,
3,1,2,2,4,1,1,4,
3,1,1,1,1,1,1,2,1,1,3,1,2,2,
1,1,1,1,5,4,3,1,
1,1,2,4,3,1,1,1,3,1,1,
2,2,2,2,1,1,1,1,4,-1},
{2,2,2,2,2,2,3,
2,2,2,2,2,2,3,
2,2,2,2,2,2,3,
2,2,2,2,2,2,3,
2,2,2,2,2,2,3,
2,2,2,2,2,2,4,-1},
{1,1,1,1,1,1,2,1,1,1,1,2,
1,1,1,1,1,1,2,1,1,1,1,2,
1,1,1,1,1,1,2,1,1,1,1,2,
1,1,1,1,1,1,2,1,1,1,1,3,-1},
{1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,2,
1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,2,
1,1,1,1,1,1,1,1,1,2,
1,1,1,1,1,1,1,2,
1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,3,-1},
{1,1,1,1,1,1,2,1,1,1,1,4,
2,1,1,4,2,1,1,4,
2,1,1,2,2,1,1,1,1,4,
2,1,1,3,1,1,1,1,1,4,
1,1,1,1,1,1,2,1,1,1,1,4,
1,1,1,1,4,1,1,1,1,4,
1,1,1,1,1,1,2,1,1,1,1,
4,4,-1},
{1,1,1,1,2,1,1,2,1,1,1,1,2,
1,1,1,1,1,1,1,1,2,2,2,2,
1,1,1,1,2,1,1,2,1,1,2,2,
1,1,1,1,1,1,1,1,2,2,2,2,
2,1,1,1,1,2,2,1,1,2,2,
1,1,1,1,1,1,1,1,2,2,2,2,
2,1,1,1,1,2,2,1,1,2,2,
1,1,1,1,1,1,1,1,2,2,2,2,-1},
{2,2,4,2,2,4,2,2,1,1,2,
4,4,2,2,4,2,2,4,
2,2,1,1,2,4,4,4,1,2,1,1,2,
2,2,4,2,2,2,2,4,4,2,2,
4,2,2,4,2,2,1,1,1,4,4,-1}
};
uint code tab[]={ //内置16个音符弹奏和播放时都是调用此数组内的数据用于定时器初值
63500, //超低音 si
63628,63835,64021,64103,64260,64400,64524, //低音do-si
64580,64684,64777,64820,64898,64968,65030, // do-si
65058,65110,65157,65178,65217,65252,65283, //高音do-si
65313 }; //超高音do
void delay1 (uint ms) //粗略1ms延时函数,不精确
{
uchar t;
while (ms--)
for(t=0;t<120;t++);
}
void delay(void)
{
uchar i;
for (i=300;i>0;i--);
}
uchar getkey(void) //矩阵按键扫描函数
{
uchar scancode,tmpcode;
if((P1&0xf0)==0xf0) //无按键按下时此表达式成立
return (0); //此函数返回值为0
scancode = 0xfe; //如果上面的if表达式不成立,此语句才可以执行
while((scancode&0x10)!=0) //有按键按下时,此表达式成立
{
P1=scancode; //将P0口赋值
if((P1&0xf0)!=0xf0) //判断P0口的状态
{
tmpcode = (P1&0xf0)|0x0f; //P0口的状态与上0xf0,然后或上0x0f
return((~scancode)+(~tmpcode)); //将两个变量的值取反相加后返回此函数
}
else scancode=(scancode<<1)|0x01; //如果上面的if语句不成立,将scancode左移一位
}
return(0xff);
}
void anjian() //按键键值识别
{
P1=0xf0; //P1口赋值
if((P1&0xf0)!=0xf0) //判断是否有按键按下
{
delay(); //去抖
if((P1&0xf0)!=0xf0) //再次判断有无按键按下
{
key=getkey(); //扫描按键
Tone_Index=0; //播放音符顺序清零
switch(key) //根据扫描的按键编码将k赋值
{
case 0x88: //按键编码为0x88
k = 0; //k赋值0
break; //已经确定键值后提前跳出switch
case 0x48: //如果不满足上一个case则继续向下判断,直到有符合
k = 1; //k赋值1
break; //下同,略
case 0x28:
k = 2 ;
break;
case 0x18:
k = 3 ;
break;
case 0x84:
k = 4 ;
break;
case 0x44:
k = 5 ;
break;
case 0x24:
k = 6 ;
break;
case 0x14:
k = 7 ;
break;
case 0x82:
k = 8 ;
break;
case 0x42:
k = 9 ;
break;
case 0x22:
k = 10 ;
break;
case 0x12:
k = 11 ;
break;
case 0x81:
k = 12 ;
break;
case 0x41:
k = 13 ;
break;
case 0x21:
k = 14 ;
break;
case 0x11:
k = 15 ;
break;
default : //如果以上都不符合,直接跳出,无键值输出
break;
}
}
}
}
void main(void) //主函数
{
SPK=0;
LED1=1;
LED2=0; //开机默认弹奏模式
P0=0xc0; //数码管显示0
IE=0x87; //定义外部中断控制器
TMOD=0x01; //定义定时器0的工作方式
IT0=1; //外部中断0为下降沿触发
IT1=1; //外部中断1为下降沿触发
while(1) //进入死循环
{
P1=0xf0; //P1口赋值
if((P1&0xf0)!=0xf0) //判断P0口是否有变化
{
anjian(); //读取键值
P0=DSY_CODE[k]; //显示键值,也就是显示音符
if(FY==0) //如果是弹奏模式
{
STH0 = tab[k]/256;
STL0 = tab[k]%256; //根据k的值赋初值给T0
TR0 = 1; //打开定时器用于定时产生频率发生
while ((P1&0xf0)!=0xf0); //按键不松开的话,T0就一直产生频率
TR0=0; //按键松开后关闭T0计时,频率停止
}
else //如果是播放模式(上面的if语句不成立就执行else)
{
while (FY==1) //进入播放模式
{
if(Song[k][Tone_Index]==-1) //一首播放完退出
{
Tone_Index=0;
SPK=0;
break;
}
STH0=(tab[Song[k][Tone_Index]])/256;
STL0=(tab[Song[k][Tone_Index]])%256; //将内置音乐数组的数据赋给定时器做为初值计时
// P0=DSY_CODE[Song[k][Tone_Index]]; //显示播放的音符
TR0 = 1; //打开定时器定时开关
delay1(300*Len[k][Tone_Index]); //节拍数组延时
Tone_Index++; //变量加准备播放下一个音符
TR0=0; //停止定时器
anjian(); //扫描按键
P0=DSY_CODE[k]; //显示音乐序号
while((P1&0xf0)!=0xf0);
}
}
}
}
}
void EXO_IXT() interrupt 0 //外部中断0
{
FY=0; //弹奏模式
LED1=1;
LED2=0; //点亮弹奏模式指示灯
Tone_Index=0; //歌曲音符序号清零,以便于下次播放内置音乐时从头播放
}
void EX1_INT() interrupt 2 //外部中断1
{
FY=1; //播放模式
LED1=0; //点亮播放模式指示灯
LED2=1;
}
void time0_int(void) interrupt 1 using 0 //定时器0
{
TH0 = STH0; //定时器赋初值
TL0 = STL0;
SPK=!SPK; //喇叭引脚取反,产生频率的音乐
}
|