这一节要教会大家两个知识点: 
第一点:数码管的动态驱动原理。 
第二点:如何通过编程,让数码管显示的内容转移到几个变量接口上,方便以后编写更上一层的窗口程序。 
 
具体内容,请看源代码讲解。 
 
(1)硬件平台:基于朱兆祺51单片机学习板。用两片74HC595动态驱动八位共阴数码管。 
 
(2)实现功能: 
      开机后显示  8765.4321  的内容,注意,其中有一个小数点。 
(3)源代码讲解如下: 
#include "REG52.H" 
 
 
void initial_myself();     
void initial_peripheral(); 
void delay_short(unsigned int uiDelayShort);  
void delay_long(unsigned int uiDelaylong); 
 
//驱动数码管的74HC595 
void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);   
void display_drive(); //显示数码管字模的驱动函数 
 
//驱动LED的74HC595 
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01); 
 
 
sbit beep_dr=P2^7; //蜂鸣器的驱动IO口 
sbit led_dr=P3^5;  //作为中途暂停指示灯 亮的时候表示中途暂停 
 
 
sbit dig_hc595_sh_dr=P2^0;     //数码管的74HC595程序 
sbit dig_hc595_st_dr=P2^1;   
sbit dig_hc595_ds_dr=P2^2;   
 
sbit hc595_sh_dr=P2^3;    //LED灯的74HC595程序 
sbit hc595_st_dr=P2^4;   
sbit hc595_ds_dr=P2^5;   
 
 
unsigned char ucDigShow8;  //第8位数码管要显示的内容 
unsigned char ucDigShow7;  //第7位数码管要显示的内容 
unsigned char ucDigShow6;  //第6位数码管要显示的内容 
unsigned char ucDigShow5;  //第5位数码管要显示的内容 
unsigned char ucDigShow4;  //第4位数码管要显示的内容 
unsigned char ucDigShow3;  //第3位数码管要显示的内容 
unsigned char ucDigShow2;  //第2位数码管要显示的内容 
unsigned char ucDigShow1;  //第1位数码管要显示的内容 
 
 
unsigned char ucDigDot8;  //数码管8的小数点是否显示的标志 
unsigned char ucDigDot7;  //数码管7的小数点是否显示的标志 
unsigned char ucDigDot6;  //数码管6的小数点是否显示的标志 
unsigned char ucDigDot5;  //数码管5的小数点是否显示的标志 
unsigned char ucDigDot4;  //数码管4的小数点是否显示的标志 
unsigned char ucDigDot3;  //数码管3的小数点是否显示的标志 
unsigned char ucDigDot2;  //数码管2的小数点是否显示的标志 
unsigned char ucDigDot1;  //数码管1的小数点是否显示的标志 
 
unsigned char ucDigShowTemp=0; //临时中间变量 
unsigned char ucDisplayDriveStep=1;  //动态扫描数码管的步骤变量 
 
 
unsigned char ucDisplayUpdate=1; //更新显示标志 
 
//根据原理图得出的共阴数码管字模表 
code unsigned char dig_table[]= 
{ 
0x3f,  //0       序号0 
0x06,  //1       序号1 
0x5b,  //2       序号2 
0x4f,  //3       序号3 
0x66,  //4       序号4 
0x6d,  //5       序号5 
0x7d,  //6       序号6 
0x07,  //7       序号7 
0x7f,  //8       序号8 
0x6f,  //9       序号9 
0x00,  //不显示  序号10 
}; 
 
void main()  
  { 
   initial_myself();   
   delay_long(100);    
   initial_peripheral();  
   while(1)   
   {  
      display_drive();  //显示数码管字模的驱动函数 
   } 
 
} 
 
/* 注释一: 
* 动态驱动数码管的原理是,在八位数码管中,在任何一个瞬间,每次只显示其中一位数码管,另外的七个数码管 
* 通过设置其公共位com为高电平来关闭显示,只要切换画面的速度足够快,人的视觉就分辨不出来,感觉八个数码管 
* 是同时亮的。以下dig_hc595_drive(xx,yy)函数,其中第一个形参xx是驱动数码管段seg的引脚,第二个形参yy是驱动 
* 数码管公共位com的引脚。 
*/ 
 
void display_drive()   
{ 
   //以下程序,如果加一些数组和移位的元素,还可以压缩容量。但是鸿哥追求的不是容量,而是清晰的讲解思路 
   switch(ucDisplayDriveStep) 
   {  
      case 1:  //显示第1位 
           ucDigShowTemp=dig_table[ucDigShow1]; 
                   if(ucDigDot1==1) 
                   { 
                      ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点 
                   } 
           dig_hc595_drive(ucDigShowTemp,0xfe); 
               break; 
      case 2:  //显示第2位 
           ucDigShowTemp=dig_table[ucDigShow2]; 
                   if(ucDigDot2==1) 
                   { 
                      ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点 
                   } 
           dig_hc595_drive(ucDigShowTemp,0xfd); 
               break; 
      case 3:  //显示第3位 
           ucDigShowTemp=dig_table[ucDigShow3]; 
                   if(ucDigDot3==1) 
                   { 
                      ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点 
                   } 
           dig_hc595_drive(ucDigShowTemp,0xfb); 
               break; 
      case 4:  //显示第4位 
           ucDigShowTemp=dig_table[ucDigShow4]; 
                   if(ucDigDot4==1) 
                   { 
                      ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点 
                   } 
           dig_hc595_drive(ucDigShowTemp,0xf7); 
               break; 
      case 5:  //显示第5位 
           ucDigShowTemp=dig_table[ucDigShow5]; 
                   if(ucDigDot5==1) 
                   { 
                      ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点 
                   } 
           dig_hc595_drive(ucDigShowTemp,0xef); 
               break; 
      case 6:  //显示第6位 
           ucDigShowTemp=dig_table[ucDigShow6]; 
                   if(ucDigDot6==1) 
                   { 
                      ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点 
                   } 
           dig_hc595_drive(ucDigShowTemp,0xdf); 
               break; 
      case 7:  //显示第7位 
           ucDigShowTemp=dig_table[ucDigShow7]; 
                   if(ucDigDot7==1) 
                   { 
                      ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点 
           } 
           dig_hc595_drive(ucDigShowTemp,0xbf); 
               break; 
      case 8:  //显示第8位 
           ucDigShowTemp=dig_table[ucDigShow8]; 
                   if(ucDigDot8==1) 
                   { 
                      ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点 
                   } 
           dig_hc595_drive(ucDigShowTemp,0x7f); 
               break; 
   } 
 
   ucDisplayDriveStep++; 
   if(ucDisplayDriveStep>8)  //扫描完8个数码管后,重新从第一个开始扫描 
   { 
     ucDisplayDriveStep=1; 
   } 
 
/* 注释二: 
* 如果直接是单片机的IO口引脚驱动的数码管,由于驱动的速度太快,此处应该适当增加一点delay延时或者 
* 用计数延时的方式来延时,目的是在八位数码管中切换到每位数码管显示的时候,都能停留一会再切换到其它 
* 位的数码管界面,这样可以增加显示的效果。但是,由于朱兆祺51学习板是间接经过74HC595驱动数码管的, 
* 在单片机驱动74HC595的时候,dig_hc595_drive函数本身内部需要执行很多指令,已经相当于delay延时了, 
* 因此这里不再需要加delay延时函数或者计数延时。 
*/ 
 
 
} 
 
 
//数码管的74HC595驱动函数 
void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01) 
{ 
   unsigned char i; 
   unsigned char ucTempData; 
   dig_hc595_sh_dr=0; 
   dig_hc595_st_dr=0; 
 
   ucTempData=ucDigStatusTemp16_09;  //先送高8位 
   for(i=0;i<8;i++) 
   {  
         if(ucTempData>=0x80)dig_hc595_ds_dr=1; 
         else dig_hc595_ds_dr=0; 
 
/* 注释三: 
*  注意,此处的延时delay_short必须尽可能小,否则动态扫描数码管的速度就不够。 
*/ 
         dig_hc595_sh_dr=0;     //SH引脚的上升沿把数据送入寄存器 
         delay_short(1);  
         dig_hc595_sh_dr=1; 
         delay_short(1);  
 
         ucTempData=ucTempData<<1; 
   } 
 
   ucTempData=ucDigStatusTemp08_01;  //再先送低8位 
   for(i=0;i<8;i++) 
   {  
         if(ucTempData>=0x80)dig_hc595_ds_dr=1; 
         else dig_hc595_ds_dr=0; 
 
         dig_hc595_sh_dr=0;     //SH引脚的上升沿把数据送入寄存器 
         delay_short(1);  
         dig_hc595_sh_dr=1; 
         delay_short(1);  
 
         ucTempData=ucTempData<<1; 
   } 
 
   dig_hc595_st_dr=0;  //ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来 
   delay_short(1);  
   dig_hc595_st_dr=1; 
   delay_short(1);  
 
   dig_hc595_sh_dr=0;    //拉低,抗干扰就增强 
   dig_hc595_st_dr=0; 
   dig_hc595_ds_dr=0; 
 
} 
 
 
//LED灯的74HC595驱动函数 
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01) 
{ 
   unsigned char i; 
   unsigned char ucTempData; 
   hc595_sh_dr=0; 
   hc595_st_dr=0; 
 
   ucTempData=ucLedStatusTemp16_09;  //先送高8位 
   for(i=0;i<8;i++) 
   {  
         if(ucTempData>=0x80)hc595_ds_dr=1; 
         else hc595_ds_dr=0; 
 
         hc595_sh_dr=0;     //SH引脚的上升沿把数据送入寄存器 
         delay_short(1);  
         hc595_sh_dr=1; 
         delay_short(1);  
 
         ucTempData=ucTempData<<1; 
   } 
 
   ucTempData=ucLedStatusTemp08_01;  //再先送低8位 
   for(i=0;i<8;i++) 
   {  
         if(ucTempData>=0x80)hc595_ds_dr=1; 
         else hc595_ds_dr=0; 
 
         hc595_sh_dr=0;     //SH引脚的上升沿把数据送入寄存器 
         delay_short(1);  
         hc595_sh_dr=1; 
         delay_short(1);  
 
         ucTempData=ucTempData<<1; 
   } 
 
   hc595_st_dr=0;  //ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来 
   delay_short(1);  
   hc595_st_dr=1; 
   delay_short(1);  
 
   hc595_sh_dr=0;    //拉低,抗干扰就增强 
   hc595_st_dr=0; 
   hc595_ds_dr=0; 
 
} 
 
 
 
 
 
void delay_short(unsigned int uiDelayShort)  
{ 
   unsigned int i;   
   for(i=0;i<uiDelayShort;i++) 
   { 
     ;   //一个分号相当于执行一条空语句 
   } 
} 
 
 
void delay_long(unsigned int uiDelayLong) 
{ 
   unsigned int i; 
   unsigned int j; 
   for(i=0;i<uiDelayLong;i++) 
   { 
      for(j=0;j<500;j++)  //内嵌循环的空指令数量 
          { 
             ; //一个分号相当于执行一条空语句 
          } 
   } 
} 
 
 
void initial_myself()  //第一区 初始化单片机 
{ 
 
  led_dr=0;  //关闭独立LED灯 
  beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。 
 
  hc595_drive(0x00,0x00);  //关闭所有经过另外两个74HC595驱动的LED灯 
 
} 
void initial_peripheral() //第二区 初始化外围 
{ 
/* 注释四: 
* 让数码管显示的内容转移到以下几个变量接口上,方便以后编写更上一层的窗口程序。 
* 只要更改以下对应变量的内容,就可以显示你想显示的数字。初学者应该仔细看看display_drive等函数, 
* 了解来龙去脉,就可以知道本驱动程序的框架原理了。 
*/ 
   ucDigShow8=8;  //第8位数码管要显示的内容 
   ucDigShow7=7;  //第7位数码管要显示的内容 
   ucDigShow6=6;  //第6位数码管要显示的内容 
   ucDigShow5=5;  //第5位数码管要显示的内容 
   ucDigShow4=4;  //第4位数码管要显示的内容 
   ucDigShow3=3;  //第3位数码管要显示的内容 
   ucDigShow2=2;  //第2位数码管要显示的内容 
   ucDigShow1=1;  //第1位数码管要显示的内容 
 
 
   ucDigDot8=0;   
   ucDigDot7=0;   
   ucDigDot6=0;  
   ucDigDot5=1;  //显示第5位的小数点 
   ucDigDot4=0;  
   ucDigDot3=0;   
   ucDigDot2=0; 
   ucDigDot1=0;  
} 
 
总结陈词: 
把本程序下载到朱兆祺51学习板上,发现显示的效果还是挺不错的。但是,本程序也有一个弱点,在一些项目中 ,主函数循环中的任务越多,就意味着在某一瞬间,每显示一位数码管停留的时间就会越久,一旦超过某个值,会严重影响显示的效果,有没有办法改善它?当然有。欲知详情,请听下回分解-----在定时中断里动态扫描数码管的程序。 
转载 
 
 |