| 初始化定时器0 void Timer0Init(void)
 {
 TMOD &= 0xf0 ;
 TMOD |= 0x01 ;      //定时器0工作方式1
 TH0  =    0xf8 ;      //定时器初始值
 TL0  =  0xcc ;
 TR0  = 1 ;
 ET0  = 1 ;
 }
 在定时器0中断处理程序中,设置时标消息。
 void Time0Isr(void) interrupt 1
 {
 TH0  =    0xf8 ;            //定时器重新赋初值
 TL0  =  0xcc ;
 g_bSystemTime2Ms = 1 ;    //2MS时标标志位置位
 }
 然后我们开始编写数码管的动态扫描函数。
 新建一个C源文件,并包含相应的头文件。
 #include <reg52.h>
 #include "MacroAndConst.h"
 #include "Timer.h"
 先开辟一个数码管显示的缓冲区。动态扫描函数负责从这个缓冲区中取出数据,并扫描显示。而其它函数则可以修改该缓冲区,从而改变显示的内容。
 uint8 g_u8LedDisplayBuffer[8] = {0} ; //显示缓冲区
 然后定义共阳数码管的段码表以及相应的硬件端口连接。
 code uint8 g_u8LedDisplayCode[]=
 {
 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
 0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,
 0xbf, //'-'号代码
 } ;
 
 sbit io_led_seg_cs = P1^4 ;
 sbit io_led_bit_cs = P1^5 ;
 
 #define LED_PORT P0
 
 再分别编写送数码管段码函数,以及位选通函数。
 static void SendLedSegData(uint8 dat)
 {
 LED_PORT      = dat ;
 io_led_seg_cs = 1 ;        //开段码锁存,送段码数据
 io_led_seg_cs = 0 ;
 }
 
 static void SendLedBitData(uint8 dat)
 {
 uint8 temp ;
 temp = (0x01 << dat ) ;  //根据要选通的位计算出位码
 LED_PORT      = temp ;
 io_led_bit_cs = 1 ;          //开位码锁存,送位码数据
 io_led_bit_cs = 0 ;
 }
 
 下面的核心就是如何编写动态扫描函数了。
 如下所示:
 
 
 void LedDisplay(uint8 * pBuffer)
 {
 static uint8 s_LedDisPos = 0 ;
 if(g_bSystemTime2Ms)
 {
 g_bSystemTime2Ms = 0 ;
 
 SendLedBitData(8) ;        //消隐,只需要设置位选不为0~7即可
 
 if(pBuffer[s_LedDisPos] == '-')      //显示'-'号
 {
 SendLedSegData(g_u8LedDisplayCode[16]) ;
 }
 else
 {
 SendLedSegData(g_u8LedDisplayCode[pBuffer[s_LedDisPos]]) ;
 }
 
 SendLedBitData(s_LedDisPos);
 
 if(++s_LedDisPos > 7)
 {
 s_LedDisPos = 0 ;
 }
 }
 }
 
 函数内部定义一个静态的变量s_LedDisPos,用来表示扫描数码管的位置。每当我们执行该函数一次的时候,s_LedDisPos的值会自加1,表示下次扫描下一个数码管。然后判断g_bSystemTime2Ms时标消息是否到了。如果到了,就开始执行相关扫描,否则就直接跳出函数。SendLedBitData(8) ;的作用是消隐。因为我们的系统的段选和位选是共用P0口的。在送段码之前,必须先关掉位选,否则,因为上次位选是选通的,在送段码的时候会造成相应数码管的点亮,尽管这个时间很短暂。但是因为我们的数码管是不断扫描的,所以看起来还是会有些微微亮。为了消除这种影响,就有必要再送段码数据之前关掉位选。
 if(pBuffer[s_LedDisPos] == '-')      //显示'-'号这行语句是为了显示’-’符号特意加上去的,大家可以看到在定义数码管的段码表的时候,我多加了一个字节的代码0xbf:
 code uint8 g_u8LedDisplayCode[]=
 {
 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
 0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,
 0xbf, //'-'号代码
 } ;
 通过SendLedSegData(g_u8LedDisplayCode[pBuffer[s_LedDisPos]]) ;送出相应的段码数据后,然后通过SendLedBitData(s_LedDisPos);打开相应的位选。这样对应的数码管就被点亮了。
 if(++s_LedDisPos > 7)
 {
 s_LedDisPos = 0 ;
 }
 然后s_LedDisPos自加1,以便下次执行本函数时,扫描下一个数码管。因为我们的系统共有8个数码管,所以当s_LedDisPos > 7后,要对其进行清0 。否则,没有任何一个数码管被选中。这也是为什么我们可以用
 SendLedBitData(8) ;        //消隐,只需要设置位选不为0~7即可
 对数码管进行消隐操作的原因。
 
 下面我们来编写相应的主函数,并实现数码管上面类似时钟的效果,如显示10-20-30
 即10点20分30秒。
 Main.c
 #include <reg52.h>
 #include "MacroAndConst.h"
 #include "Timer.h"
 #include "Led7Seg.h"
 
 sbit io_led = P1^6 ;
 
 
 void main(void)
 {
 io_led = 0 ;      //发光二极管与数码管共用P0口,这里禁止掉发光二极管的锁存输出
 Timer0Init() ;
 g_u8LedDisplayBuffer[0] = 1 ;
 g_u8LedDisplayBuffer[1] = 0 ;
 g_u8LedDisplayBuffer[2] = '-' ;
 g_u8LedDisplayBuffer[3] = 2 ;
 g_u8LedDisplayBuffer[4] = 0 ;
 g_u8LedDisplayBuffer[5] = '-' ;
 g_u8LedDisplayBuffer[6] = 3 ;
 g_u8LedDisplayBuffer[7] = 0 ;
 EA = 1 ;
 while(1)
 {
 LedDisplay(g_u8LedDisplayBuffer) ;
 }
 }
 将整个工程进行编译,看看效果如何
 
 
 
 
 
 动起来
 
 既然我们想要模拟一个时钟,那么时钟肯定是要走动的,不然还称为什么时钟撒。下面我们在前面的基础之上,添加一点相应的代码,让我们这个时钟走动起来。
 我们知道,之前我们以及设置了一个扫描数码管用到的2 ms时标。 如果我们再对这个时标进行计数,当计数值达到500,即500 * 2 = 1000 ms 时候,即表示已经逝去了1 S的时间。我们再根据这个1 S的时间更新显示缓冲区即可。听起来很简单,让我们实现它吧。
 首先在Timer.c中声明如下两个变量:
 bit g_bTime1S = 0 ;                      //时钟1S时标消息
 static uint16 s_u16ClockTickCount = 0 ;  //对2 ms 时标进行计数
 
 再在定时器中断函数中添加如下代码:
 if(++s_u16ClockTickCount == 500)
 {
 s_u16ClockTickCount = 0 ;
 g_bTime1S = 1 ;
 }
 从上面可以看出,s_u16ClockTickCount计数值达到500的时候,g_bTime1S时标消息产生。然后我们根据这个时标消息刷新数码管显示缓冲区:
 void RunClock(void)
 {
 if(g_bTime1S )
 {
 g_bTime1S = 0 ;
 if(++g_u8LedDisplayBuffer[7] == 10)
 {
 g_u8LedDisplayBuffer[7] = 0 ;
 if(++g_u8LedDisplayBuffer[6] == 6)
 {
 g_u8LedDisplayBuffer[6] = 0 ;
 if(++g_u8LedDisplayBuffer[4] == 10)
 {
 g_u8LedDisplayBuffer[4]    = 0 ;
 if(++g_u8LedDisplayBuffer[3] == 6)
 {
 g_u8LedDisplayBuffer[3] = 0 ;
 if(    g_u8LedDisplayBuffer[0]<2)
 {
 if(++g_u8LedDisplayBuffer[1]==10)
 {
 g_u8LedDisplayBuffer[1] = 0 ;
 g_u8LedDisplayBuffer[0]++;
 }
 }
 else
 {
 if(++g_u8LedDisplayBuffer[1]==4)
 {
 g_u8LedDisplayBuffer[1] = 0 ;
 g_u8LedDisplayBuffer[0] = 0 ;
 }
 }
 }
 }
 }
 
 }
 }
 }
 这个函数的作用就是对每个数码管缓冲位的值进行判断,判断的标准就是我们熟知的24小时制。如秒的个位到了10 就清0,同时秒的十位加1….诸如此类,我就不一一详述了。
 同时,我们再编写一个时钟初始值设置函数,这样,可以很方便的在主程序开始的时候修改时钟初始值。
 void SetClock(uint8 nHour, uint8 nMinute, uint8 nSecond)
 {
 g_u8LedDisplayBuffer[0] = nHour / 10 ;
 g_u8LedDisplayBuffer[1] = nHour % 10 ;
 g_u8LedDisplayBuffer[2] = '-' ;
 g_u8LedDisplayBuffer[3] = nMinute / 10 ;
 g_u8LedDisplayBuffer[4] = nMinute % 10 ;
 g_u8LedDisplayBuffer[5] = '-' ;
 g_u8LedDisplayBuffer[6] = nSecond / 10 ;
 g_u8LedDisplayBuffer[7] = nSecond % 10 ;
 }
 然后修改下我们的主函数如下:
 void main(void)
 {
 io_led = 0 ;      //发光二极管与数码管共用P0口,这里禁止掉发光二极管的锁存输出
 Timer0Init() ;
 SetClock(10,20,30) ;  //设置初始时间为10点20分30秒
 EA = 1 ;
 while(1)
 {
 LedDisplay(g_u8LedDisplayBuffer) ;
 RunClock();
 }
 }
 编译好之后,下载到我们的实验板上,怎么样,一个简单的时钟就这样诞生了。
 
 
 至此,本章所诉就告一段落了。至于如何完成数码管的闪烁显示,就像本章开头所说的那个数码管时钟的功能,就作为一个思考的问题留给大家思考吧。
 同时整个LED篇就到此结束了,在以后的**中,我们将开始学习如何编写实用的按键扫描程序。
 |