一、简介
基于8051微控制器用于控制LED显示和音乐播放,同时具备按键输入功能。程序通过不同的模式控制LED的显示效果,使用定时器中断来切换显示模式,并能够根据按键输入来改变运行模式、播放音乐或调整系统速度。
二、头文件选择,变量定义
2.1头文件:
头文件:包含了针对8051微控制器的头文件REG52.H和自定义的SoundPlay.h
2.2全局变量:
RunMode:用于控制程序的运行模式
2.3定时器初始化:
InitialTimer2:初始化定时器2,用于系统速度控制
2.4主要外设接口:
P3:连接数码管,来显示当前所选择的模式
P1 & P0:连接八个LED,用来显示循环彩灯现象
P2.1:连接按钮,该按钮用来控制模式
P2.4:连接按钮,该按钮用来控制循环速度(加速)
P2.5:连接按钮,该按钮用来控制循环速度(减速)
P2.6:连接扬声器,在模式9时播放音乐
三、音乐编码:
音乐编码是通过一系列数组来表示的,这些数组包含了用于生成音乐旋律的指令。每个数组的元素由两部分组成:音调和持续时间。音乐编码通常用于简单的声音合成器或者音乐播放器,它们可以是微控制器上的蜂鸣器或其他声音输出设备。
每个数组的元素格式通常是:
高字节(高8位)表示音调或音符的编号。
低字节(低8位)表示每个音符的持续时间。
//*****************************Music******************************************************
//挥着翅膀的女孩
unsigned char code Music_Girl[]={
0x17,0x02, 0x17,0x03, 0x18,0x03, 0x19,0x02, 0x15,0x03, 0x16,0x03, 0x17,0x03, 0x17,0x03, 0x17,0x03, 0x18,0x03, 0x19,0x02, 0x16,0x03, 0x17,0x03, 0x18,0x02, 0x18,0x03, 0x17,0x03, 0x15,0x02, 0x18,0x03, 0x17,0x03, 0x18,0x02, 0x10,0x03, 0x15,0x03, 0x16,0x02, 0x15,0x03, 0x16,0x03, 0x17,0x02, 0x17,0x03, 0x18,0x03, 0x19,0x02, 0x1A,0x03, 0x1B,0x03, 0x1F,0x03, 0x1F,0x03, 0x17,0x03, 0x18,0x03, 0x19,0x02, 0x16,0x03, 0x17,0x03, 0x18,0x03, 0x17,0x03, 0x18,0x03, 0x1F,0x03, 0x1F,0x02, 0x16,0x03, 0x17,0x03, 0x18,0x03, 0x17,0x03, 0x18,0x03, 0x20,0x03, 0x20,0x02, 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 0x20,0x03, 0x21,0x03, 0x20,0x03, 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 0x1F,0x03, 0x1B,0x03, 0x19,0x03, 0x19,0x03, 0x15,0x03, 0x1A,0x66, 0x1A,0x03, 0x19,0x03, 0x15,0x03, 0x15,0x03, 0x17,0x03, 0x16,0x66, 0x17,0x04, 0x18,0x04, 0x18,0x03, 0x19,0x03, 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 0x20,0x03, 0x21,0x03, 0x20,0x03, 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 0x1F,0x03, 0x1B,0x03, 0x19,0x03, 0x19,0x03, 0x15,0x03, 0x1A,0x66, 0x1A,0x03, 0x19,0x03, 0x19,0x03, 0x1F,0x03, 0x1B,0x03, 0x1F,0x00, 0x1A,0x03, 0x1A,0x03, 0x1A,0x03, 0x1B,0x03, 0x1B,0x03, 0x1A,0x03, 0x19,0x03, 0x19,0x02, 0x17,0x03, 0x15,0x17, 0x15,0x03, 0x16,0x03, 0x17,0x03, 0x18,0x03, 0x17,0x04, 0x18,0x0E, 0x18,0x03, 0x17,0x04, 0x18,0x0E, 0x18,0x66, 0x17,0x03, 0x18,0x03, 0x17,0x03, 0x18,0x03, 0x20,0x03, 0x20,0x02, 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 0x20,0x03, 0x21,0x03, 0x20,0x03, 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 0x1F,0x04, 0x1B,0x0E, 0x1B,0x03, 0x19,0x03, 0x19,0x03, 0x15,0x03, 0x1A,0x66, 0x1A,0x03, 0x19,0x03, 0x15,0x03, 0x15,0x03, 0x17,0x03, 0x16,0x66, 0x17,0x04, 0x18,0x04, 0x18,0x03, 0x19,0x03, 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 0x20,0x03, 0x21,0x03, 0x20,0x03, 0x1F,0x03, 0x1B,0x03, 0x1F,0x66, 0x1F,0x03, 0x1B,0x03, 0x19,0x03, 0x19,0x03, 0x15,0x03, 0x1A,0x66, 0x1A,0x03, 0x19,0x03, 0x19,0x03, 0x1F,0x03, 0x1B,0x03, 0x1F,0x00, 0x18,0x02, 0x18,0x03, 0x1A,0x03, 0x19,0x0D, 0x15,0x03, 0x15,0x02, 0x18,0x66, 0x16,0x02, 0x17,0x02, 0x15,0x00, 0x00,0x00};
//同一首歌
unsigned char code Music_Same[]={
0x0F,0x01, 0x15,0x02, 0x16,0x02, 0x17,0x66, 0x18,0x03, 0x17,0x02, 0x15,0x02, 0x16,0x01, 0x15,0x02, 0x10,0x02, 0x15,0x00, 0x0F,0x01, 0x15,0x02, 0x16,0x02, 0x17,0x02, 0x17,0x03, 0x18,0x03, 0x19,0x02, 0x15,0x02, 0x18,0x66, 0x17,0x03, 0x19,0x02, 0x16,0x03, 0x17,0x03, 0x16,0x00, 0x17,0x01, 0x19,0x02, 0x1B,0x02, 0x1B,0x70, 0x1A,0x03, 0x1A,0x01, 0x19,0x02, 0x19,0x03, 0x1A,0x03, 0x1B,0x02, 0x1A,0x0D, 0x19,0x03, 0x17,0x00, 0x18,0x66, 0x18,0x03, 0x19,0x02, 0x1A,0x02, 0x19,0x0C, 0x18,0x0D, 0x17,0x03, 0x16,0x01, 0x11,0x02, 0x11,0x03, 0x10,0x03, 0x0F,0x0C, 0x10,0x02, 0x15,0x00, 0x1F,0x01, 0x1A,0x01, 0x18,0x66, 0x19,0x03, 0x1A,0x01, 0x1B,0x02, 0x1B,0x03, 0x1B,0x03, 0x1B,0x0C, 0x1A,0x0D, 0x19,0x03, 0x17,0x00, 0x1F,0x01, 0x1A,0x01, 0x18,0x66, 0x19,0x03, 0x1A,0x01, 0x10,0x02, 0x10,0x03, 0x10,0x03, 0x1A,0x0C, 0x18,0x0D, 0x17,0x03, 0x16,0x00, 0x0F,0x01, 0x15,0x02, 0x16,0x02, 0x17,0x70, 0x18,0x03, 0x17,0x02, 0x15,0x03, 0x15,0x03, 0x16,0x66, 0x16,0x03, 0x16,0x02, 0x16,0x03, 0x15,0x03, 0x10,0x02, 0x10,0x01, 0x11,0x01, 0x11,0x66, 0x10,0x03, 0x0F,0x0C, 0x1A,0x02, 0x19,0x02, 0x16,0x03, 0x16,0x03, 0x18,0x66, 0x18,0x03, 0x18,0x02, 0x17,0x03, 0x16,0x03, 0x19,0x00, 0x00,0x00 };
//两只蝴蝶
unsigned char code Music_Two[] ={
0x17,0x03, 0x16,0x03, 0x17,0x01, 0x16,0x03, 0x17,0x03, 0x16,0x03, 0x15,0x01, 0x10,0x03, 0x15,0x03, 0x16,0x02, 0x16,0x0D, 0x17,0x03, 0x16,0x03, 0x15,0x03, 0x10,0x03, 0x10,0x0E, 0x15,0x04, 0x0F,0x01, 0x17,0x03, 0x16,0x03, 0x17,0x01, 0x16,0x03, 0x17,0x03, 0x16,0x03, 0x15,0x01, 0x10,0x03, 0x15,0x03, 0x16,0x02, 0x16,0x0D, 0x17,0x03, 0x16,0x03, 0x15,0x03, 0x10,0x03, 0x15,0x03, 0x16,0x01, 0x17,0x03, 0x16,0x03, 0x17,0x01, 0x16,0x03, 0x17,0x03, 0x16,0x03, 0x15,0x01, 0x10,0x03, 0x15,0x03, 0x16,0x02, 0x16,0x0D, 0x17,0x03, 0x16,0x03, 0x15,0x03, 0x10,0x03, 0x10,0x0E, 0x15,0x04, 0x0F,0x01, 0x17,0x03, 0x19,0x03, 0x19,0x01, 0x19,0x03, 0x1A,0x03, 0x19,0x03, 0x17,0x01, 0x16,0x03, 0x16,0x03, 0x16,0x02, 0x16,0x0D, 0x17,0x03, 0x16,0x03, 0x15,0x03, 0x10,0x03, 0x10,0x0D, 0x15,0x00, 0x19,0x03, 0x19,0x03, 0x1A,0x03, 0x1F,0x03, 0x1B,0x03, 0x1B,0x03, 0x1A,0x03, 0x17,0x0D, 0x16,0x03, 0x16,0x03, 0x16,0x0D, 0x17,0x01, 0x17,0x03, 0x17,0x03, 0x19,0x03, 0x1A,0x02, 0x1A,0x02, 0x10,0x03, 0x17,0x0D, 0x16,0x03, 0x16,0x01, 0x17,0x03, 0x19,0x03, 0x19,0x03, 0x17,0x03, 0x19,0x02, 0x1F,0x02, 0x1B,0x03, 0x1A,0x03, 0x1A,0x0E, 0x1B,0x04, 0x17,0x02, 0x1A,0x03, 0x1A,0x03, 0x1A,0x0E, 0x1B,0x04, 0x1A,0x03, 0x19,0x03, 0x17,0x03, 0x16,0x03, 0x17,0x0D, 0x16,0x03, 0x17,0x03, 0x19,0x01, 0x19,0x03, 0x19,0x03, 0x1A,0x03, 0x1F,0x03, 0x1B,0x03, 0x1B,0x03, 0x1A,0x03, 0x17,0x0D, 0x16,0x03, 0x16,0x03, 0x16,0x03, 0x17,0x01, 0x17,0x03, 0x17,0x03, 0x19,0x03, 0x1A,0x02, 0x1A,0x02, 0x10,0x03, 0x17,0x0D, 0x16,0x03, 0x16,0x01, 0x17,0x03, 0x19,0x03, 0x19,0x03, 0x17,0x03, 0x19,0x03, 0x1F,0x02, 0x1B,0x03, 0x1A,0x03, 0x1A,0x0E, 0x1B,0x04, 0x17,0x02, 0x1A,0x03, 0x1A,0x03, 0x1A,0x0E, 0x1B,0x04, 0x17,0x16, 0x1A,0x03, 0x1A,0x03, 0x1A,0x0E, 0x1B,0x04, 0x1A,0x03, 0x19,0x03, 0x17,0x03, 0x16,0x03, 0x0F,0x02, 0x10,0x03, 0x15,0x00, 0x00,0x00 };
//***********************************************************************************
模块介绍:
3.1延时函数:
Delay1ms:用来实现毫秒级的延迟,其中count参数用来调整时间长度
主要作用:数码管消影,以及避免运行速度过快导致的显示错误
void Delay1ms(unsigned int count)
{
unsigned int i,j;
for(i=0;i<count;i++)
for(j=0;j<120;j++);
}
3.2定时器中断
定时器2的中断服务程序,用于处理LED模式的切换
定时器初始化函数:
unsigned int Timer0Count,SystemSpeed,SystemSpeedIndex;
void InitialTimer2(void)
{
T2CON = 0x00; //16 Bit Auto-Reload Mode
TH2 = RCAP2H = 0xFC; //重装值,初始值 TL2 = RCAP2L = 0x18;
ET2=1; //定时器 2 中断允许
TR2 = 1; //定时器 2 启动
EA=1;
}
定时器中断函数:
void Timer2(void) interrupt 5 using 3
{
TF2 = 0; //中断标志清除( Timer2 必须软件清标志!)
if(++Timer0Count>=SystemSpeed)
{
Timer0Count = 0;
Timer0EventRun();
}
}
中断处理函数:
void Timer0EventRun(void)
{
if(RunMode==0x00)
{
Mode_0();
}
else if(RunMode ==0x01)
{
Mode_1();
}
else if(RunMode ==0x02)
{
Mode_2();
}
else if(RunMode ==0x03)
{
Mode_3();
}
else if(RunMode ==0x04)
{
Mode_4();
}
else if(RunMode ==0x05)
{
Mode_5();
}
else if(RunMode ==0x06)
{
Mode_6();
}
else if(RunMode ==0x07)
{
Mode_7();
}
else if(RunMode ==0x08)
{
Mode_8();
}
}
3.3初始化函数:
void InitialCPU(void)
{
RunMode = 0x00;
Timer0Count = 0;
SystemSpeedIndex = 9;
P1 = 0x00;
P0 = 0x00;
P2 = 0xFF;
P3 = 0x00;
Delay1ms(500);
P1 = 0xFF;
P0 = 0xFF;
P2 = 0xFF;
P3 = 0xFF;
SetSpeed(SystemSpeedIndex);
Display(RunMode);
}
3.4LED显示函数:
LEDDisplayCode:定义了用于LED显示的编码数组
unsigned char code LEDDisplayCode[] = {
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8, //0~7 0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0xFF};
Display:控制LED显示数字
void Display(unsigned char Value)
{
P3 = LEDDisplayCode[Value];
}
LEDFlash:实现LED闪烁效果
void LEDFlash(unsigned char Count)
{
unsigned char i;
bit Flag;
for(i = 0; i<Count;i++)
{
Flag = !Flag;
if(Flag)
Display(RunMode);
else
Display(0x10);
Delay1ms(100);
}
Display(RunMode);
}
3.5按键读取函数:
GetKey:读取按键状态的函数。
参数说明:
该函数不接受任何参数。
返回值说明:
返回值是unsigned char类型的值,表示按键的状态。如果所有按键都未被按下,返回0x00。
函数逻辑:
函数初始化三个局部变量:KeyTemp、CheckValue 和 Key。
读取P2端口的值并与0x32进行AND操作,以检测特定的按键状态,并将结果存储在CheckValue中。
如果CheckValue等于0x32,表示没有按键被按下,函数立即返回0x00。
为了消除按键抖动,调用Delay1ms函数进行短暂延时。
延时后,再次读取按键状态并存入KeyTemp。
如果KeyTemp与CheckValue不同,表示按键状态发生了变化,这可能是由于按键抖动,因此函数返回0x00。
如果检测到按键按下(即KeyTemp不等于CheckValue),则通过AND操作和NOT操作更新Key的相应位,以反映哪些按键被按下。
最后,函数返回表示按键状态的Key值。
unsigned char GetKey(void)
{
unsigned char KeyTemp, CheckValue, Key = 0x00;
CheckValue = P2 & 0x32; // 读取P2端口,并与0x32进行AND操作
if(CheckValue == 0x32)
return 0x00; // 如果所有相关位都是高电平,表示没有按键按下
Delay1ms(10); // 短暂延时去抖动
KeyTemp = P2 & 0x32; // 再次读取按键状态
if(KeyTemp != CheckValue)
return 0x00; // 如果第二次读取的状态与第一次不同,表示按键抖动,不处理按键
Key |= ~(CheckValue & 0x02); // 检查第二位,如果为低电平,设置Key的第二位
Key |= ~(CheckValue & 0x10); // 检查第五位,如果为低电平,设置Key的第五位
Key |= ~(CheckValue & 0x20); // 检查第六位,如果为低电平,设置Key的第六位
return Key; // 返回按键状态的Key值
}
3.6速度控制:
SpeedCode:定义了速度代码数组
unsigned int code SpeedCode[]={
1, 2, 3, 5, 8, 10, 14, 17, 20, 30,
40, 50, 60, 70, 80, 90, 100, 120, 140, 160,
180, 200, 300, 400, 500, 600, 700, 800, 900,1000};//30
SetSpeed:设置系统速度的函数
void LEDShow(unsigned int LEDStatus)
{
P1 = ~(LEDStatus&0x00FF);
P0 = ~((LEDStatus>>8)&0x00FF);
}
3.7LED显示模式函数:
八种模式:
//Mode 0 从左往右循环显示
unsigned int LEDIndex = 0;
bit LEDDirection = 1,LEDFlag = 1;
void Mode_0(void)
{
LEDShow(0x0001<<LEDIndex);
LEDIndex = (LEDIndex+1)%16;
}
//Mode 1 从右往左循环显示
void Mode_1(void)
{
LEDShow(0x8000>>LEDIndex);
LEDIndex = (LEDIndex+1)%16;
}
//Mode 2 从左往右后从右往左交替
void Mode_2(void)
{
if(LEDDirection)
LEDShow(0x0001<<LEDIndex);
else
LEDShow(0x8000>>LEDIndex);
if(LEDIndex==15)
LEDDirection = !LEDDirection;
LEDIndex = (LEDIndex+1)%16;
}
//Mode 3 与模式0亮灯相反
void Mode_3(void)
{
if(LEDDirection)
LEDShow(~(0x0001<<LEDIndex));
else
LEDShow(~(0x8000>>LEDIndex));
if(LEDIndex==15)
LEDDirection = !LEDDirection;
LEDIndex = (LEDIndex+1)%16;
}
//Mode 4 循环方式与模式2相同,但亮后不灭
void Mode_4(void)
{
if(LEDDirection)
{
if(LEDFlag)
LEDShow(0xFFFE<<LEDIndex);
else
LEDShow(~(0x7FFF>>LEDIndex));
}
else
{
if(LEDFlag)
LEDShow(0x7FFF>>LEDIndex);
else
LEDShow(~(0xFFFE<<LEDIndex));
}
if(LEDIndex==15)
{
LEDDirection = !LEDDirection;
if(LEDDirection) LEDFlag = !LEDFlag;
}
LEDIndex = (LEDIndex+1)%16;
}
//Mode 5 四个灯同时亮,循环方式与模式2相同
void Mode_5(void)
{
if(LEDDirection)
LEDShow(0x000F<<LEDIndex);
else
LEDShow(0xF000>>LEDIndex);
if(LEDIndex==15)
LEDDirection = !LEDDirection;
LEDIndex = (LEDIndex+1)%16;
}
//Mode 6 与模式5亮灯相反
void Mode_6(void)
{
if(LEDDirection)
LEDShow(~(0x000F<<LEDIndex));
else
LEDShow(~(0xF000>>LEDIndex));
if(LEDIndex==15)
LEDDirection = !LEDDirection;
LEDIndex = (LEDIndex+1)%16;
}
//Mode 7 六个灯同时亮,循环方式与模式2相同
void Mode_7(void)
{
if(LEDDirection)
LEDShow(0x003F<<LEDIndex);
else
LEDShow(0xFC00>>LEDIndex);
if(LEDIndex==9)
LEDDirection = !LEDDirection;
LEDIndex = (LEDIndex+1)%10;
}
//Mode 8 二进制规则亮灯
void Mode_8(void)
{
LEDShow(++LEDIndex);
}
KeyDispose:按键处理
参数说明:
Key:一个无符号字符(unsigned char),表示用户的按键操作。值是通过位运算得到的,不同的位代表不同的按键状态。
函数逻辑:
函数首先检查Key参数的各个位是否被设置(即对应的按键是否被按下)。
使用&运算符与0x01、0x02、0x04等进行位运算来检查特定按键的状态。
如果第一位(LSB)被设置,表示一个按键被按下,函数将:
重置LEDDirection为1。
将LEDIndex设置为0,表示LED的起始位置。
将LEDFlag设置为1。
增加RunMode的值,并用10作为模数来循环(即切换模式)。
根据RunMode的值,可能停止或启动定时器2(TR2)。
如果第二位被设置,函数将:
如果当前是音乐播放模式(RunMode为9),则在音乐列表中向前或向后切换音乐曲目。
如果不是音乐播放模式,则增加或减少系统速度索引,并调用SetSpeed来更新系统速度。
如果速度索引超出范围,则调用LEDFlash函数来闪烁LED作为提示。
该函数通过修改全局变量来改变系统的行为,这些变量随后会影响主循环中LED的显示模式和音乐播放。
void KeyDispose(unsigned char Key)
{
if(Key&0x01)
{
LEDDirection = 1;
LEDIndex = 0;
LEDFlag = 1;
RunMode = (RunMode+1)%10;
Display(RunMode);
if(RunMode==0x09)
TR2 = 0;
else
TR2 = 1;
}
if(Key&0x02)
{
if(RunMode==0x09)
{
MusicIndex =(MusicIndex+MUSICNUMBER-1)%MUSICNUMBER;
}
else
{
if(SystemSpeedIndex>0)
{
--SystemSpeedIndex;
SetSpeed(SystemSpeedIndex);
}
else
{
LEDFlash(6);
}
}
}
if(Key&0x04)
{
if(RunMode==0x09)
{
MusicIndex =(MusicIndex+1)%MUSICNUMBER;
}
else
{
if(SystemSpeedIndex<28)
{
++SystemSpeedIndex;
SetSpeed(SystemSpeedIndex);
}
else
{
LEDFlash(6);
}
}
}
}
3.8音乐播放函数:
SelectMusic:索引选择音乐
函数参数说明:
SoundIndex:一个无符号字符(unsigned char),用作音乐选择的索引。
函数返回值:
函数返回一个指向unsigned char的指针,该指针指向被选中音乐数组的第一个元素的地址。
函数逻辑:
根据SoundIndex的值,switch语句将选择相应的音乐数组。
每个case对应一个可能的索引值,当索引匹配时,将MusicAddress设置为对应音乐数组的起始地址。
如果没有匹配的索引,default分支将不会执行任何操作,函数将返回一个空指针(0)。
选中的音乐数组的地址随后可以被用于播放函数,以按顺序播放音乐数组中的音符。
unsigned char * SelectMusic(unsigned char SoundIndex)
{
unsigned char *MusicAddress = 0;
switch (SoundIndex)
{
case 0x00:
MusicAddress = &Music_Girl[0]; //挥着翅膀的女孩
break;
case 0x01:
MusicAddress = &Music_Same[0]; //同一首歌
break;
case 0x02:
MusicAddress = &Music_Two[0]; //两只蝴蝶
break;
case 0x03:
break;
case 0x04:
break;
case 0x05:
break;
case 0x06:
break;
case 0x07:
break;
case 0x08:
break;
case 0x09:
break;
default:break;
}
return MusicAddress;
}
PlayMusic:播放音乐
void PlayMusic(void)
{
Delay1ms(200);
Play(SelectMusic(MusicIndex),0,3,360);
}
四.主函数说明:
初始化CPU、声音和定时器。
循环检测按键输入,根据按键调用KeyDispose函数处理不同的按键事件。
如果RunMode是0x09,调用PlayMusic播放音乐。
main()
{
unsigned char Key;
InitialCPU();
InitialSound();
InitialTimer2();
while(1)
{
Key = GetKey();
if(RunMode==0x09)
{
PlayMusic();
}
if(Key!=0x00)
{
KeyDispose(Key);
}
}
}
五.仿真电路图
5.1电路图设计:
5.2八种模式:
MODE0 & MODE1 & MODE2:
MODE3:
MODE4:
MODE5:
MODE6:
MODE7:
MODE8:
六.总结
该项目主要用于控制LED显示和音乐播放,同时具备按键输入功能。程序通过不同的模式控制LED的显示效果,使用定时器中断来切换显示模式,并能够根据按键输入来改变运行模式、播放音乐或调整系统速度。
通过该项目,学会了如何显示数码管,学会了用按键来改变模式,改变数码管显示模式。
通过该项目,学会了如何定义定时器,如何用定时器进行中断。
通过该项目,学会了如何定义音乐编码,以及用扬声器来播放音乐。
评论(0)
您还未登录,请登录后发表或查看评论