项目4 LED广告灯设计
★ 知识目标:
1. 单片机I/O端口及端口的基本应用; 2. 单片机C语言的基本结构及设计方法; 3. 程序对单片机端口的控制方法。
★ 能力目标:
1. 能根据设计任务要求编制程序流程图,理解程序对发光二极管的控制原理; 2. 会利用绘制流水广告灯电路原理图;
3. 会用keil C51软件对源程序进行编译调试及与protues软件联调,实现电路仿真。
任务1 流水广告灯的设计
广告灯是一种常见的装饰,常用于街上的广告及舞台装饰等场合。最简单的流水广告灯就是各个灯依次发光。本任务利用AT89S51单片机来实现这一功能。
1.1 提出任务
用AT89S51的P1口做输出口,接八只发光二极管D1、D2、D3、D4、D5、D6、D7、D8,编写程序,使发光二级管循环点亮,时间间隔为0.2s。即刚开始时D1点亮,延时0.2s后,接着是D2点亮,接着依次点亮D3、D4、D5、D6、D7、D8,然后再点亮D7、D6、D5、D4、D3、D2、D1,重复循环。
1.2 分析任务 1.硬件电路设计
电路组成。这里选择具有内部程序存储器的AT89S51单片机作为控制电路,其P1口接8个发光二极管(LED)和8个限流电阻,硬件电路原理图如图4.1所示。
电路分析。要使LED点亮,则P1口的对应端子输出低电平,即P1.0=0时,D1亮。一般情况下,驱动LED的电流约10mA左右,而LED本身的压降为2V。当P1.0输出为低电平时,输出为0V,则流经D1的电流为I?(5?2)V?6.3mA,为了在仿真实验中让LED更亮一些,在这里取限流电阻为100Ω。
470?相反,当P1.0输出为高电平时,输出电压为5V,则流经D1的电流为0mA,D1不亮(熄灭),即P1.0=1时,D1不亮(熄灭)。
时钟电路单片机芯片AT89C51U1393837363534333221222324252627281011121314151617P0.0/AD0P0.1/AD1P0.2/AD2P0.3/AD3P0.4/AD4P0.5/AD5P0.6/AD6P0.7/AD7P2.0/A8P2.1/A9P2.2/A10P2.3/A11P2.4/A12P2.5/A13P2.6/A14P2.7/A15P3.0/RXDP3.1/TXDP3.2/INT0P3.3/INT1P3.4/T0P3.5/T1P3.6/WRP3.7/RDAT89C51XTAL119电源C122pX118CRYSTALXTAL2C2RST922pR2100RR3100RR4100RR5100RR6100RR7100RR8100RR9100RC3PSENALEEA29303110uR110k12345678D8D7D6D5D4D3D2D1P1.0P1.1P1.2P1.3P1.4P1.5P1.6P1.7复位电路发光二极管电路 图4.1 流水广告灯电路图
2.软件设计思路
P1口输出电平分析。在图3.1中,P1口的每一位都接有一个LED,要实现流水灯功能,就是要让各个LED依次点亮一段时间,再熄灭一段时间,然后再点亮下一个LED一段时间,然后再熄灭一段时间,如此循环。换句话来说,就是让P1口周而复始地输出高电平和低电平,要实现这一功能,最简单和最直接的方法是依次将数据送往P1口,每送一个数据延时一段时间。根据上述分析,我们列出一个功能表,如表4-1所示。
表4-1 任务分析功能表
发光二极管 P1口 P1口输出 P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0 (16进制)1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0xfe 0xfd 0xfb 0xf7 0xef 0xdf 0xbf 0x7f 0xbf 0xdf 0xef 0xf7 0xfb 0xfd 0xfe D8 D7 D6 D5 D4 D3 D2 D1 功能说明 D1点亮 D2点亮 D3点亮 D4点亮 D5点亮 D6点亮 D7点亮 D8点亮 D7点亮 D6点亮 D5点亮 D4点亮 D3点亮 D2点亮 D1点亮 输出电平 0 1 1 1 1 1 1 1 从表4-1可以看出,要实现设计任务功能,P1口输出的8个数据分别是11111110B、11111101B、11111011B、11110111B、11101111B、11011111B、10111111B、011111111B,转化成十六进制分别是0xfe、0xfd、0xfb、0xf7、0xef、0xdf、0xbf和0x7f。送完这8个数据后再反过来送011111111B 、10111111B、11011111B、11101111B、11110111B、11111011B、11111101B、11111110B,转化成十六进制分别是0x7f 、0xbf 、0xdf、0xef、0xf7、0xfb、0xfd和0xfe。送完后从头开始循环。
如何采用单片机C语言编程实现数据从输出P1口呢?从这些数据来看,有这么一个规律,D1至D8依次点亮时,就是数据中的二进制0的位置依次往左移动了1位,D8至D1依次点亮时,就是往右依次移动1位。在单片机C51中,要直接实现数据的这种计算是不容易的,如果将数据的所有二进制取反后,D1至D8依次点亮时的数据就变成了:0x01、0x01、0x04、0x08、0x10、0x20、0x40、0x80,也就是后一个数是在前一个数的基础上乘以2(或者直接左移1位)。
根据前面分析,实现任务的思路是:程序开始时,给某一个变量赋初始值0x01,并从端口输出反码,延时一段时间后,让显示变量左移1位,再次输出反码并延时,直到输出所有左移数据为止,接下来就实现右移数据输出完毕,再次重复整个过程。
延时程序编写。单片机程序的延时有两种,一种是软件延时,一种是硬件延时,在这里我们重点讨论软件延时。当系统加电后,单片机就开始工作,按照设计的程序开始运行(也称执行指令)。单片机执行一条指令要花一定的时间,那么单片机执行一条指令的执行时间成为指令周期。指令周期是以机器周期为单位的。MCS-51单片机规定,一个机器周期为单片机振荡器的12个振荡周期。如果单片机时钟电路中的晶振频率为12MHz,则一个机器周期为1us。
单片机的指令运行速度是很快的,要想在端口获得一定的延时时间,就要编写程序,使单片机运行设计程序产生时间延迟。
任务中要求获得0.2s的时间长度,当单片机的指令周期是1us时,0.2s就是1us的200 000倍。在程序编写中常用循环语句来完成计数和时间延迟,从而获得需要的延时时间。
采用单片机C语言编写的一个0.2s延时程序如下:
void delay02s(void) // 定义延时0.2s函数 {
unsigned char i,j,k; // 声明3个无符号字符型变量i、j、k for(i=2;i>0;i--) // 外循环2次,每次约0.1s,延时0.2s {
for(j=200;j>0;j--) // 外循环200次,每次约0.5ms,延时0.1s {
for(k=250;k>0;k--) // 内循环250次,每次约2us,延时05ms
{;} // 里面的循环的循环体什么也不做,但每次循环延时2us } } }
上述程序可以简化为:
void delay02s(void) {
unsigned char i,j,k; for(i=2;i>0;i--) for(j=200;j>0;j--) for(k=250;k>0;k--); }
整个子程序延时为:2us×250×200×2=200 000us=0.5s
◇ 思考: 如将延时改为1s,该怎样修改程序呢? 1.3 源程序编写
根据程序流程图编写的源程序如下: // lsd4-1.c
# include \ // 包含头文件
#define uchar unsigned char // 定义uchar为无符号数据类型 void delay02s(void) // 延时0.2s函数 {
unsigned char i,j,k; for ( i=2;i>0;i-- ) for (j=200;j>0;j-- ) for (k=250;k>0;k-- ); }
void main (void) // 主函数 {
uchar i,j; // 定义变量 while (1) // 死循环 {
j=0x01; // j初始化为0x01,左移初始值 for(i=0;i<8;i++) // for循环语句,完成8个循环 {