8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用
Posted Neutionwei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 中断系统结构
以下这张图是从中断引脚到中断入口所经过的通道:
从图中不难看出T0
与T1
经过了TCON、IE、IP
这些寄存器,因此我们在写程序时得把这些寄存器功能配置好,CPU才会按照我们的想法只执行!下面分别对这些寄存器进行介绍(稍微了解一下即可,忘记的时候再查)。
1.1 TCON寄存器
TCON(Timer Control Register),中文叫定时器/计数器控制寄存器,TCON寄存器是用于中断触发方式设置以及中断标志。
各寄存器位的作用如下:
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),中文叫中断允许寄存器,它的作用是控制所有中断源的开放或禁止,以及每个中断源是否被允许。
各寄存器位的作用如下:
IE寄存器位 | 作用 |
---|---|
EA | EA = 0 时,所有中断禁止(即不产生中断);EA = 1 时,各中断的产生由个别的允许位决定 |
ES | 串行口RX/TX 中断允许 |
ET1 | 定时器T1 中断允许 |
EX1 | 外中断INT1 中断允许 |
ET0 | 定时器T0 中断允许 |
EX0 | 外部中断INT0 中断允许 |
1.3 IP寄存器
IP(Interrupt Priority),中文叫中断优先级寄存器,它是用来设定各个中断源属于两级中断中的哪一级。
各寄存器位的作用如下:
IP寄存器位 | 作用 |
---|---|
PS | 串行口RX/TX 中断优先 |
PT1 | 定时器T1 中断优先 |
PX1 | 外中断INT1 中断优先 |
PT0 | 定时器T0 中断优先 |
PX0 | 外部中断INT0 中断优先 |
2 定时器/计数器结构
以下这张图是定时器/计数器结构框图:
从图中不难如果我们要使用定时器,必须要初始化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),中文叫定时器模式寄存器,它用于给定时器设置工作方式。
我们看到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:
② 工作模式1:
③ 工作模式2:
④ 工作模式3:
事实上,对于工作模式的设置,通常我们用得最多的是工作模式1和工作模式2。 为了更好地说明工作模式1和工作模式2的使用,我们先记住一句话:1个机器周期含6个状态周期,12个振荡周期。如果使用12M频率的晶振,那么它的振荡周期就是1/12(us),即机器周期为1us!那么定时器/计数器每计数1次,则花费1us。
对于工作模式1,我们可以理解以下这个例子:
通过计算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灯:
② 数码管原理图:
③ 数码管的位选使用138译码器进行解析,关于这块我们可以参考这篇文章:《数字器件认识 | 74HC138三八译码器的应用》。
④ MCU原理图:
4 代码
① 中断服务函数:
我们知道我们编写的C程序,函数的执行是从main主函数开始执行,现在有了中断,自然就产生一个中断服务函数:
从图中我们可以知道单片机在发生中断的时候,程序的执行过程会从主程序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;
}
}
以上是关于8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用的主要内容,如果未能解决你的问题,请参考以下文章
8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用
8051单片机实战分析(以STC89C52RC为例) | 03 - LED流水灯
8051单片机实战分析(以STC89C52RC为例) | 02 - LED延时约5s闪烁
8051单片机实战分析(以STC89C52RC为例) | 08 - 矩阵按键驱动