目录
  • 一、定时器简介
  • 二、STC89C52定时器资源
  • 三、定时器工作原理
    • 1. 时钟信号的提供
    • 2. 中断系统简介
      • 中断的概念
      • 中断的优先级
      • 中断的嵌套
      • 中断流程图
      • STC89C52的中断资源
    • 3. 相关的寄存器
      • TCON寄存器
      • TMOD寄存器
    • 4. 定时器程序中断通路配置图
  • 四、编码实现
    • 初始化代码
      • 1. 配置TMOD寄存器
      • 2. 配置TCON寄存器
      • 3. 配置计数寄存器的初始值
      • 4. 配置中断通路
      • 5. 总的配置代码
    • 中断程序代码
    • 使用STC-ISP自带的定时器计算器
  • 五、将计时器封装成模块

参考资料:https://www.bilibili.com/video/BV1Mb411e7re?p=17

一、定时器简介

  • 定时器介绍:
    51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成
  • 定时器作用:
    • (1) 用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作
    • (2)替代长时间的Delay, 提高CPU的运行效率和处理速度
    • ...

二、STC89C52定时器资源

  • 定时器个数: 3个 (T0 T1T2),T0和T1与传统的51单片机兼容
    T2是此型号单片机增加的资源
  • 注意:定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的

三、定时器工作原理

定时器在单片机内部就像一个小闹钟一样,根据时钟的输出信号,每隔“一秒"(一个单位时间)计数单元的数值就增加一,当计数单元数值增加到“设定的闹钟提醒时间"时,计数单元就会向中断系统发出中断申请,产生"响铃提醒",使程序跳转到中断服务函数中执行

image-20210819135139999

在实现细节上:时钟部分每隔单位时间就会传递一个脉冲信号到计数部分,而计数部分会对脉冲个数进行计数,当脉冲个数达到TH0,TL0能存储的最大数量后(即发生溢出,这里一共有16位,即只能表示0~65535这些数字,继续接收信号即溢出),TF0将会被置一,此时就会发出中断申请,然后程序跳转到中断服务程序中执行。

1. 时钟信号的提供

可以看到,在时钟部分有两个输入来源:

  1. 系统时钟
  2. T0引脚

当我们使用T0引脚时往往是将这个模块作为计数器记录脉冲的个数的,而不是用作定时器,故这里我们一般只考虑系统时钟的信号输入

SYSclk:系统时钟,即晶振周期,本开发板上的晶振为12MHz

 C/T=0 时选择的是系统时钟作为输入,而 C/T=1 时选择的是T0引脚作为输入,故我们需要选择第一种。

2. 中断系统简介

中断系统是为使CPU具有对外界紧急事件实时处理能力而设置的。

中断的概念

当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断

中断的优先级

实现这种功能的部件称为中断系统,请示CPU中断的请求源称为中断源。微型机的中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,要求为它服务的时候,这就存在CPU优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU总是先响应优先级别最高的中断请求

中断的嵌套

当CPU正在处理一个中断源请求的时候( 执行相应的中断服务程序),发生了另外一个优先级比它还高的中断源请求。如果CPU能够

  1. 暂停对原来中断源的服务程序
  2. 转而去处理优先级更高的中断请求源,处理完该请求
  3. 回到原低级中断服务程序

这样的过程称为中断嵌套,这样的中断系统称为多级中断系统

没有中断嵌套功能的中断系统称为单级中断系统

中断流程图

image-20210819141614525

STC89C52的中断资源

  • 中断源个数: 8个

    1. 外部中断0
    2. 定时器0中断
    3. 外部中断1
    4. 定时器1中断
    5. 串口中断
    6. 定时器2中断
    7. 外部中断2
    8. 外部中断3
  • 中断优先级个数: 4个

  • 中断号

image-20210819142345557

3. 相关的寄存器

TCON寄存器

image-20210819144150739

TF和TR

image-20210819145610987

当计数器溢出时TF0会被置一TR0用于控制T0计时器是否计数。

TMOD寄存器

TMOD寄存器主要是用于配置我们的定时器的功能

image-20210819143240884

GATE

image-20210819143853409

C/T

image-20210819143534190

M1、M0

image-20210819143355849

总图

image-20210819144643006

4. 定时器程序中断通路配置图

image-20210819144422214

四、编码实现

初始化代码

1. 配置TMOD寄存器

首先我们希望使用定时器0,我们需要配置:

  1. GATE = 0
  2. C/T = 0
  3. M1,M0 = 01

所以我们可以使用与或赋值法进行赋值:

TMOD &= 0xF0; // 1111 0000
TMOD |= 0x01; // 0000 0001

tips:TMOD寄存器是不可位寻址的,故我们只能通过整体赋值的方式进行配置

2. 配置TCON寄存器

我们需要启用T0计数器,并给它的溢出标志位TF0置零:

TF0 = 0;
TR0 = 1;

3. 配置计数寄存器的初始值

计数器的计数范围:0~65535
大约每隔1us计数加一,故其总共定时时间为65535us

而我们希望计时1ms即发生中断,则我们可以将计数寄存器初始值为64536,因为64536离计数器溢出差值1000,所以当计时时间为1ms时即可触发中断。

而计数寄存器由两个寄存器拼接而成,则我们可以分高低位赋值:

TH0 = 64536 / 0x0100; // 高八位
TL0 = 64536 % 0x0100; // 低八位

4. 配置中断通路

即按照上一部分的第4点的图进行配置即可:

ET0 = 1;
EA = 1;
PT0 = 0;

5. 总的配置代码

void Timer0Init(void)
{
    // 1.配置TMOD寄存器
    TMOD &= 0xF0; // 1111 0000
    TMOD |= 0x01; // 0000 0001
    // 2.配置TCON寄存器
    TF0 = 0;
    TR0 = 1;
    // 3.配置计数寄存器的初始值
    TH0 = 64536 / 0x0100;
    TL0 = 64536 % 0x0100;
    // 4.配置中断通路
    ET0 = 1;
    EA = 1;
    PT0 = 0;
}

中断程序代码

语法:相当于在函数后面加上 interrupt 关键字:

void 中断程序名() interrupt 中断号 {
    ...
}

其中中断号需要和中断源相对应

使用STC-ISP自带的定时器计算器

打开STC-ISP烧录工具,并点击【定时器计算器】的tab:

image-20210819160820456

配置如下:

image-20210819161239960

然后我们即可获得相应的初始化代码了!

注意:在我们当前版本的单片机中是没有AUXR这个寄存器的,我们不需要配置此项(删除即可),然后我们还需要加入中断的配置,即【配置中断通路】部分的三行代码。

使用自动生成的代码:

void Timer0Init(void)	//1毫秒@12.000MHz
{
    TMOD &= 0xF0;	//设置定时器模式
    TMOD |= 0x01;	//设置定时器模式
    TL0 = 0x18;		//设置定时初值
    TH0 = 0xFC;		//设置定时初值
    TF0 = 0;		//清除TF0标志
    TR0 = 1;		//定时器0开始计时
    // 配置中断通路
    ET0 = 1;
    EA = 1;
    PT0 = 0;
}
unsigned int T0Count = 0;
void timer0_routine() interrupt 1 {
    T0Count++;
    TL0 = 0x18;		//设置定时初值
    TH0 = 0xFC;		//设置定时初值
    if (T0Count == 1000) {
        T0Count = 0;
        P2_0 =! P2_0;
    }
}

五、将计时器封装成模块

新建Timer0.hTimer0.c文件:

// Timer0.h
#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0Init(void);

#endif // __TIMER0_H__
#include <Atmel/REGX52.H>
#include "Timer0.h"

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

这样我们只需要在主函数中使用Timer0Init(),我们即可在固定的时间内调用中断程序了。