51单片机的音乐跑马灯设计
单片机闪烁灯跑马灯控制课程设计

单片机闪烁灯跑马灯控制课程设计单片机闪烁灯跑马灯控制课程设计报告一、引言本课程设计旨在通过学习和实践单片机(MCU)编程,实现闪烁灯和跑马灯的控制。
我们将使用嵌入式C语言编程,通过了解单片机的内部结构、电路设计和编程流程,深入理解单片机的工作原理和应用。
二、系统硬件设计本课程设计选用51单片机作为主控芯片,外接8个LED灯和1个按键。
硬件电路设计如下:1.单片机:采用AT89C51,该芯片具有32K字节的Flash存储器,256字节的RAM,以及两个16位定时器/计数器。
2.LED灯:采用普通LED灯珠,与单片机引脚相连,通过编程控制LED灯的亮灭状态。
3.按键:采用机械按键,与单片机的外部中断0(EX0)相连,用于触发闪烁灯和跑马灯的切换。
三、系统软件设计1.闪烁灯模式:在此模式下,8个LED灯将按照一定的频率交替闪烁。
我们可以通过计时器和GPIO口控制LED灯的亮灭状态。
void blink_LED(void) {int i;while(1) {for(i = 0; i < 8; i++) {P1_0 = ~P1_0; // 翻转LED状态delay(500); // 延时,控制闪烁频率}}}2.跑马灯模式:在此模式下,8个LED灯将按照一定的顺序依次点亮。
我们可以通过计时器和GPIO口控制LED灯的亮灭状态。
void marquee_LED(void) {int i;int led_state[8] = {0, 1, 0, 1, 0, 1, 0, 1}; // LED状态数组,初始为交替亮灭while(1) {for(i = 0; i < 8; i++) {P1_0 = led_state[i]; // 设置LED状态delay(50); // 延时,控制跑马灯速度}}}四、按键处理程序我们通过外部中断0(EX0)接收按键信号,当按键按下时,将切换闪烁灯和跑马灯模式。
按键处理程序如下:void EX0_ISR(void) interrupt 0 { // EX0中断服务程序if (key_flag) { // 如果按键已经被按下过if (key_value == 0) { // 如果按键状态为低电平marquee_LED(); // 切换到跑马灯模式key_flag = 0; // 标记按键状态已经改变} else { // 如果按键状态为高电平blink_LED(); // 切换到闪烁灯模式key_flag = 0; // 标记按键状态已经改变}key_value = ~key_value; // 翻转按键状态值} else { // 如果按键还没有被按下过key_value = ~key_value; // 翻转按键状态值if (key_value == 0) { // 如果按键状态为低电平blink_LED(); // 切换到闪烁灯模式key_flag = 1; // 标记按键状态已经改变} else { // 如果按键状态为高电平marquee_LED(); // 切换到跑马灯模式key_flag = 1; // 标记按键状态已经改变}}}。
51单片机实用教程入门之跑马灯

MCS-8051 单片机实用教程深圳伟凡数码 QQ:59059381上一节讲了 KEIL 软件的基本使用, 本节开始学习编程,编写第一个程序,如果从没学过的朋友不需要深入了解,只 要按照下面写出第一个程序。
好, 就来试试,很有意思的。
这里主要讲些技巧, 而不是专门的 C 语言,如过想学习更深入 的 C 语言, 请参考相关 C 语言书籍。
前面我们已经讲了怎样建立文件,这里就不多讲了。
按照下面动手写下面程序,当写好后, 我们就可以进行编译了, 点工具栏上的按钮或者 按键盘的功能键 F7 开始编译了,在最下面一个白色窗口显示编译结果, 0 Error(s)表示没有错误,正确的通过编译,0 Warning 表示 0 警告,这里 如果有 1 个或多个警告,并不影响程序的正确编译, 只是表明程序有多余的没有应用的程序。
下面表示程序已经正确的通过了. 如果你的也是这样的, 那就恭喜你成功了。
下面我们就来说说这个程序的基本知识,要记住的, a. 符号// 后面表示注释部分,或者程序的说明部分, 比如上面的程序名称, 日期等 b. /* */ 这个也是表示注释部分. 注意, 注释部分是不被编译的,注释是方便我们更能理解程序 c . #include<stdio.h> 这个表示头文件,表示我们要用到的函数或者定义,由#include<头文件名> 组成,编写规则头文件MCS-8051 单片机实用教程存深圳伟凡数码 QQ:59059382后面不能 有分号" ; ". #include<reg51.h> 也是头文件, 这是我们 51 单片机的头文件, 定义了单片机的特殊功能寄器的定义,我们可以打开头文件查看, 方法是: 将鼠标移动到文件名上, 点鼠标右键,出现以下菜单,点击蓝色部 分,这样就打开了 51 头文件。
打开 51 文件之后, 我们看到了对 51 单片机的端口定时器中断等进行了定义,想要了解含义,去看看单片机的资料, 这里不多讲。
单片机课程设计--跑马灯设计

单片机课程设计(跑马灯设计)专业:电气自动化摘要AT89C51是一种带4K字节闪存可编程可擦除只读存储器)(FPEROM—Flash Programmable and Erasable Read Only Memory 的低电压、高性能CMOS 8位微处理器,即单片机。
AT89C2051是一种带2K字节闪存可编程可擦除只读存储器的单片机。
单片机的可擦除只读存储器可以反复擦除1000次。
该器件采用ATMEL高密度非易失存储器制造技术制造,与工业标准的MCS-51指令集和输出管脚相兼容。
该设计使用A T89C51芯片作为控制芯片,利用P1口连接8个发光二极管,通过I/O的值控制“跑马灯”的亮灭,以达到显示效果。
开始时所有灯全亮,按下按键S时开始跑马灯,再按下按键S时停止,再按下S时继续,并要求有多种亮暗组合。
时继续,并要求有多种亮暗组合。
按键跑马灯 按键单片机 跑马灯关键词:A T89C51单片机目录摘要 (I)第一章芯片分析和设计概述 (3)第一节 AT89C51芯片分析 (3)第一节第二节 设计概述 (8)第二节第二章硬件电路设计 (9)第三章程序部分设计 (10)参考文献 (18)第一章 芯片分析和设计概述第一节 AT89C51芯片分析ATMEL 的AT89S51是一种高效微控制器,将多功能8位CPU 和闪烁存储器组合在单个芯片中,为很多嵌入式控制系统提供了一种灵活性高且价廉的方案。
且价廉的方案。
AT89C51AT89C51的芯片引脚图如下:的芯片引脚图如下:图1.1 AT89C51引脚图引脚图各引脚的说明和功能分析如下:各引脚的说明和功能分析如下:VCC VCC:供电电压。
:供电电压。
:供电电压。
GND GND:接地。
:接地。
:接地。
P0口:口:P0P0口为一个8位漏级开路双向I/O 口,每脚可吸收8TTL 门电流。
当P1口的管脚第一次写1时,被定义为高阻输入。
时,被定义为高阻输入。
P0P0能够用于外部程序数据存储器,它可以被定义为数据外部程序数据存储器,它可以被定义为数据//地址的第八位。
51单片机8个跑马灯程序汇编设计思路

【51单片机8个跑马灯程序汇编设计思路】1. 引言在嵌入式系统中,跑马灯程序是一个非常常见且基础的程序设计。
通过控制LED灯的亮灭顺序,实现灯光在一组灯中顺序轮流亮起的效果。
其中,51单片机是一种常用的嵌入式系统开发评台,本文将探讨如何通过汇编语言设计实现8个跑马灯程序的思路和方法。
2. 分析题目我们需要对题目进行细致的分析。
51单片机8个跑马灯程序要求我们设计并实现一个程序,能够控制8个LED灯依次轮流亮起的效果。
这意味着我们需要对LED灯进行控制,并且需要考虑如何实现循环、延时等功能。
3. LED灯控制在实现跑马灯程序时,首先需要考虑如何控制LED灯的亮灭。
一种常见的方法是通过I/O口控制LED灯的高低电平,从而实现灯的亮灭。
我们需要了解51单片机的I/O口控制方式,并结合LED灯的连接方式进行设计。
4. 循环控制跑马灯程序的核心在于实现LED灯的依次轮流亮起。
这就需要我们设计循环控制的程序结构。
在汇编语言中,可以通过跳转指令和计数器来实现循环效果,我们需要考虑如何设计循环的次数和顺序。
5. 延时控制为了让人眼能够观察到LED灯的亮灭效果,我们需要在程序中添加延时控制。
这需要我们了解51单片机的定时器控制和时钟频率,并根据LED灯的亮度要求设计合适的延时程序。
6. 汇编设计思路在进行汇编设计时,可以按照以下步骤进行:1)设置I/O口控制LED灯的引脚,确定LED的连接方式;2)设计循环控制结构,确定LED灯的顺序和次数;3)添加延时程序,控制LED灯亮灭的时间间隔;4)编写中断程序,处理定时器中断等事件;5)调试程序,验证跑马灯效果是否符合要求。
7. 个人观点和理解通过设计这个跑马灯程序,我深切体会到了汇编语言的精妙之处。
通过对硬件的直接控制和对程序结构的精心设计,我感受到了嵌入式系统开发中的乐趣和挑战。
而对于初学者来说,设计跑马灯程序也是一个很好的学习过程,可以加深对于51单片机结构和编程思想的理解。
跑马灯

基于C51单片机的跑马灯程序设计本文由仇看风雨贡献/************ 单片机学习板V1.0 ********************//*功能描述:跑马灯练习 */*//*功能:控制学习板上的8个LED显示 *//*接外部晶振11.0592 MHZ *//**************************************************///P0口控制8个LED 把学习板上的4位拨码开关的 1 拨到ON#include <reg51.h>#include <stdio.h>#define uchar unsigned char#define uint unsigned int#define ulong unsigned longvoid delay(unsigned int count);//延时程序void led_display(void);//LED显示子程序//数据模式 LED_DATA[0]显示次数 LED_DATA[1]显示速度LED_DATA[2]-[18]显示数据uchar codeLED_DATA1[18]={3,50,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0 x40,0x20,0x10,0x08,0x04,0x02,0x01,0x00};//弹跳灯uchar codeLED_DATA2[18]={5,120,0xC3,0x66,0x3C,0x18,0x18,0x3C,0x66,0xC3, 0xC3,0x66,0x3C,0x18,0x18,0x3C,0x66,0xC3};//双灯对撞模式uchar codeLED_DATA3[18]={5,80,0x00,0x81,0xc3,0xe7,0xff,0xe7,0xc3,0x81,0 x00,0x81,0xc3,0xe7,0xff,0xe7,0xc3,0x81};//拉链模式uchar codeLED_DATA4[18]={3,50,0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF,0 xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x00};//LED灯柱uchar codeLED_DATA5[18]={3,100,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa, 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa};//动感灯uchar codeLED_DATA6[18]={10,40,0xFF,0x00,0xFF,0x00,0xAA,0x00,0x55,0x00, 0x2A,0x00,0x54,0x28,0x00,0x28,0x00,0x10};//特殊爆闪uchar codeLED_DATA7[18]={10,50,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00, 0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00};//爆闪/***************************延时子程序start***************************/void delay(unsigned int count){unsigned int i,j;for(j=0;j<120;j++);}/***************************延时子程序end***************************/void led_display(void){uchar i,j;/****************显示模式1**********************/ for(i=0;i<LED_DATA1[0];i++){ for(j=2;j<18;j++){P0=~LED_DATA1[j];delay(LED_DATA1[1]);}}/**********************************************/ /****************显示模式2**********************/ for(i=0;i<LED_DATA2[0];i++){ for(j=2;j<18;j++){P0=~LED_DATA2[j];delay(LED_DATA2[1]);}}/**********************************************/ /****************显示模式3**********************/ for(i=0;i<LED_DATA3[0];i++){P0=~LED_DATA3[j];delay(LED_DATA3[1]);}}/**********************************************/ /****************显示模式4**********************/ for(i=0;i<LED_DATA4[0];i++){ for(j=2;j<18;j++){P0=~LED_DATA4[j];delay(LED_DATA4[1]);}}/**********************************************/ /****************显示模式5**********************/ for(i=0;i<LED_DATA5[0];i++){ for(j=2;j<18;j++){P0=~LED_DATA5[j];delay(LED_DATA5[1]);}}/**********************************************/ /****************显示模式6**********************/for(i=0;i<LED_DATA6[0];i++){ for(j=2;j<18;j++){P0=~LED_DATA6[j];delay(LED_DATA6[1]);}}/**********************************************/ /****************显示模式7**********************/ for(i=0;i<LED_DATA7[0];i++){ for(j=2;j<18;j++){P0=~LED_DATA7[j];delay(LED_DATA7[1]);}}/**********************************************/ }main(){P0=1;//清LEDwhile(1){led_display();//调用LED显示程序}}。
51单片机8个跑马灯程序设计思路

51单片机8个跑马灯程序设计思路单片机是一种集成电路,内部包含了计算机的主要组件,如中央处理单元(CPU)、存储器等。
通过编程,可以实现各种不同的功能。
在这里,我将以设计一个8个跑马灯的程序为例,来讲解单片机程序的设计思路。
1.硬件准备首先,我们需要准备一个适用于单片机的开发板,例如STC89C52,以及8个LED灯。
将LED灯连接到开发板的GPIO引脚上,并通过电阻进行限流。
使用跳线将GPIO引脚与LED灯的正极连接。
2.程序结构设计在单片机程序中,我们通常会使用循环结构。
因此,在设计跑马灯程序时,我们可以使用一个无限循环,来实现LED灯的闪烁效果。
具体的程序结构如下所示:```c#include <reg52.h>sbit led0 = P0 ^ 0; //第1个LED灯sbit led1 = P0 ^ 1; //第2个LED灯sbit led2 = P0 ^ 2; //第3个LED灯//...sbit led7 = P0 ^ 7; //第8个LED灯void mainwhile (1)//跑马灯代码}}```在这段代码中,我们首先引入头文件`reg52.h`,它包含了STC89C52的寄存器定义和常用函数的声明。
然后,我们定义了8个宏,用于表示8个LED灯所连接的引脚号。
接下来,在`main(`函数中,我们使用一个无限循环`while(1)`,来实现LED灯的闪烁效果。
3.跑马灯实现思路在无限循环中,我们需要通过对GPIO的控制来实现8个LED灯的闪烁。
具体的实现思路如下:- 首先,我们可以定义一个变量`index`,用于表示当前亮起的LED灯的索引值。
初始时,`index`的值为0,表示第一个LED灯亮起。
- 然后,我们可以使用`switch...case`语句来判断当前的`index`值,从而控制不同的LED灯亮起。
- 在每次循环中,我们可以通过对`index`的递增来实现灯的滚动效果。
单片机 跑马灯实验精选全文

可编辑修改精选全文完整版实验一跑马灯实验一、实验内容1、基本的流水灯根据图1电路,编写一段程序,使8个发光二极管D1、D2、D3、D4、D5、D6、D7、D8顺序(正序)点亮:先点亮D1,再点亮D2、D3……D8、D1……,循环点亮。
每点亮一个LED,采用软件延时一段时间。
2、简单键控的流水灯不按键,按正序点亮流水灯;按下K1不松手,按倒序点亮流水灯,即先点亮D8,再顺序点亮D7、D6……D1、D8……。
松手后,又按正序点亮流水灯。
3、键控的流水灯上电,不点亮LED,按一下K1键,按正序点亮流水灯。
按一下K2键,按倒序点亮流水灯,按一下K3键,全部关闭LED。
二、实验方案1、总体方案设计考虑到K4键未被使用,所以将实验内容中的三项合并到一个主函数中:K4键代替实验内容第二项中的K1键;单片机一开机即执行实验内容第一项;K1、K2、K3键实现实验内容第三项。
所用硬件:AT89C52、BUTTON、LED-BLUE、电源输入:P2.0-K1;P2.1-K2;P2.2-K3;P2.3-K4。
低电平有效输出:P0.0~P0.7-D0~D7。
LED组连线采用共阳极,低电平有效软件设计:软件延时采用延时函数delay(t),可调整延迟时间:void delay(uint t){uint i;while(t--)for(i=0;i<1000;i++){if(P2!=oldK&&P2!=K[0])break;//按下了其他键退出循环}}由于涉及到按键变化所以要设置一个变量oldK保留按键键值,要在延时程序中检测是否按键,当按键后立即设置oldK的值。
按键判断采用在while循环中利用条件语句判断P2的值然后执行该键对应的代码段,达到相应的响应。
为了让K4键的效果优化,即状态变化从当前已亮灯开始顺序点亮或逆序点亮,利用全局变量n来记录灯号,利用算法即可实现。
主要算法:1、全局变量的定义:uchar D[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0X7f};//单个LED亮uchar AllOff=0xff;//LED全灭uchar AllOn=0x00;//LED全亮uchar K[]={0xff,0xfe,0xfd,0xfb,0xf7};//按键开关uchar oldK;//记录已按键int n;2、顺序、逆序点亮流水灯:void forward(){for(n=0;n<=7;n++){out=D[n];delay(15);if(P2!=oldK&&P2!=K[0])break;}out=AllOff;}void backward(){for(n=7;n>=0;n--){out=D[n];delay(15);if(P2!=oldK&&P2!=K[0])break;}out=AllOff;}3、实验内容第二项流水灯灯亮顺序变换:void hold(){n=8;while(1){if(P2==K[4]){//一直按着K4键,逆序点亮跑马灯oldK=K[4];if(n==-1)n=7; //D0灯亮后点亮D7while(n>=0){out=D[n];n--;if(delay4(15))break;}}if(P2==K[0]){//未按下K4键,一直正序点亮跑马灯oldK=K[0];if(n==8)n=0;//D7灯亮后点亮D0while(n<=7){out=D[n];n++;if(delay4(15))break;}}if(P2!=K[4]&&P2!=K[0]){//按下了其他键,退出hold函数break;}}}4、对应实验内容第一项,开机顺序点亮流水灯:while(1){//开机即正序点亮流水灯forward();if(P2!=K[0]){break;}}2、实验原理图图2-1 实验原理图3、程序流程图图2-2 程序流程图三、源程序#include"reg51.h"#define uchar unsigned char#define uint unsigned int#define out P0uchar D[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0X7f};//单个LED亮uchar AllOff=0xff;//LED全灭uchar AllOn=0x00;//LED全亮uchar K[]={0xff,0xfe,0xfd,0xfb,0xf7};//按键开关uchar oldK;//记录已按键int n;//记录当前亮的灯号void delay(uint t){uint i;while(t--)for(i=0;i<1000;i++){if(P2!=oldK&&P2!=K[0])break;//按下了其他键退出循环}}void delay10ms(){uint i;for(i=0;i<10000;i++);}void forward(){for(n=0;n<=7;n++){out=D[n];delay(15);if(P2!=oldK&&P2!=K[0])break;}out=AllOff;}void backward(){for(n=7;n>=0;n--){out=D[n];delay(15);if(P2!=oldK&&P2!=K[0])break;}out=AllOff;}int delay4(uint t){uint i;while(t--)for(i=0;i<1000;i++){if(P2!=oldK){ //按键变化退出循环return 1;}}return 0;}void hold(){n=8;while(1){if(P2==K[4]){//一直按着K4键,逆序点亮跑马灯oldK=K[4];if(n==-1)n=7; //D0灯亮后点亮D7while(n>=0){n--;if(delay4(15))break;}}if(P2==K[0]){//未按下K4键,一直正序点亮跑马灯oldK=K[0];if(n==8)n=0;//D7灯亮后点亮D0while(n<=7){out=D[n];n++;if(delay4(15))break;}}if(P2!=K[4]&&P2!=K[0]){//按下了其他键,退出hold函数break;}}}void main(){oldK=K[0];while(1){//开机即正序点亮流水灯forward();if(P2!=K[0]){break;}}while(1){out=AllOff;if((P2&0x0f)!=0x0f){//检测有键按下delay10ms();//延时10ms再去检测//P2.0_K1键按下正序点亮流水灯if(P2==K[1]){oldK=K[1];while(1){forward();if(P2!=K[1]&&P2!=K[0]){//按下了其他键,退出break;}}}//P2.1_K2键按下逆序点亮流水灯if(P2==K[2]){while(1){backward();if(P2!=K[2]&&P2!=K[0]){//按下了其他键,退出break;}}}//P2.2_K3键按下关闭全部LEDif(P2==K[3]){oldK=K[3];out=AllOff;}//P2.3_K4键按下长按逆序点亮流水灯,不按正序点亮流水灯,直到其他键按下停止if(P2==K[4]){hold();}}}}四、实验结果1、基本的流水灯:开机后即重复顺序点亮流水灯,等待其他按键。
如何用51单片机数码管实现跑马灯功能?

如何用51单片机数码管实现跑马灯功能?51单片机数码管显示跑马灯程序源代码讲解基于51单片机学习板。
用S1键作为控制跑马灯的方向按键,S5键作为控制跑马灯方向的加速度按键,S9键作为控制跑马灯方向的减速度按键,S13键作为控制跑马灯方向的启动或者暂停按键。
记得把输出线P0.4一直输出低电平,模拟独立按键的触发地GND。
(2)实现功能:跑马灯运行:第1个至第8个LED灯一直不亮。
在第9个至第16个LED灯,依次逐个亮灯并且每次只能亮一个灯。
每按一次独立按键S13键,原来运行的跑马灯会暂停,原来暂停的跑马灯会运行。
用S1来改变方向。
用S5和S9来改变速度,每按一次按键的递增或者递减以10为单位。
数码管显示:本程序只有1个窗口,这个窗口分成3个局部显示。
8,7,6位数码管显示运行状态,启动时显示“on”,停止时显示“oFF”。
5位数码管显示数码管方向,正向显示“n”,反向显示“U”。
4,3,2,1位数码管显示速度。
数值越大速度越慢,最慢的速度是550,最快的速度是50。
(3)源代码讲解如下:#include "REG52.H"#define const_voice_short 40 //蜂鸣器短叫的持续时间#define const_key_time1 20 //按键去抖动延时的时间#define const_key_time2 20 //按键去抖动延时的时间#define const_key_time3 20 //按键去抖动延时的时间#define const_key_time4 20 //按键去抖动延时的时间void initial_myself();void initial_peripheral();void delay_short(unsigned int uiDelayShort);void delay_long(unsigned int uiDelaylong);。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
摘要单片机技术是一门不可或缺的技术,对我们将来的工作以及生活和学习都有很密切的联系。
近年来,随着电子技术和微机计算机的迅速发展,单片机的档次不断提高,其应用领域也在不断的扩大,已在工业控制、尖端科学、智能仪器仪表、日用家电、汽车电子系统、办公自动化设备、个人信息终端及通信产品中得到了广泛的应用,成为现代电子系统中最重要的智能化的核心部件。
本设计使用AT89C52芯片,利用P0的8个端口连接8个发光二极管,P1的8个端口连接8个发光二极管,通过P0.0到P0.7的值和P1.0到P1.7的值控制“跑马灯”的亮灭,以达到显示效果。
设计的中断程序要对多个按键动作进行响应,灯光变换的花样有15种,用模式按钮切换。
按下模式按钮键,程序将按十五种模式切换,每按一次模式按钮键,切换一次跑马灯模式,而加速按钮和减速按钮可以改变闪烁速度;最后一种模式为音乐模式,加速按钮可切换音乐。
在单片机运行时,可以在不同状态下让跑马灯显示不同的组合,作为单片机系统正常的指示。
当单片机系统出现故障时,可以利用跑马灯显示当前的故障码,对故障做出诊断。
此外,跑马灯在单片机的调试过程中也非常有用,可以在不同时候将需要的寄存器或关键变量的值显示在跑马灯上,提供需要的调试信息。
关键词:音乐跑马灯;AT89C52单片机;74LS245驱动芯片;LED发光二极管1 设计概述 (1)1.1设计目的 (1)1.2设计作用 (1)1.3设计要求 (1)1.4系统设计框图 (1)2元器件介绍 (3)2.1AT89C52单片机 (3)2.2驱动芯片74LS245 (3)2.3其他元件及功能 (4)3 硬件电路设计 (6)3.1单片机最小系统 (6)3.2LED显示部分 (7)3.3按钮控制部分 (7)3.4数码管显示电路 (8)3.5蜂鸣器部分 (8)3.6系统总电路图 (9)4 软件设计 (10)4.1 程序流程图 (10)4.2 程序设计 (10)5 结束语 (32)参考文献 (33)1 设计概述1.1设计目的利用所学单片机的理论知识进行软硬件整体设计,培养学生分析、解决问题的能力,锻炼学生理论联系实际、综合应用的能力。
通过实践动手制作硬件和软件,综合应用本学期所学的单片机知识,达到加深学习该专业知识的目的。
1.2设计作用跑马灯是一种能像马儿一样跑的灯,就是利用单片机控制LED灯的闪烁方式使其就像马儿奔跑时马蹄的起落。
音乐跑马灯,就是在普通跑马灯的基础上加入了音乐,并通过喇叭将其在适当的时刻播放出来。
单片机的音乐跑马灯由16个LED发光二极管组成,在单片机系统中一般用来指示和显示单片机的运行状态。
通过程序控制使得单片机不同状态下的16个LED发光二级管显示不同的组合,以此显示单片机的工作状态,也可检查单片机是否发生故障。
当然,在实际生活中音乐跑马灯还有许多用处,其可以应用于各种建筑物、大楼、酒吧、KTV和夜总会等娱乐场所,可以制作出各种各样的炫目多彩的霓虹灯,为夜晚带来不一样的光彩。
1.3设计要求⑴有16个发光二极管做跑马灯,其中跑马灯有16种灯亮模式。
⑵有专门的键盘用以切换跑马灯的模式,并且对于任何一种跑马灯模式都可以对亮灯速度进行控制。
⑶每一种跑马灯模式用LED数码管进行显示。
⑷当跑马灯处于一种模式时,伴随的音乐响起,音乐有3首,并可以对其进行切换。
1.4系统设计框图基于AT89C52单片机的多模式带音乐的跑马灯控制系统由电路电源、单片机主控电路、模式切换以及调速按键控制电路、LED数码管显示电路和十六个发光二极管的跑马的电路几部分组成,系统框图如图1.1所示。
图1.1系统设计框图2元器件介绍2.1AT89C52单片机AT89C52是51系列单片机的一个型号,它是ATMEL公司生产的。
AT89C52是一个低电压,高性能CMOS 8位单片机,片内含8k bytes的可反复擦写的Flash只读程序存储器和256 bytes的随机存取数据存储器(RAM),器件采用ATMEL公司的高密度、非易失性存储技术生产,兼容标准MCS-51指令系统,片内置通用8位中央处理器和Flash存储单元,功能强大的AT89C52单片机可为您提供许多较复杂系统控制应用场合。
AT89C52有40个引脚,32个外部双向输入/输出(I/O)端口,同时内含2个外中断口,3个16位可编程定时计数器,2个全双工串行通信口,2个读写口线,AT89C52可以按照常规方法进行编程,也可以在线编程。
其将通用的微处理器和Flash存储器结合在一起,特别是可反复擦写的Flash存储器可有效地降低开发成本。
AT89C52单片机如图2.1所示。
图2.1 AT89C52单片机2.2驱动芯片74LS24574LS245是我们常用的芯片,用来驱动LED或者其他的设备,它是8路同相三态双向总线收发器,可双向传输数据。
74LS245还具有双向三态功能,既可以输出,也可以输入数据。
当AT89C52单片机的P0口总线负载达到或超过P0最大负载能力时,必须接入74LS245等总线驱动器。
当片选端CE低电平有效时,BA=“0”,信号由 B 向 A 传输;AB=“1”,信号由 A 向B 传输;(发送)当CE为高电平时,A、B均为高阻态。
由于P2口始终输出地址的高8位,接口时74LS245的三态控制端1G和2G接地,P2口与驱动器输入线对应相连。
P0口与74LS245输入端相连, CE端接地,保证数据线畅通。
8051的RD和PSEN相与后接AB/BA,使得RD和PSEN有效时,74LS245输入(P0.1←D1),其它时间处于输出(P0.1→D1)。
74LS245驱动芯片如图2.2所示。
图2.2 74LS245驱动芯片2.3其他元件及功能⑴ LED发光二极管:指示和显单片机状态。
⑵扬声器:播放歌曲。
⑶按钮:模式按钮用于切换单片机工作模式,加速按钮用于加快LED灯的闪烁频率,减速按钮用于减缓LED灯的闪烁频率,复位按钮用于人工复位。
⑷数码管:用于显示单片机当前处于何种模式。
⑸晶体振荡器:用于构成单片机的复位电路。
⑹电容:用于构成单片机的复位电路。
⑺电阻:限流分压作用,是电路正常工作并保护电路。
元件清单如表2.1所示。
表2.1 元件清单3 硬件电路设计3.1单片机最小系统单片机最小系统或者称为最小应用系统,素质用最少的元件组成的单片机可以工作的系统,对51系列单片机来说,最小系统一般应该包括:单片机、复位电路、晶振电路。
复位电路:该复位电路采用手动复位和按键复位,所谓手动复位,是指通过接通一按钮开关,使单片机进入复位状态,使RST获得高电平,该方法可实现比较快速的复位。
当然,若不按下按钮,需等待电容充完电后使得RST获得高电平复位,复位电路如图3.1所示。
图3.1复位电路晶振电路:8051单片机的时钟信号通常用两种电路形式电路得到:内部震荡方式和外部中断方式。
在引脚XTAL1和XTAL2外部接晶振电路器(简称晶振)或陶瓷晶振器,就构成了内部晶振方式。
由于单片机内部有一个高增益反相放大器,当外接晶振后,就构成了自激振荡器并产生振荡时钟脉冲。
内部振荡方式的外部电路如下图所示。
其电容值一般在5~30pF,晶振频率的典型值为12MHz,采用6MHz的情况也比较多。
内部振荡方式所得的时钟信号比较稳定,实用电路使用较多。
晶振电路图如图3.2所示。
图3.2 晶振电路3.2LED显示部分显示部分,用十六个发光二极管通过总线和74LS245驱动芯片分别接到了AT89C52单片机的P0和P1口。
通过程序控制P0和P1的十六个端口按一定方式一次为低电平点亮端口LED。
若把P0口当作了通用的I/O口,则需要加上拉电阻,可是我们所采用的是使用总线方式输出因此不需要上拉电阻。
本设计采用的是发光二极管阳极接电源,因此要求P0口P1口输出低电平时,二极管才会发光。
LED显示部分如图3.3所示。
图3.3 LED显示部分3.3按钮控制部分用一个按钮进行模式的切换,即用该按钮控制多种不同的亮灯模式,分别有16种模式,从模式“0”至模式“F”,开启模式“F”是有音乐放出。
用一个按钮(加速按钮)进行发光二极管亮灯加速的调节,在第“F”模式时该按钮则可进行歌曲的切换。
用一个按钮(减速按钮)进行发光二极管亮灯减速的调节,在第9模式时该按钮则可进行歌曲的切换。
按钮控制部分如图3.4所示。
图 3.4 按钮控制部分3.4数码管显示电路数码管上分别显示0~F十六个数字,分别代表十六种模式。
采用共阳极连接,即数码管的a~f端要输入低电平时内部二极管才导通。
在数码管每个端口与74LS24 5驱动芯片与数码管之间连上100Ω的电阻。
数码管显示电路的具体硬件设计如图3.5所示。
图3.5 数码管显示电路3.5蜂鸣器部分蜂鸣器是一种将电信号转换成声音信号的电声元件。
确切的说,蜂鸣器工作实际上是把一定范围内的音频电功率讯号通过换能方式转变失真小并且有足够声压级的可听声音。
本课程设计直接将蜂鸣器街道单片机的P2.6端口,蜂鸣器具体电路如图3.6所示。
图3.6 蜂鸣器电路3.6系统总电路图音乐跑马灯的总电路图如3.7所示。
图3.7 音乐跑马灯总电路3.7操作说明在仿真软件中,按下屏幕左下角开始按键时,数码管显示为“0”,发光二极管以一定方式开始闪烁,此时按下模式按钮使得数码管显示为“1”时,发光二极管以一另种方式开始闪烁。
此时,若按加速按钮,则加快了发光二极管的闪烁速度,有4种不同的速度。
当加速到最大速度时,再按加速按钮则无法继续加速,此时可以按减速按钮进行减速。
以此类推,模式“1”到模式“E”,每种模式都给有一种闪烁方式。
当继续按模式按钮使的模式转换为模式“F”时,蜂鸣器开始放歌,发光二极管按照音乐音调变动闪烁。
此时的加速按钮可以进行歌曲的切换,按一次按钮换一首歌。
4 软件设计4.1 程序流程图该程序采用两个程序编写:第一个位单片机主程序,作用是使单片机完成相应上电功能;第二个是音乐产生程序,在第一个程序中包含第二个程序的头文件即可。
程序流程图如图4.1及4.2所示。
图4.1 主程序 图4.2 音乐程序流程图4.2 程序设计#include <REG52.H>//包括一个52标准的内核头文件//#include <SoundPlay.h> unsigned char RunMode;void Delay1ms(unsigned int Count) //延时子程序 {unsigned int i,j;for(i=0;i<Count;i++)for(j=0;j<1200;j++);}unsigned char code LEDDisplayCode[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff}; //LED数码管void Display(unsigned char Value){P3=LEDDisplayCode [Value];} //数值输出至LED void LEDFlash(unsigned char Count){unsigned char i;bit Flag;for(i=0;i<Count;i++){Flag=!Flag;if(Flag)Display(RunMode);elseDisplay(0x10);Delay1ms(100);}Display(RunMode);}unsigned char GetKey(void) //判断按键是否按下{unsigned char KeyTemp,CheckValue,Key=0x00;CheckValue=P2&0x32;if(CheckValue==0x32)return 0x00;Delay1ms(10); //调用延时KeyTemp=P2&0x32;if(KeyTemp==CheckValue) return 0x00;if(!(CheckValue&0x02)) Key|=0x01;if(!(CheckValue&0x10)) Key|=0x02;if(!(CheckValue&0x20)) Key|=0x04;return Key;}unsigned int Timer0Count,SystemSpeed,SystemSpeedIndex;void InitialTimer2(void){T2CON=0x00;TH2=RCAP2H=0xfc;ET2=1; //定时器2中断允许TR2=1; //定时器2启动EA=1;}unsigned int code SpeedCode[]={1,100,500,1000};void SetSpeed(unsigned char Speed) //跑马灯速度控制{SystemSpeed=SpeedCode[Speed];}void LEDShow(unsigned int LEDStatus) //跑马灯的输出{P1=~(LEDStatus&0x00ff);P0=~((LEDStatus>>8)&0x00ff);}{RunMode=0x00;Timer0Count=0; SystemSpeedIndex=4;P1=0x00;P0=0x00;P2=0xff;P3=0x00;Delay1ms(500);P1=0xff;P0=0xff;P2=0xff;P3=0xff;SetSpeed(SystemSpeedIndex); Display(RunMode);}unsigned int LEDIndex=0;bit LEDDirection=1,LEDFlag=1; void Mode_0(void){LEDShow(0x0001<<LEDIndex); LEDIndex=(LEDIndex+1)%16; }void Mode_1(void){LEDShow(0x8000>>LEDIndex); LEDIndex=(LEDIndex+1)%16; }{if(LEDDirection)LEDShow(0x0001<<LEDIndex); elseLEDShow(0x8000>>LEDIndex);if(LEDIndex==15)LEDDirection=!LEDDirection; LEDIndex=(LEDIndex+1)%16;}void Mode_3(void){if(LEDDirection)LEDShow(~(0x0001<<LEDIndex)); elseLEDShow(~(0x8000>>LEDIndex)); if(LEDIndex==15)LEDDirection=!LEDDirection; LEDIndex=(LEDIndex+1)%16;}void Mode_4(void){if(LEDDirection){if(LEDFlag)LEDShow(0xfffe<<LEDIndex);elseLEDShow(~(0x7fff>>LEDIndex)); }{if(LEDFlag)LEDShow(0x7fff>>LEDIndex);elseLEDShow(~(0xfffe<<LEDIndex)); }if(LEDIndex==15){LEDDirection=!LEDDirection;if(LEDDirection)LEDFlag=!LEDFlag;}LEDIndex=(LEDIndex+1)%16;}void Mode_5(void){if(LEDDirection)LEDShow(0x000f<<LEDIndex); elseLEDShow(~(0xf000>>LEDIndex)); if(LEDIndex==15)LEDDirection=!LEDDirection; LEDIndex=(LEDIndex+1)%16;}void Mode_6(void){if(LEDDirection)LEDShow(~(0x000f<<LEDIndex));LEDShow(~(0xf000>>LEDIndex)); if(LEDIndex==15)LEDDirection=!LEDDirection; LEDIndex=(LEDIndex+1)%16;}void Mode_7(void){if(LEDDirection)LEDShow(0x003f<<LEDIndex); elseLEDShow(0xfc00>>LEDIndex);if(LEDIndex==9)LEDDirection=!LEDDirection; LEDIndex=(LEDIndex+1)%10;}void Mode_8(void){LEDShow(++LEDIndex);}void Mode_9(void){LEDShow(0x0003<<LEDIndex); LEDIndex=(LEDIndex+1)%16;}void Mode_A(void){LEDShow(0xc000>>LEDIndex); LEDIndex=(LEDIndex+1)%16;}void Mode_b(void){if(LEDDirection)LEDShow(0x0003<<LEDIndex); elseLEDShow(0xc000>>LEDIndex); if(LEDIndex==15)LEDDirection=!LEDDirection; LEDIndex=(LEDIndex+1)%16;}void Mode_C(void){if(LEDDirection)LEDShow(0x8080>>LEDIndex); elseLEDShow(0x0101<<LEDIndex); if(LEDIndex==7)LEDDirection=!LEDDirection; LEDIndex=(LEDIndex+1)%8;}void Mode_d(void){if(LEDDirection)LEDShow(0x1111<<LEDIndex); elseLEDShow(0x8888>>LEDIndex); if(LEDIndex==3)LEDDirection=!LEDDirection;LEDIndex=(LEDIndex+1)%4;}void Mode_E(void){if(LEDDirection)LEDShow(0x5555<<LEDIndex);elseLEDShow(0xaaaa>>LEDIndex);if(LEDIndex==3)LEDDirection=!LEDDirection;LEDIndex=(LEDIndex+1)%4;}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();}else if(RunMode==0x09) {Mode_9();}else if(RunMode==0x0a) {Mode_A();}else if(RunMode==0x0b) {Mode_b();}else if(RunMode==0x0c) {Mode_C();}else if(RunMode==0x0d) {Mode_d();}else if(RunMode==0x0e) {Mode_E();}}void Timer2(void) interrupt 5 using 3 {TF2=0;if(++Timer0Count>=SystemSpeed) {Timer0Count=0;timer0eventrun();}}unsigned char MusicIndex=0;int MUSICNUMBER=3;void KeyDispose(unsigned char Key) {if(Key&0x01){LEDDirection=1;LEDIndex=0;LEDFlag=1;RunMode=(RunMode+1)%16;Display(RunMode);if(RunMode==0x0f)TR2=0;elseTR2=1;}if(Key&0x02){if(RunMode==0x0f){MusicIndex=(MusicIndex+MUSICNUMBER-1)%MUSICNUMBER;} else{if(SystemSpeedIndex>0){--SystemSpeedIndex;SetSpeed(SystemSpeedIndex);}else{LEDFlash(6);}}}if(Key&0x04){if(RunMode==0x0f){MusicIndex=(MUSICNUMBER+1)%MUSICNUMBER;}else{if(SystemSpeedIndex<3){++SystemSpeedIndex;SetSpeed(SystemSpeedIndex);}else{LEDFlash(6);}}}}unsigned char code Music_1[]={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,0x1b,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,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,0x03}; unsigned char code Music_2[]={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,0x22,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,0x03,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,0x33,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_3[]={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};unsigned char * SelectMusic(unsigned char SoundIndex){unsigned char * MusicAddress=0;switch(SoundIndex){case 0x00:MusicAddress=&Music_1[0];break;case 0x01:MusicAddress=&Music_2[0];break;case 0x02:MusicAddress=&Music_3[0];break;case 0x03:break;case 0x04:break;case 0x05:break;default:break;}return MusicAddress;}#ifndef __SOUNDPLAY_H_REVISION_FIRST__#define __SOUNDPLAY_H_REVISION_FIRST__#define SYSTEM_OSC 12000000 //定义晶振频率#define SOUND_SPACE 4/5//定义普通音符演奏长度分率,每四分音符间隔#define MUSICNUMBER 3 //歌曲的数目sbit BeepIO=P2^6; //定义输出引脚extern void LEDShow(unsigned int LEDStatus);extern unsigned char GetKey(void);extern void KeyDispose(unsigned char Key);extern void Delay1ms(unsigned int count);extern unsigned char MusicIndex;unsigned int code FreTab[12]={262,277,294,311,330,349,369,392,415,440,466,494}; //原始频率表unsigned char code SignTab[7]={0,2,4,5,7,9,11}; //1~7在频率表中的位置unsigned char code LengthTab[7]={1,2,4,8,16,32,64};unsigned char Sound_Temp_TH0,Sound_Temp_TL0; //音符定时器初值暂存unsigned char Sound_Temp_TH1,Sound_Temp_TL1; //音长定时器初值暂存void InitialSound(void){BeepIO=0;Sound_Temp_TH1=(65535-(1/1200)*SYSTEM_OSC)/256;//计算TL1应装入的初值Sound_Temp_TL1=(65535-(1/1200)*SYSTEM_OSC)%256;//计算TH1应装入的初值TH1=Sound_Temp_TH1;TL1=Sound_Temp_TL1;TMOD|=0x11;ET0=1;ET1=0;TR0=0;TR1=0;EA=1;}void BeepTimer0(void) interrupt 1 //音符发生中断BeepIO=!BeepIO;TH0=Sound_Temp_TH0;TL0=Sound_Temp_TL0;}void Play(unsigned char *Sound,unsigned char Signature,unsigned int Octachord,unsigned int Speed){unsigned int NewFreTab[12]; //新的频率表unsigned char i,j;unsigned int Point,LDiv,LDiv0,LDiv1,LDiv2,LDiv4,CurrentFre,Temp_T,SoundLength;unsigned char TONE,Length,SL,SH,SM,SLen,XG,FD,Key,LEDFlash,OFFSet;for(i=0;i<12;i++) //根据调号及升降8度来生成新的频率表{j=i+Signature;if(j>11){j=j-12;NewFreTab[i]=FreTab[j]*2;}elseNewFreTab[i]=FreTab[j];if(Octachord==1)NewFreTab[i]>>=2;else if(Octachord==3)NewFreTab[i]<<=2;}SoundLength=0;while(Sound[SoundLength]!=0x00) //计算歌曲长度SoundLength+=2;}Point=0;TONE=Sound[Point];Length=Sound[Point+1]; //读出第一个音符和它的时值LDiv0=12000/Speed; //算出1分音符的长度LDiv4=LDiv0/4; //算出4分音符的长度LDiv4=LDiv4-LDiv4*SOUND_SPACE; //普通音最长间隔标准TR0=0;TR1=1;while(Point<SoundLength){SL=TONE%10; //计算出音符SM=TONE/10%10; //计算出高低音SH=TONE/100; //计算是否升半LEDFlash=SM*((SL/2)+1)+2;LEDShow(~(0xFFFE<<LEDFlash));OFFSet=2;CurrentFre=NewFreTab[SignTab[SL-1]+SH]; //查出对应音符的频率if(SL!=0){if(SM==1)CurrentFre>>=2; //低音if(SM==3)CurrentFre<<=2; //高音Temp_T=65536-(50000/CurrentFre)*10/(12000000/SYSTEM_OSC);//计算计数器初值Sound_Temp_TH0=Temp_T/256;Sound_Temp_TL0=Temp_T%256;TH0=Sound_Temp_TH0;TL0=Sound_Temp_TL0+6; //对中断延时补偿}SLen=LengthTab[Length%10]; //算出是几分音符XG=Length/10%10; //算出音符类型FD=Length/100;LDiv=LDiv0/SLen; //算出连音音符演奏的长度if(FD==1)LDiv=LDiv+LDiv/2;if(XG!=1)if(XG==0) //算出普通音符演奏的长度if(SLen<=4)LDiv1=LDiv-LDiv4;elseLDiv1=LDiv*SOUND_SPACE;elseLDiv1=LDiv/2; //算出顿音的演奏长度elseLDiv1=LDiv;if(SL==0){LDiv1=0;LDiv2=LDiv-LDiv1;} //算出不发音的长度if(SL!=0){TR0=1;for(i=LDiv1;i>0;i--) //发规定长度的音{OFFSet=(OFFSet+1)%5;LEDShow(~(0xFFFE<<(LEDFlash+OFFSet-2)));while(TF1==0){Key=GetKey();if(Key!=0x00){KeyDispose(Key);TR0=0;TR1=0;BeepIO=0;return;}}TH1=Sound_Temp_TH1;TL1=Sound_Temp_TL1;TF1=0;}}if(LDiv2!=0){TR0=0;BeepIO=0;for(i=LDiv2;i>0;i--) //音符间的间隔{OFFSet=(OFFSet+1)%5;LEDShow(~(0xFFFE<<(LEDFlash+OFFSet-2)));while(TF1==0){Key=GetKey();if(Key!=0x00){KeyDispose(Key);TR0=0;TR1=0;BeepIO=0;return;}}TH1=Sound_Temp_TH1;TL1=Sound_Temp_TL1;TF1=0;}}Point+=2;TONE=Sound[Point];Length=Sound[Point+1];}BeepIO=0;MusicIndex=(MusicIndex+1)%MUSICNUMBER; LEDShow(0x0001);Delay1ms(300);}void PlayMusic(void){Delay1ms(200);Play(SelectMusic(MusicIndex),0,3,360);}void main(){unsigned char Key;InitialCPU();InitialSound(); InitialTimer2();while(1){Key=GetKey();if(RunMode==0x0f){PlayMusic();}if(Key!=0x00){KeyDispose(Key);} }}#endif5 结束语通过单片机课程设计,我不仅加深了对单片机理论的理解,将理论很好地应用到实际当中去,而且我还学会了如何去培养我们的创新精神,从而不断地战胜自己,超越自己。