单片机-89C51部分:8、定时器

飞书文档https://x509p6c8to.feishu.cn/wiki/IUiswrNZpij2jpkbU6UcmCBpn9b

一、简介

定时器,顾名思义,是用来控制时间的。它可以根据设定的时间间隔,准时产生触发信号,用于控制程序的执行流程。

我们为什么要使用定时器呢?

回想一下之前写的程序,在实现延时这一功能时,我们使用了delay() 函数,这个函数并没有采用任何外设,只是写了两个循环嵌套,让cpu计数,当计数完成也就代表延时结束,简单点说就是让cpu通过不停的计数来消耗时间,所以这种方式有个很大的弊端,就是当cpu "死跑" 延时的时候,是做不了其他事情的,如果这个时候需要一个工具来帮助cpu完成计时,这就是定时器的作用。

51单片机有两个定时器,T0和T1。这两个定时器可以通过寄存器进行配置,可实现定时和计数功能。

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 定时器的本质原理是,每经过一个机器周期,计数器的值就加1。当计数器的值达到设定的值时,就会产生一个中断信号,通知CPU进行相应的处理。 在定时器模式下,计数器的输入信号是机器周期。因此,当配置为定时器使用时,每经过1个机器周期,计数存储器的值就加1。这种方式可以用来产生定时信号,以控制时间间隔或者实现定时操作。比如在工厂自动化生产线、智能家居控制、网络通信等场景中,都需要定时器来进行精准的时间控制。 而在计数器模式下,计数器的输入信号是外部信号。当配置为计数器时,每来一个负跳变信号就加1,以此达到计数的目的。这种方式可以用来对外部事件进行计数或者脉冲测量。如统计按键按下的次数,统计电机编码器转的圈数。 |

我们可以根据具体的需求来选择使用定时器还是计数器。比如,在需要精确控制程序执行时间的情况下,我们可以选择使用定时器;而在需要对某些事件进行计数时,则可以选择使用计数器。

二、定时**/** 计数器相关的寄存器

模式选择

STC89C52RC T0 T1 均有 4 种工作模式:

  • 模式0:13位定时器/计数器
  • 模式 1 16 位定时器 / 计数器 (常用) 13 位适用于短时间计时, 16 位适用于较长时间计时
  • 模式2:8位自动重装模式时器/计数器 此模式下,计时到设定值后会自动重新开始计时,实现周期性计时功能
  • 模式3:两个8位计数器(禁用定时器1,把定时器2设置为两个8位计数器)

|------------------------------------------------------------------------------------|
| TMOD这个寄存器是不可位寻址的, 如要想让M0这位置一的话,直接 M0=1;这个写法是错误的,因为它不能位寻址。 只能 TMOD = 0x01(00000001) |

|----------------------------------------------------------------------------------------------------------------------------|
| 例如初始化定时器0的模式为模式1, 设置TMOD寄存器的第0位M0为1,第1位M1为0 0b0000 0001 TMOD=0x01; //设置16bit定时器模式 如果是初始化定时器1的模式为模式1 0b0001 0000 TMOD=0X10; |

模式****1 :时钟源选择

0b 0000 0001

TMOD = 0x01

C/T 寄存器:时钟来源选择

来源一: SYSclk ,系统时钟,作为定时模式(常用)

来源二:外部脉冲输入引脚 T0 ,也就是 P3.4 引脚,作为计数模式

|------------------------------------------------------------------------------------|
| 两个定时器的C/T寄存器对应TMOD寄存器的第2位和第6位 这里设置定时器0时钟源为系统时钟,则设置TMOD第2位为0 0b0000 0001 TMOD=0x01; |

系统时钟: 12T/6T 分频模式选择

上面的开关是选择分频器,选择12分频,假设芯片时钟是12Mhz进行12分频就是1Mhz(1MHz=1000kHz,1kHz=1000Hz,每秒钟计数100万次),每个计数一次就是1us,这样配置通这条线路送到计数器,每隔1us就要记1次数。

在烧录时设置,默认为12T模式

开关控制

非门:输入 1 ,输出 0 ,输入 0 ,输出 1

或门:输入任意一个为 1 ,输出都为 1

与门:输入两个都为 1 ,输出才是 1 ,其它都是 0

GATE INT0 TR0 一起作为定时器开关的控制寄存器

|------|------|-----|-------|
| GATE | INT0 | TR0 | 定时器状态 |
| 0 | / | 1 | 使能 |
| / | 1 | 1 | 使能 |

当GATE为0时,经过非门变为1,再与INT0通过或门,此时无论INT0为0/1,都会输出1,然后与TR0经过与门,TR0也要为1,最终才能输出1。

当INT0为1时,不管GATE是0/1,通过或门都输出1,然后与TR0经过与门,TR0也要为1,最终才能输出1。

GATE **:**门控位;GATE=0,设置软件启动;

INT0 **:**INT0=1 设置为硬件启动,INT0对应外部中断0,也就是可以通过外部中断0可以控制定时器开关。

|-------------------------------------------------------------------------------------------|
| 这里我们设置为软件启动 两个定时器的GATE寄存器对应TMOD寄存器的第3位和第7位 这里设置定时器0使能,则设置TMOD第3位为0 0b0000 0001 TMOD=0x01; |

TR0 **:**运行控制位

可位寻址

|-----------------------|
| TR0 = 1; //使能定时器0开始计时 |

最终

|-----------------------------------------------------------|
| TMOD=0x01; //设置定时器0为:模式1、系统时钟源、软件启动 TR0 = 1; //使能定时器0开始计时 |

预装载值

TL0 TH0 :定时器预装载值

16位=TL0寄存器的8bit+TH0寄存器的8bit

可以看到,计数器里有两个时基单元,TL0(低8位)和 TH0(高8位)总共16位,所以叫16位定时/计数器,它最多可以计数 2^16=65535次,也就是定时器增加到 65536 时,会触发一次溢出。

单片机系统时钟是11.0592MHz时,在12T模式,定时器频率为11.0592MHz除12为921600Hz,就是一秒计数921600次,10ms计数9216次。 20ms 9216*2

如果我们需要定时器10ms溢出一次,我们可以设置定时器的预装载值为65536-9216,这样,计算9216次后达到65536就会溢出一次。

|-----------------------------------------------------|
| //10ms定时器 TIMS=65536-9216; TL0=TIMS; TH0=TIMS>>8; |

溢出中断

TF0:溢出标志位

|---------------------------------|
| ET0=1; //使能定时器0中断 EA=1; //使能总中断 |

所以,下方是使能定时器0定时10ms的完整程序

复制代码
#include <reg52.h>

#define TIMS (65536-9216)
                    
void main()
{
        TMOD = 0x01;                    //配置定时器0为16位定时器,TH0、TL0全用
        TH0 = TIMS >> 8;                //设置定时初值高8位
        TL0 = TIMS;                     //设置定时初值低8位
        ET0 = 1;  //开启定时器0中断                                         
        EA  = 1;  //开启全局中断                                                     
        TR0 = 1;  //定时器0开始计数       
        while(1);
}

//10ms执行一次
void Timer0() interrupt 1
{
        //每次产生中断后重新设置下次定时器初值
        TH0 = TIMS >> 8;
        TL0 = TIMS;
        //这里可以写中断函数要执行的操作
}

如果我们想让LED间隔1s闪烁,我们可以添加指示灯操作,添加变量count,每次触发定时器中断时加1,加到1000ms,也就是1s时,改变LED的状态,就可以实现啦。

复制代码
#include <reg52.h>

#define TIMS (65536-9216)
sbit led = P2^7;
unsigned int count = 0;
                    
void main()
{
        TMOD = 0x01;                    //配置定时器0为16位定时器,TH0、TL0全用
        TH0 = TIMS >> 8;                //设置定时初值高8位
        TL0 = TIMS;                     //设置定时初值低8位
        ET0 = 1;  //开启定时器0中断                                         
        EA  = 1;  //开启全局中断                                                     
        TR0 = 1;  //定时器0开始计数       
        while(1);
}

//10ms执行一次
void Timer0() interrupt 1
{
        //每次产生中断后重新设置下次定时器初值 - 10毫秒产生1次中断
        TH0 = TIMS >> 8;
        TL0 = TIMS;
        //1000毫秒执行一次P1电平反转
        count++;
        if(count >= 100)
        {
                led = ~led;
                count = 0;
        }
}
相关推荐
XWXnb61 小时前
STM32 中断系统深度剖析
c语言·开发语言·stm32·嵌入式硬件
光子物联单片机1 小时前
GD32F407单片机开发入门(十七)内部RTC实时时钟及实战含源码
stm32·单片机·嵌入式硬件·mcu·gd32单片机
.似水1 小时前
2025.4.26_STM32_SPI
stm32·单片机·嵌入式硬件
nuannuan2311a8 小时前
97AB-ASEMI机器人功率器件专用97AB
单片机
bloxd yzh9 小时前
简易版2D我的世界C++程序(有点BUG,但是可以玩!!!)
stm32·单片机·嵌入式硬件
落雨封海10 小时前
【5】GD32 基础通信外设:USART、I2C、SPI
单片机
yt9483213 小时前
STM32裸机编程架构与思路
单片机·嵌入式硬件
夜月yeyue17 小时前
STM32 USB配置详解
stm32·单片机·嵌入式硬件
技术干货贩卖机17 小时前
0基础 | Proteus仿真 | 继电器
嵌入式硬件·51单片机·proteus·继电器·0基础