第三章 4位数码管计数器
前言
4位数码管计数器与1位数码管计数器相比,增加了片选电路,以确定选择哪个数码管进行工作。单片机定时器的应用也与中断处理相似,需要设置一些规定的寄存器,以此来规定单片机的工作状态。
一、多位数码管显示程序
定义一个数组Dat[ ]={0,0,0,0}存放4位数,初值都为0。用变量S作为计数,tmp作为片选值。
数字分离:百位数分离用Dat[0] = S/100,例如S=235则S/100=2,Dat[]是整数,小数部分自动删除。十位数分离用Dat[1]=S%100/10公式解决,S%100取除100的余数,再把这个余数除10,就得到十位数了。例如,还是以上数字S=235,S%100/10=3。最后是个位数的分离,用Dat[2]=S%10,取S除以10的余数。
这里Dat[0]存放百位数,Dat[1]存放十位数,Dat[2]存放个位数。在输出时要按从高到低的顺序输出,不能搞错,否则数字就反了。在输出时,先选定片选值,即决定在哪个数码管输出,送到P2口,因为我们将片选线接在P2口。第1个片选值是1,根据数码管的标识在最左边位输出,然后通过P0口输出码段值,这时,就会在第1位显示数据了。接着,把片选值向左移动一位,为下一位输出做准备,停留片刻后进行下一位输出。这样,共循环3次,将3位数都输出显示在数码管上了。因为,我们的眼睛有视觉残留,所以感觉3位数字一起显示出来。但由于在输出完毕后还要停留较长时间才会进入下一个数的显示,输出会有闪烁的感觉。
#include <reg51.h>//单片机头文件
unsigned char code Tab[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//共阳数码管码段表
unsigned char Dat[]={0,0,0,0};//存放4位数据数组
unsigned char tmp,i,j;//定义临时变量、循环变量
unsigned int S=0;//定义计数变量
void Delay(int n)//延时子程序
{
int i;
for(i=0;i<n;i++);
}
void main()//主程序
{
while(1)//无限循环
{
S++;//计数值加1
if(S>500)//如果计数值大于500则清零
S=0;
Dat[0]=S/100;//取计数值的百位数
Dat[1]=S%100/10;//取计数值的十位数
Dat[2]=S%10;//取计数值的个位数
tmp=0x01;//片选初值
for (i=0;i<3;i++)//循环3次
{
P2=tmp;//将片选值送P2口
P0=Tab[Dat[i]];//将某一位码段值送P0口
tmp=tmp<<1;//片选值移至下一位
Delay(500);//调用延时子程序
}
for(i=0;i<150;i++)//延时一段时间
for(j=0;j<100;j++)
Delay(60000);//调用延时子程序
}
}
二、定时器原理
单片机定时/计数器的工作原理及工作方式,可以参考https://blog.csdn.net/kafmws/article/details/88046805这篇博文,在此不赘述。
计算定时器的初值,计算公式和一般方法如下:
1)T(初值)=2^N-定时时间/机器周期时间
2)N与工作方式有关
方式0 N=13
方式1 N=16
方式2、3 N=8
3)1机器周期时间=12/fosc=12/12MHz=1μs
例:晶振12MHz,求方式1,T0定时0.2ms初值
2^16-200/1=65536-200=FF38H则,高位TH0=FFH,低位TL0=38H
再举个例子,用T0,方式1,计时0.05s,设计一个发光二极管闪烁(0.05s开关一次)程序。计算初值。(-50000的补码为2^16-50000)
高位TH0=-50000/256
低位TL0=-50000%256
#include <reg51.h>//单片机头文件
sbit led=P0^0;//定义发光二极管引脚
void main()//主程序
{
EA=1;//允许所有中断
ET0=1;//允许T0中断
TMOD=0x01;//T0方式1计时0.05S
TH0=-50000/256;//定时器T0的高八位
TL0=-50000%256;//定时器T0的低八位
TR0=1;//启动T0定时器
for(;;);//无限循环
}
void intserv1 (void) interrupt 1 using 1//定时器0中断服务子程序,中断号1
{
TH0=-50000/256;//重新定义初值
TL0=-50000%256;
LED=!LED;//电平取反
}
编译程序,在单片机上加载程序,可看到发光二极管0.05s的闪烁。由于间隔时间很短,发光二极管闪烁的很快。如何能使得间隔时间长一点呢?这就需要把定时器初值设的大一点,但是,最大也不能超过2^16,即65536,也就65536μs=0.065536s。如果要设置成1s闪烁一次该怎么办呢?显然,定时器初值设置成1s是不可能的,只有另外设置变量来延长定时时间。
我们修改一下中断处理程序如下:
void intserv1 (void) interrupt 1 using 1
{
TH0=-50000/256;//重新定义初值
TLO=-500005256;
t++;//变量t加1
if(t>=20)//如果变量t=20;
{
t=0;//变量t清零
LED=!LED;//电平取反
}
}
在程序头部加上一个全局变量t,定义为unsigned char t;,作为中断子程序的中间变量。每次定时器时间到,处理中断时,给变量t加1,若t=20,就是0.05s×20=1s。即每次0.05s时间到中断时并不执行发光二极管电平取反,直到20次时,就是1s时,才执行一次。这样就实现了定时1s的功能。
三、程序设计与仿真
在前面我们实现了多位数码管显示程序,并学习了1s定时器功能的实现,两者结合,设计一个四位数码管计数器。前2位表示分钟,后2位表示秒。从0分0秒开始计时,逢60进1,每秒变化一次。
proteus 电路
程序
#include <reg51.h>//单片机头文件
unsigned char code Tab[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//共阳数码管码段表
unsigned char Dat[]={0,0,0,0};//存放4位数字数组
int i ,t;//定义变量,作为循环,定时计数
unsigned char tmp;//定义片选变量
unsigned char second=0,min=0;//定义秒、分变量
void Delay()//延时子程序,作为数码管显示延迟
{
unsigned char i;
for(i=0;i<250;i++);
}
void main()
{
IT0=1;EX0=1;//开外部中断0和外部中断0允许
IT1=1;EX1=1;//开外部中断1和外部中断1允许
EA=1;//允许所有中断
ET0=1;//允许T0中断
TMOD=0x01;//T0方式1计时0.05S
TH0=-50000/256;//定时器T0的高八位
TL0=-50000%256;//定时器T0的低八位
TR0=1;//启动定时器0
while(1)//无限循环
{
tmp=0x01;//片选初值
for(i=0;i<4;i++)//循环4次
{
P2=tmp;//片选初值
P0=Tab[Dat[i]];//输出某一位数字的码段值
tmp=tmp<<1;//片选值左移一位
Delay();//调用延时
}
}
}
void intserv1 (void) interrupt 1 using 1//定时器0中断服务子程序
{
TH0=-50000/256;//定时器高八位赋值
TL0=-50000%256;//定时器低八位赋值
t++;//变量t加1
if(t==20)//20次到,即1秒到
{
t=0;//变量t清零
second++;//秒加1
if(second>=60)//60秒到
{
second=0;//秒变量清零
min++;//分加1
if(min>=60)//60分到
min=0;//分变量清零
}
Dat[0]=min/10;//计算分高位
Dat[1]=min%10;//计算分低位
Dat[2]=second/10;//计算秒高位
Dat[3]=second%10;//计算秒低位
}
}
void intersvr0 (void) interrupt 0 using 1//外部中断0子程序
{
min++;//分加1
if(min>=60)//如果分到60
min=0;//分清零
}
void intersvr1 (void) interrupt 2 using 1//外部中断1子程序
{
second++;//秒加1
if(second>=60)//如果秒到60
second=0;//秒清零
}
总结
定时1S的功能将会在很多程序功能中被使用,需要大家好好学习消化这一知识点。
标签:10,定时器,void,unsigned,Dat,数码管,proteus,keil5 From: https://blog.csdn.net/muling0504/article/details/136876388