加一计时器——每隔1s六位数码管显示数字加1,直至999999,之后归零,重新开始。

加一计时器

2021-01-23,51单片机学习笔记

每隔1s六位数码管显示数字加1,直至999999,之后归零,重新开始。

加一计时器——每隔1s六位数码管显示数字加1,直至999999,之后归零,重新开始。_第1张图片

代码:
#include 
#define uchar unsigned char
#define uint unsigned int
#define ulint unsigned long int
sbit dula=P2^6;
sbit wela=P2^7;
uint num,num_set,n;//中断计次num,中断次数预设,中断服务初始化参数n
ulint disnum;
//欲显示的数字,因其最大值为999999,已经超过uint的范围(0~65535),这里采用ulint

uchar code table_du[]={
     
0x3f,0x06,0x5b,0x4f,0x66,0x6d,
0x7d,0x07,0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};//六位共阴极8段数码管段显编码

void display(ulint);//展示数字 声明
void delayms(uint); //延时函数 声明

void main()
{
     
	n=50000;
	num_set=20;
	
	EA=1;
	ET0=1;
	TMOD=0x01;
	TH0=(65536-n)/256; 
	TL0=(65536-n)%256;
	TR0=1;
	while(1)
	{
     	/**************************************************************************
		此处,n=50000,num_set=20,时间间隔是:50000*1us*20=100ms=1s;
		若要调整时间间隔,需要调整n和num_set,如:
		(1)当n=50000,num_set=2时,时间间隔是:50000*1us*2=100ms=0.1s;
		(2)当n=5000 ,num_set=2时,时间间隔是:5000 *1us*2=10ms=0.01s,
		此时,同时需要将“if(num==num_set)”改为“if(num>=num_set)”,
		否则数码管显示数字会出错!
		
		【出现错误的原因可能是:程序运行到此处时,中断服务响应次数已经超过预设值,
		这当然会对数码管显示值的精度造成影响!而且这种误差还会随着时间累加。
		这一点是此代码的缺陷。】
		
		同时,也要合理地调整延时函数的参数,以保证显示效果稳定。
		如果延时时间不合理,就会造成显示时能够看出明显的闪烁。
		
		***************************************************************************/
		if(num==num_set)//每间隔1s(=2*50ms),更新一次disnum
		{
     
			num=0;
			if(disnum==1000000)//溢出回零
			{
     
			disnum=0;
			}
			disnum++;	
		}
		display(disnum);//显示当前数字
	}
}

//中断服务程序
void time0() interrupt 1 
{
     
	TH0=(65536-n)/256; 
	TL0=(65536-n)%256;
	num++;//记录中断次数
	//晶振f=12MHz,震荡周期=1/12um,机器周期=1us
	//n=50000时,单次中断服务耗时50ms(=50000*1us)
}

void display(ulint disnum)  
{
     
	uint ms;//延时函数参数
	ms=1;
	
	if(disnum>=100000)//当欲显示数字大于 该位显示的最小值 时才点亮
	{
     
		P0=table_du[disnum/100000];//取十万位
		dula=1;	
		dula=0;//段显锁存
		P0=0xfe;//11 11 1110 (对应LED1(第一位数码管);LED1~6自左到右分布)
		//位显编码(六位共阴极8段数码管,由P0口低六位控制,低电平时相应位点亮)
		wela=1;
		wela=0;//位显锁存
		delayms(ms);
	}
	
	if(disnum>=10000)
	{
     
		P0=table_du[disnum%100000/10000];//取万位
		dula=1;	
		dula=0;
		P0=0xfd;//11 11 1101 (对应LED2)
		wela=1;
		wela=0;
		delayms(ms);
	}	
	
	if(disnum>=1000)
	{
     
		P0=table_du[disnum%100000%10000/1000];//取千位
		dula=1;	
		dula=0;
		P0=0xfb;//11 11 1011 (对应LED3)
		wela=1;
		wela=0;
		delayms(ms);
	}
	
	if(disnum>=100)
	{
     
		P0=table_du[disnum%100000%10000%1000/100];//取百位
		dula=1;
		dula=0;
		P0=0xf7;//11 11 0111 (对应LED4)
		wela=1;
		wela=0;
		delayms(ms);
	}

	if(disnum>=10)
	{
     
		P0=table_du[disnum%100000%10000%1000%100/10];//取十位
		dula=1;
		dula=0;
		P0=0xef;//11 10 1111 (对应LED5)
		wela=1;
		wela=0;
		delayms(ms);
	}
	
	if(disnum>=0)
	{
     
		P0=table_du[disnum%100000%10000%1000%100%10];//取个位
		dula=1;
		dula=0;
		P0=0xdf;//11 01 1111 (对应LED6)
		wela=1;
		wela=0;
		delayms(ms);
	}
}

void delayms(uint ms) //延时函数,ms=100时,延时约为100ms   
{
      
   uchar k; 
   while(ms--) 
   {
      
      for(k = 0; k < 90; k++); 
   } 
}

思考:

当间隔时间小到0.01s时,程序运行到if(num==num_set){...}位置时,中断服务响应次数已经超过预设值,则不满足条件“num==num_set”,就无法执行后续语句,导致显示数值一直停留在某个数值。这时,将判断条件改为“num>=num_set”,就可以避免上述情况发生,但是,实际中断次数已经大于预设值,也造成了计时器的误差。这种误差会随着while循环次数的增加而累加。

这该如何解决呢?

你可能感兴趣的:(笔记,单片机)