51单片机之数码管从点亮到动态时钟的实现

一、点亮数码管

首先看一下案例源码:

#include <reg52.h>

sbit dula= P2^6;  //声明U1锁存器的锁存端
sbit wela= P2^7;  //声明U2锁存器的锁存端

int main()
{
    
    
	wela= 1;	//打开U2锁存端(控制灯亮的锁存器)
	P0= 0xfc;  //送入位选信号(亮灯的位置,十六进制从右到左,显示的是从左到右)
	wela= 0;

	dula= 1;	//打开U1锁存端(控制灯显示的锁存器)
	P0= 0x07;	//送入段选信号(灯亮的数字,十六进制转二进制依次对应hgfe dcba段,h段为0其余为1则全亮)
	dula= 0;	//关闭U1锁存端
	while(1);  //程序停止
}

(1)全局变量区域的sbit表示声明锁存器的锁存端,简单来说就是需要用这两个锁存端来控制段选和位选信号的保持。段选用来控制数码管上的数字,位选用来控制数码管上的位置,因此这两个索存端就是用来控制在数码管的哪个(或哪几个)位置上显示什么数。
(2)主函数中就用上了在全局声明的锁存端,wela则是控制位选(哪个位置),为1时则打开,为0时则关闭,并保持设定好了的位选。dula段选控制同理。
(3)不管是段选还是位选,都是用P0来传入信号。

二、八位数码管同时从0到F

首先提供一下从0到F的段选信号:

//0到F段选信号
uchar code table[]={
    
    
	0x3f, 0x06, 0x5b, 0x4f,
	0x66, 0x6d, 0x7d, 0x07,
	0x7f, 0x6f, 0x77, 0x7c,
	0x39, 0x5e, 0x79, 0x71	
};

(1)我们的本次要显示的是所有的数码管,这里是八位数码管,wela= 1; P0= 0x00; //位选信号,全亮 wela= 0;
(2)在点亮全部数码管的基础上,将这一系列的段选信号依次遍历,传入到P0中,再加上延迟函数,就能够实现动态变化了。

/*让8个数码管同时点亮,
依次显示0到F,时间间隔为0.5秒*/
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int

//锁存器与全局变量
sbit dula= P2^6;
sbit wela= P2^7;
uchar num;
//0到F段选信号
uchar code table[]={
    
    
	0x3f, 0x06, 0x5b, 0x4f,
	0x66, 0x6d, 0x7d, 0x07,
	0x7f, 0x6f, 0x77, 0x7c,
	0x39, 0x5e, 0x79, 0x71	
};
void delay(uint);  //函数声明

void main()
{
    
    
	wela= 1;
	P0= 0x00;  //位选信号,全亮
	wela= 0;
	while(1)
	{
    
    
		for(num=0; num<16; num++)	//16个数循环显示
		{
    
    
			dula= 1;
			P0= table[num];	  //段选信号,依次取数组中的值
			dula= 0;
			delay(500);	  //延时0.5秒
		}
	}
}

void delay(uint x)  //延时x毫秒的函数
{
    
    
	uint i,j;
	for(i=x; i>0; i--)
	{
    
    
		for(j=110; j>0; j--) ;
	}
}

三、显示学号(指定数字)

稍微想一下就明白了,只要我们将依次将各个学号数字所对应的段选信号通过P0传入,位选信号我们都不用变,就可以在数码管上显示所对应的数字了。

/*显示学号*/
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
void delayms(uint x);  //延时函数声明

//U1和U2锁存器的锁存端
sbit dula = P2^6;
sbit wela = P2^7;
//学号的段选信号
uchar code table[]={
    
    
0x5b, 0x3f, 0x06, 0x6f,
0x66, 0x5b, 0x7d, 0x3f
};

//存放位选信号
uchar D[]= {
    
    
0xfe, 0xfd, 0xfb, 0xf7,
0xef, 0xdf, 0xbf, 0x7f
};

void main()
{
    
    
	uint i;
	while(1)
	{
    
    
		for(i=0; i<sizeof(table); i++)
		{
    
    
			dula= 1;
			P0= table[i];  //送入段选信号
			dula= 0;
			P0= 0xff;  //送位选数据前关闭数码管的显示,防止位选锁存器混乱
			wela= 1;
			P0= D[i];  //送入位选信号,只亮一位,但由于视觉残留效应,会觉得是同时亮着的
			wela= 0;
			delayms(2);  //(必须有延时)延时2毫秒,对于人眼的视觉暂留根本看出来在跳 
		}
		
	}
}

//延时x毫秒的函数
void delayms(uint x)  
{
    
    
	uint i,j;
	for(i=x; i>0; i--)
	{
    
    
		for(j=110; j>0; j--) ;
	}
}

四、中断机制的引入

首先理解一下中断是什么,举个例子:我本来在上课,突然有电话打来,我去接了个电话,然后继续回来上课。在这个例子中,我去接电话就是在执行一个中断请求,执行完该中断之后,我会继续回到课堂上,继续断点位置的讲课,而不是从头开始讲课,这也就是中断的好处了。

1、中断允许控制位
EA:中断允许总开关控制位。(1:所有中断请求被允许;0:所有中断请求被屏蔽)

  • ES:串行口中断允许控制位。(1:允许串口中断;0:禁止串口中断)
  • ET1:定时器/计数器T1的溢出中断允许控制位。(1:允许T1溢出中断;0:禁止T1溢出中断)
  • EX1:外部中断1中断允许位。(1:允许外部中断1中断;0:禁止外部中断1中断)
  • ET0:定时器/计数器T0的溢出中断允许控制位。(1:允许T1溢出中断;0:禁止T1溢出中断)
  • EX0:外部中断0中断允许位。(1:允许外部中断1中断;0:禁止外部中断1中断)

注意:除了EA总中断控制,以上中断类型中EX0即外部中断0的等级是最高的,就是0;从下往上依次递减,ES串行口中断等级为4,即最低的。

2、中断请求方式的选择
01

  • IT0(TCON.0),外部中断0触发方式控制位。
    当IT0=0时,为低电平触发方式。
    当IT0=1时,为边沿触发方式(下降沿有效)。
  • IE0(TCON.1),外部中断0中断请求标志位。
  • IT1(TCON.2),外部中断1触发方式控制位。
  • IE1(TCON.3),外部中断1中断请求标志位。
  • TF0(TCON.5),定时/计数器T0溢出中断请求标志位。
  • TF1(TCON.7),定时/计数器T1溢出中断请求标志位。

3、中断服务的必要操作
(1)中断初始化

 EA=1//打开总中断开关
EX0=1//开外部中断0
IT0=0/1//设置外部中断的触发方式
//便是以上中断方式TOCN的选择

(2)写中断服务函数

void int0 () interrupt 0  //外部中断0,即最高级别的中断,所以为0
 
{
    
    
  //do anything that you want
}

3、外部中断0简单案例

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
void delayms(uint z);
void display();

//锁存器
sbit wela= P2^7;  //位选
sbit dula= P2^6;  //段选
//0到9的段选信号
uchar code table[]={
    
    
0x3F, 0x06, 0x5B, 0x4F, 0x66,
0x6D, 0x7D, 0x07, 0x7F, 0x6F
};

void main()
{
    
    
	//主函数变量
	uint i;

	//初始化中断方式
	IT0= 0;  //低电平触发中断0
	//中断基础设置
	EA= 1;  //开总中断
	EX0= 1;  //允许外部中断0
	PX0= 1;  //外部中断0为高优先级

	
	while(1)
	{
    
    
		for(i=0; i<=6; i++)
		{
    
    
			wela= 1;
			P0= 0xbF;
			wela= 0;
			dula= 1;
			P0= table[i];
			dula= 0;
			delayms(10000);
		}	
	}
}

void display() interrupt 0
{
    
    
	uint i;
	uchar temp= P0;
	for(i=0; i<10; i++)
	{
    
    
		wela= 1;
		P0= 0x7F;
		wela= 0;
		dula= 1;
		P0= table[i];
		dula= 0;
		delayms(10000);
	}
	P0= temp;
}

void delayms(uint z)
{
    
    
	uint i, j;
   	for(i=z; i>0; i--)
		for(j=0; j<110; j--)  ;
}

五、利用中断实现动态时钟

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
void delayms(uint z); 	 //延时z毫秒
void dis_shi();	  //显示十位的中断服务函数
void dis_ge();	  //显示个位的中断服务函数
void display(uchar wei, uint zz);  //显示函数,wei是位选信号,zz是个位或者十位
void dis_gang(uchar gang);

//初始化全局变量
uint num, num1=0, num2=0, shi=0, ge=0;	 //num1,shi分别为十位的数大小、十位的下标
uint shi2=0, ge2=0, num3=0;
uint shi3=0, ge3=0, num4=0;

//锁存器
sbit wela= P2^7;  //位选
sbit dula= P2^6;  //段选
//0到9的段选信号
uchar code table[]={
    
    
0x3F, 0x06, 0x5B, 0x4F, 0x66,
0x6D, 0x7D, 0x07, 0x7F, 0x6F
};

void main()
{
    
    
	TMOD= 0x11;  //设置定时器0和定时器1为工作方式,即为设置为(0001 0001)

	TH0= 156;  //装初值(高八位) 
	TL0= 156;  //低八位 
	TH1= 156;
	TL1= 156;
	EA= 1;  //开总中断
	ET0= 1;  //开定时器0中断
	ET1= 1;  //开定时器1中断
	TR0= 1;  //启动定时器0
	TR1= 1;  //启动定时器1
	while(1)
	{
    
    
		display(0xfd, shi3);
		display(0xfe, ge3);
		display(0xf7, shi2);
		display(0xef, ge2);
		display(0xbf, shi);
		display(0x7f, ge);
	}
}

void display(uchar wei, uint shi)
{
    
    
	//送段选数据,点亮数码管 
	dula= 1;
	P0= table[shi];
	dula= 0;
	P0= 0xff;  //关闭数码管,防止打开位选锁存器时原来的段选数据通过位选造成混乱 
	//送位选数据 
	wela= 1; 
	P0= wei;
	wela= 0;
	delayms(1);	  //等待会触发中断,执行后一位的计数

	dis_gang(0xfb);
	dis_gang(0xdf);
}

void dis_gang(uchar gang)
{
    
    
	dula= 1;  //倒数第三位
	P0= 0x40;
	dula= 0;
	P0= 0xff;
	wela= 1;
	P0= gang;
	wela= 0;
	delayms(1);
}

void dis_ge() interrupt 3
{
    
    
	TH0= 156;  //重装初值
	TL0= 156;
	num1++;
	if(num1==4)  //如果到了4次,说明200ms时间到 
	{
    
    
		num1= 0;  //则把num1清零,再重新计算4次  
	} 
}

//先执行高级的定时器
void T1_shi() interrupt 1  //定时器1中断的序号是3
{
    
    
	TH0= 156;  //重装初值
	TL0= 156;
	num2++;
	if(num2==20)  //1秒到
	{
    
    
		num2= 0;
		num++;
		if(num==60)	 //1分钟到
		{
    
    
			num= 0;  //总数到了60则清零
			num3++;
			if(num3==60)   //1小时到
			{
    
    
				num3= 0;
				num4++;
				if(num4==24)
				{
    
    
					num4= 0;
				} 		
			}
		} 
		//控制十位和个位显示的数字(因为段选信号的下标与应该显示的数字是对应的,从0开始)
		shi3= num4/10;
		ge3= num4%10;
		shi2= num3/10;  
		ge2= num3%10; 
		shi= num/10;  
		ge= num%10;		
	 }
} 


void delayms(uint z)
{
    
    
	uint i, j;
	for(i=z; i>0; i--)
		for(j=110; j>0; j--)  ;
} 

猜你喜欢

转载自blog.csdn.net/Viewinfinitely/article/details/111711962