蓝桥杯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 time_init();
void delay(uint x);
void display(uchar addr, uchar sign);
void write_addr(uchar addr);
void write_data(uint sign);
void timer();
void dis_gang();

sbit dula = P2^6;
sbit wela = P2^7;
code uchar table[]={0x3F, 0x06, 0x5B, 0x4F, 0x66,
					0x6D, 0x7D, 0x07, 0x7F, 0x6F}; //0-9段选
uint sign[6]={0, 0, 0, 0, 0, 0};  //用来显示数据的位置,不包括横杠

uint second=0;
uint minute=0;
uint hour=0;
uint n=0;  //用来计数判断的中间变量

void time_init()
{
	TMOD = 0x02;
	ET0 = 1;	  //可用定时器0
	TR0 = 1;	  //运行定时器0
	TH0 = 156;
	TL0 = 156;
	EA = 1;
}

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

	dis_gang();	
}

void dis_gang()	  //在数字交界显示横杠
{
	wela = 1;
	P0 = 0xDB;	//第3位和第6位
	wela = 0;
	dula = 1;
	P0 = 0x40;  //一个杠
	dula = 0;
	delay(1);	
}	

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

void main()
{
	time_init();
	while(1)
	{
		display(0x7F, sign[0]);
		display(0xBF, sign[1]);
		display(0xEF, sign[2]);
		display(0xF7, sign[3]);
		display(0xFD, sign[4]);
		display(0xFE, sign[5]);
	}
}

void timer() interrupt 1
{
    n++;
    if(n==10000)
    {
		n=0;
   		second++;
		if(second==60)
		{
			second=0;
			minute++;
			if(minute==60)
			{
				minute=0;
				hour++;
				if(hour==24)
				{
					hour=0;
				}
			}
		}
		sign[0]= second%10;  //获得秒个位的下标
		sign[1]= second/10;  //获得秒十位
		sign[2]= minute%10;
		sign[3]= minute/10;
		sign[4]= hour%10;
		sign[5]= hour/10;
    }
}
  • 5
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鸿蒙Next

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值