功能:单片机烧录好程序后,按key1按键播放《IF YOU》,再次按key1可暂停播放,按key2可切换歌曲到《国际歌》,再按key3可切换歌曲到《孤勇者》
芯片:STC15F2K61S2
完整工程文件和hex文件在这
提取码:1111
前言:相信大家焊好板子后最喜欢玩的就是蜂鸣器播放音乐,路过每个教室基本都是各种杂音,毕竟这玩意真挺好玩的,于是我就根据老师的代码和谱子写了播放其他歌曲的文件(国际歌是我直接拿往届学长的音乐代码,来凑个数的),说实话这玩意看起来挺简单的,实际上挺难的,还得学一遍乐理知识,然后对着各种拍子和音调不断调试才能得到一个比较理想的状态,但是if you和孤勇者这两首曲子总感觉有些地方不是很完美,但对于摆烂人来说能听就行了。反正也是娱乐娱乐
废话不多说,代码如下:(谱子在最后)
/**********************
文件名称:beep.c
说明:进行音乐播放的例程,按下key1键后开始播放音乐
修改记录:
***********************/
/**********************
基于STC15F2K60S2系列单片机C语言编程实现
使用如下头文件,不用另外再包含"REG51.H"
***********************/
#include <STC15F2K60S2.h>
#define uint unsigned int
#define uchar unsigned char
sbit beep=P3^4; //蜂鸣器
sbit key1=P3^2; //按键1
sbit key2=P3^3; //按键2
uchar timeh,timel,jindu=0,gequ=0; //定义定时器的重装值
bit flag; //播放标志位
uchar code music1[] = //音乐代码,歌曲为《IF YOU》,格式为: 音符, 节拍, 音符, 节拍,
{
0x26,0x10,
0x25,0x10,
0x23,0x10,
0x21,0x10,
0x22,0x10,
0x23,0x10,
0x16,0x20,
0x22,0x08,
0x21,0x08,
0x22,0x08,
0x21,0x08,
0x22,0x08,
0x21,0x08,
0x22,0x10,
0x21,0x08,
0x22,0x08,
0x22,0x08,
0x21,0x08,
0x23,0x20,
0x26,0x10,
0x25,0x10,
0x23,0x10,
0x21,0x10,
0x22,0x10,
0x23,0x10,
0x16,0x20,
0x16,0x08,
0x17,0x10,
0x21,0x08,
0x21,0x08,
0x16,0x08,
0x21,0x08,
0x16,0x08,
0x21,0x08,
0x22,0x08,
0x23,0x08,
0x22,0x08,
0x22,0x10,
0x21,0x08,
0x21,0x08,
0x21,0x10,
0x15,0x08,
0x21,0x20,
0x31,0x10,
0x31,0x08,
0x31,0x08,
0x27,0x08,
0x26,0x08,
0x21,0x10,
0x32,0x10,
0x33,0x10,
0x36,0x10,
0x35,0x10,
0x33,0x10,
0x32,0x10,
0x31,0x10,
0x32,0x30,
0x32,0x08,
0x31,0x08,
0x32,0x08,
0x23,0x08,
0x32,0x10,
0x25,0x24,
0x25,0x08,
0x32,0x08,
0x31,0x08,
0x32,0x08,
0x33,0x24,
0x31,0x08,
0x31,0x10,
0x32,0x10,
0x33,0x10,
0x36,0x10,
0x35,0x10,
0x33,0x10,
0x32,0x10,
0x31,0x08,
0x31,0x08,
0x32,0x10,
0x31,0x30,
0x26,0x08,
0x26,0x08,
0x31,0x08,
0x26,0x08,
0x31,0x08,
0x31,0x24,
0x31,0x08,
0x31,0x08,
0x32,0x08,
0x31,0x08,
0x31,0x08,
0x31,0x24,
0x00,0x10,
};
uchar code music2[] = //歌曲为国际歌
{
0x25, 0x10, 0x31, 0x18, 0x27, 0x08, 0x32, 0x08, 0x31, 0x08, 0x25, 0x08, 0x23, 0x08, 0x26, 0x18, 0x26, 0x08, 0x24, 0x10, 0x20, 0x08,
0x26, 0x08, 0x32, 0x18, 0x31, 0x08, 0x27, 0x08, 0x26, 0x08, 0x25, 0x08, 0x24, 0x08, 0x23, 0x30,
0x25, 0x10, 0x31, 0x18, 0x27, 0x08, 0x32, 0x08, 0x31, 0x08, 0x25, 0x08, 0x23, 0x08, 0x26, 0x20, 0x24, 0x08,
0x26, 0x08, 0x32, 0x08, 0x31, 0x08, 0x27, 0x10, 0x32, 0x10, 0x34, 0x10, 0x27, 0x10, 0x31, 0x20, 0x31, 0x08, 0x30, 0x08,
0x33, 0x08, 0x32, 0x08, 0x27, 0x20, 0x26, 0x08, 0x27, 0x08, 0x31, 0x08, 0x26, 0x08, 0x27, 0x20, 0x25, 0x08,
0x25, 0x08, 0x24, 0x08, 0x25, 0x08, 0x26, 0x18, 0x26, 0x08, 0x32, 0x18, 0x31, 0x08, 0x27, 0x20, 0x27, 0x08, 0x20, 0x08,
0x32, 0x10, 0x32, 0x18, 0x27, 0x08, 0x25, 0x08, 0x25, 0x08, 0x24, 0x08, 0x25, 0x08, 0x33, 0x20, 0x31, 0x08,
0x26, 0x08, 0x27, 0x08, 0x31, 0x08, 0x27, 0x10, 0x32, 0x10, 0x31, 0x10, 0x26, 0x10, 0x25, 0x20, 0x25, 0x08, 0x20, 0x08,
0x33, 0x08, 0x32, 0x08, 0x31, 0x20, 0x25, 0x18, 0x23, 0x08, 0x26, 0x20, 0x24, 0x08, 0x20, 0x08,
0x32, 0x0c, 0x31, 0x04, 0x27, 0x20, 0x26, 0x10, 0x25, 0x10, 0x25, 0x20, 0x25, 0x08, 0x20, 0x08,
0x25, 0x10, 0x33, 0x20, 0x32, 0x10, 0x25, 0x10, 0x31, 0x20, 0x27, 0x18,
0x27, 0x08, 0x26, 0x18, 0x25, 0x08, 0x26, 0x10, 0x32, 0x10, 0x32, 0x20, 0x32, 0x08, 0x30, 0x08,
0x33, 0x0c, 0x32, 0x04, 0x31, 0x20, 0x25, 0x18, 0x23, 0x08, 0x26, 0x20, 0x24, 0x08, 0x20, 0x08,
0x32, 0x0c, 0x31, 0x04, 0x27, 0x20, 0x26, 0x10, 0x25, 0x10, 0x33, 0x30,
0x33, 0x10, 0x35, 0x20, 0x34, 0x10, 0x33, 0x10, 0x32, 0x18, 0x33, 0x08, 0x34, 0x10, 0x30, 0x08,
0x34, 0x08, 0x33, 0x18, 0x33, 0x08, 0x32, 0x18, 0x32, 0x08, 0x31, 0x30,
0x00, 0x00
};
uchar code music3[] = //歌曲为孤勇者
{
0x16,0x08,0x17,0x08,
0x21,0x08,0x22,0x08,
0x17,0x08,0x21,0x08,
0x21,0x10,0x21,0x08,
0x17,0x08,0x21,0x08,
0x22,0x08,0x17,0x08,
0x21,0x08,0x21,0x10,
0x21,0x08,0x22,0x08,
0x23,0x08,0x22,0x08,
0x23,0x08,0x22,0x08,
0x23,0x10,0x23,0x08,
0x22,0x08,0x23,0x10,
0x25,0x10,0x23,0x10,
0x16,0x08,0x17,0x08,
0x21,0x08,0x22,0x08,
0x17,0x08,0x21,0x08,
0x21,0x10,0x21,0x08,
0x17,0x08,0x21,0x08,
0x22,0x08,0x17,0x08,
0x21,0x08,0x21,0x10,
0x21,0x08,0x22,0x08,
0x23,0x08,0x22,0x08,
0x23,0x08,0x22,0x08,
0x23,0x10,0x23,0x08,
0x22,0x08,0x23,0x10,
0x25,0x10,0x23,0x10,
0x25,0x10,0x23,0x14,
0x25,0x08,0x23,0x14,
0x25,0x08,0x23,0x08,
0x25,0x08,0x26,0x08,
0x23,0x08,0x25,0x10,
0x25,0x08,0x23,0x14,
0x25,0x08,0x23,0x14,
0x25,0x08,0x23,0x08,
0x25,0x08,0x26,0x08,
0x23,0x08,0x25,0x10,
0x25,0x08,0x25,0x08,
0x23,0x08,0x22,0x08,
0x22,0x10,0x22,0x10,
0x21,0x08,0x23,0x08,
0x23,0x08,0x22,0x08,
0x22,0x10,0x22,0x10,
0x21,0x08,0x21,0x08,
0x16,0x20,0x25,0x08,
0x25,0x08,0x23,0x08,
0x22,0x08,0x22,0x10,
0x22,0x10,0x21,0x08,
0x23,0x08,0x23,0x08,
0x22,0x08,0x22,0x10,
0x22,0x10,0x21,0x08,
0x21,0x08,0x16,0x20,
0x00
};
uchar code quzi[] = //此数组数据为各个音符在定时器中的重装值,第一列是高位,第二列是低位 //时间
{
0xf8,0x8c, //低八度,低1
0xf9,0x5b,
0xfa,0x15, //低3
0xfa,0x67,
0xfb,0x04, //低5
0xfb,0x90,
0xfc,0x0c, //低7
0xfc,0x44, //中央C调
0xfc,0xac, //中2
0xfd,0x09,
0xfd,0x34, //中4
0xfd,0x82,
0xfd,0xc8, //中6
0xfe,0x06,
0xfe,0x22, //高八度,高1
0xfe,0x56,
0xfe,0x6e, //高3
0xfe,0x9a,
0xfe,0xc1, //高5
0xfe,0xe4,
0xff,0x03 //高7
};
/**********************
函数名称:void delay(unsigned int xms)
功能描述:延时
入口参数:xms:输入需要延时的毫秒值
出口参数:无
备注:
***********************/
void delay(unsigned int xms)
{
uint i,j;
for(i=xms; i>0; i--)
for(j=124; j>0; j--);
}
/**********************
函数名称:uchar quyin(uchar tem)
功能描述:在quzi数组中,找到music数组定义的简谱音符的重装值,并返回其在quzi数组中的位置
入口参数:tem:music数组中定义的简谱音符
出口参数:返回的是tem音符在quzi数组中的位置值
备注:
***********************/
uchar quyin(uchar tem)
{
uchar qudiao,jp,weizhi; //定义曲调,音符和位置
qudiao=tem/16; //高4位是曲调值
jp=tem%16; //低4位是音符
if(qudiao==1) //当曲调值为1时,即是低八度,低八度在quzi数组中基址为0
qudiao=0;
else if(qudiao==2) //当曲调值为2时,即是中八度,中八度在quzi数组中基址为14
qudiao=14;
else if(qudiao==3) //当曲调值为3时,即是高八度,高八度在quzi数组中,基址为28
qudiao=28;
weizhi=qudiao+(jp-1)*2; //通过基址加上音符作为偏移量,即可定位此音符在quzi数组中的位置
return weizhi; //返回这一个位置值
}
/**********************
函数名称:void playmusic()
功能描述:播放音乐
入口参数:无
出口参数:无
备注:
***********************/
void playmusic()
{
uchar p,m,tem; //m为节拍
while(1)
{
if(flag==1) //若播放的标志位为1则播放音乐
{
if(gequ==0) //歌曲选择位为0,则播放同一首歌
{
p=music1[jindu];
if(p==0x00) //如果碰到结束符,延时1秒,回到开始再来一遍
{
jindu=0;
delay(1000);
break;
}
else if(p==0xff) //若碰到休止符,延时100ms,继续取下一音符
{
jindu=jindu+1;
delay(100);
TR0=0;
break;
}
else //正常情况下取音符和节拍
{
tem=quyin(music1[jindu]); //取出当前音符在quzi数组中的位置值
timeh=quzi[tem]; //把音符相应的计时器重装载值赋予timeh和timel
timel=quzi[tem+1];
jindu++;
TH0=timeh; //把timeh和timel赋予计时器
TL0=timel;
m=music1[jindu]; //取得节拍
jindu++;
}
TR0=1; //开定时器1
delay(m*180); //等待节拍完成, 通过P3^4口输出音频
TR0=0; //关定时器1
beep=0; //使beep端口置0,起保护蜂鸣器作用
}
else if(gequ==1) //歌曲选择位为1,则播放小毛驴
{
p=music2[jindu];
if(p==0x00) //如果碰到结束符,延时1秒,回到开始再来一遍
{
jindu=0;
delay(1000);
break;
}
else if(p==0xff) //若碰到休止符,延时100ms,继续取下一音符
{
jindu=jindu+1;
delay(100);
TR0=0;
break;
}
else //正常情况下取音符和节拍
{
tem=quyin(music2[jindu]); //取出当前音符在quzi数组中的位置值
timeh=quzi[tem]; //把音符相应的计时器重装载值赋予timeh和timel
timel=quzi[tem+1];
jindu++;
TH0=timeh; //把timeh和timel赋予计时器
TL0=timel;
m=music2[jindu]; //取得节拍
jindu++;
}
TR0=1; //开定时器1
delay(m*300); //等待节拍完成, 通过P3^4口输出音频
TR0=0; //关定时器1
beep=0; //使beep端口置0,起保护蜂鸣器作用
}
else if(gequ==2) //歌曲选择位为2,则播放天空之城
{
p=music3[jindu];
if(p==0x00) //如果碰到结束符,延时1秒,回到开始再来一遍
{
jindu=0;
delay(1000);
break;
}
else if(p==0xff) //若碰到休止符,延时100ms,继续取下一音符
{
jindu=jindu+1;
delay(100);
TR0=0;
break;
}
else //正常情况下取音符和节拍
{
tem=quyin(music3[jindu]); //取出当前音符在quzi数组中的位置值
timeh=quzi[tem]; //把音符相应的计时器重装载值赋予timeh和timel
timel=quzi[tem+1];
jindu++;
TH0=timeh; //把timeh和timel赋予计时器
TL0=timel;
m=music3[jindu]; //取得节拍
jindu++;
}
TR0=1; //开定时器1
delay(m*200); //等待节拍完成, 通过P3^4口输出音频
TR0=0; //关定时器1
beep=0; //使beep端口置0,起保护蜂鸣器作用
}
}
else //播放标志位不为1时,暂停播放音乐
while(flag!=1);
}
}
/**********************
函数名称:void init_sys()
功能描述:系统初始化,功能是配置IO口
入口参数:无
出口参数:无
备注:
***********************/
void init_sys()
{
P0M0=0xff; //设置推挽模式
P0M1=0x00;
P2M0=0x08;
P2M1=0x00;
P3M0=0x10;
P3M1=0x00;
P4M0=0x00;
P4M1=0x00;
P5M0=0x00;
P5M1=0x00;
}
/**********************
函数名称:void init()
功能描述:定时器和外部中断的初始化
入口参数:无
出口参数:无
备注:
***********************/
void init()
{
TMOD=0x01; //设置定时器0,定时方式1,16位手动重装模式
TH0=0xD8; //设置定时初值
TL0=0xEF;
IE=0x87; // 1000 0111 EA=1,EX0=1,ET0=1,EX1=0;
IP=0x02; // 0000 0010 PT0=1; 定时器0优先级高
TR0=0; //定时器0开始运行
beep=0; //使beep端口置0,起保护蜂鸣器作用
}
void main()
{
init_sys(); //系统初始化
init();
P0=0x00;
key1=1; //两个按键设为输入状态,检测中断
key2=1;
flag=0; //播放标志位清零
while(1)
{
playmusic(); //播放音乐函数
}
}
/**********************
函数名称:void tim1() interrupt 1
功能描述:定时器0中断处理,重新装值,并把beep值取反,产生方波
入口参数:无
出口参数:无
备注:
***********************/
void tim1() interrupt 1 //计时器控制频率
{
TH0=timeh; //赋初值
TL0=timel;
beep=~beep; //中断使得beep翻转产生方波
}
/**********************
函数名称:void ex1() interrupt 0
功能描述:按下按键1的外部中断,对标志位取反,功能是暂停和播放音乐
入口参数:无
出口参数:无
备注:
***********************/
void ex1() interrupt 0
{
delay(5);
if(key1==0) //判断key1是否按下
{
delay(5);
if(key1==0)
{
while(!key1);
flag=~flag; //播放中断位取反,播放或者暂停
}
}
}
/**********************
函数名称:void ex2() interrupt 2
功能描述:按下按键2的外部中断,作用为切换歌曲
入口参数:无
出口参数:无
备注:
***********************/
void ex2() interrupt 2
{
delay(5);
if(key2==0) //按键key2按下,切换歌曲
{
jindu=0; //歌曲的进度清零,从头开始演唱
gequ++; //下一曲
if(gequ==3) //到达最后一首调到第一首
gequ=0;
}
}