0
点赞
收藏
分享

微信扫一扫

8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用


在第一篇到第九篇博文中,我们认识到了一些基于IO口输入与输出的基础电子器件使用:
《​​8051单片机实战分析(以STC89C52RC为例) | 01 - 点亮一个LED​​》
《​​8051单片机实战分析(以STC89C52RC为例) | 02 - LED延时约5s闪烁​​》
《​​8051单片机实战分析(以STC89C52RC为例) | 03 - LED流水灯​​》
《​​8051单片机实战分析(以STC89C52RC为例) | 04 - 蜂鸣器驱动​​》
《​​8051单片机实战分析(以STC89C52RC为例) | 05 - 静态数码管驱动​​》
《​​8051单片机实战分析(以STC89C52RC为例) | 06 - 动态数码管驱动​​》
《​​8051单片机实战分析(以STC89C52RC为例) | 07 - 独立按键驱动​​》
《​​8051单片机实战分析(以STC89C52RC为例) | 08 - 矩阵按键驱动​​》
《​​8051单片机实战分析(以STC89C52RC为例) | 09 - LED点阵显示数字​​》
但现在我们要开始回到8051单片机内部,通过实战来认识它们的工作原理,你会发现通过它们可以去开发一些更有意思的东西!

这篇博文带领大家认识一下STC89C52RC单片机定时器中断的使用,如果你不了解什么是中断,建议你先看这篇:
《​​STC89C52RC单片机额外篇 | 01 - 认识中断、中断源以及中断优先级​​》

1 中断系统结构

以下这张图是从中断引脚到中断入口所经过的通道:


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_定时器

从图中不难看出​​T0​​​与​​T1​​​经过了​​TCON、IE、IP​​这些寄存器,因此我们在写程序时得把这些寄存器功能配置好,CPU才会按照我们的想法只执行!下面分别对这些寄存器进行介绍(稍微了解一下即可,忘记的时候再查)。

1.1 TCON寄存器

TCON(Timer Control Register),中文叫定时器/计数器控制寄存器,TCON寄存器是用于中断触发方式设置以及中断标志。


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_定时器_02

各寄存器位的作用如下:

TCON寄存器位

作用

​TF0(TF1)​

计数溢出标志位。当计数器计数溢出时,该位置​​1​

​TR0(TR1)​

定时器运行控制位。当​​TR0(TR1)=0​​​停止定时器/计数器工作;当​​TR0(TR1)=1​​启动定时器/计数器工作

​IE0(IE1)​

外中断请求标志位。当CPU采样到​​P3.2(P3.3)​​​出现有效中断请求时,此位由硬件置1。在中断响应完成后转向中断服务时,再由硬件自动清​​0​

​IT0(IT1)​

外中断请求信号方式控制位。当​​IT0(IT1) =1​​​ 脉冲方式(后沿负跳有效);当​​IT0(IT1)=0​​​电平方式(低电平有效)此位由软件置​​1​​​或清​​0​

​TF0(TF1)​

计数溢出标志位。当计数器产生计数溢出时,此位由硬件置​​1​​​。当转向中断服务时,再有硬件自动清​​0​​。计数溢出的标志位的使用有两种情况:采用中断方式时,作中断请求标志位来使用;采用查询方式时,作查询状态位来使用

1.2 IE寄存器

IE(Interrupt Enable),中文叫中断允许寄存器,它的作用是控制所有中断源的开放或禁止,以及每个中断源是否被允许。

8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_单片机_03

各寄存器位的作用如下:

IE寄存器位

作用

​EA​

​EA = 0​​​时,所有中断禁止(即不产生中断);​​EA = 1​​时,各中断的产生由个别的允许位决定

​ES​

串行口​​RX/TX​​中断允许

​ET1​

定时器​​T1​​中断允许

​EX1​

外中断​​INT1​​中断允许

​ET0​

定时器​​T0​​中断允许

​EX0​

外部中断​​INT0​​中断允许

1.3 IP寄存器

IP(Interrupt Priority),中文叫中断优先级寄存器,它是用来设定各个中断源属于两级中断中的哪一级。

8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_定时器_04

各寄存器位的作用如下:

IP寄存器位

作用

​PS​

串行口​​RX/TX​​中断优先

​PT1​

定时器​​T1​​中断优先

​PX1​

外中断​​INT1​​中断优先

​PT0​

定时器​​T0​​中断优先

​PX0​

外部中断​​INT0​​中断优先

2 定时器/计数器结构

以下这张图是定时器/计数器结构框图:


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_51单片机_05

从图中不难如果我们要使用定时器,必须要初始化​​TH1(TH0)​​​、​​TL1(TL0)​​​、​​TCON​​​与​​TMOD​​​寄存器,对于​​TCON​​​寄存器,在前面的中断系统结构中,我们已经学习到了,下面分别对​​TH1(TH0)​​​、​​TL1(TL0)​​​与​​TMOD​​寄存器的使用进行介绍。

2.1 TH1(TH0)与TL1(TL0)寄存器

​TH1(TH0)​​​与​​TL1(TL0)​​​,它们都是定时值存储寄存器,用于存储定时器的计数值,其中​​TH1/TL1​​​ 用于 ​​T1​​​,​​TH0/TL0​​​ 用于 ​​T0​​。以下是它们各个寄存器的作用:

定时值存储寄存器

作用

​TH1​

定时器​​T1​​定时值的高字节

​TL1​

定时器​​T1​​定时值的低字节

​TH0​

定时器​​T0​​定时值的高字节

​TL0​

定时器​​T0​​定时值的低字节

2.2 TMOD寄存器

TMOD(TIMER MODE),中文叫定时器模式寄存器,它用于给定时器设置工作方式。


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_定时器_06

我们看到​​TMOD​​​寄存器前面四位与后面四位一模一样,其实前四位是用于设置定时器​​T1​​​的模式,后四位是用于设置定时器​​T0​​的模式。

各寄存器位的作用如下:

TMOD寄存器位

作用

​GATE​

该位被置 ​​1​​​ 时为门控位。当​​INTx​​​脚为高电平并且​​TRx​​​控制位被置​​1​​​时使能定时器,定时器开始计时;当该位被清​​0​​​时,只要​​TRx​​​位被置​​1​​​,定时器 就使能开始计时,而不受到单片机引脚​​INTx​​外部信号的干扰,常用来测量外部信号脉冲宽度

​C/T​

定时器/计数器选择位。该位被清零时用作定时器功能(内部系统时钟),被置 ​​1​​用作计数器功能

上面这部分寄存器一般使用较少,通常不设置,使用复位默认值!下面是​​TMOD​​​寄存器 ​​M1/M0​​工作模式:

M1

M0

工作模式

描述

0

0

0

13 位定时器/计数器,​​TH1(TH0​​​)的 8 位和​​TL1(TL0)​​的 5 位组成一个 13 位定时器

0

1

1

​TH1(TH0)​​​和 ​​TL1(TL0)​​ 组成一个 16 位的定时器/计数器

1

0

2

8 位自动重装模式,定时器/计数器溢出后​​TH1(TH0)​​​重装到​​TL1(TL0)​​中

1

1

3

禁用定时器/计数器 ​​T1​​​,定时器/计数器​​T0​​变成 2 个 8 位定时器/计数器

下面列出各个工作模式下的内部工作图:

① 工作模式0:


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_嵌入式_07

② 工作模式1:


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_嵌入式_08

③ 工作模式2:


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_定时器中断_09

④ 工作模式3:


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_51单片机_10

事实上,对于工作模式的设置,通常我们用得最多的是工作模式1和工作模式2。 为了更好地说明工作模式1和工作模式2的使用,我们先记住一句话:1个机器周期含6个状态周期,12个振荡周期。如果使用12M频率的晶振,那么它的振荡周期就是1/12(us),即机器周期为1us!那么定时器/计数器每计数1次,则花费1us。


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_嵌入式_11

对于工作模式1,我们可以理解以下这个例子:


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_51单片机_12

通过计算​​N=500​​​,即要定时​​1ms​​​,需要机器周期运行​​500​​​次,那么为什么​​X=65536-500​​​?结合前面的内部工作图,我们知道溢出​​(0xFFFF+1=65536)​​​才能​​TF0​​​位设置为​​1​​​,由于定时器计数是往上自加的,所以从​​65026​​​开始自加到​​65536​​​刚好计数​​500​​​次,也就是​​1ms​​​。所以在此处​​TH0=0xFE​​​,​​TL0=0x0C​​​,也就是​​X​​的前八位与后八位。

对于工作模式2也是,由于只使用​​TL0​​​进行计数,所以最大计数为​​256​​​次,自动重装模式的意思是每次溢出​​(0xFF+1=256)​​​后,​​TH0​​​里面存的计数值会直接复制到​​TL0​​​,因此​​TH0​​只存计数值,而不会计数。

3 原理图

① LED灯:


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_单片机_13

② 数码管原理图:

8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_单片机_14

③ 数码管的位选使用138译码器进行解析,关于这块我们可以参考这篇文章:《​​数字器件认识 | 74HC138三八译码器的应用​​》。

8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_嵌入式_15

④ MCU原理图:


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_51单片机_16

4 代码

① 中断服务函数:

我们知道我们编写的C程序,函数的执行是从main主函数开始执行,现在有了中断,自然就产生一个中断服务函数:


8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用_单片机_17

从图中我们可以知道单片机在发生中断的时候,程序的执行过程会从主程序A跳到中断服务程序B,在执行完中断服务程序B后,会返回到之前主程序A被中断打断处继续执行程序。

那么我们如何指定中断服务程序?具体参考以下模板(对于函数名你可以随便写,当然最好贴近有意义的命名)。

外部中断0的中断服务函数:

void Int0() interrupt 0
{
... // 中断服务程序中要执行内容
}

定时器0的中断服务函数:

void Timer0() interrupt 1
{
... // 中断服务程序中要执行内容
}

外部中断1的中断服务函数:

void Int1() interrupt 2
{
... // 中断服务程序中要执行内容
}

定时器1的中断服务函数:

void Timer1() interrupt 3
{
... // 中断服务程序中要执行内容
}

串行口的中断服务函数:

void Serial() interrupt 4
{
... // 中断服务程序中要执行内容
}

② 使用单片机内部定时器​​T0​​可以实现准确延时,让LED循环点亮1秒,熄灭1秒:

#include "reg52.h"       //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit led=P2^0; //定义P20口是led

/*******************************************************************************
* 函 数 名 : Timer0Init
* 函数功能 : 定时器0初始化
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer0Init()
{
TMOD|=0x01; //选择为定时器0模式,工作模式1,仅用TR0打开启动。

TH0=0xFC; //给定时器赋初值,定时1ms
TL0=0x18;
ET0=1; //打开定时器0中断允许
EA=1; //打开总中断
TR0=1; //打开定时器
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
Timer0Init(); //定时器0初始化
while(1);
}

/*******************************************************************************
* 函 数 名 : void Timer0() interrupt 1
* 函数功能 : 定时器0中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer0() interrupt 1
{
static u16 i;
TH0=0xFC; //给定时器赋初值,定时1ms
TL0=0x18;
i++;
if(i==1000)
{
i=0;
led=~led;
}
}

③ 使用单片机内部定时器​​T1​​可以实现准确延时,让数码管最后一位间隔一秒循环显示0-F:

#include "reg52.h"           //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

u8 code LedChar[]={
0x3F, //"0"
0x06, //"1"
0x5B, //"2"
0x4F, //"3"
0x66, //"4"
0x6D, //"5"
0x7D, //"6"
0x07, //"7"
0x7F, //"8"
0x6F, //"9"
0x77, //"A"
0x7C, //"B"
0x39, //"C"
0x5E, //"D"
0x79, //"E"
0x71, //"F"
0xff, //全亮
0x00 //熄灭
};

u8 n=0;

/*******************************************************************************
* 函 数 名 : Timer1Init
* 函数功能 : 定时器1初始化
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer1Init()
{
TMOD|=0X10; //选择为定时器1模式,工作模式1,仅用TR1打开启动。

TH1=0xFC; //给定时器赋初值,定时1ms
TL1=0x18;
ET1=1; //打开定时器1中断允许
EA=1; //打开总中断
TR1=1; //打开定时器
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
LSA=0;
LSB=0;
LSC=0;
Timer1Init(); //定时器1初始化
while(1);
}

/*******************************************************************************
* 函 数 名 : void Timer1() interrupt 3
* 函数功能 : 定时器0中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer1() interrupt 3
{
static u16 i;
TH1=0xFC; //给定时器赋初值,定时1ms
TL1=0x18;
i++;
if(i==1000)
{
i=0;
P0=LedChar[n++];
if(n==16)n=0;
}
}


举报

相关推荐

51单片机STC89C52RC——6.2 定时器

0 条评论