基于51单片机的心形流水灯+无源蜂鸣器播放音乐

系列文章目录


前言

你还在为送什么生日礼物给女神而烦恼吗?不用多想了,快试试这个用51单片机制作的有音乐播放功能的流水灯吧,相信一定能打动她的芳心。爱她就TMD(甜蜜地)给她一个TMD(甜蜜的)惊喜!

本作品所用单片机芯片是:STC89C52RC,无源蜂鸣器播放音乐有:《生日快乐》、《祝寿歌》、《生日祝福歌》、《小幸运》

播放效果:B站搜索“甘腾胜”或“gantengsheng”查看。

源代码下载:B站对应视频的简介有下载链接。

一、效果展示

在这里插入图片描述

二、制作背景

时隔十年,突然又想玩一下单片机。

七月份,通过看B站江科大(江协科技)的51单片机入门教程复习单片机知识和C语言。

八月份,想着老婆的生日快到了(十一月中旬),为什么不用单片机做一个特别的礼物给她一个甜蜜的惊喜呢?决定做一个心形流水灯。

有了这个想法就开干!用几十个LED在一块较大的洞洞板上摆出心形,内部再摆一个字。后来想了一下,这样会比较单调,通过江科大的视频学习了如何用蜂鸣器播放音乐,能不能播放流水灯的同时播放音乐呢?我简单理论分析了一下,是可行的,并且用开发板测试了一下,发现没问题,就再焊接上无源蜂鸣器、三极管和电阻。

因为要给她一个惊喜,所以不能在家里做,只能在宿舍牺牲休息时间进行设计、焊接、编程、调试等。经过一个多月的时间完成了(其实不用那么久,用这么长的时间是因为每天制作的时间有限,有时隔几天才继续制作),提前两个多月做好了。

大家想制作,如果没有C语言和单片机的基础的话,建议在B站看江协科技的51单片机入门教程学习一遍,可以不用全部学习,学习这几方面就差不多了:能随心所欲地实现8个LED流水灯(心形流水灯只是要控制的灯变多了而已)、数码管的扫描显示原理、定时器的功能和使用、蜂鸣器播放音乐。如果想做呼吸灯效果,还要了解PWM。没有烧录器的话,还要上网买一个烧录器。

三、接线图

1、总体布局

请添加图片描述

2、单片机最小系统

请添加图片描述

3、LED驱动

请添加图片描述

4、蜂鸣器驱动

请添加图片描述

5、电池接线

请添加图片描述

四、原理分析

1、LED的控制

总共有56个灯,如果一个IO口控制一个LED的话,IO口的数量还差很多,如果通过74HC595芯片级联扩展IO口的话,虽然占用IO口很少,但要焊接很多个595芯片,很占空间,也需要焊接很多线,也影响美观。

数码管一位有8个LED,驱动八位数码管是用了扫描的方法,即某一时刻让其中一位数码管导通,按顺序一个一个循环导通显示,当扫描速度比较快的时候,由于人眼的视觉暂留(当物体的影像消失后,人眼仍然会保留该影像一段时间),我们感觉不到LED在闪烁,一般是2ms切换到下一位数码管导通。

LED的显示也可以用扫描的方法,这样,只需要几个三极管和十几个电阻就可以了。由于单片机是弱上拉和强下拉,所以一般用共阳接法,一般用PNP三极管(低电平导通)。用定时器定时,每隔2ms切换到下一组LED导通。定义一个全局数组变量,定时器中断的时候,中断函数中控制P1切换到下一组导通,同时将数组中对应数据赋值给P2口,我们显示流水灯的时候改变数组中的数据就行了,剩下的交给定时器扫描。跟数码管扫描显示一样,要进行消影处理。

由于用扫描的方法点亮LED,一个扫描周期只有1/7的时间是通电的,所以为了提高亮度,LED的限流电阻要小一点,用100欧姆。

2、音乐的播放

用无源蜂鸣器播放音乐,无非是要控制两个方面,一个是音符的频率,一个是音符的时长。

江协科技的教程中,例程是用一个定时器通过改变重装值来控制音符的频率,然后在主循环中控制音符的时长。如果只是用蜂鸣器播放音乐的话是没有问题的,我们如果在播放音乐的同时还要控制流水灯,就会出现问题,因为播放音乐的延时会造成主函数出现阻塞,影响LED的控制,LED的控制也会影响播放音乐的延时,相互影响。

解决方案是用两个定时器,定时器0(Timer0)负责LED的扫描、控制音符的时长、设置定时器1的重装值(即设定音符的频率)、控制音符之间短暂的间隔(5ms),定时器1(Timer1)负责控制翻转无源蜂鸣器IO口的电平,使蜂鸣器按设置的频率响。

要注意的是,定时器1的优先级要比定时器0的高(即要设置为PT0=0,PT1=1),否则蜂鸣器发声会受到影响。

五、各模块代码

1、延时函数

h文件

#ifndef	__DELAY_H__
#define	__DELAY_H__
 
void Delay(unsigned int xms);
 
#endif

c文件

/**
  * @brief	延时函数,晶振@12.0000MHz时,输入参数延时的单位是1ms
  * @param	xms 延时的时间,范围:0~65535
  * @retval	无
  */
void Delay(unsigned int xms)
{
	unsigned char i, j;
	while(xms)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}

2、乐谱头文件

h文件

#ifndef	__MUSIC_H__
#define	__MUSIC_H__

//播放速度,值为十六分音符的时长(ms)
unsigned char code MusicSpeed[]={	//“code”:不占RAM空间
150,	//《生日快乐》速度,一分钟100个四分音符
125,	//《祝寿歌》速度,一分钟120个四分音符
125,	//《生日祝福歌》速度,一分钟120个四分音符
188,	//《小幸运》速度,一分钟80个四分音符
};

//音符与索引对应表,P:休止符,L:低音,M:中音,H:高音,下划线:升半音符号#
#define P	0
#define L1	1
#define L1_	2
#define L2	3
#define L2_	4
#define L3	5
#define L4	6
#define L4_	7
#define L5	8
#define L5_	9
#define L6	10
#define L6_	11
#define L7	12
#define M1	13
#define M1_	14
#define M2	15
#define M2_	16
#define M3	17
#define M4	18
#define M4_	19
#define M5	20
#define M5_	21
#define M6	22
#define M6_	23
#define M7	24
#define H1	25
#define H1_	26
#define H2	27
#define H2_	28
#define H3	29
#define H4	30
#define H4_	31
#define H5	32
#define H5_	33
#define H6	34
#define H6_	35
#define H7	36

//索引与频率对照表
unsigned int code FreqTable[]={
0,	//休止符,即进入定时器1中断程序后之后不赋初值,则从TH=0和TL=0开始计数,并且蜂鸣器端口不翻转
63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64524,
64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
};

//乐谱
unsigned char code Music[]={
//音符,时值,
//即频率,十六分音符时长的倍数

	0,	//第0个数据为0,目的是主函数中开启中断后,能立刻进入定时器0的中断函数进行频率和时值的赋值

	//生日快乐歌
	
	P,2,	//第1个数据和第2个数据,加多一个休止符,用来控制让心形先亮

	M2,2,	//原本简谱上是G调,我自己改了调号,升高点音调
	M2,2,
	M3,4,
	M2,4,
	
	M5,4,
	M4_,4+4,	//祝你生日快乐
	
	M2,2,
	M2,2,
	M3,4,
	M2,4,
	
	M6,4,
	M5,4+4,	//祝你生日快乐
	
	M2,2,
	M2,2,
	H2,4,
	M7,4,
	
	M5,4,
	M4_,4,
	M3,4,	//祝你生日快乐
	
	H1,2,
	H1,2,
	M7,4,
	M5,4,
	
	M6,4,
	M5,4+4,	//祝你生日快乐
	
	0xF0,0xF0,	//切换播放速度标记(也可以设置为其他的值,不要跟音符索引和时值重复就行了)
	P,2,

	//祝寿歌
	
	M6,2,
	M6,4,
	M5,2,
	M6,2,
	M5,2,
	M3,4,	//恭祝你福寿
	
	H1,4,
	H2,2,
	H1,2,
	M5,4+4,	//与天齐
	
	M5,2,
	M3,4,
	M5,2,
	M6,2,
	M2,4,
	M4,2,	//庆贺你生辰快
	
	M3,12,	//乐
	
	M2,2,
	M2,4,
	M6,2,
	M5,2,
	M6,2,
	M3,4,	//年年都有今日
	
	M5,2,
	M5,4,
	M7,2,
	M6,2,
	M7,2,
	M6,4,	//岁岁都有今朝
	
	M6,4,
	M5,2,
	M6,2,
	M5,2,
	M3,8,	//恭喜你
	
	H3,4,
	H2,2,
	H3,2,
	H1,16,	//恭喜你
	
	P,8,	//停顿一下再播放下一首

	0xF0,0xF0,	//切换播放速度标记

	//生日祝福歌(海底捞)
	
	M3,2,
	M5,2,
	M5,2,
	M6,2,
	M5,4,
	P,4,	//今天你生日
	
	M2,2,
	M5,2,
	M5,2,
	M6,2,
	M5,4,
	P,4,	//送上我祝福
	
	M1,2,
	M1,2,
	M1,2,
	M1,2,
	M1,4,
	H1,4,	//特别的日子有
	
	M7,2,
	M5,2,
	M5,2,
	M6,2,
	M5,4+4,	//灿烂的笑容
	
	M6,2,
	H1,2,
	H1,2,
	H2,2,
	H1,4,
	H1,2,
	M6,2,	//我们来相聚,带着
	
	M5,2,
	M6,2,
	M6,2,
	M5,2,
	M3,8,	//满满的关爱
	
	M4,2,
	M4,4,
	M3,2,
	M4,2,
	M3,4,
	M1,2,	//祝福你好运常
	
	M2,8,
	P,8,	//伴

	M3,2,
	M5,2,
	M5,2,
	M6,2,
	M5,4,
	P,4,	//点燃了蜡烛
	
	M2,2,
	M5,2,
	M5,2,
	M6,2,
	M5,4,
	P,4,	//许下你心愿
	
	M1,2,
	M1,2,
	M1,2,
	M1,2,
	M1,4,
	H1,2,
	H1,2,	//未来的日子每个
	
	M7,2,
	M5,2,
	M5,2,
	M6,2,
	M5,4+4,	//梦想都实现
	
	M6,2,
	H1,2,
	H1,2,
	H2,2,
	H1,4,
	H1,2,
	M6,2,	//唱首祝福歌,我们
	
	M5,2,
	H3,2,
	H3,2,
	H2,2,
	H1,4+4,	//相亲又相爱
	
	M6,2,
	M6,4,
	M5,2,
	M6,2,
	M6,4,
	H1,2,	//祝福你健康平
	
	H2,16+8,	//安
	P,4,
	P,2,
	M5,2,	//对
	
	H3,4,
	H1,3,
	H2,1,
	H3,4,
	H1,4,	//所有的烦恼
	
	H2,2,
	P,2,
	M5,2,
	P,2,
	M5,4,
	P,2,
	M3,2,	//说bye bye,对
	
	H1,4,
	M6,3,
	M7,1,
	H1,2,
	M6,2,
	M6,4,	//所有的快乐
	
	M7,2,
	P,2,
	H5,2,
	P,2,
	H5,4+4,	//说hi hi
	
	H4,4,
	H4,3,
	H3,1,
	H2,4,
	H3,3,
	H4,1,	//亲爱的亲爱的
	
	H3,2,
	P,2,
	H2,2,
	P,2,
	H1,2,
	M6,2,
	M6,4,	//生日快乐
	
	H1,4,
	H1,3,
	M6,1,
	H1,4,
	H2,2,
	H3,2,	//每一天都精
	
	H2,4+4+4,	//彩
	P,2,
	M5,2,	//看
	
	H3,4,
	H1,3,
	H2,1,
	H3,4,
	H1,2,
	H1,2,	//幸福的花儿为
	
	H2,2,
	P,2,
	M5,2,
	P,2,
	M5,4,
	P,2,
	M3,2,	//你盛开,听
	
	H1,4,
	M6,3,
	M7,1,
	H1,4,
	M6,3,
	M6,1,	//美妙的音乐为
	
	M7,2,
	P,2,
	H5,2,
	P,2,
	H5,4+4,	//你喝彩
	
	H4,4,
	H4,3,
	H3,1,
	H2,4,
	H3,3,
	H4,1,	//亲爱的亲爱的
	
	H3,2,
	P,2,
	H2,2,
	P,2,
	H1,2,
	M6,2,
	M6,4,	//生日快乐
	
	H1,4,
	H1,2,
	M6,2,
	H1,4,
	H2,2,
	H3,3,	//祝福你幸福永
	
	H2,4,
	P,2,
	M7,2,
	H1,4,
	H1,2,
	H2,2,	//远,幸福永
	
	H1,16+4,	//远
	P,8,

	0xF0,0xF0,	//切换播放速度标记


	//小幸运
	
	P,0,
	M3,2,
	M3,2,
	M5,2,
	M5,2,
	H1,2,
	H1,2,
	M7,2,	//我听见雨滴落在
	
	M7,2,
	M6,2,
	M3,2,
	M6,10,	//青青草地
	
	P,2,
	M6,2,
	M6,2,
	M7,2,
	M7,2,
	H3,2,
	H3,2,
	M7,2,	//我听见远方下课
	
	M7,2,
	M5,2,
	M3,2,
	M5,10,	//钟声响起
	
	P,2,
	M3,2,
	M3,2,
	M5,2,
	M5,2,
	H1,2,
	H1,2,
	M7,2,	//可是我没有听见
	
	M7,2,
	M6,2,
	M3,2,
	M6,6,
	M6,2,
	M7,2+2,
	M6,2,
	M7,2,
	H3,4,
	H2,4,
	H1,2+16,	//你的声音,认真呼唤我姓名

	P,0,
	M3,2,
	M3,2,
	M5,2,
	M5,2,
	H1,2,
	H1,2,
	M7,2,	//爱上你的时候还
	
	M7,2,
	M6,2,
	M3,2,
	M6,10,	//不懂感情
	
	P,2,
	M6,2,
	M6,2,
	M7,2,
	M7,2,
	H3,2,
	H3,2,
	M7,2,	//离别后才觉得
	
	M7,2,
	M5,2,
	M3,2,
	M5,10,	//刻骨铭心
	
	P,2,
	M3,2,
	M3,2,
	M5,2,
	M5,2,
	H1,2,
	H1,2,
	M7,2,	//为什么没有发现
	
	M7,2,
	M6,2,
	M3,2,
	M6,6,
	M6,2,
	M7,2+2,	//遇见了你,是生
	M6,2,
	M7,2,
	H3,2,
	H3,2,
	H2,4,
	H1,2+8,	//命最好的事
	H3,2,
	H2,1,
	H1,3,
	M7,2,	//也许当时
	
	M6,2,	
	M6,2,
	M6,2,
	M6,2,
	M6,2,
	H3,2,
	H2,2,
	H2,10,	//忙着微笑和哭泣
	H2,2,
	H1,1,
	M7,3,
	M6,2,	//忙着追逐
	
	M5,2,
	M5,2,
	M5,2,
	M3,2,
	M5,2,
	H2,2,
	H1,2,
	H1,2+4,	//天空中的流星
	H1,2,
	H1,2,
	M5,2,
	M5,2,
	M1,2,	//人理所当然
	
	M3,4,
	M2,2,
	M6,8,
	M6,2+2,	//的忘记,是
	M6,2,
	M6,2,
	M6,1,
	H1,3,
	M6,2,
	H1,2,
	M6,2,	//谁风里雨里一直
	
	H1,2,
	H1,2,
	H1,2,
	H1,2,
	H3,2,
	H2,4,
	H2,2+6,	//默默守护在原地
	M5,2,
	H3,2,
	H2,1,
	H1,3,
	H2,2,	//原来你是我
	
	H3,2,
	M5,2,
	H2,2,
	H3,4,
	M5,2,
	H2,2,
	H3,2+2,	//最想留住的幸运
	H2,2,
	H2,2,
	H3,1,
	H4,3,
	H3,2,
	H2,2,
	M7,2,	//原来我们和爱情
	
	H1,2,
	M3,2,
	M6,2,
	H1,4,
	M3,2,
	M6,2,
	M7,2+2,	//曾经靠得那么近
	M7,2,
	M7,2,
	H3,1,
	H5,3,
	H3,2,
	H1,2,
	M7,2,	//那为我对抗世界
	
	M6,2,
	H4,1,
	H4,5,
	P,2,
	H5,2,
	H4,2,
	H3,2,	//的决定,那陪我
	
	M5,2,
	H3,1,
	H3,5,
	P,2,
	H4,2,
	H3,2,
	H1,2,	//淋的雨,一幕幕
	
//	H4_,2,	//疑似谱有问题,改成中音4升半调
	M4_,2,
	H2,1,
	H2,5,
	P,2,
	H2,2,
	H1,2,
	H3,2+2,	//都是你,一尘不
	H2,2,
	H1,2,
	H3,4,
	H2,2,
	H1,2,
	H1,2,	//染的真心
	
	H3,2,
	M5,2,
	H2,2,
	H3,4,
	M5,2,
	H2,2,
	H3,2+2,	//与你相遇好幸运
	H2,2,
	H2,2,
	H3,1,
	H4,3,
	H3,2,
	H2,2,
	M7,2,	//可我已失去为你
	
	H1,2,
	M3,2,
	M6,2,
	H1,4,
	M3,2,
	M6,2,
	M7,2+2,	//泪流满面的权利
	M7,2,
	M7,2,
	H3,1,
	H5,3,
	H3,2,
	H1,2,
	M7,2,	//但愿在我看不到
	
	M6,2,
	H4,1,
	H4,5,
	P,2,
	H5,2,
	H4,2,
	H3,2,	//的天际,你张开
	
	M5,2,
	H3,1,
	H3,5,
	P,2,
	H4,2,
	H3,2,
	H1,2,	//了双翼,遇见你
	
//	H4_,2,	//疑似谱有问题,改成中音4升半调
	M4_,2,
	H2,1,
	H2,13,	//的注定
	
	P,2,
	H3,2,
	H1,2,
	H1,2,
	H3,3,
	H2,3,
	H1,18,	//他会有多幸运
	
	0xF0,0xF0,	//最后再加两个切换播放速度标记,在定时器中断函数中让MusicSpeedSelect自增并清零
	P,4,	//短暂停顿一下再停止播放

	0xFF,0xFF,	//终止标记

};

#endif

3、定时器0

h文件

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);

#endif

c文件

#include <REGX52.H>

/**
  * @brief	定时器0初始化,1毫秒@12.000MHz
  * @param  无
  * @retval 无
  */
void Timer0_Init(void)
{
	TMOD&=0xF0;	//设置定时器模式(高四位不变,低四位清零)
	TMOD|=0x01;	//设置定时器模式(通过低四位设为“定时器0工作方式1”的模式)
	TL0=0x18;	//设置定时初值,定时1ms
	TH0=0xFC;	//设置定时初值,定时1ms
	TF0=0;	//清除TF0标志
	TR0=1;	//定时器0开始计时
	ET0=1;	//打开定时器0中断允许
	EA=1;	//打开总中断
	PT0=0;	//当PT0=0时,定时器0为低优先级,当PT0=1时,定时器0为高优先级
}

/*定时器中断函数模板
void Timer0_Routine() interrupt 1	//定时器0中断函数
{
	static unsigned int T0Count;	//定义静态变量
	TL0=0x18;	//设置定时初值,定时1ms
	TH0=0xFC;	//设置定时初值,定时1ms
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		
	}
}
*/

4、定时器1

h文件

#ifndef __TIMER1_H__
#define __TIMER1_H__
 
void Timer1_Init(void);

#endif

c文件

#include <REGX52.H>

/**
  * @brief  定时器1初始化,1毫秒@12.000MHz
  * @param  无
  * @retval 无
  */
void Timer1_Init(void)
{
	TMOD&=0x0F;	//设置定时器模式(低四位不变,高四位清零)
	TMOD|=0x10;	//设置定时器模式(通过高四位设为“定时器1工作方式1”的模式)
	TL1=0x18;	//设置定时初值,定时1ms
	TH1=0xFC;	//设置定时初值,定时1ms
	TF1=0;	//清除TF1标志
	TR1=1;	//定时器1开始计时
	ET1=1;	//打开定时器1中断允许
	EA=1;	//打开总中断
	PT1=1;	//当PT1=0时,定时器1为低优先级,当PT1=1时,定时器1为高优先级
			//定时器1的优先级要比定时器0的高,否则发声不正常
			//即要设置成:PT0=0;PT1=1;
}

/*定时器中断函数模板
void Timer1_Routine() interrupt 3	//定时器1中断函数
{
	static unsigned int T1Count;	//定义静态变量
	TL1=0x18;	//设置定时初值,定时1ms
	TH1=0xFC;	//设置定时初值,定时1ms
	T1Count++;
	if(T1Count>=1000)
	{
		T1Count=0;
		
	}
}
*/

六、主函数

main.c

/*
by甘腾胜@20240915
效果:B站搜索“甘腾胜”或“gantengsheng”查看
单片机:STC89C52RC
晶振:12T@12.0000MHz
*/

#include <REGX52.H>	//包含头文件(安装目录下)
#include <INTRINS.H>	//用来控制循环左移或右移
#include "Delay.h"	//包含头文件(工程目录下)
#include "Timer0.h"	//包含头文件相当于把头文件中的内容插入此处
#include "Timer1.h"
#include "Music.h"	//将乐谱放在头文件,防止其在main.c文件中占太多空间

//蜂鸣器端口定义
sbit Buzzer=P3^7;	//随便用一个端口控制蜂鸣器

//宏定义
#define LEDSpeed 125	//流水灯速度,值越小,速度越快

//定义全局变量
unsigned char MusicSpeedSelect;	//音乐速度选择变量
unsigned char MusicOffFlag;	//停止播放音乐的标志,1:停止,0:不停止
unsigned char FreqSelect;	//频率选择变量
unsigned char TimeIntervalFlag;	//相邻音符之间短暂间隔的标志,1:到了短暂间隔的时刻,0:未到短暂间隔的时刻
unsigned char WordFlashFlag;	//心形中间的字闪烁的标志
unsigned int MusicSelect;	//音乐选择变量,用来选择音符的频率和时长,因为音符较多,所以用整型
unsigned char code Bit[]={0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,};	//扫描LED的位码,按顺序扫描LED,0接通,1断开
unsigned char LED[]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,};	//LED显示的缓存数据,56个灯共7个字节,0点亮,1熄灭
															//显示动画直接操作缓存数据就行了

//阴码(1为点亮),从上往下或从下往上熄灭或点亮(要取反)
unsigned char code LEDTable1[][7]={	//字从上往下点亮或从上往下熄灭或从下往上点亮或从下往上熄灭,都可以
0xFF,0xFF,0xFF,0xFF,0x00,0x20,0x00,	//点亮的话要取反
0xFF,0xFF,0xFF,0xFF,0xE0,0x37,0x00,	//查表显示
0xFF,0xFF,0xFF,0xFF,0xF1,0x3F,0xC0,
0xFF,0xFF,0xFF,0xFF,0xF9,0x3F,0xC0,
0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xE4,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,};

//阴码(1为点亮),全体LED从上往下熄灭或点亮(要取反)
unsigned char code LEDTable2[][7]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x30,0x30,0x00,0x00,0x00,0x00,
0x00,0x78,0x78,0x00,0x00,0x00,0x00,
0x00,0xF8,0xF8,0x00,0x00,0x00,0x00,
0x00,0xFC,0xFC,0x00,0x00,0x20,0x00,
0x00,0xFE,0xFE,0x00,0xE0,0x37,0x00,
0x00,0xFE,0xFE,0x00,0xF1,0x3F,0xC0,
0x00,0xFF,0xFF,0x00,0xF9,0x3F,0xC0,
0x80,0xFF,0xFF,0x80,0xFD,0xBF,0xE4,
0xC0,0xFF,0xFF,0xC0,0xFF,0xFF,0xF6,
0xE0,0xFF,0xFF,0xE0,0xFF,0xFF,0xFF,
0xF0,0xFF,0xFF,0xF0,0xFF,0xFF,0xFF,
0xF8,0xFF,0xFF,0xF8,0xFF,0xFF,0xFF,
0xFC,0xFF,0xFF,0xFC,0xFF,0xFF,0xFF,
0xFE,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};

//阴码(1为点亮),全体LED从左往右熄灭或点亮(要取反)
unsigned char code LEDTable3[][7]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x03,0x00,0x00,0x00,0x00,0x00,
0x80,0x07,0x00,0x00,0x00,0x00,0x00,
0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,
0xE0,0x0F,0x00,0x00,0x00,0x00,0x00,
0xF0,0x1F,0x00,0x00,0x00,0x00,0x00,
0xF8,0x1F,0x00,0x00,0x82,0x00,0x00,
0xFC,0x3F,0x00,0x00,0xCF,0x00,0x00,
0xFE,0x7F,0x00,0x00,0xDF,0xC0,0x00,
0xFF,0xFF,0x00,0x00,0xFF,0xC8,0x08,
0xFF,0xFF,0x80,0x01,0xFF,0xF8,0x18,
0xFF,0xFF,0xC0,0x03,0xFF,0xFC,0x7C,
0xFF,0xFF,0xE0,0x07,0xFF,0xFE,0x7E,
0xFF,0xFF,0xE0,0x0F,0xFF,0xFF,0xFF,
0xFF,0xFF,0xF0,0x1F,0xFF,0xFF,0xFF,
0xFF,0xFF,0xF0,0x3F,0xFF,0xFF,0xFF,
0xFF,0xFF,0xF8,0x7F,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFC,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};

//阴码(1为点亮),全体LED旋转(要取反)
unsigned char code LEDTable4[][7]={
0x01,0x80,0x80,0x01,0x00,0x08,0x08,
0x06,0x00,0x60,0x00,0x00,0x78,0x08,
0x18,0x00,0x18,0x00,0x00,0xC7,0x60,
0x60,0x00,0x06,0x00,0x06,0xC1,0xE4,
0x80,0x01,0x01,0x80,0x0C,0x80,0x24,
0x00,0x06,0x00,0x60,0x09,0x80,0x26,
0x00,0x18,0x00,0x18,0xD9,0x00,0x33,
0x00,0x60,0x00,0x06,0x70,0xC8,0x18,
};

void DelayUs(unsigned int t)	//大约延时2t+4 us
{	//用来控制呼吸灯
	while(t--);
}

void LED1(void)	//LED全部点亮
{
	unsigned char i;
	for(i=0;i<7;i++)
	{
		LED[i]=0x00;	//设置缓存数组的数据,交给定时器扫描
	}
}	

void LED0(void)	//LED全部熄灭
{
	unsigned char i;
	for(i=0;i<7;i++)
	{
		LED[i]=0xFF;
	}
}	

void LED40(void)	//心形LED全部熄灭
{
	unsigned char i;
	for(i=0;i<4;i++)
	{
		LED[i]=0xFF;
	}
}	

void LED31(void)	//字的LED全部点亮
{
	unsigned char i;
	for(i=4;i<7;i++)
	{
		LED[i]=0x00;
	}
}	

void main()
{
	unsigned char LEDPWM;	//控制每个周期LED亮的时长
	unsigned char LEDCycle=100;	//控制呼吸灯亮度的档位,共100档
	unsigned char LEDDelay=20;	//控制呼吸灯每个状态的时长,即控制呼吸灯的快慢
	unsigned char i,j,k;	//控制流水灯的循环
	unsigned int MusicFlash;	//用来控制LED随音乐节奏闪烁
	
	Timer0_Init();	//定时器0初始化
	Timer1_Init();	//定时器1初始化,注意定时器1的优先级要设置比定时器0高,否则会影响蜂鸣器发声
					//即要设置为:PT0=0;PT1=1;
	
	EA=0;	//关闭所有中断
	//其实不关闭也行,可以再设多一个标志,用来控制显示呼吸灯的时候不扫描LED和不播放音乐就行了,我懒得弄了
	//同时也可以利用定时器,通过比较的方法实现呼吸灯,我懒得弄了
	
	P1=0xFF;
	Delay(1000);	//复位后过1s再显示呼吸灯,不需要可以删掉
	
	//三次呼吸灯
	P1=0x00;	//选中所有位
	for(j=0;j<3;j++)
	{
		for(LEDPWM=0;LEDPWM<LEDCycle;LEDPWM++)	//8个LED由暗到亮
		{
			for(i=0;i<LEDDelay;i++)	//计次延时,通过LEDDelay控制每个档位的时间,以此控制呼吸快慢
			{
				P2=0x00;	//LED亮
				DelayUs(LEDPWM);	//延时LEDPWM
				P2=0xFF;	//LED灭
				DelayUs(LEDCycle-LEDPWM);	//延时LEDCycle-LEDPWM
			}
		}
		
		for(LEDPWM=LEDCycle;LEDPWM>0;LEDPWM--)	//8个LED由亮到暗
		{
			for(i=0;i<LEDDelay;i++)	//计次延时,通过LEDDelay控制每个档位的时间,以此控制呼吸快慢
			{
				P2=0x00;	//LED亮
				DelayUs(LEDPWM);	//延时LEDPWM
				P2=0xFF;	//LED灭
				DelayUs(LEDCycle-LEDPWM);	//延时LEDCycle-LEDPWM
			}
		}
	}
	
	LED0();	//确保全部LED熄灭
	Delay(250);	//延时一小段时间
	
	//全部LED闪烁3次
	P1=0x00;
	for(i=0;i<3;i++)
	{
		P2=0x00;	//LED亮
		Delay(500);	//延时500ms,即0.5s
		P2=0xFF;	//LED灭
		Delay(500);
	}
	
	EA=1;	//开启所有中断,开始播放音乐
	
	while(1)
	{
		MusicSelect=0;	//音乐和流水灯播放完了就从这里开始重新播放,所以音乐选择变量要清零
		TR1=1;	//音乐播放完之后定时器1暂停了,所以要重新启动
		MusicOffFlag=0;	//音乐停止标志位清零
		
		//第一首音乐《生日快乐》播放完之后再跳出循环
		while(MusicSpeedSelect==0)
		{
			if(MusicSelect>2)	//防止没播放音乐就开始点亮LED
			{
				for(i=0;i<7;i++)
				{
					if(i<4){LED[i]=0xFF;}
					else{LED[i]=0x00;}
				}
			}
			MusicFlash=MusicSelect;
			while(MusicFlash==MusicSelect);	//播放下一个音符前,一直等待
			MusicFlash=MusicSelect;
			for(i=0;i<7;i++)	//心形的LED先亮
			{
				if(i<4){LED[i]=0x00;}
				else{LED[i]=0xFF;}
			}
			MusicFlash=MusicSelect;
			while(MusicFlash==MusicSelect);	//播放下一个音符前,一直等待
			MusicFlash=MusicSelect;
		}
		LED0();	//每个动画结束后确保所有LED都熄灭
		Delay(LEDSpeed);	//延时一会
		
		//一个LED按顺序移动
		for(j=0;j<7;j++)
		{
			for(i=0;i<8;i++)
			{
				if(j>0){LED[j-1]=0xFF;}
				if(j==5 && i==0){LED[4]=0xF7;Delay(LEDSpeed);LED[4]=0xFF;}
				if(j<2){LED[j]=~(0x01<<i);}
				else{LED[j]=~(0x80>>i);}
				Delay(LEDSpeed);
			}
		}
		LED0();
		
		//按顺序点亮所有LED
		for(j=0;j<7;j++)
		{
			for(i=0;i<8;i++)
			{
				if(j==5 && i==0){Delay(LEDSpeed);}	//“又”字有个交叉的地方,需要停顿一下
				if(j==0 || j==1){LED[j]=(0xFE<<i);}
				else{LED[j]=(0x7F>>i);}
				Delay(LEDSpeed);
			}
		}
		LED1();

		//按顺序全部熄灭所有LED
		for(j=0;j<7;j++)
		{
			for(i=0;i<8;i++)
			{
				if(j==0 || j==1){LED[j]=~(0xFE<<i);}
				else{LED[j]=~(0x7F>>i);}
				Delay(LEDSpeed);
			}
		}
		LED0();
		
		//心形和字依次按顺序从上往下亮
		for(i=0;i<8;i++)
		{
			LED[1]=0x7F>>i;
			LED[2]=0x7F>>i;
			Delay(LEDSpeed);
		}
		for(i=0;i<8;i++)
		{
			LED[0]=0x7F>>i;
			LED[3]=0x7F>>i;
			Delay(LEDSpeed);
		}
		for(j=0;j<7;j++)
		{
			for(i=0;i<7;i++)
			{
				LED[i]=~LEDTable1[j][i];	//通过查表的方法,可以实现任意流水灯
			}
			Delay(LEDSpeed);
		}
		LED1();
		
		//心形和字依次闪烁5次
		for(j=0;j<5;j++)
		{
			for(i=0;i<4;i++)
			{
				LED[i]=0xFF;
			}
			Delay(LEDSpeed);
			for(i=0;i<4;i++)
			{
				LED[i]=0x00;
			}
			Delay(LEDSpeed);
		}
		for(j=0;j<5;j++)
		{
			for(i=4;i<7;i++)
			{
				LED[i]=0xFF;
			}
			Delay(LEDSpeed);
			for(i=4;i<7;i++)
			{
				LED[i]=0x00;
			}
			Delay(LEDSpeed);
		}
		LED1();

		//心形两边分别有2个熄灭的LED从上到下移动
		LED[1]=0x80;
		LED[2]=0x80;
		Delay(LEDSpeed);
		LED[1]=0xC0;
		LED[2]=0xC0;
		Delay(LEDSpeed);
		for(i=0;i<6;i++)
        {
			LED[1]=_cror_(LED[1],1);
			LED[2]=_cror_(LED[2],1);
			Delay(LEDSpeed);
        }
		LED[1]=0x01;
		LED[2]=0x01;
		LED[0]=0x80;
		LED[3]=0x80;
		Delay(LEDSpeed);
		LED[1]=0x00;
		LED[2]=0x00;
		LED[0]=0xC0;
		LED[3]=0xC0;
		Delay(LEDSpeed);
		for(i=0;i<6;i++)
        {
			LED[0]=_cror_(LED[0],1);
			LED[3]=_cror_(LED[3],1);
			Delay(LEDSpeed);
        }
		LED[0]=0x01;
		LED[3]=0x01;
		Delay(LEDSpeed);
		LED[0]=0x00;
		LED[3]=0x00;
		Delay(LEDSpeed);
		
		//从上往下熄灭所有LED
		for(j=0;j<16;j++)
		{
			for(i=0;i<7;i++)
			{
				LED[i]=LEDTable2[j][i];	//查表显示LED
			}
			Delay(LEDSpeed);
		}
		LED0();
		
		//8个LED按出现,顺时针移动,再消失,循环2次
		for(k=0;k<2;k++)
		{
			for(j=0;j<5;j++)
			{
				for(i=0;i<8;i++)
				{
					if(j<2)
					{
						LED[j]=(0xFE<<i);
						if(j>0){LED[j-1]=~(0xFE<<i);}
					}
					else
					{
						if(j<4){LED[j]=(0x7F>>i);}
						if(j==2){LED[j-1]=~(0xFE<<i);}
						else {LED[j-1]=~(0x7F>>i);}
					}
					Delay(LEDSpeed/2);
				}
			}
			LED0();
			Delay(LEDSpeed);
		}
		
		//8个LED出现,逆时针移动,再消失,循环2次
		for(k=0;k<2;k++)
		{
			for(j=4;j<5;j--)
			{
				for(i=7;i<8;i--)
				{
					if(j<2)
					{
						LED[j]=(0xFE<<i);
						if(j>0){LED[j-1]=~(0xFE<<i);}
					}
					else
					{
						if(j<4){LED[j]=(0x7F>>i);}
						if(j==2){LED[j-1]=~(0xFE<<i);}
						else {LED[j-1]=~(0x7F>>i);}
					}
					if((j==2 || j==1 || j==0) && i==7){LED[j+1]=0xFF;}
					Delay(LEDSpeed/2);
				}
			}
			LED0();
			Delay(LEDSpeed);
		}

		//心形两边分别有8个LED从上到下移动,循环2次
		for(k=0;k<2;k++)
		{
			for(i=0;i<8;i++)
			{
				LED[1]=(0x7F>>i);
				LED[2]=(0x7F>>i);
				Delay(LEDSpeed/2);
			}
			for(i=0;i<8;i++)
			{
				LED[0]=(0x7F>>i);
				LED[3]=(0x7F>>i);
				LED[1]=~(0x7F>>i);
				LED[2]=~(0x7F>>i);
				Delay(LEDSpeed/2);
			}
			for(i=0;i<8;i++)
			{
				LED[0]=~(0x7F>>i);
				LED[3]=~(0x7F>>i);
				Delay(LEDSpeed/2);
			}
			LED0();
			Delay(LEDSpeed);
		}

		//心形两边分别有8个LED从下到上移动,循环2次
		for(k=0;k<2;k++)
		{
			for(i=0;i<8;i++)
			{
				LED[0]=(0xFE<<i);
				LED[3]=(0xFE<<i);
				Delay(LEDSpeed/2);
			}
			for(i=0;i<8;i++)
			{
				LED[1]=(0xFE<<i);
				LED[2]=(0xFE<<i);
				LED[0]=~(0xFE<<i);
				LED[3]=~(0xFE<<i);
				Delay(LEDSpeed/2);
			}
			for(i=0;i<8;i++)
			{
				LED[1]=~(0xFE<<i);
				LED[2]=~(0xFE<<i);
				Delay(LEDSpeed/2);
			}
			LED0();
		}

		//从左往右点亮,再熄灭,循环4次,速度逐渐加快
		for(k=2;k<6;k++)
		{
			for(j=0;j<19;j++)
			{
				for(i=0;i<7;i++)
				{
					LED[i]=~LEDTable3[j][i];
				}
				Delay(LEDSpeed/k);	//k增大,则延时减小,速度变快
			}
			for(j=0;j<19;j++)
			{
				for(i=0;i<7;i++)
				{
					LED[i]=LEDTable3[j][i];
				}
				Delay(LEDSpeed/k);
			}
		}

		//从上往下点亮,再熄灭,循环4次,速度逐渐加快
		for(k=2;k<6;k++)
		{
			for(j=0;j<16;j++)
			{
				for(i=0;i<7;i++)
				{
					LED[i]=~LEDTable2[j][i];
				}
				Delay(LEDSpeed/k);
			}
			for(j=0;j<16;j++)
			{
				for(i=0;i<7;i++)
				{
					LED[i]=LEDTable2[j][i];
				}
				Delay(LEDSpeed/k);
			}
		}

		//光棒顺时针旋转,每转一圈,速度逐渐加快
		for(k=3;k<25;k++)
		{
			for(j=0;j<8;j++)
			{
				for(i=0;i<7;i++)
				{
					LED[i]=~LEDTable4[j][i];
				}
				Delay(LEDSpeed*2/k);
			}
		}
		LED40();	//熄灭心形的LED
		LED31();	//点亮中间的字

		//中间的字整体闪烁,心形4扇形顺时针变化,循环4次
		WordFlashFlag=1;	//字闪烁标志位置1,字开始闪烁
		for(k=0;k<4;k++)
		{
			for(j=0;j<8;j++)
			{
				for(i=0;i<4;i++)
				{
					if(i==0 || i==1){LED[i]=(0xFE<<j);}
					else {LED[i]=(0x7F>>j);}
				}
				Delay(LEDSpeed/2);
			}
			for(j=0;j<8;j++)
			{
				for(i=0;i<4;i++)
				{
					if(i==0 || i==1){LED[i]=~(0xFE<<j);}
					else {LED[i]=~(0x7F>>j);}
				}
				Delay(LEDSpeed/2);
			}
		}
		WordFlashFlag=0;	//字闪烁标志位清0,暂停闪烁
		LED0();

		//光棒顺时针和逆时针旋转,速度逐渐加快
		for(k=3;k<25;k++)
		{
			for(j=0;j<8;j++)
			{
				for(i=0;i<7;i++)
				{
					LED[i]=~LEDTable4[7-j][i];
				}
				Delay(LEDSpeed*2/k);
			}
		}
		LED40();
		LED31();

		//中间的字整体闪烁,心形扇形逆时针变化,循环4次
		WordFlashFlag=1;
		for(k=0;k<4;k++)
		{
			for(j=0;j<8;j++)
			{
				for(i=0;i<4;i++)
				{
					if(i==0 || i==1){LED[i]=(0x7F>>j);}
					else {LED[i]=(0xFE<<j);}
				}
				Delay(LEDSpeed/2);
			}
			for(j=0;j<8;j++)
			{
				for(i=0;i<4;i++)
				{
					if(i==0 || i==1){LED[i]=~(0x7F>>j);}
					else {LED[i]=~(0xFE<<j);}
				}
				Delay(LEDSpeed/2);
			}
		}
		LED0();

		//中间的字整体闪烁,心形顺时针依次全部点亮,再顺时针依次全部熄灭,速度加快
		for(j=3;j<8;j++)
		{
			for(i=0;i<8;i++)
			{
				LED[2]=(0x7F>>i);
				Delay(LEDSpeed/j);
			}
			for(i=0;i<8;i++)
			{
				LED[3]=(0x7F>>i);
				Delay(LEDSpeed/j);
			}
			for(i=0;i<8;i++)
			{
				LED[0]=(0xFE<<i);
				Delay(LEDSpeed/j);
			}
			for(i=0;i<8;i++)
			{
				LED[1]=(0xFE<<i);
				Delay(LEDSpeed/j);
			}
			for(i=0;i<8;i++)
			{
				LED[2]=~(0x7F>>i);
				Delay(LEDSpeed/j);
			}
			for(i=0;i<8;i++)
			{
				LED[2]=0xFF;
				LED[3]=~(0x7F>>i);
				Delay(LEDSpeed/j);
			}
			for(i=0;i<8;i++)
			{
				LED[3]=0xFF;
				LED[0]=~(0xFE<<i);
				Delay(LEDSpeed/j);
			}
			for(i=0;i<8;i++)
			{
				LED[0]=0xFF;
				LED[1]=~(0xFE<<i);
				Delay(LEDSpeed/j);
			}
			LED[1]=0xFF;
		}		
		LED40();

		//中间的字整体闪烁,心形从两侧由上向下点亮,再由上往下熄灭,循环3次
		for(k=0;k<3;k++)
		{
			for(i=0;i<8;i++)
			{
				LED[1]=0x7F>>i;
				LED[2]=0x7F>>i;
				Delay(LEDSpeed/2);
			}
			for(i=0;i<8;i++)
			{
				LED[0]=0x7F>>i;
				LED[3]=0x7F>>i;
				Delay(LEDSpeed/2);
			}
			for(i=0;i<8;i++)
			{
				LED[1]=~(0x7F>>i);
				LED[2]=~(0x7F>>i);
				Delay(LEDSpeed/2);
			}
			for(i=0;i<8;i++)
			{
				LED[0]=~(0x7F>>i);
				LED[3]=~(0x7F>>i);
				Delay(LEDSpeed/2);
			}
		}
		LED40();

		//中间的字整体闪烁,8个LED顺时针循环移动,速度加快
		for(i=0;i<8;i++)	//8个LED依次出现
		{
			LED[2]=(0x7F>>i);
			Delay(LEDSpeed*2/7);
		}
		for(j=7;j<128;j++)
		{
			for(i=0;i<8;i++)
			{
				if(j!=7)LED[2]=(0x7F>>i);
				if(j!=7)LED[1]=~(0xFE<<i);
				if(j!=7)Delay(LEDSpeed*2/j);	//随着j增大,延时时间变短
			}
			LED[1]=0xFF;
			for(i=0;i<8;i++)
			{
				LED[3]=(0x7F>>i);
				LED[2]=~(0x7F>>i);
				Delay(LEDSpeed*2/j);
			}
			LED[2]=0xFF;
			for(i=0;i<8;i++)
			{
				LED[0]=(0xFE<<i);
				LED[3]=~(0x7F>>i);
				Delay(LEDSpeed*2/j);
			}
			LED[3]=0xFF;
			for(i=0;i<8;i++)
			{
				LED[1]=(0xFE<<i);
				LED[0]=~(0xFE<<i);
				Delay(LEDSpeed*2/j);
			}
			LED[0]=0xFF;
		}
		
		//中间的字整体闪烁,4*4个等间隔的LED顺时针循环移动
		LED[0]=0xF0;
		LED[1]=0xF0;
		LED[2]=0x0F;
		LED[3]=0x0F;
		Delay(LEDSpeed);
		for(i=0;i<32;i++)
		{
			LED[0]=_crol_(LED[0],1);
			LED[1]=_crol_(LED[1],1);
			LED[2]=_cror_(LED[2],1);
			LED[3]=_cror_(LED[3],1);
			Delay(LEDSpeed);
		}
		LED40();
                    		
		//中间的字整体闪烁,8个LED逆时针循环移动,速度加快
		for(i=0;i<8;i++)
		{
			LED[1]=(0x7F>>i);
			Delay(LEDSpeed*2/7);
		}
		for(j=7;j<128;j++)
		{
			for(i=0;i<8;i++)
			{
				if(j!=7)LED[1]=(0x7F>>i);
				if(j!=7)LED[2]=~(0xFE<<i);
				if(j!=7)Delay(LEDSpeed*2/j);
			}
			LED[2]=0xFF;
			for(i=0;i<8;i++)
			{
				LED[0]=(0x7F>>i);
				LED[1]=~(0x7F>>i);
				Delay(LEDSpeed*2/j);
			}
			LED[1]=0xFF;
			for(i=0;i<8;i++)
			{
				LED[3]=(0xFE<<i);
				LED[0]=~(0x7F>>i);
				Delay(LEDSpeed*2/j);
			}
			LED[0]=0xFF;
			for(i=0;i<8;i++)
			{
				LED[2]=(0xFE<<i);
				LED[3]=~(0xFE<<i);
				Delay(LEDSpeed*2/j);
			}
			LED[3]=0xFF;
		}
		LED0();
		
		//中间的字整体闪烁,4*4个等间隔的LED逆时针循环移动
		LED[0]=0xF0;
		LED[1]=0xF0;
		LED[2]=0x0F;
		LED[3]=0x0F;
		Delay(LEDSpeed);
		for(i=0;i<32;i++)
		{
			LED[0]=_cror_(LED[0],1);
			LED[1]=_cror_(LED[1],1);
			LED[2]=_crol_(LED[2],1);
			LED[3]=_crol_(LED[3],1);
			Delay(LEDSpeed);
		}
		LED40();
			
		//中间的字整体闪烁,心形按音乐节奏左右闪烁
		WordFlashFlag=1;
		for(i=0;i<16;i++)
		{
			LED[0]=0x00;
			LED[1]=0x00;
			LED[2]=0xFF;
			LED[3]=0xFF;
			MusicFlash=MusicSelect;
			while(MusicFlash==MusicSelect);
			MusicFlash=MusicSelect;
			LED[0]=0xFF;
			LED[1]=0xFF;
			LED[2]=0x00;
			LED[3]=0x00;
			MusicFlash=MusicSelect;
			while(MusicFlash==MusicSelect);
			MusicFlash=MusicSelect;
		}
		LED40();

		//中间的字整体闪烁,每两个灯为一组,相邻组交替闪烁,循环16次
		for(j=0;j<16;j++)
		{
			for(i=0;i<4;i++)
			{
				 LED[i]=0x99;
			}
			Delay(LEDSpeed*2);
			for(i=0;i<4;i++)
			{
				{LED[i]=0x66;}
			}
			Delay(LEDSpeed*2);
		}

		//中间的字整体闪烁,相邻的灯交替闪烁,直到音乐播放结束
		while(MusicSpeedSelect==3)
		{
			for(i=0;i<4;i++)
			{
				if(i==0 || i==1){LED[i]=0x55;}
				else {LED[i]=0xAA;}
			}
			if(MusicSelect>100){Delay(LEDSpeed*2);}
			for(i=0;i<4;i++)
			{
				if(i==0 || i==1){LED[i]=0xAA;}
				else {LED[i]=0x55;}
			}
			if(MusicSelect>100){Delay(LEDSpeed*2);}
		}
		WordFlashFlag=0;	//字闪烁的标志清零
		LED0();	//所有LED熄灭
		Delay(1000);	//播放结束后延时1s再重新开始播放和显示
	}
}


void Timer0_Routine() interrupt 1	//定时器0中断函数
{
	static unsigned int T0Count1,T0Count2,T0Count3,T0Count4;	//定义静态变量
	static unsigned char i,j;	//定义静态变量
	TL0=0x18;	//设置定时初值,定时1ms,晶振@12.0000MHz
	TH0=0xFC;	//设置定时初值,定时1ms,晶振@12.0000MHz
	T0Count1++;
	T0Count2++;
	T0Count3++;
	if(WordFlashFlag){T0Count4++;}	//用来控制中间的字闪烁,字闪烁的标志WordFlashFlag为0则暂停闪烁
	else {j=0;T0Count4=249;}	//用来使标志WordFlashFlag设为1之后,字能尽快闪烁,并且字闪烁时先亮再灭
	if(T0Count1>=2)	//扫描LED,类似数码管的扫描,2ms切换一次显示
	{
		T0Count1=0;	//静态计数变量清零
		P2=0xFF;	//消影
		P1=Bit[i];	//切换“位”,即切换到下一组LED导通
		P2=LED[i];	//切换“段”
		i++;	//通过静态变量i来进行切换
		if(i>6){i=0;}	//总共有7位,即7个字节的LED,7*8=56个
	}
	if(T0Count2>=Music[MusicSelect]*MusicSpeed[MusicSpeedSelect])	//控制每个音符响的时长
	{	//如果上一个音符的时长到了(即如果时间=倍数*十六分音符的时长),则进入此函数,重新赋值
		T0Count2=0;
		
		//如果是终止标志0xFF,则音乐选择变量清零,定时器1暂停,音乐停止标志置1
		if(Music[MusicSelect+1]==0xFF){MusicSelect=0;TR1=0;MusicOffFlag=1;}
		
		if(MusicOffFlag==0)	//如果音乐停止标志为0,则播放音乐
		{
			//如果下一个数据是切换播放速度的标志0xF0,则音乐速度选择的变量MusicSpeedSelect自增,并跳过标志0xF0(两个)
			if(Music[MusicSelect+1]==0xF0){MusicSpeedSelect++;MusicSpeedSelect%=4;MusicSelect=MusicSelect+2;}
			
			MusicSelect++;	//音乐选择变量自增,此时对应Music数组里音符的频率
			FreqSelect=Music[MusicSelect];	//将下一个音符的频率赋值给频率选择变量FreqSelect
			
			//音乐选择变量自增,此时对应Music数组里↑上面频率的时长数据,即时长为十六分音符的倍数
			MusicSelect++;	//此时对应的Music数组的数据用来控制从现在到下次进入此函数的时间
			
			TimeIntervalFlag=1;	//控制音符之间短暂间隔(5ms)的标志置1
			TR1=0;	//定时器1暂停计时,蜂鸣器不发声
			T0Count3=0;	//T0Count3清零
		}
	}
	if(T0Count3>=5 && TimeIntervalFlag==1)	//5ms
	{
		T0Count3=0;
		TimeIntervalFlag=0;	//控制音符之间短暂间隔(5ms)的标志清零,在置1前不会进入此函数
		TR1=1;	//定时器1继续计时,蜂鸣器继续发声
	}
	if(T0Count4>=250)	//控制心形中间的字的闪烁,250ms进入一次此函数,即闪烁周期为500ms
	{
		T0Count4=0;
		if(j%2){LED[4]=0x00;LED[5]=0x00;LED[6]=0x00;}	//点亮
		else {LED[4]=0xFF;LED[5]=0xFF;LED[6]=0xFF;}	//熄灭
		j++;	//通过静态变量j控制字的亮灭
	}
}

void Timer1_Routine() interrupt 3	//定时器1中断函数
{
	if(FreqTable[FreqSelect])	//如果不是休止符
	{
		/*取对应频率值的重装载值到定时器*/
		TL1 = FreqTable[FreqSelect]%256;	//设置定时初值
		TH1 = FreqTable[FreqSelect]/256;	//设置定时初值
		Buzzer=!Buzzer;	//翻转蜂鸣器IO口
	}
}

七、制作过程

请添加图片描述
请添加图片描述

请添加图片描述
请添加图片描述

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

附录A:所用到的音乐的简谱

(图片来源:百度图片)

1、生日快乐

请添加图片描述

2、祝寿歌

请添加图片描述

3、生日祝福歌

请添加图片描述

4、小幸运

请添加图片描述

附录B:不同频率的定时器重装值的计算

图片来源:江科大51单片机入门教程
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值