第三章 数码管的显示原理及实现

发布时间 2023-05-09 09:55:56作者: yf.x

第三章 数码管的显示原理及实现

 

实验板上用了274HC573来驱动数码管,分别控制位选和段选信号。

  1. 让第一个数码管显示一个8字,代码如图1所示。

 

1 数码管显示8

 

  1. 6个数码管同时点亮,间隔0.5s,依次显示0-F

#include<reg52.h>

#define uchar unsigned char

#define uint unsigned int

sbit wela=P2^6;

sbit dula=P2^7;

uchar num;

uchar code table[]={0x3f,0x06,0x5b,0x4f,

                              0x66,0x6d,0x7d,0x07,

                              0x7f,0x6f,0x77,0x7c,

                              0x39,0x5e,0x79,0x71};

void delayms(uint);

 

void main()

{

wela=1; // turn on u2 latch

P0=0xc0; // 6 bits on

wela=0; // turn off u2 latch

while(1)

{

for(num=0;num<16;num++)

{

dula=1; //turn on seg choose

P0=table[num];

dula=0; //turn off seg choose

delayms(500);

}

}

 

}

 

void delayms(uint xms)

{

uint i,j;

for(i=xms;i>0;i++)

  for(j=110;j>0;j++);

}

难点在于,位选只需要打开一次,就被锁存了,也就是这里的6的数码管都一直被选中了,然后用段选在6个数码管上同时显示0-f。这也是用74573锁存器的好处。

 

  1. 数码管动态显示。每个数码管依次显示不同的数字。

#include<reg52.h>

#define uchar unsigned char

#define uint unsigned int

sbit dula=P2^6;

sbit wela=P2^7;

uchar code table[]={

0x3f,0x06,0x5b,0x4f,

0x66,0x6d,0x7d,0x07,

0x7f,0x6f,0x77,0x7c,

0x39,0x5e,0x79,0x71};

void delayms(uint);

 

void main()

{

  while(1)

{

    dula=1;      //seg1 display

    P0=table[1];

    dula=0;

    P0=0xff;

    wela=1;

    P0=0xfe;

    wela=0;

    delayms(500);

 

dula=1;

    P0=table[2];

    dula=0;

    P0=0xff;

    wela=1;

    P0=0xfd;

    wela=0;

    delayms(500);

 

dula=1;

    P0=table[3];

    dula=0;

    P0=0xfb;

    wela=1;

    P0=0xfe;

    wela=0;

    delayms(500);

 

dula=1;

    P0=table[4];

    dula=0;

    P0=0xff;

    wela=1;

    P0=0xf7;

    wela=0;

    delayms(500);

 

dula=1;

    P0=table[5];

    dula=0;

    P0=0xff;

    wela=1;

    P0=0xef;

    wela=0;

    delayms(500);

 

dula=1;

    P0=table[6];

    dula=0;

    P0=0xff;

    wela=1;

    P0=0xdf;

    wela=0;

    delayms(500);

  }

}

 

void delayms(uint xms)

{

uint i,j;

for(i=xms;i>0;i--)

  for(j=110;j>0;j--);

}

这段代码依次控制6个数码管先锁存段选,再取消,然后锁存位选,再延时。其中,P0=0xff,是为了消隐。可以尝试把延时分别调至100ms10ms1ms,可以看到数码管可以越来越稳定的显示1-6,就像6个数码管同时显示一样。这就是动态显示效果。所谓动态显示,即轮流向各位数码管送出字形码和相应的位选,利用发光管的余晖和视觉暂留作用,使人觉得数码管好像在同时显示,而实际上是多位数码管一位一位显示,只是轮流的速度非常快,人眼已无法分辨出来。

 

  1. 中断概念

52单片机共有6个中断源:

INT0:外部中断0,由P3.2口引入,低电平或下降沿引起;

INT1:外部中断1,由P3.3口引入,低电平或下降沿引起;

T0:定时器/计数器0中断,由T0计数器计满回0引起;

T1:定时器/计数器1中断,由T1计数器计满回0引起;

T2:定时器/计数器2中断,由T2计数器计满回0引起;

TI/RI:串口中断,串口完成一帧字符发送/接收后引起。

单片机使用时,通常需要设置两个与中断有关的寄存器:中断允许寄存器IE和中断优先级寄存器IP

中断允许寄存器:

EA:全局中断允许位,1开,0关;

ET2:定时器2/计数器2中断允许位;

ES:串口中断允许位;

ET1:定时器1/计数器1中断允许位;

EX1:外部中断1中断允许位;

ET0:定时器0/计数器0中断允许位;

EX0:外部中断0中断允许位。

中断优先级寄存器IP

PS:串口中断优先级控制位,1高优先级;0低优先级;

PT1:定时器/计数器1中断优先级控制位;

PX1:外部中断1中断优先级控制位;

PT0:定时器/计数器0中断优先级控制位;

PX0:外部中断0中断优先级控制位。

 

3.5 定时器中断

   定时器/计数器的实质是加1计数器,由高8位和低8位两个寄存器组成。TMOD是其工作方式寄存器,确定工作方式和功能;TCON是控制寄存器,控制T0T1的启动,停止及设置溢出标志。

1计数器输入的计数脉冲有两个来源,一个是由系统的时钟振荡器输出脉冲经12分频后送来;另一个是由T0T1引脚输入的外部脉冲源。每来一个脉冲,计数器加1,当加到计数器全为1时,再输入一个脉冲,就使计数器回零,且计数器的溢出,使TCON寄存器的TF0TF11,向CPU发出中断请求(定时器/计数器中断允许时)。如果定时器/计数器工作于定时模式,则表示定时时间已到。如果工作于计数模式,则表示计数已满。

由此可见,由溢出时计数器的值减去计数初值才是加1计数器的计数值。

设置为定时器模式时,加1计数器是对内部机器周期计数(1个机器周期等于12个振荡周期,即计数频率为晶振频率的1/12)。计数值N乘以机器周期Tcy就是定时时间t

设置为计数器模式时,外部事件计数脉冲由T0T1引脚输入到计数器。在每个机器周期的S5P2期间采样T0T1引脚电平。当某周期采样到一高电平输入,下一周期又采样到一低电平时,则计数器加1。更新的计数值在下一机器周期S3P1期间装入计数器。由于检测一个从1-0下降沿需要2个机器周期,因此要求被采样的电平至少要维持一个机器周期。当晶振频率为12M时,最高计数频率不超过1/2M,即计数脉冲周期要大于2us

TMOD的高4位用于设置定时器1,低4位用于设置定时器0

GATE-门控制位;

GATE=0,定时器/计数器的启动、停止仅受TCON中的TRX控制。

GATE=1,定时器/计数器的启动、停止受TRX和外部中断引脚INT0INT1上的电平共同控制。

C/T0-计数器模式;1-定时器模式;

M1M0:工作方式选择位。00-13位定时器/计数器;01-16位定时器/计数器;10-8位初值自动重装的8位定时器/计数器;11-仅适用于T0,分成两个8位计数器,T1停止计数。

TCON用来控制启停,标志溢出和中断情况。

TF1-定时器1溢出标志位。当定时器1计满溢出时,由硬件使TF11,并申请中断。进入中断服务程序后,由硬件自动清0.

TR1-定时器1运行控制位。

TF0-定时器0溢出标志位。

TR0-定制器0运行控制位。

IE1-外部中断1请求标志。

IT1-外部中断1触发方式选择。

IE0-

IT0-

定时器初值的计算方法:当用定时器方式1时,设机器周期为Tcy,定时器产生一次中断的时间为t,那么需要计数的个数N=t/Tcy,装入THXTLX中的数分别为

THX=65536-N/256

TLX=65536-N%256

在写单片机定时器程序时,在程序开始处需要对定时器及中断寄存器做初始化设置,通常定时器初始化过程如下:

TMOD赋值,以确定T0T1的工作方式;

计算初值,并将初值写入TH1TL1TH0TL0

中断方式时,对IE赋值,开放中断;

使TR0TR1置位,启动定时器/计数器定时或计数。

 

//发光管以1秒间隔闪烁

#include<reg52.h>

#define uchar unsigned char

#define uint unsigned int

sbit led1=P1^0;

uchar num;

void main()

{

TMOD=0x01;  //设置定时器0为工作方式1M1M001

TH0=(65536-45872)/256; //装初值11.0592M晶振定时50ms数为45872

TL0=(65536-45872)%256; //

EA=1;  //开总中断

ET0=1; //开定时器0中断

TR0=1; //启动定时器0

while(1); //程序停在这里等待中断发生

}

 

void T0_time() interrupt 1

{

TH0=(65536-45872)/256;  //重装初值

TL0=(65536-45872)%256;

num++;   //num每加1次判断是否到达20

if(num==20) //如果到了20次,说明1秒时间到

{

num=0;  //num0重新计20

led1=~led1; //让发光管状态取反

}

}

 

分析:进入主程序后,首先是对定时器和中断有关的寄存器初始化,我们按照上面讲到的通常初始化过程来操作。定时50ms,初值为45872.启动定时器后,主程序停在while1)处,中断是如何执行呢?一旦开启定时器,定时器便开始计数,当计数溢出时,自动进入中断服务程序。执行完中断服务程序,回到原来处继续执行,也就是继续等待。

用定时器0的方式1实现第1个发光二极管以200ms间隔闪烁。用定时器1的方式1实现数码管前两位59s循环计时。

#include<reg52.h>

#define uchar unsigned char

#define uint unsigned int

sbit dula=P2^6;

sbit wela=P2^7;

sbit led1=P1^0;

uchar code table[]={

0x3f,0x06,0x5b,0x4f,

0x66,0x6d,0x7d,0x07,

0x7f,0x6f,0x77,0x7c,

0x39,0x5e,0x79,0x71};

void delayms(uint);

void display(uchar,uchar);

uchar num,num1,num2,shi,ge;

void main()

{

TMOD=0x11; //定时器01工作方式

TH0=(65536-45872)/256; //装初值

TL0=(65536-45872)%256;

TH1=(65536-45872)/256; //装初值

TL1=(65536-45872)%256;

EA=1; //开总中断

ET0=1; //开定时器0中断

ET1=1; //开定时器1中断

TR0=1; //启动定时器0

TR1=1; //启动定时器1

while(1)

{

display(shi,ge);

}

}

 

void display(uchar shi,uchar ge) //显示子函数

{

dula=1;

P0=table[shi]; //送段选数据

dula=0;

P0=0xff; //送位选数据前关闭所有显示,防止打开位选锁存时

         //原来段选数据通过位选锁存器造成混乱

wela=1;

P0=0xfe; //送位选数据

wela=0;

delayms(5); //延时

 

dula=1;

P0=table[ge];

dula=0;

P0=0xff;

wela=1;

P0=0xfd;

wela=0;

delayms(5);

}

 

void delayms(uint xms)

{

uint i,j;

for(i=xms;i>0;i--)

  for(j=110;j>0;j--);

}

 

void T0_time() interrupt 1

{

TH0=(65536-45872)/256; //重装初值

TL0=(65536-45872)%256;

num1++;

if(num1==4) //200ms

{

num1=0;

led1=~led1;

}

}

 

void T1_time() interrupt 3

{

TH1=(65536-45872)/256;

TL1=(65536-45872)%256;

num2++;

if(num2==20) //1s

{

num2=0;

num++;

if(num==60) //这个数送显,到60后归0

num=0;

shi=num/10;

ge=num%10;

}

}

本例用了2个中断函数,进入哪个中断,是靠interrupt后面的序号决定。