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

本文将带你探索STC89C52RC单片机的定时器T0和T1中断系统,包括TCON、IE和IP寄存器配置,以及如何利用定时器实现延时和数码管动态显示。通过中断服务函数和工作模式设置,掌握精确时间控制技术。

在第一篇到第九篇博文中,我们认识到了一些基于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 中断系统结构

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

从图中不难看出T0T1经过了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寄存器位作用
EAEA = 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)TCONTMOD寄存器,对于TCON寄存器,在前面的中断系统结构中,我们已经学习到了,下面分别对TH1(TH0)TL1(TL0)TMOD寄存器的使用进行介绍。

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

TH1(TH0)TL1(TL0),它们都是定时值存储寄存器,用于存储定时器的计数值,其中TH1/TL1 用于 T1TH0/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工作模式:

M1M0工作模式描述
00013 位定时器/计数器,TH1(TH0)的 8 位和TL1(TL0)的 5 位组成一个 13 位定时器
011TH1(TH0)TL1(TL0) 组成一个 16 位的定时器/计数器
1028 位自动重装模式,定时器/计数器溢出后TH1(TH0)重装到TL1(TL0)
113禁用定时器/计数器 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=0xFETL0=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;	
	}	
}
<think>好的,我现在需要回答用户关于AT89C51单片机P20、P21、P22端口连接显示器的具体引脚及其功能的问题。首先,我需要回顾用户提供的引用内容,看看有没有相关的信息。 查看引用[2],里面提到数码管的连接方式,特别是P0和P2口的使用。用户提到的是P2.0到P2.2,而引用[2]中P2口用于位控扫描,控制数码管的位选,比如轮流给每个数码管供电。这可能与用户的问题相关,因为P2口的部分引脚可能用于控制显示器的位选。 另外,引用[5]解释了共阳极和共阴极数码管的区别,以及驱动方法的不同。这可能涉及到P2口作为位控时输出高或低电平来选择对应的数码管。 接下来,我需要确定AT89C51的P2口结构和功能。根据一般知识,AT89C51的P2口是8位双向I/O口,内部有上拉电阻。当用作通用I/O时,可以输出高或低电平。在外部存储器扩展时,P2口输出高8位地址,但用户的问题似乎不涉及外部存储器,因此P2口作为普通I/O使用。 用户的问题是关于连接显示器的具体引脚和功能。通常,数码管的连接分为段选和位选。段选控制显示什么字符,位选控制哪个数码管亮。引用[2]中提到P0口负责段码,而P2口作为位控。比如,四位一体的数码管可能需要4个位选线,但用户的问题只涉及P2.0-P2.2三个引脚,可能用于控制三个数码管,或者作为其他功能。 需要确认的是,当P2口的P20、P21、P22连接显示器时,通常用作位选控制,即选择哪个数码管被激活。每个位选线对应一个数码管的共阳或共阴端。如,在共阴数码管中,P2.0输出高电平会选中第一个数码管,低电平则不选。或者根据电路设计,可能相反,具体取决于数码管的类型和驱动电路。 此外,引用[5]中提到共阳极和共阴极的驱动方法不同,程序中的电平逻辑也会不同。如果数码管是共阳,P2口需要输出低电平来选通;如果是共阴,则需要输出高电平。这需要结合具体的硬件设计来判断。 总结:P20、P21、P22可能作为位选信号,控制多个数码管中的哪一个被点亮。每个引脚对应一个数码管的位控端,通过轮流输出有效电平来实现动态显示。如,三位数码管的话,每个P2.x控制一位,依次激活,利用视觉暂留效果形成稳定显示。 需要确保回答中说明引脚的功能是位选控制,对应数码管的位选端,并解释其工作原理,同时引用相关的引用内容,如引用[2]和[5]。</think>### AT89C51单片机P2.0-P2.2连接显示器的引脚功能说明 #### 1. **P2端口的基本特性** AT89C51的P2口是8位双向I/O口,内部集成上拉电阻。在未扩展外部存储器时,可作为通用I/O口使用,支持高/低电平输出及输入检测。其驱动能力较强,可直接驱动LED或通过三极管放大电流[^4]。 #### 2. **P2.0-P2.2连接显示器的典型场景** 在数码管显示系统中,P2.0、P2.1、P2.2通常用于**位选控制**(即选择激活哪一位数码管),具体功能如下: - **位选功能**:每个引脚控制一个数码管的共阴/共阳端,通过输出特定电平选择当前显示的数码管位。 - **共阴极数码管**:P2.x输出高电平(如`P2.0=1`)时,对应位数码管被激活[^5]。 - **共阳极数码管**:P2.x输出低电平(如`P2.0=0`)时,对应位数码管被激活[^5]。 - **动态扫描原理**:通过依次切换P2.0-P2.2的电平状态,轮流点亮不同数码管位,利用视觉暂留实现稳定显示,扫描间隔通常为1-5ms[^2]。 #### 3. **硬件连接示** 假设使用三位共阴极数码管: - **P2.0**:连接第1位数码管的阴极(位选1)。 - **P2.1**:连接第2位数码管的阴极(位选2)。 - **P2.2**:连接第3位数码管的阴极(位选3)。 - **段码控制**:通常由其他端口(如P0口)输出段码,控制具体显示的数字或字符。 #### 4. **软件实现示(C语言)** ```c #include <reg51.h> void delay_ms(unsigned int t) { while(t--); } void main() { while(1) { // 显示第1位数字"1" P0 = 0x06; // 段码(共阴极数码管显示"1") P2 = 0x01; // 位选1(P2.0=1) delay_ms(1000); // 延时1ms // 显示第2位数字"2" P0 = 0x5B; // 段码(显示"2") P2 = 0x02; // 位选2(P2.1=1) delay_ms(1000); // 显示第3位数字"3" P0 = 0x4F; // 段码(显示"3") P2 = 0x04; // 位选3(P2.2=1) delay_ms(1000); } } ``` #### 5. **注意事项** - **电流驱动能力**:若数码管亮度不足,需增加三极管或驱动芯片(如74HC245)放大电流。 - **消隐处理**:切换位选时,应短暂关闭段码输出以避免残影。 --- ###
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式逍遥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值