2024年6月5日发(作者:)
题目:
基于AT89C52单片机的多模式流水灯设计——程序设计
一、 概述
AT89C52是一个低电压,高性能CMOS 8位单片机,片内含8k bytes的可反复擦写的
Flash只读程序存储器和256 bytes的随机存取数据存储器(RAM),器件采用ATMEL公司
的高密度、非易失性存储技术生产,兼容标准MCS-51指令系统,片内置通用8位中央处理
器和Flash存储单元。AT89C52有40个引脚,32个外部双向输入/输出(I/O)端口,同时内
含2个外中断口,3个16位可编程定时计数器,2个全双工串行通信口,2个读写口线,AT89C52
可以按照常规方法进行编程。
P为40 脚双列直插封装的8 位通用微处理器,采用工业标准的C51内核,在内部功能
及管脚排布上与通用的8xc52 相同,其主要用于会聚调整时的功能控制。功能包括对会聚主
IC 内部寄存器、数据RAM及外部接口等功能部件的初始化,会聚调整控制,会聚测试图控
制,红外遥控信号IR的接收解码及与主板CPU通信等。主要管脚有:XTAL1(19 脚)和X
TAL2(18 脚)为振荡器输入输出端口,外接12MHz 晶振。RST/Vpd(9 脚)为复位输入
端口,外接电阻电容组成的复位电路。VCC(40 脚)和VSS(20 脚)为供电端口,分别接
+5V电源的正负端。P0~P3 为可编程通用I/O 脚,其功能用途由软件定义。
二、 实验原理与设计方案
设计平台:
AT89C51单片机;PC机,含Keil软件平台,Proteus软件平台;单片机开发板。
第1页 共17页
如下图2为开发板:
设计思路:
本系统主要通过P1口来控制LED的闪烁,有10种模式(0~9)可供选择。通过模式按键来选择
需要的闪烁方式,这里主要通过几个计数器来控制,按下一次按键,计数器加1,模式切换到下一
种模式。同时利用数码管将模式显示出来。另外还有一个加速和减速的按键,系统设定有30种速度
可供选择。按下一次加速键,速度计数器加1,按下一次减速键,速度计数器减1。其中加速减速主
要是通过设定定时器2的定时时间来改变的,加速,减速主要是将定时器的时间加减。特别一提的
是,按键的按下读取要消除抖动。
设计方案与电路模块:
要实现流水灯功能,我们只要将发光二极管LED1~LED16依次点亮、熄灭,16只LED灯便会
一亮一暗的做流水灯了。在这个设计中,我们不只是单一的做向左运动或向右运动,而是设计了十
个模式,而每一种模式也有30种速度可供选择,因此我们在普通的流水灯基础上增加了模式的选择
功能、速度的选择功能和一个数码显示管来显示模式编号。在此我们还应注意一点,由于人眼的视
觉暂留效应以及单片机执行每条指令的时间很短,我们在控制二极管亮灭的时候应该延时一段时间,
否则我们就看不到“流水”效果了。
该流水灯实验一共可分为六个模块:晶振电路模块、复位电路模块、数码管显示模块、AT89C
52单片机模块、功能模式选择模块、LED流水灯模块。
其中五个模块连接在AT89C52单片机上构成一个完整的系统。此系统的原理框图如图1所示:
第2页 共17页
图1
1、晶振电路模块
晶振是晶体振荡器的简称。在电气上它可以等效成一个电容和一个电阻并联再串联一个电容的
二端网络,电工学上这个网络有两个谐振点,以频率的高低分其中较低的频率是串联谐振,较高的
频率是并联谐振。由于晶体自身的特性致使这两个频率的距离相当的接近,在这个极窄的频率范围
内,晶振等效为一个电感,所以只要晶振的两端并联上合适的电容它就会组成并联谐振电路。这个
并联谐振电路加到一个负反馈电路中就可以构成正弦波振荡电路,由于晶振等效为电感的频率范围
很窄,所以即使其他元件的参数变化很大,这个振荡器的频率也不会有很大的变化。
晶振有一个重要的参数,那就是负载电容值,选择与负载电容值相等的并联电容,就可以得到
晶振标称的谐振频率。一般的晶振的负载电容为15p或12.5p,如果再考虑元件引脚的等效输入电容,
则两个22p的电容构成晶振的振荡电路就是比较好的选择。
2、复位电路模块
这个模块主要进行复位操作。
3、LRD流水灯模块
要实现流水灯功能,我们只要将发光二极管LED1~LED16依次点亮、熄灭,16只LED灯便会
一亮一暗的做流水灯了。在此我们还应注意一点,由于人眼的视觉暂留效应以及单片机执行每条指
令的时间很短,我们在控制二极管亮灭的时候应该延时一段时间,否则我们就看不到“流水”效果了。
且每一个LED灯于一个电阻串联。
4、功能模式选择模块
在该模块中,我们要运用3个键,分别是模式键、加速键和减速键。因为该设计一共设有10
种模式,因此模式键的功能就是选择运行哪一个模式。加速键顾名思义,是调节增加流水灯的运行
速度。减速键就是减少流水灯的运行速度。
5、数码管显示模块
此模块主要是用来显示按下模式键后选择是哪个模式,其选择范围为0—9,既10个模式,因
此只用一个数码管就可以了。
第3页 共17页
三、硬件电路图与流程图
1、电路图:
硬件电路图
2、消抖模块流程图
因为在按键产生高电平的时候,数码管会产生抖动。在上升沿处抖动不强烈,可以忽略。但在下降
沿处抖动会很强烈,而使我们很难观察到数码管的变化,只是会看到数码管在一直闪动,因此我们
需要增加延迟的时间来消除抖动。如减慢键的程序:
if(!(CheckValue&0x20)) //判断是否被按下
{
delay(5); //判断上升沿
if(!(CheckValue&0x20))
{
第4页 共17页
Key|=0x01;
delay(500); // 下降沿,消除抖动
}
else
return 0x00;
}
上升沿的抖动可以忽略,故延迟时间只是5ms,而下降沿抖动剧烈,故延迟时间给了500ms,用以
消除抖动。
3、定时器中断和速度改变模块
定时器中断程序:
unsigned int TimerCount,SystemSpeed,SystemSpeedIndex;
void InitialTimer2(void)
{
T2CON = 0x00; //8位自动重装模式,设定时间,控制中断,控制流水灯速度
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(++TimerCount>=SystemSpeed)
{
TimerCount = 0;
TimerEventRun();
}
}
速度改变程序:
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种速度
第5页 共17页
void SetSpeed(unsigned char Speed)
{
SystemSpeed =SpeedCode[Speed];
}
这里采用数组法,并且定义一开始的速度是第十个,既30ms。
4、主程序流程图
由调用模式的模块实现:
void TimerEventRun(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)
第6页 共17页
{
Mode_9();
}
}
四、实验结果举例
1、模式0:
一个亮灯循环左移 ,按下列步骤运行,初始状态○○○○○○○○
1 ○○○○○○○●
2 ○○○○○○●○
3 ○○○○○●○○
4 ○○○○●○○○
5 ○○○●○○○○
6 ○○●○○○○○
7 ○●○○○○○○
8 ●○○○○○○○
回初始状态重复循环
2、 模式5:
四个亮灯一起循环左移,按下列步骤运行,初始状态○○○○○○○○
1 ○○○○○○○●
第7页 共17页
2 ○○○○○○●●
3 ○○○○○●●●
4 ○○○○●●●●
5 ○○○●●●●○
6 ○○●●●●○○
7 ○●●●●○○○
8 ●●●●○○○○
9 ●●●○○○○○
10●●○○○○○○
11●○○○○○○○
12○○○○○○○○
3、 模式8:
五、实验总结
在这次的作业中,我们选择做流水灯的实验,虽然难度不大,但我们用C语言编写了10种流水
灯的模式,并从中较好的理解和运用AT89C52单片机的功能。我们利用空余时间焊了一个实物
电路板,但由于粗心等原因,焊板质量比较粗糙。最终该焊板也没能正常工作。以下是我们
的电路板:
第8页 共17页
由于电路板不能正常工作,我们只好把原程序稍作改动,移植到开发板上,如图:
另外上面所提到的电平抖动(电平毛刺)问题,按键抖动的问题对所有的处理器都存在,单
片机、CPLD/FPGA以及ARM等等,都会存在抖动,所以在编写程序的时候,需要适当的绕开抖动
的边沿再去采样,也就是说,要避免直接去采样按键瞬间产生的不稳定的信号。通常采用的方法有:
延时采样和多次采样等,前者应用较多,如单片机中,按键之后延时一段时间再读取信号即可。本
实验就是采用这种方法来消除抖动的。 关于原程序的改动,就是去除了rst电路,其它原理和
原程序完全一致。
在编写程序时用的是C语言,其主要原因是因为相对之下,我比较掌握C语言,对于接
近硬件方面的汇编不是很熟悉。编写最开始是要调用库文件、单片机端口、变量等。程序主
要分5个部分:部分一是关于模式键的判断;部分二是流水灯的功能实现;部分三是调用模
式;部分四是中断处理;部分五是执行循环。上面已经提过,消除抖动非常重要,主要办法
是在下降沿地方给一个较大的延迟时间,以覆盖抖动的区域,不然实际操作中我们是看不到
数码管的显示。通过这次作业,我对单片机的学习有了更深刻的掌握,并复习了C语言,对
以后的设计提供基础。
第9页 共17页
六、附录:完整C代码及注释
#include
unsigned char RunMode;
sbit dula = P2^6;
sbit wela = P2^7;
sbit leden = P2^5;
void delay(unsigned int z) //无返回值,调用无符号变量z
{
unsigned int x,y;
for(x=z;x>0;x--) //x减1,循环语句
for(y=110;y>0;y--);
}
//**********************************System
Fuction*************************************************第一部分
void Delay1ms(unsigned int count)
{
unsigned int i,j;
for(i=0;i for(j=0;j<120;j++); } unsigned char code LEDDisplayCode[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //0—9,定义一个无符号数组,显示一 共有10种模式 void Display(unsigned char Value) //上面10个取一个放进value { P0 = LEDDisplayCode[Value]; //P0端口接数码管 } void LEDFlash(unsigned char Count) //设定LED的闪烁次数 { unsigned char i; bit Flag; //定义flag是一个位数 for(i = 0; i { Flag = !Flag; if(Flag) Display(RunMode); else Display(0x10); Delay1ms(100); //延时100ms } Display(RunMode); } //整个功能为开电源后数码管闪了0.1s后熄灭 第10页 共17页 unsigned char GetKey(void) //判断是模式,加快还是减慢按键被按下 { //部分二,判断键是否被按下 unsigned char KeyTemp,CheckValue,Key = 0x00; //初始三个键为0 CheckValue = P3&0xe0; if(CheckValue==0xe0) //什么都没有按下 return 0x00; Delay1ms(10); KeyTemp = P3&0xe; if(KeyTemp==CheckValue) return 0x00; if(!(CheckValue&0x20)) //减慢键,判断是否按下 { delay(5); //判断上升沿 if(!(CheckValue&0x20)) { Key|=0x01; delay(500); //判断下降沿,消除抖动 } else return 0x00; } if(!(CheckValue&0x40)) //加速键 { delay(5); if(!(CheckValue&0x40)) { Key|=0x02; delay(500); } else return 0x00; } if(!(CheckValue&0x80)) //模式键 { delay(5); if(!(CheckValue&0x80)) { Key|=0x04; delay(500); } else return 0x00; } return Key; 第11页 共17页 } unsigned int TimerCount,SystemSpeed,SystemSpeedIndex; //定义计数器、速度、速度数值 void InitialTimer2(void) //调用硬件 { T2CON = 0x00; //8位自动重装模式,设定时间,控制中断,控制流灯速度 TH2 = RCAP2H = 0xFC; //重装值,初始值 TL2 = RCAP2L = 0x18; ET2=1; //定时器 2 中断允许 TR2 = 1; //定时器 2 启动 EA=1; } //部分三,实现功能的主程序 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 种速度,当速度加到最大值时数码管会闪动 void SetSpeed(unsigned char Speed) { SystemSpeed =SpeedCode[Speed]; } void LEDShow(unsigned int LEDStatus) //初始全亮 { P1 = ~(LEDStatus&0x00FF); } void InitialCPU(void) //初始化CPU { RunMode = 0x00; //数码管 TimerCount = 0; SystemSpeedIndex = 10; //定义初始速度为第十个:30ms leden = 1; //使用上 wela = 1; //使用上 P0 = 0x00; //全亮 wela = 0; //没使用 P1 = 0x00; Delay1ms(500); //闪烁 P1 = 0xFF; SetSpeed(SystemSpeedIndex); //取速度值 Display(RunMode); } //Mode 0,一个灯由上至下依次点亮 unsigned int LEDIndex = 0; bit LEDDirection = 1,LEDFlag = 1; void Mode_0(void) 第12页 共17页 { LEDShow(0x0001< 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< else LEDShow(0x8000>>LEDIndex); if(LEDIndex==15) LEDDirection = !LEDDirection; LEDIndex = (LEDIndex+1)%16; } //Mode 3,灯首先全亮,一个灭灯先由下至上依次熄灭,再由上至下熄灭 void Mode_3(void) { if(LEDDirection) LEDShow(~(0x0001< else LEDShow(~(0x8000>>LEDIndex)); if(LEDIndex==15) LEDDirection = !LEDDirection; LEDIndex = (LEDIndex+1)%16; } //Mode 4,灯首先全亮,由下至上依次熄灭,再由下至上依次点亮,再由上至下点亮,最后由上至 下熄灭 void Mode_4(void) { if(LEDDirection) { if(LEDFlag) LEDShow(0xFFFE< else LEDShow(~(0x7FFF>>LEDIndex)); } else { if(LEDFlag) 第13页 共17页 LEDShow(0x7FFF>>LEDIndex); else LEDShow(~(0xFFFE< } if(LEDIndex==15) { LEDDirection = !LEDDirection; if(LEDDirection) LEDFlag = !LEDFlag; } LEDIndex = (LEDIndex+1)%16; } //Mode 5,四个亮灯一起先由下至上点亮,再由上至下熄灭,和mode2对比 void Mode_5(void) { if(LEDDirection) LEDShow(0x000F< else LEDShow(0xF000>>LEDIndex); if(LEDIndex==15) LEDDirection = !LEDDirection; LEDIndex = (LEDIndex+1)%16; } //Mode 6,四个灭灯一起先由下至上熄灭,再由上至下点亮,与mode5相反 void Mode_6(void) { if(LEDDirection) LEDShow(~(0x000F< else LEDShow(~(0xF000>>LEDIndex)); if(LEDIndex==15) LEDDirection = !LEDDirection; LEDIndex = (LEDIndex+1)%16; } //Mode 7,六个灯一起先由下至上熄灭,再由上至下点亮 void Mode_7(void) { if(LEDDirection) LEDShow(0x003F< else LEDShow(0xFC00>>LEDIndex); if(LEDIndex==9) LEDDirection = !LEDDirection; LEDIndex = (LEDIndex+1)%10; 第14页 共17页 } //Mode 8,由上至下亮灯往下走,当第一个亮灯走到第二个灯位时,第二个亮灯开始走,如此积累, 知道8个灯全亮 void Mode_8(void) { LEDShow(++LEDIndex); } //Mode 9,与mode8相反 void Mode_9(void) { LEDShow(--LEDIndex); } //调用模式 void TimerEventRun(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) { 第15页 共17页 Mode_7(); } else if(RunMode ==0x08) { Mode_8(); } else if(RunMode ==0x09) { Mode_9(); } } //中断处理函数 void Timer2(void) interrupt 5 using 3 { TF2 = 0; //中断端口,中断标志清除( Timer2 必须软件清除标志) if(++TimerCount>=SystemSpeed) { TimerCount = 0; TimerEventRun(); } } unsigned char MusicIndex = 0; void KeyDispose(unsigned char Key) { if(Key&0x01) // 按键设定8个模式,判断key值 { LEDDirection = 1; LEDIndex = 0; LEDFlag = 1; RunMode = (RunMode+1)%10; //循环计数 Display(RunMode); } if(Key&0x02) //流水灯流动的速度减慢 { if(SystemSpeedIndex>0) //第几个速度 { --SystemSpeedIndex; SetSpeed(SystemSpeedIndex); } else { LEDFlash(6); //数码管闪烁时间 } } if(Key&0x04) //流水灯流动的速度加快 { if(SystemSpeedIndex<28) 第16页 共17页 { ++SystemSpeedIndex; SetSpeed(SystemSpeedIndex); } else { LEDFlash(6); } } } //*********************************************************************************** main() { unsigned char Key; InitialCPU(); InitialTimer2(); while(1) //条件语句,死循环,永远执行 { Key = GetKey();//获取按键 if(Key!=0x00) { KeyDispose(Key);//按照设定的模式和速度亮灯 } } } 第17页 共17页
发布评论