80c51day07定时器/计数器

定时器

先看一下各种与时间有关的东西

  1. 时钟周期
      时钟周期,一般也称振荡周期。如果晶振的输出没有经过分频或倍频就直接作为cpu的工作时钟,则时钟周期就等于晶振的振荡周期;如果晶振的输出经过分频或倍频后作为cpu的工作时钟,则时钟周期就就是分频或倍频后的。即,时钟周期是CPU的实际工作频率的倒数,它在这里插入代码片是计算机中最基本的、最小的时间单位。
      比如,一个8051单片机外接一个8MHz的晶振,晶振不分频也不倍频,直接用于CPU的工作,那么此8051单片机的时钟频率就位1/8(us);同样,一个STM32F10XD的单片机外接一个8MHz的晶振,晶振倍频7倍,即72MHz,然后用于CPU的工作,那么此STM32F10XD的时钟频率为1/72(us)。

  2. 机器周期
      在计算机中, 为了便于管理, 通常把一条指令的执行划分为若干个阶段, 每一个阶段完成一项基本任务,如: 取指令、存储器读、存储器写等, 这每一项工作称为一个基本操作,完成一个基本操作所需要的时间为机器周期。一般情况下,一个机器周期由若干个状态周期(时钟周期)组成。
      8051系列单片机的一个机器周期由6个状态周期组成, 1个状态周期=2个时钟周期,所以8051单片机的一个机器周期=6个状态周期=12个时钟周期。

  3. 指令周期
      指令周期是执行一条指令所需要的时间,即CPU从内存取出一条指令并执行这条指令的时间总和。一般由若干个机器周期组成,从取指令、分析指令到执行完所需的全部时间。指令不同,所需的机器周期数也不同。
      对于一些简单的的单字节指令,在取指令周期中,指令取出到指令寄存器后,立即译码执行,不再需要其它的机器周期。对于一些比较复杂的指令,例如转移指令、乘法指令,则需要两个或者两个以上的机器周期。通常含一个机器周期的指令称为单周期指令,包含两个机器周期的指令称为双周期指令。
    ————————————————
    版权声明:本文为CSDN博主「y_q_m」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/y_q_m/article/details/88412723
    在这里插入图片描述
    终于知道那两个引脚的作用了
    在这里插入图片描述
    在这里插入图片描述
    TR0/TR1是开始计时/数的开关
    在这里插入图片描述
    不可位寻址说明只能对寄存器整个进行操作比如TMOD=FFH(数字是乱写的)

代码

#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选

//共阴数码管段选表0-9
uchar  code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

/*====================================
函数	: delay(uint z)
参数	:z 延时毫秒设定,取值范围0-65535
返回值	:无
描述	:12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
	uint x,y;
	for(x = z; x > 0; x--)
		for(y = 114; y > 0 ; y--); 		
} 

/*====================================
函数	:display(uchar i)
参数	:i 显示数值,取值范围0-255
返回值	:无
描述	:三位共阴数码管动态显示
====================================*/
void display(uchar i)
{
	uchar bai, shi, ge;
	bai = i / 100; //236 / 100  = 2
	shi = i % 100 / 10;	//236 % 100 / 10 = 3
	ge  = i % 10;//236 % 10 =6
	
	//第一位数码管  		
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = 0XFE; //1111 1110
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[bai];//
	DU = 0;//锁存段选数据
	delay(5);

	//第二位数码管
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = 0XFD; //1111 1101
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[shi];//
	DU = 0;//锁存段选数据
	delay(5);

	//第三位数码管
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = 0XFB; //1111 1011
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[ge];//
	DU = 0;//锁存段选数据
	delay(5);
}
//定时器0初始化
void timer0Init()
{
	TR0 = 1;	 //启动定时器0
	TMOD = 0X01; //定时器工作模式1,16位定时器计数模式
	TH0 = 0x4b;//设定初始时间使其每溢出一次相当于计时50ms,根据晶振频率可以算出来的
	TL0 = 0xfd; //定时50ms
}

void main()//main函数自身会循环
{	
	uchar mSec, Sec;//毫秒和秒储存变量
	timer0Init();//定时器0初始化
	while(1)
	{
		if(TF0 == 1)//判断是否溢出
		{
			TF0 = 0;//软件清零溢出标志位
			TH0 = 0x4b;//设定初始时间使其每溢出一次相当于计时50ms,根据晶振频率可以算出来的
			TL0 = 0xfd; //定时50ms
			mSec++;//50ms到
			if(mSec == 20)//对应前面50
			{
				mSec = 0;
				Sec++;//1秒时间到
			}					
		}
		display(Sec); //数码管显示函数
		if(Sec > 10)
			Sec = 0;//秒清零 
	}	
}  

TH存计数器高8位的数据,TL存计数器低8位的数据
这个没有开启中断,只是一直检测中断标志位溢出了没,然后还要注意,这里的标志位检测到为1后一定一定要置零,他不会自动置零的(因为没有中断)

计数器

就是把TMOD改一下,然后计数是根据那两个引脚接受的脉冲个数来的

代码

#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit LED1 = P1^0;//LED1实验开始前已经将P1^0与T0通过杜邦线连起来了。
//共阴数码管段选表0-9
uchar  code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

/*====================================
函数	: delay(uint z)
参数	:z 延时毫秒设定,取值范围0-65535
返回值	:无
描述	:12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
	uint x,y;
	for(x = z; x > 0; x--)
		for(y = 114; y > 0 ; y--); 		
} 

/*====================================
函数	:display(uchar i)
参数	:i 显示数值,取值范围0-255
返回值	:无
描述	:三位共阴数码管动态显示
====================================*/
void display(uchar i)
{
	uchar bai, shi, ge;
	bai = i / 100; //236 / 100  = 2
	shi = i % 100 / 10;	//236 % 100 / 10 = 3
	ge  = i % 10;//236 % 10 =6
	
	//第一位数码管  		
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = 0XFE; //1111 1110
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[bai];//
	DU = 0;//锁存段选数据
	delay(5);

	//第二位数码管
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = 0XFD; //1111 1101
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[shi];//
	DU = 0;//锁存段选数据
	delay(5);

	//第三位数码管
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = 0XFB; //1111 1011
	WE = 0;//锁存位选数据
	
	DU = 1;//打开段选锁存器
	P0 = tabel[ge];//
	DU = 0;//锁存段选数据
	delay(5);
}
//定时器0初始化
void timer0Init()
{
	TR0 = 1;	 //启动定时器0
	TMOD |= 0X05; //定时器0工作模式1,16位计数模式
	TH0 = 0;
	TL0 = 0; 
}

//定时器1初始化
void timer1Init()
{
	TR1 = 1;	 //启动定时器1
	TMOD |= 0X10; //定时器1工作模式1,16位定时模式
	TH1 = 0x4b;
	TL1 = 0xfd; //定时50ms

}

void main()//main函数自身会循环
{	
	uchar mSec, Sec;//毫秒和秒储存变量
	timer0Init();//定时器0初始化
	timer1Init();//定时器1初始化
	while(1)
	{
		if(TF1 == 1)//判断是否溢出
		{
			TF1 = 0;//软件清零溢出标志位
			TH1 = 0x4b;
			TL1 = 0xfd; //定时50ms
			mSec++;//50ms到
			if(mSec == 10) //定时50毫秒到
			{
				mSec = 0;
				LED1 = ~LED1;//产生方波
			}					
		}
		display(TL0); //数码管显示函数
	}	
}  

呃,这个关于为什么要弄P1^ 0而不是T0,因为实验开始前已经将P1^0与T0通过杜邦线连起来了。

中断实现

上面都是一直在检测是否溢出,未打开中断,接下来用中断实现
在这里插入图片描述
方便起见,又把中断向量表放了出来

代码

#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值
uchar mSec, Sec;//毫秒和秒储存变量

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

/*====================================
函数	: delay(uint z)
参数	:z 延时毫秒设定,取值范围0-65535
返回值	:无
描述	:12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
	uint x,y;
	for(x = z; x > 0; x--)
		for(y = 114; y > 0 ; y--); 		
} 

/*====================================
函数	:display(uchar i)
参数	:i 显示数值,取值范围0-255
返回值	:无
描述	:三位共阴数码管动态显示
====================================*/
void display(uchar i)
{
	static uchar wei; 		
	P0 = 0XFF;//清除断码
	WE = 1;//打开位选锁存器
	P0 = SMGwei[wei];
	WE = 0;//锁存位选数据
	switch(wei)
	{
		case 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
		case 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;	
		case 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;		
	}
	wei++;
	if(wei == 3)
		wei = 0;
}
//定时器0初始化
void timer0Init()
{
	EA = 1;	//打开总中断
	ET0 = 1;//打开定时器0中断
	TR0 = 1;	 //启动定时器0
	TMOD = 0X01; //定时器工作模式1,16位定时模式
	TH0 = 0xED;
	TL0 = 0xFF; //定时5ms
}

void main()//main函数自身会循环
{	
	timer0Init();//定时器0初始化
	while(1)
	{
		if(key_s2 == 0)//判断S2是否被按下
		{
			delay(20);//按键消抖
			if(key_s2 == 0)
			{
				if(num != 120)
				num++;
				while(!key_s2);//松手检测
			}	
		}
		if(key_s3 == 0)//判断S3是否被按下
		{
			delay(20);//按键消抖
			if(key_s3 == 0)
			{
				if(num > 0)
					num--;
				while(!key_s3);//松手检测
			}	
		}
	}	
} 

//定时器0中断函数
void timer0() interrupt 1
{
	TH0 = 0xED;
	TL0 = 0xFF; //定时5ms
	display(num); //数码管显示函数	
} 

就是每过5ms就会触发中断(标志位自动置0,不用手动写了),然后显示,有两个按键按一下数字会增加或减小。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值