【51单片机】定时器/计数器的定时和计数功能(详细)

目录

一、 定时器/计数器寄存器的功能

二、定时

 1、定时器初值计算

2、定时器/计数器初始化配置

3、定时功能的实例

(1)定时1s实现流水灯

(2)数码管间隔1s进行移位 

(3)数码管显示0~999,间隔1s。(对数码管进行消除残影)

三、计数 

 1、计数器初始化配置

2、案例应用

(1)每按下一次按键数码管数值加一(0~9)

(2) 每当开关按下3次,灯闪烁5次


STC89C52系列单片机内部设置的两个16位定时器/计数器T0和T1都具有计数方式和定时方式两种工作方式。对每个定时器/计数器(T0和T1),在特殊功能寄存器TMOD中都有一控制位C/T来选择T0或T1为定时器还是计数器。定时器/计数器的核心部件是一个加法(也有减法)的计数器,其本质是对脉冲进行计数。只是计数脉冲来源不同: 如果计数脉冲来自系统时钟,则为定时方式,此时定时器/计数器每12个时钟或者每6个时钟得到一个计数脉冲(可以在烧录时设置),计数值加1;如果计数脉冲来自单片机外部引脚(TO为P3.4,T1为P3.5),则为计数方式,每来一个脉冲加1。

一、 定时器/计数器寄存器的功能

7401832ceadc4e5f89b0ccdd185fa419.png

f55b376f595b45d6bb7e2f112274f52b.png

定时器/计数器0和1的相关寄存器标题

a015533146b0413a9d5e91ef99a962cc.png

定时器/计数器中断控制寄存器TCON(可位寻址)

TF0/1定时器/计数器T0/1溢出标志。T0/1被允许计数以后,从初值开始加1计数。当最高位产生溢出时由硬件将TF0/1置“1”,并向CPU请求中断,一直保持到CPU响应中断时,才由硬件将TF0/1清“0”(TF1也可由程序查询清“0”)。
TR0/1定时器/计数器T0/1运行控制位。该位由软件置位和清零。当GATE=0,TR0/1=1时就允许T0/1开始计数,TR0/1=0时禁止T0/1计数。当GATE=1,TR0/1=1且INT0/1输入高电平时,才允许T0/1计数。
IE0/1外部中断0/1请求源。IE0/1=1,外部中断向CPU请求中断,当CPU响应该中断时由硬件将IE0/1清“0”。
IT0/1外部中断T0/1触发方式控制位。IT0/1=0时,外部中断0为低电平触发方式,当INT0/1(P3.2/P3.3)输入低电平时,置位IE0/1。采用低电平触发方式时,外部中断源(输入到INT0/1)必须保持低电平有效,直到该中断被CPU响应,同时在该中断服务程序执行完之前,外部中断源必须被清除(即P3.2/P3.3输入高电平),否则将产生另一次中断。当IT0/1=1时,则外部中断0/1(INT0/1)端口由“1”→“0”下降沿跳变,激活中断请求标志位IE0/1,向主机请求中断处理。
  • 不可位寻址:只能对整体赋值

  • 可位寻址:可对每一位单独赋值也可以整体赋值

473223d8218b4ddda5ea32b79e8d15cd.png

定时器/计数器工作模式寄存器TMOD(不可位寻址)

GATE门控信号。置1时只有在INT0/1脚为高和TR0/1=1时才能打开定时器/计数器0/1
C/T计数器/定时器选择位。置0则用作定时器(从内部系统时钟输入);置1则用作计数器(从P3.4/3.5脚输入)
M0、M1定时器/计数器工作模式选择
M1M0C/T工作模式选择
0013位定时器/计数器,兼容8048定时模式,TL0/1只用低5位参与分频,TH0整个8位全用。
0116位定时器/计数器,TL0/1、TH0/1全用
108位自动重装载定时器,当溢出时将TH0存放的值自动重装入TL0
11

定时器0此时作为双8位定时器/计数器。TL0作为一个8位定时器/计数器,通过标准定时器0的控制位控制。TH0仅作为一个8位定时器,由定时器1的控制位控制。

注意该工作模式下定时器1此时无效(停止计数)。

d111477553794aa297783dc18a7b0094.png

ET0/1/2定时/计数器T0/1/2的溢出中断允许位
EX0/1外部中断0/1中断允许位
ES串行口1中断允许控制位
EACPU的总中断允许控制位,EA=1,CPU开放中断;EA=0,CPU屏蔽所有的中断申请

afb37321f231456db657160c24d757d8.png

定时器/计数器工作流程

二、定时

 1、定时器初值计算

若晶振震荡频率(fosc)为12MHz,则机械周期 = 12 / fosc

12/(12*10^6)=1us

//12为分频系数,绝大多数51单片机默认12分频,分频的作用是增长时钟周期

 假设要定时的时间为1000us即1ms,计时初值为x(从x开始计时到65536溢出)则有

(2^16-x)*1us=1000

//16为定时器位数,假设为16位定时器

解得定时初值为

x=65536-1000/1=64536

 将定时器初值分为高8位和低8位存储

TH0 = (65536-1000/1)/256;		//设置定时初值,取高8位
TL0 = (65536-1000/1)%256;		//设置定时初值,取低8位

 对于低位和高位的提取,我们先以10进制数1234为例:

取高2位:1234/10^2=12,取低2位:1234%10^2=34

同理,对2进制低位和高位的提取:

取高8位:TH0 = 64536/2^8=64536/256=252;        
取低8位:TL0 = 64536%2^8=64536%256=24;       

 为简化,可以写成16进制形式

TH0 = 0xFC;		
TL0 = 0x18;		

 若你的晶振震荡频率为11.0596MHz,同理得

TH0 = 0xFC;		
TL0 = 0x66;		

 如果你懒得计算,可以用51单片机的烧录器STC-ISP中的定时器计算器求得

a7b545194be1418c9ae7ccd61fad5f46.png

 (其生成代码中的寄存器AUXR可以不配置)

那么问题来了,我们可以无限地设置定时器的时间吗?这当然是不行的,从上面的计算公式我们就可以知道,晶振震荡频率为12MHz的单片机,其最大定时间隔为=2^16*1us=65536us

2、定时器/计数器初始化配置

 对于不可寻址的TMOD,设置为定时器模式,选择工作模式2即16位定时器/计数器

d31553861d15449d9f4c3be2cecd3bdd.png

 以开启定时器0为例:

TMOD=0x01;  //设置定时器模式

 若你还使用了其他定时器,为避免值覆盖问题,也可以这样设置:

TMOD &= 0xF0;	//设置定时器模式
TMOD |= 0x01;	//设置定时器模式

 完整配置为

void Timer0_Init(void)
{
    TMOD &= 0xF0;	//设置定时器模式
    TMOD |= 0x01;	
    TH0 = 0xFC;		//设置定时初值
    TL0 = 0x18;		
    TF0 = 0;		//清除TF0标志
    TR0 = 1;		//使能定时器,设置运行控制位
    ET0=1;          //使能中断
    PT0=0;          //设置中断优先级
    EA=1;           //启动定时器
}

 由上面的工作流程图可知PT0和TF0默认为0,所以也可不配置。而可位寻址IE、TCON也可以进行整体赋值,如下:

dbe041aca4bd42fba02e4bad074e5312.png

fc321dbb303a44949df450c9253fc52f.png

void Timer0_Init(void)
{
    TMOD = 0x01;	//设置定时器模式
    TH0 = 0xFC;		//设置定时初值
    TL0 = 0x18;		//设置定时初值
    IE = 0x82;      //ET0=1,EA=1;
    TCON = 0x10;    //TR0 = 1;		    
}

3、定时功能的实例

(1)定时1ms实现流水灯

#include <REGX52.H>

sbit led=P1^7;
int count,k;

void Timer0Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;			//设置定时初值
	TH0 = 0xFC;			//设置定时初值
	TF0 = 0;			//清除TF0标志
	TR0 = 1;			//定时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;
}

void Timer0_Routine() interrupt 1   //定时器0
{
	TL0 = 0x18;		   //重新设置定时初值
	TH0 = 0xFC;		   //重新设置定时初值
	P2 = ~(0x01<<k);
	k++;
	if(k == 8)k=0;
}

void main(void)
{
	Timer0Init();
	while(1)
	{
	}
}

c5ffb36323374b22b41fba3ddcbf9931.png

(2)数码管间隔1s进行移位 

#include <REGX52.H>

#define uchar unsigned char
#define uint unsigned int

uchar count;

uchar code NixieTable[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};//共阴0--9
uchar code Location[]={0x1C, 0x18, 0x14, 0x10, 0x0C, 0x08, 0x04, 0x00};

 
void Timer0Init(void)
{
	TMOD=0x01;       //选择定时器器模式,选择工作模式2
	TH0=(65536-1000)/256; 	    
	TL0=(65536-1000)%256;    
	ET0=1;						     
	TR0=1;
	EA=1;    
}

void Timer0_Routine() interrupt 1
{
	int T0count;
	TH0=(65536-1000)/256; 	    
	TL0=(65536-1000)%256;    
	T0count++;
	if(T0count>=1000)
	{
		T0count=0;
		count++;
		if(count==8)
		{
			count=0;
		}
	}
}

void main(void)
{
    Timer0Init();	
	while(1)
	{	
		P0=NixieTable[8];
		P2=Location[count];
	}
}

3ae2767113a04acca409c8446fa0a36e.png

(以上两种定时器初始化方式效果一致)

(3)数码管显示0~999,间隔1s。(对数码管进行消除残影)

1、main.c

#include <REGX52.H>
#include "Delay.h"
#include "Nixie.h"
#include "Timer0.h"

unsigned char Number = 0;

void show(unsigned char n)
{
	Nixie(1, n / 100 % 10);			//显示百位
	Nixie(2, n / 10 % 10);			//显示十位	
	Nixie(3, n % 10);				//显示个位
}

void main(void)
{
    Timer0_Init();					//中断初始化
	while(1)
	{
		show(Number);
	}
}

void Timer0_Routine() interrupt 1    //定时器中断0
{
	static unsigned int T0Count;
	TL0 = 0x18;						//设置定时初值
	TH0 = 0xFC;						//设置定时初值
	T0Count++;
	if(T0Count>=1000)				//一秒执行一次中断
	{
		T0Count=0;
		Number++;
		if(Number >= 1000)
		{
			Number = 0;
		}
	}
}

 2、Timer0.c

#include <REGX52.H>

/**
  * @brief  定时器0初始化,1毫秒@12.000MHz
  * @param  无
  * @retval 无
  */
void Timer0_Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;
}

3、Timer0.h

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);

#endif

4、Nixie.c

#include <REGX52.H>
#include "Delay.h"


unsigned char NixieTable[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};//阴极数码管段码:0--9

void Nixie(unsigned int Location, Number)//位码
{
	switch(Location)
	{
		case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;
		case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;
		case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;
		case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;
		case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;
		case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;
		case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;
		case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;
	}
	P0 = NixieTable[Number];
	Delay(1);    //稳定清零
	P0 = 0x00;   //消影:将上一位数据清零
}

5、Nixie.h

#ifndef __NIXIE_H__
#define __NIXIE_H__

void Nixie(unsigned int Location, Number);

#endif

6、Delay.c

void Delay(unsigned int xms)		
{
	unsigned char i, j;
	while(xms--)
	{
	i = 2;
	j = 199;
		do
		{
			while (--j);
		} while (--i);
	}
}

7、Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

3bace3a06a3c4c3a9d6843c6a7d6332d.png

三、计数 

e87309c261754382b694be63babb7c98.png

 1、计数器初始化配置

将C/T设置为计数器模式,M0/1选择模式3即8位自动重装载定时器。

为什么不用16位(模式2),16位也能实现起到相同的作用,但为节省资源,选择8位

void Timer0_Init(void)
{
    TMOD &= 0xF0;	//设置定时器模式
    TMOD |= 0x06;	//设置定时器模式
    TH0 = 0xFF;		//设置定时初值
    TL0 = 0xFF;		//设置定时初值
    TF0 = 0;		//清除TF0标志
    TR0 = 1;		//定时器0开始计时
    ET0=1;
    PT0=0;
    EA=1;
}

若没有特殊要求,将定时器初值定义为65535即

TH0 = 0xFF;		//设置定时初值
TL0 = 0xFF;		//设置定时初值

 每按下一次按键,计数器加1,65535加1达到65536溢出,执行中断程序,中断期间将定时初值重新设置。

2、案例应用

(1)每按下一次按键数码管数值加一(0~9)

#include <REGX52.H>

#define uchar unsigned char
#define uint unsigned int

uchar count;

uchar code NixieTable[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};//共阴0--9
uchar code Location[]={0x1C, 0x18, 0x14, 0x10, 0x0C, 0x08, 0x04, 0x00};

 
void Timer0Init(void)
{
	TMOD=0x06;       //选择计数器模式(外部脉冲),选择工作模式2
	TH0=0xFF; 	    
	TL0=0xFF;    
	ET0=1;						     
	TR0=1;
	EA=1;    
}

void Timer0_Routine() interrupt 1
{
	TH0=0xFF; 	     
	TL0=0xFF;    
	count++;
	if(count==10)
	{
		count=0;
	}
}

void main(void)
{
    Timer0Init();	
	while(1)
	{	
		P0=NixieTable[count];
		P2=Location[0];
	}
}

4539fa4528774d448c27fca9c4d0fdb9.png

(2) 每当开关按下3次,灯闪烁5次

#include <REGX52.H>

int count,i;
void Delay(unsigned int xms)		
{
	unsigned char i, j;
	while(xms--)
	{
	i = 2;
	j = 199;
		do
		{
			while (--j);
		} while (--i);
	}
}
void Timer0Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x06;		//设置定时器模式
	TL0 = 0xff;			//设置定时初值
	TH0 = 0xff;			//设置定时初值
	TF0 = 0;			//清除TF0标志
	TR0 = 1;			//定时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;
}

void Timer0_Routine() interrupt 1   //定时器0
{
	TL0 = 0xff;		//重新设置定时初值
	TH0 = 0xff;		//重新设置定时初值
	count++;
}

void main(void)
{
	int i=0;
	Timer0Init();
	while(1)
	{
		if(count==3)
		{
			for(i=0;i<5;i++)
			{
				P2=0xfe;
				Delay(1000);
				P2=0xff;
				Delay(1000);
			}
			count=0;
		}
		
	}
}

50d4d2b168374bfea2799cb2fb6351a8.png

如果想要更为详细地了解定时器/计数器,可以去查阅 STC89C52的数据手册。

STC89C52RC_PDF_数据手册_Datasheet_规格书 - 半导小芯 (semiee.com)

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值