STC89C52单片机启动(五)定时器

51单片机定时/计数器的工作原理

可以作为定时器的原因是内部时钟脉冲是一个周期性的信号,我们通过t=n*T就可以通过计数内部时钟脉冲的数量来得到一段固定的时间。每个定时/计数器还可以对外部信号进行计数,所以STC89C52单片机的两个定时器还对应了两个引脚,用来接收外部信号。

这两个引脚就是我们的T0和T1分别对应P3.4和P3.5

前面的外部中断我们知道两路外部中断对应的引脚:INT0和INT1分别对应了引脚的P3.2和P3.3

外部中断和定时器中接收外部信号引发的中断通常是通过检测引脚的电平变化来触发的。对于外部中断,当引脚检测到特定的电平变化时(例如低电平或下降沿),会触发外部中断请求;而对于定时器中接收外部信号引发的中断,也是通过检测引脚的电平变化来触发的。

下面来介绍一下具体原理

本款单片机的计数原则是加法计数,加到最大值就会溢出

对上面原理图的介绍(介绍顺序:从右往左,从上到下)

右边部分

TFx:溢出中断标志位,这里的x可以取到0和1,分别对应我们的定时器T0,和T1,是不是觉得十分熟悉,没错,就是我上篇文章提到的中断标志寄存器TCON中的两位TF0,TF1。

TF0(TCON.5),定时/计数器T0溢出中断请求标志位。

TF1(TCON.7),定时/计数器T1溢出中断请求标志位。

回顾前面文章说过的,TFx并不需要我们手动进行设置

对于IT0、IT1、TR0和TR1这些位进行手动设置,而IE0、IE1、TF0和TF1这些位则由硬件自动管理,无需手动干预。

51单片机中的计数器是由两个8位的寄存器所组成的,THx(对应高8位)与TLx(对应低8位)同样这里的x也是可以取到0和1,分别对应我们的定时器T0,和T1。所以我们51单片机中最大只能到65535,再加一就会溢出。

左上角部分

51单片机定时器的计数来源有两个:

一个是振荡器(内部时钟脉冲信号)

在我们这款单片机中,晶振的频率是12Mhz(1M=10^6),并且还有一个1/12分频部件,作用是本来产生的是12Mhz的脉冲信号,但是分频后就变成了1Mhz信号,对应的周期T=1^10-6S。

另一个是Tx,对外部脉冲信号进行计数。使用哪一种计数来源通过图中的开关CT来控制。

左下角部分(了解即可)

GATE来控制定时器的启动/停止方式,TRx/INTX控制定时器的启动和停止

(GATE=0)使用TRx控制定时器的启动和停止:通过寄存器TCON的位TRx来控制定时器的启动与停止;

(GATE=1)使用INTx控制定时器的启动和停止:通过外部的引脚信号INTx来控制定时器的启动与停止。

具体是通过TMOD寄存器中的GATE位来控制选用哪种方式来控制定时器的启动和停止,选择了GATE的值之后,再由对应的这种方式来控制定时器的启动和停止

TMOD寄存器(用来初始化设置)

TMOD寄存器介绍

TMOD寄存器分为两个部分,高四位(蓝色的部分)对应定时器1,低四位(黄色的部分)对应定时器0。

仅仅使用定时器1时,配置高4位(前4位),低4位此时全置0;

仅仅使用定时器0时,配置低4位(后4位),高4位此时全置0;

寄存器每一位功能介绍

GATE:决定定时器的启动方式(通常我们GATE置0)

C/T:选择计数信号来源(内部时钟脉冲或外部引脚输入),定时或计数功能的选择,置0是定时模式,置1是计数模式

注意区分GATE位和C/T位

  • 当GATE位为0时,由TR0和TR1来启动/停止定时器.
  • 当GATE位为1时,由外部引脚的信号来控制定时器/计数器的启动和停止,当外部触发信号消失时,定时器会停止计数。
  • C/T 位用于选择定时器模式或计数器模式,当 C/T 位为 0 时表示定时器模式(接收时钟脉冲),为 1 时表示计数器模式(接收引脚的外部信号脉冲)。

M1,M0用来设置工作方式

定时/计数器的四种工作方式

方式一和方式四我们不常用就不做介绍。

方式二:计数位数是16位(最大加到65535再加一溢出),由TL0作为低8位,TH0 作为高8位,组成了16位加1计数器 。

方式三:自动重装初值的8位计数方式。(最大加到255再加一溢出)

方式三的好处是:自动重装初值,使得计数更加准确一点。坏处是最大计数值为255,再加一就会溢出,计数范围小 。

方式三具体原理解释:

  1. 自动重装工作模式: 在自动重装工作模式下,当定时器溢出时,定时器会自动重新装载我们设定的初值,然后继续计数。这意味着在每次计数完成后,定时器都会重新开始从我们设定的初值开始计数,而不需要手动重新写入初值。这种模式适用于周期性的定时任务,能够简化程序设计和操作。

  2. 具体来说是:初始化时,将我们设定的定时器的开始计数值(<256)存储了两份,各存储一份到低8位寄存器和高8位寄存器,定时器开始计数。当定时器溢出时,在中断服务程序中,首先将高8位寄存器的值取出,然后将其低8位中的初始值自动重新加载到定时器高8位寄存器中;

  3. 普通16位工作模式: 在普通16位工作模式下,定时器值溢出时不会自动重新装载,需要手动重新写入初值才能继续计数。否则第二次计数会从0开始计数,不会像从第一次的预设值开始计数,这种模式更加灵活,可以根据需求手动控制定时器的计数行为,但也增加了编程的复杂度。

具体代码,共分为两部分,分别是对定时寄存器进行初始化设置和编写定时器中断服务函数 

1.计算好计数初值并进行赋值,对TMOD进行设置,对IE寄存器(负责总开关和独立开关)和 TCON寄存器进行设置(定时器启动/停止当然这是建立在GATE取0的情况下才会对TCON进行设置)

2.编写中断服务函数

格外注意:对TMOD赋值的时候必须是字节操作,不能一位一位进行赋值,必须一次性对8位寄存器整体赋值。

定时器程序设计参考:

初始化块:

TMOD:  选择定时器0工作,TMOD寄存器高四位全为0; 选择接收时钟脉冲,C/T为0;选择使用TR0控制定时器启动,GATE位为0; 选择了模式1工作,M1M0=01; 所以为0000 0001对应0x01

TH0和TH1: 设置定时器初始值,这里这个写法很简单,这里表示的是要计数50000个脉冲,大概50ms,其他时长同理

IE : 让EA为1,ET0也为1(让使能端为1)

TCON: 在GATE为0的情况下,通过让TR0=1来启动定时器 

定时器0中断服务函数:

由于使用的是16位计数模式,并不会自动进行重装初始值,所以在这里要重新赋初始值,当然,这种重新赋值方式会有时间上的一点小误差

还要注意的就是中断服务函数的书写格式

对外部信号接收实例

具体描述:按下三次K3按键就让LED1状态反转

#include<regx52.h>
sbit D1=P2^0;
sbit button=P3^2;
void delay(unsigned int t){
	while(t--);
}
void init_T0(){
	TMOD=0x06;//00000110这里注意要整个字节赋值,可以先写出对应二进制再转为16进制
	TH0=253;//设置初始值,这里采用了自动重装的工作模式,
//所以初始化代码和用16位寄存器的初始化代码不太一样
	TL0=253;
	ET0=1;//使能端赋1
	EA=1;//总使能端赋1
	TR0=1;//TCON中TR0位赋1,定时器启动
}
void Service_T0()interrupt 1{
	D1=~D1;
}
void make_sign(){
	if(button==0){
		delay(200);
		while(button==0);
		P3_4=0;
		P3_4=1;//一次按下在松开之后相当于发射了一个外部信号,累计三次灯状态改变
	}
}
void main(){
	init_T0();
	while(1){
		make_sign();//人为创造外部信号使得T0引脚电位变化
	}
}

对代码的解释:

这里我们设置的是TMOD寄存器中C/T位为1,表示接收外部引脚T0(P3_4)的电位变化,给一次低电位就相当于接收到了一次外部信号,通过每按下一次K3按键就使得P3_4电位变化一次

make_sign();函数的作用:人为创造外部信号使得T0引脚电位变化,相当于按下一次按键T0引脚就接收到了一次外部信号,累计三次计数器溢出,LED1状态反转.这里也不用担心按下时间太久再松开,导致会一次发射多个信号,因为这里是在松开按键之后才会发射信号.但这里确实比较容易受干扰信号影响

对内部信号接收实例

分析:使用16位定时计数器模式的话,累加5000才50ms(从0开始累加最多只能累计65536,显然达不到1s),这里要实现1s的长定时,我们需要累计多次定时来实现1s的长定时.

上一篇文章我们讲了中断服务函数要与外部做数据交互可以使用全局变量,这里我们就可以这样来做,以此来达到累计20次目的.

#include<regx52.h>
sbit D1=P2^0;
sbit button=P3^2;
unsigned int count=0;
void init_T0(){
	TMOD=0x01;//00000001
	TH0=(65536-50000)/256;
	TL0=(65536-50000)%256;
	ET0=1;
	EA=1;
	TR0=1;
}
void Service_T0()interrupt 1{
	TH0=(65536-50000)/256;
	TL0=(65536-50000)%256;
	count++;
	if(count%10==0){
		D1=~D1;
	}
}
void main(){
	init_T0();
	while(1){
	}
}

对代码的解释:

1.这里使用了一个全局变量实现中断服务函数与外部交互实现计次目的

2.不要忘记这里使用的是工作模式1,要在中断服务函数中进行再次赋初值

下一篇文章中将会介绍:综合练习(利用定时计数器来实现秒表)

STC89C52单片机启动--综合案例秒表-CSDN博客

  • 28
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STC89C52单片机可以同时开启定时器1和其他功能。在使用定时器1时,您需要设置定时器的计数器初值和定时器模式,并将定时器1中断使能。以下是一个简单的示例代码,演示如何使用STC89C52单片机定时器1: ```c #include <reg52.h> #define TIMER1_RELOAD_VALUE 65535-1000+1 // 1ms定时器中断 void init_timer1() { TMOD |= 0x10; // 设置定时器1为16位定时器模式 TH1 = TIMER1_RELOAD_VALUE / 256; // 设置计数器的初值高8位 TL1 = TIMER1_RELOAD_VALUE % 256; // 设置计数器的初值低8位 ET1 = 1; // 使能定时器1中断 TR1 = 1; // 启动定时器1 } void main() { init_timer1(); // 其他功能代码 while(1) { // 主循环代码 } } void timer1_isr() interrupt 3 { TH1 = TIMER1_RELOAD_VALUE / 256; // 重新设置计数器的初值高8位 TL1 = TIMER1_RELOAD_VALUE % 256; // 重新设置计数器的初值低8位 // 定时器1中断处理代码 } ``` 在上面的代码中,我们使用定时器1来实现1ms的定时器中断。在`init_timer1()`函数中,我们设置定时器1为16位定时器模式,并将计数器的初值设置为`TIMER1_RELOAD_VALUE`,即65535-1000+1,以便在每次计数器溢出时产生一次中断。在中断服务程序中,我们重新设置计数器的初值,并执行定时器中断处理代码。 请注意,在使用定时器1时,您需要确保不会与其他功能发生冲突。例如,如果您使用了定时器0或定时器2,您需要相应地修改定时器的设置和中断处理程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值