单片机-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;
        }
}
相关推荐
国科安芯3 小时前
ASP4644芯片低功耗设计思路解析
网络·单片机·嵌入式硬件·安全
充哥单片机设计4 小时前
【STM32项目开源】基于STM32的智能厨房火灾燃气监控
stm32·单片机·嵌入式硬件
CiLerLinux11 小时前
第四十九章 ESP32S3 WiFi 路由实验
网络·人工智能·单片机·嵌入式硬件
时光の尘11 小时前
【PCB电路设计】常见元器件简介(电阻、电容、电感、二极管、三极管以及场效应管)
单片机·嵌入式硬件·pcb·二极管·电感·三极管·场效应管
Lu Zelin11 小时前
单片机为什么不能跑Linux
linux·单片机·嵌入式硬件
宁静致远202112 小时前
stm32 freertos下基于hal库的模拟I2C驱动实现
stm32·嵌入式硬件·freertos
Wave84516 小时前
STM32--智能小车
stm32·单片机·嵌入式硬件
wdfk_prog19 小时前
[Linux]学习笔记系列 -- lib/timerqueue.c Timer Queue Management 高精度定时器的有序数据结构
linux·c语言·数据结构·笔记·单片机·学习·安全
充哥单片机设计1 天前
【STM32项目开源】基于STM32的智能家居环境(空气质量)检测系统
stm32·单片机·嵌入式硬件
夜月yeyue1 天前
ART 加速器、流水线与指令预测的关系详解
linux·服务器·c语言·单片机·嵌入式硬件·性能优化·嵌入式高阶技巧