基于AT89S51键盘电子乐器的设计
专业班级:
设计者:
指导老师:
设计时间: 一、设计目的: 键盘电子乐器
二、功能要求:
利用单片机AT89C51端口作为键盘接口,设计一个具有2个八度音阶的键盘乐器,音阶键不少于16个,采用扫描方式管理键盘,音域范围自行定义。也可播放1-3首音乐
三、方案论证:
系统框图设计如图所示:
四、系统硬件设计:
系统硬件设计如图2所示:
2
图2 系统硬件设计
五、软件设计:
3
(1) 弹奏程序流程图如图3所示:
图3流程图
(2)播放歌曲程序流程图如图4
4
图4 流程图
(2)程序:
/***** 文件名 :
键盘电子乐器.c*****/
#include 5 #include #define uchar unsigned char #define uint unsigned int uchar Count; void Play_Song(uchar i); sbit ge=P3^0; //歌曲开关按键 sbit Speak =P3^7; //扬声器控制脚 uint FreqTemp; unsigned int code Freqtab[] = { //定时半周期的初始值 64021,64103,64260,64400, //低音3 4 5 6 64524,64580,64684,64777, //低音7,中音1 2 3 64820,64898,64968,65030, //中音4 5 6 7 65058,65110,65157,65178 //高音1 2 3 4 6 }; //关于半周期的初始值与频率的关系,可见: /************************************************************* * 函数功能 : 用扫描法读 P1 外接 4×4 键盘 * 函数返回 : 按下键:返回0~15、如无键按下:返回16 **************************************************************/ uchar Keyscan(void) { uchar i, j, temp, Buffer[4] = {0xfe, 0xfd, 0xfb, 0xf7}; for(j = 0; j < 4; j++) //循环四次,扫描四行 { P1 = Buffer[j]; //在低四位分别输出一个低电平 _nop_(); temp = 0x80; //计划先读出P1.7位 for(i = 0; i < 4; i++) //循环四次,检查四列 7 { if(!(P1 & temp)) //从高四位,截取1位 { return (i + j * 4); //返回取得的按键值 } temp >>= 1; //往右边一位 } } return 16; //没有键按下就返回16 } /************************************************************** * 函数功能 :按键数码管显示 * 输入 : k (键盘数值) ***************************************************************/ 8 void Display(uchar k) //数码管显示 { if(k<8) P2 =0xC7 ; else P2=0x89 ; } void mmkey(void) { uchar Key_Value = 16, Key_Temp1, Key_Temp2;//读出的键值 TMOD = 0x01; //T0定时方式1 ET1 = 1; //允许T1中断 EX1 = 1; //允许外部中断INT1中断允许 EA = 1; //开总中断 while(1) 9 { if(ge==0) Play_Song(0); //Play_Song(0)为祝你平安 Play_Song(1)为路边的野花你不要采 TR1 = 0; //暂不发音 Key_Temp1 = Keyscan(); //读入按键 if(Key_Temp1 != 16) //有键按下 { Display(Key_Value); //显示键值、延时消抖 Key_Temp2 = Keyscan(); //再读一次 if (Key_Temp1 == Key_Temp2)//两次相等 { Key_Value = Key_Temp1; //就确认下来 FreqTemp = Freqtab[Key_Value]; //根据键值,取出定时半周期的初始值 Display(Key_Value); //显示 TR1 = 1; //启动定时器,发音 while (Keyscan() < 16); //等待释放 10 Speak = 0; //停止发音 } } } } void T1_INT(void) interrupt 3 { TL1 = FreqTemp; //载入定时半周期的初始值 TH1 = FreqTemp >> 8; Speak = ~Speak; //发音 } /*以下数组是音符编码*/ unsigned char code SONG[] ={ //祝你平安 0x26,0x20,0x20,0x20,0x20,0x20,0x26,0x10,0x20,0x10,0x20,0x80,0x26,0x20,0x30,0x20, 11 0x30,0x20,0x39,0x10,0x30,0x10,0x30,0x80,0x26,0x20,0x20,0x20,0x20,0x20,0x1c,0x20, 0x20,0x80,0x2b,0x20,0x26,0x20,0x20,0x20,0x2b,0x10,0x26,0x10,0x2b,0x80,0x26,0x20, 0x30,0x20,0x30,0x20,0x39,0x10,0x26,0x10,0x26,0x60,0x40,0x10,0x39,0x10,0x26,0x20, 0x30,0x20,0x30,0x20,0x39,0x10,0x26,0x10,0x26,0x80,0x26,0x20,0x2b,0x10,0x2b,0x10, 0x2b,0x20,0x30,0x10,0x39,0x10,0x26,0x10,0x2b,0x10,0x2b,0x20,0x2b,0x40,0x40,0x20, 0x20,0x10,0x20,0x10,0x2b,0x10,0x26,0x30,0x30,0x80,0x18,0x20,0x18,0x20,0x26,0x20, 0x20,0x20,0x20,0x40,0x26,0x20,0x2b,0x20,0x30,0x20,0x30,0x20,0x1c,0x20,0x20,0x20, 0x20,0x80,0x1c,0x20,0x1c,0x20,0x1c,0x20,0x30,0x20,0x30,0x60,0x39,0x10,0x30,0x10, 0x20,0x20,0x2b,0x10,0x26,0x10,0x2b,0x10,0x26,0x10,0x26,0x10,0x2b,0x10,0x2b,0x80, 12 0x18,0x20,0x18,0x20,0x26,0x20,0x20,0x20,0x20,0x60,0x26,0x10,0x2b,0x20,0x30,0x20, 0x30,0x20,0x1c,0x20,0x20,0x20,0x20,0x80,0x26,0x20,0x30,0x10,0x30,0x10,0x30,0x20, 0x39,0x20,0x26,0x10,0x2b,0x10,0x2b,0x20,0x2b,0x40,0x40,0x10,0x40,0x10,0x20,0x10, 0x20,0x10,0x2b,0x10,0x26,0x30,0x30,0x80,0x00, //路边的野华不要采 0x20,0x40,0x1C,0x10,0x18,0x10,0x20,0x10,0x1C,0x10,0x18,0x40,0x1C,0x20,0x20,0x20, 0x1C,0x20,0x18,0x20,0x20,0x80,0xFF,0x20,0x30,0x1C,0x10,0x18,0x20,0x15,0x20,0x1C, 0x20,0x20,0x20,0x26,0x40,0x20,0x20,0x2B,0x20,0x26,0x20,0x20,0x20,0x30,0x80,0xFF, 0x20,0x20,0x1C,0x10,0x18,0x10,0x20,0x20,0x26,0x20,0x2B,0x20,0x30,0x20,0x2B,0x40, 0x20,0x20,0x1C,0x10,0x18,0x10,0x20,0x20,0x26,0x20,0x2B,0x20,0x30,0x20,0x2 13 B,0x40, 0x20,0x30,0x1C,0x10,0x18,0x20,0x15,0x20,0x1C,0x20,0x20,0x20,0x26,0x40,0x20,0x20, 0x2B,0x20,0x26,0x20,0x20,0x20,0x30,0x80,0x20,0x30,0x1C,0x10,0x20,0x10,0x1C,0x10, 0x20,0x20,0x26,0x20,0x2B,0x20,0x30,0x20,0x2B,0x40,0x20,0x15,0x1F,0x05,0x20,0x10, 0x1C,0x10,0x20,0x20,0x26,0x20,0x2B,0x20,0x30,0x20,0x2B,0x40,0x20,0x30,0x1C,0x10, 0x18,0x20,0x15,0x20,0x1C,0x20,0x20,0x20,0x26,0x40,0x20,0x20,0x2B,0x20,0x26,0x20, 0x20,0x20,0x30,0x30,0x20,0x30,0x1C,0x10,0x18,0x40,0x1C,0x20,0x20,0x20,0x26,0x40, 0x13,0x60,0x18,0x20,0x15,0x40,0x13,0x40,0x18,0x80,0x00, }; /******************************************************************** 14 * 名称 : Time0_Init() * 功能 : 定时器的初始化,定时时间可用光碟中软件计算,11.0592MZ晶振,10ms * 输入 : 无 * 输出 : 无 ***********************************************************************/ void Time0_Init() { TMOD = 0x01; IE = 0x82; TH0 = 0xDC; TL0 = 0x00; } // 名称 : Time0_Int() //功能 : 定时器中断,中断中实现 Count 加一 15 void Time0_Int() interrupt 1 { TH0 = 0xDC; TL0 = 0x00; Count++; //长度加1 } /******************************************************************** * 名称 : Delay_xMs() * 功能 : 延时子程序,经过软件调试,测得延时程序大概为55us. * 输入 : x * 输出 : 无 ***********************************************************************/ void Delay_xMs(uint x) { uint i,j; 16 for(i=0; i for(j=0;j<3; j++); } } /******************************************************************** * 名称 : Play_Song() * 功能 : 播放扬声器器控制程序 * 输入 : i (选择播放哪首歌曲,0为“祝你平安”,1为“路边的野花你不要采”* 输出 : 无 ***********************************************************************/ void Play_Song(uchar i) { uchar Temp1,Temp2; 17 uint Addr; Count = 0; //中断计数器清0 Addr = i * 217; while(1) { Temp1 = SONG[Addr++]; if (Temp1 == 0xFF) //休止符 { TR0 = 0; Delay_xMs(100); } else if (Temp1 == 0x00) //歌曲结束符 { return; 18 } else { Temp2 = SONG[Addr++]; TR0 = 1; while(1) { Speak = ~Speak; Delay_xMs(Temp1); if(Temp2 == Count) { Count = 0; break; } //计时 开始 //P3.7开始发声 19 } } if(ge==1) break; } } void Main() { Time0_Init(); //定时器0中断初始化 while(1) { mmkey(); } } 六、仿真图:如 图 5 所示 20 图5 仿真图 七、心得体会: 这次设计从硬件方面来讲不是很难,程序也相对长一点,但都是书本上所学的知识,主要是中断及其服务程序的编写。在protues上仿真,则起到很好的效果。经过多次调试、修改才得以出结果。将程序烧入芯片,调试成功后,可弹奏自己想要的旋律。同时可以播放一两首示例歌曲,但是也有不足之处的,声音有点杂,不是很稳定。 经过本次课程设计,我们比较好的把理论知识与实践相结合,而我们也在本次设计中收获不少。设计过程中,首先,对于C语言多了一层了解,其次,还有硬件的接线,还有8051芯片的引脚方面,都让我们收获不少。加强了自身的动手能力,同时也感谢辅导老师的细心辅导和教诲。 21 因篇幅问题不能全部显示,请点此查看更多更全内容