前言
本章介绍了使用定时器和数字管作为一个简单的计数器,使用3个2位数字管分别代表时间、分、秒。使用锁定器来控制数字管的段选和位选。
先放一张完整的仿真图:
锁存器:
锁定器是数字管驱动中非常常用的设备。简单来说,锁定器的主要功能是缓存。可以根据以下简单描述来理解:
当LE当给出1时,锁定器将打开,然后单片机将数据输入到D0-D7,然后关闭LE端口,也就是LE此时0,此时锁定器的输出端,即Q0-Q7.之前的单片机输入将保持不变D0-D7的数据。
段选与位选:
结合上面的锁存器,我们经常在一些文章中看到段选和位选。如下图所示:
段选是控制数码管显示的内容,即实际写给数码管8灯的数据,如下:
选择是控制数个数字管显示,是写给数字管控制位的数据,如下:
显示数据:
关于单片机如何控制数码管显示数据,可以参考数码管静态显示的内容。网上有很多描述,我就简单说一下。
单个数字管的编号图如下:
这些数字都是通用的,小数点,有的编成H,有些编为DP,你知道这意味着一个小数点。除了确定编号外,还确定了共阴共阳。事实上,数字管是8个LED只是灯,共阴极意味着这八个LED负极接在一起,所以在控制上,位选端口给0,A-H哪个数据端口给1,哪个亮。
假设我们的电路图是两位一体的,假设我们的共阴极(实际上是共阴极,
如果我们需要让左边的数码管显示0,右边的不显示,那么我们就需要把1端口置0,2端口置1,这时候就相当于位选选择了左边的数码管,而A-H需要A-F给1,G用H给0,然后就会显示0的图案,换算成2进制,A-H分别是11111100,此时需要注意一个题目,假如我们用单片机P一口直接控制8位数据,所以我P1口是赋值0x3f(00111111)xfc(11111100),当我第一次学习单片机时,我必须写0xfc,后来发现是不对的,我以为是十六进制第一个对应单片机端口的第一个,其实恰恰相反,这个应该是跟芯片设计时的大小端模式有关。如果您感兴趣,可以了解百度大小端模式的相关内容。
那么由于我们电路中加入了锁存器,那么就要把锁存器的内容给加上:
结合上述锁定器的图纸,我们可以知道,要使数字管显示0,我们需要将0所需的数据写入段选锁定器,然后让位选锁定器H1为0,测试程序如下:
uint dat; //存储位选数据 uint number; //存储段选数据 WE = 1; P2 = 0xfe; ///赋值位选 WE = 0; //关闭位选 DATA = 1; ///打开段选 P2 = 0x3f; ///段选信号 DATA = 0; //关闭段选
效果如图:
之后我把共阴极的数字管数据,带小数点,不带小数点的每个数组,为以后备用,如下:
//没有小数点的数字数组0~9 uchar code table_nonPos[]= { 0x3f,0x06,0x5b,0x4f,0x66, 0x6d,0x7d,0x07,0x7f,0x6f }; ///有小数点的数字数组0~9 uchar code table_Pos[]= { 0xbf,0x86,0xdb,0xcf,0xe6, 0xed,0xfd,0x87,0xff,0xef };
然后是定时器计时程序的编写。关于定时器的配置,我通常将定时器0的工作模式设置为工作模式1。这部分手册可以直接找到,然后使用12M晶振定时50ms:
/************************************************* 功能:定时器0初始化函数 *************************************************/ void time_init() { //采用12晶振M晶振 TMOD=0x01; ///设置定时器0的工作模式为工作模式1 TH0=(65525-50000)/256; ///赋初值,定时50ms TL0=(65525-50000)%6; EA=1; ///开总中断 ET0=1; ///开定时器0中断 TR0=1; ///启动定时器0 }
在后续的定时器中断程序中,使用三个变量分别代表时间、分、秒,然后记录时间:
/********************************************* 功能:定时器中断函数 ***********************************************/ void T0_time() interrupt 1 { TH0=(65525-50000)/256; ///赋初值,定时50ms TL0=(65525-50000)%6; num ; //50ms if(num==20) //1s时间到 { num=0; //清零 s ; if(s==60) //60s时间到 { s=0; m ; if(m==60) //60分钟到 { m=0; h ; if(h==60) //60小时到,全部清零,重新计时 { h=0; m=0; s=0; } } } } }
在定时器中断函数中记录时,分,秒,然后在mian驱动数字管显示在函数中。我已经写了这个驱动函数,所有完整的程序如下:
#include <reg52.h> #define uchar unsigned char #define uint unsigned int int num=0; ///定时器计数变量 int h=0,m=0,s=0; //分别代表时,分,秒 int hour_1=0; //时第1位 int hour_2=0; //时第2位 int min_1=0; //分第1位 int min_2=0; //分第2位 int s_1=0; //秒第1位 int s_2=0; //秒第2位 sbit WE = P1^2; //锁定器位选 sbit DATA = P1^3; //选择锁定器段 //没有小数点的数字数组0~9 uchar code table_nonPos[]= { 0x3f,0x06,0x5b,0x4f,0x66, 0x6d,0x7d,0x07,0x7f,0x6f }; ///有小数点的数字数组0~9 uchar code table_Pos[]= { 0xbf,0x86,0xdb,0xcf,0xe6, 0xed,0xfd,0x87,0xff,0xef }; /************************************** 功能:延迟函数 **************************************/ void delay(uint num) { int i,j; for(i=0;i<num;i ) for(j=0;j<5;j ); } /************************************** 功能:二位一体数码管驱动函数, number数码管段选 x数码管位选 1位H1 2为H2 3为M1 4为M2 5为S1 6为S2 **************************************/ void two_display(uchar number,uint x) { ///注意端口赋值的倒序 uint dat; //存储位选数据 if(x == 1) /// dat = 0xfe; else if(x == 2) dat = 0xfd; else if(x == 3) dat = 0xfb; else if(x == 4) dat = 0xf7; else if(x == 5) dat = 0xef; else if(x == 6) dat = 0xdf; WE = 1; P2 = dat; ///赋值位选 WE = 0; //关闭位选 DATA = 1; ///打开段选 P2 = number; ///段选信号 DATA = 0; //关闭段选 } /************************************************* 功能:定时器0初始化函数 *************************************************/ void time_init() { //采用12晶振M晶振 TMOD=0x01; ///设置定时器0的工作模式为工作模式1 TH0=(65525-50000)/256; ///赋初值,定时50ms TL0=(65525-50000)%6; EA=1; ///开总中断 ET0=1; ///开定时器0中断 TR0=1; ///启动定时器0 } /************************************************* 功能:数字管显示函数 *************************************************/ void display() { //秒 s_1=s/10; //位
two_display(table_nonPos[s_1],5);
delay(1);
P2=0xff; //全部清除,防止数据混乱
s_2=s%10; //个位 下同
two_display(table_nonPos[s_2],6);
delay(1);
P2=0xff; //全部清除,防止数据混乱
//分
min_1=m/10;
two_display(table_nonPos[min_1],3);
delay(1);
P2=0xff; //全部清除,防止数据混乱
min_2=m%10;
two_display(table_nonPos[min_2],4);
delay(1);
P2=0xff; //全部清除,防止数据混乱
//时
hour_1=h/10;
two_display(table_nonPos[hour_1],1);
delay(1);
P2=0xff; //全部清除,防止数据混乱
hour_2=h%10;
two_display(table_nonPos[hour_2],2);
delay(1);
P2=0xff; //全部清除,防止数据混乱
}
void main()
{
time_init();
while(1)
{
display(); //数码管显示函数
}
}
/*********************************************
功能:定时器中断函数
***********************************************/
void T0_time() interrupt 1
{
TH0=(65525-50000)/256; //赋初值,定时50ms
TL0=(65525-50000)%256;
num++; //50ms
if(num==20) //1s时间到
{
num=0; //清零
s++;
if(s==60) //60s时间到
{
s=0;
m++;
if(m==60) //60分钟时间到
{
m=0;
h++;
if(h==60) //60小时到,全部清零,重新计时
{
h=0;
m=0;
s=0;
}
}
}
}
}
点个赞喽!