中断系统

引起 CPU 中断的根源称为 中断源。中断源向 CPU 提出中断请求,CPU 暂时中断原来的事务 A,转去处理事件 B,对事件 B 处理完毕后,再回到原来被中断的地方(即断点),称为 中断返回。实现上述中断功能的部件称为 中断系统(中断机构)。

CPU 总是先响应 优先级别最高的中断请求。发生了另外一个 优先级比它还高的中断源请求。如果 CPU 能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,这样的过程称为 中断嵌套。这样的中断系统称为 多级中断系统,没有中断嵌套功能的中断系统称为 单级中断系统

中断优点

  • 分时操作

    CPU 可以分时为多个 I/O 设备服务,提高了计算机的利用率;

  • 实时响应

    CPU 能够及时处理应用系统的随机事件,系统的实时性大大增强;

  • 可靠性高

    CPU 具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高

注意

51系列单片机一定有基本的 5 个中断,但不全有8个中断,需要查看芯片手册,通常我们使用的都是基本的5个中断: INT0、INT1、定时器0/1,串口中断,所有的中断都具有 四个 中断优先级(基本型只有两个)。

  • 高优先级的中断请求可以打断低优先级的中断,反之,低优先级的中断请求不可以打断高优先级及同优先级的中断。

  • 当两个相同优先级的中断同时产生时,将由查询次序来决定系统先响应哪个中断。

  • 中断查询次序即为中断号,这个中断号在编程时非常重要,当中断来临时,只有中断号正确才能进入中断。

下面是51单片机均有的5个基本中断:

INT0 对应的是 P3.2口 的附加功能,可由 IT0(TCON.0) 选择其为 低电平有效还是下降沿有效 。当 CPU 检测到 P3.2 引脚上出现有效的中断信号时,中断标志IE0(TCON.1) 置1 ,向 CPU 申请中断。

INT1 对应的是 P3.3口 的附加功能,可由 IT1(TCON.2) 选择其为 低电平有效还是下降沿有效。当 CPU 检测到 P3.3 引脚上出现有效的中断信号时,中断标志IE1(TCON.3) 置1 ,向 CPU 申请中断。

T0 对应的是 P3.4口 的附加功能,TF0(TCON.5),片内定时/计数器 T0 溢出中断请求标志。当 定时/计数器T0发生溢出时,置位 TF0,并向 CPU 申请中断。

T1 对应的是 P3.5口 的附加功能,TF1(TCON.7),片内定时/计数器 T1 溢出中断请求标志。 当定时/计数器T1发生溢出时 ,置位 TF1,并向 CPU 申请中断。

RXD 和 TXD 对应的是 P3.0 和 P3.1口 的附加功能,RI(SCON.0) 或 TI(SCON.1),串行口中断请求标志。当 串行口接收完一帧串行数据时置位RI或当串行口发送完一帧串行数据时置位TI ,向 CPU 申请中断。

中断相关寄存器

(1)中断允许控制CPU对中断系统所有中断以及某个中断源的开放和屏蔽是由 中断允许寄存器IE控制 的。

EX0(IE.0),外部中断0 允许位;

ET0(IE.1), 定时/计数器T0 中断允许位;

EX1(IE.2),外部中断0 允许位;

ET1(IE.3), 定时/计数器T1 中断允许位;

ES(IE.4), 串行口 中断允许位;

EA(IE.7), CPU 中断允许( 总允许)位

(2)中断请求标志TCON:

IT0(TCON.0),外部中断0触发方式控制位。当 IT0=0时,为 电平触发方式;当 IT0=1时,为 边沿触发方式(下降沿有效)

IE0(TCON.1),外部中断0 中断请求标志位。

IT1(TCON.2),外部中断1 触发方式控制位。

IE1(TCON.3),外部中断1 中断请求标志位。

TF0(TCON.5),定时/计数器T0 溢出中断请求标志位。

TF1(TCON.7),定时/计数器T1 溢出中断请求标志位。

(3)中断优先级:同一优先级中的中断申请不止一个时,则有中断优先权排队问题。同一优先级的中断优先权排队,由中断系统硬件确定的自然优先级形成。

(4)中断号:

(5)中断响应条件 : ①中断源有中断请求;②此中断源的中断允许位为1;③CPU开中断(即EA=1)。以上三条 同时满足 时,CPU 才有可能响应中断。

在使用中断时我们需要做什么呢?

①你想使用的中断是哪个?选择相应的中断号;

②你所希望的触发条件是什么?

③你希望在中断之后干什么?

中断服务函数:

void break_off()interrupt 0 using 1
{
	//编写用户所需的功能代码
}
//interrupt是一个关键字,表示51单片机中断。后面的“0”是中断号
//using 1 可以省略

外部中断0操作函数代码:

/********************************************************************************
*利用外部中断0让LED1亮灭*
********************************************************************************/

# include "reg52.h"

typedef unsigned int u16;
typedef unsigned char u8;

//定义按键管脚
sbit key3=P3^2;
sbit key=P3^3;
//定义LED管脚
sbit LED1=P2^0;

//延时函数
void delay_10us(u16 ten_us)
{
	while(ten_us--);
}

//中断函数初始化
void external_init(void)
{
	EX0=1;//开外部中断0
	IT0=1;//下降沿触发
	EA=1;//CPU总中断
}

void external_0()interrupt 0
{
	delay_10us(1000);
	if(key3==0)
	{
		LED1=!LED1;
	}
}

void main(void)
{

	external_init();//进入中断函数
	while(1)
	{
		
	}
	while(1);
}

定时器中断

CPU 时序的知识

  • 振荡周期(T)

振荡周期指为单片机 提供定时信号 的振荡源的周期或外部输入时钟的周期

  • 状态周期(S)

它是振荡周期的 两倍,分为 P1 节拍和 P2 节拍,通常在 P1节拍 完成 算术逻辑操作,在 P2节拍 完成 内部寄存器之间的数据传送操作。(注意P1 和P2的相位关系)

  • 机器周期

    一个机器周期由 6个状态周期组成 ,如果把一条指令的执行过程分作几个基本操作,则将 完成一个基本操作所需的时间称作机器周期。单片机的单周期指令执行时间就为一个机器周期。

  • 指令周期

指令周期是 执行一条指令所需的全部时间。MCS-51单片机的指令周期通常由1、2、4个机器周期组成。

例如

外接晶振为 12MHz时,51单片机相关周期的具体值为:振荡周期=1/12us;状态周期=1/6us;机器周期=1us;指令周期=1~4us;

学习定时器前需要明白的几点

  • 51 单片机有两组定时器/计数器,因为 既可以定时,又可以计数,故称之为定时器/计数器。
  • 定时器/计数器和单片机的 CPU 是 相互独立的。定时器/计数器工作的过程是自动完成的, 不需要 CPU 的参与
  • 51 单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据 加 1

有了定时器/计数器之后,可以增加单片机的效率,一些简单的重复加 1 的工作可以交给定时器/计数器处理。CPU 转而处理一些复杂的事情。同时可以实现精确定时作用。

定时器原理

定时/计数器的实质是加 1 计数器(16 位),由 高 8 位和低 8 位 两个寄存器 THx 和 TLx 组成它随着计数器的输入脉冲进行 自加 1 ,也就是 每来一个脉冲,计数器就自动加 1 , 当加到计数器为全 1 时,再输入一个脉冲就使计数器 回零 ,且计数器的溢出使相应的中断标志位置 1,向 CPU 发出中断请求(定时/计数器中断允许时)。 如果定时/计数器工作于定时模式,则表示定时时间已到; 如果工作于计数模式,则表示计数值已满。可见, 由溢出时计数器的值减去计数初值才是加 1 计数器的计数值。

上图中的 T0 和 T1 引脚对应的是单片机 P3.4 和 P3.5 管脚

  • TMOD 是定时/计数器的工作方式寄存器, 确定工作方式和功能;
  • TCON 是控制寄存器,控制 T0、T1 的启动和停止及设置溢出标志。

工作方式寄存器 TMOD

  • GATE 是门控位, GATE = 0 时,用于控制定时器的启动是否受外部中断源信号的影响。只要用软件使 TCON 中的 TR0 或 TR1 为 1, 就可以启动定时/计数器工作;
  • GATA = 1 时,要用软件 使 TR0 或 TR1 为 1,同时外部中断引脚 INT0/1 也为高电平时,才能启动定时/计数器工作。即此时定时器的启动条件,加上了 INT0/1 引脚为高电平这一条件。
  • C/T:定时/计数模式选择位。C/T =0 为定时模式;C/T =1 为计数模式
  • M1M0:工作方式设置位。定时/计数器有 四种工作方式

控制寄存器 TCON

TCON 的 低 4 位 用于 控制外部中断,TCON 的 高 4 位 用于 控制定时/计数器的启动和中断申请

  • TF1(TCON.7):T1 溢出中断请求标志位。T1 计数溢出时由硬件自动置 TF1为 1。CPU 响应中断后 TF1 由硬件自动清 0。T1 工作时,CPU 可随时查询 TF1 的状态。所以,TF1 可用作查询测试的标志。TF1 也可以用软件置 1 或清 0,同硬件置 1 或清 0 的效果一样。
  • TR1(TCON.6):T1 运行控制位。TR1 置 1 时,T1 开始工作;TR1 置 0 时,T1 停止工作。TR1 由软件置 1 或清 0。所以,用软件可控制定时/计数器的启动与停止。
  • TF0(TCON.5):T0 溢出中断请求标志位,其功能与 TF1 类同。
  • TR0(TCON.4):T0 运行控制位,其功能与 TR1 类同。

单片机定时/计数器的工作方式

方式 0

方式 0 为 13 位计数, 由 TL0 的低 5 位(高 3 位未用)和 TH0 的 8 位组成。TL0 的低 5 位溢出时向 TH0 进位,TH0 溢出时,置位 TCON 中的 TF0 标志,向 CPU 发出中断请求。其结构图如下所示:

当 GATE = 0 时,经过 "非门"变成 1,然后再到 “或门”(只要一个为真,都为真),这时候不管 INTO 引脚是否为真,然后来到 “与门”,这时候取决于 TR0,当 TR0 为1(真) 开关才会闭合工作。计数初值与计数个数的关系为:

X=213NX=2^{13}-N

方式1

方式 1 的计数位数是 16 位,由 TL0 作为低 8 位,TH0 作为高 8 位,组成了16 位加 1 计数器。其结构图如下所示:

原理跟方式 0 差不多唯一不同的就是计数方式不同, 计数初值与计数个数的关系为:

X=216NX=2^{16}-N

方式 2

方式 2 为 自动重装初值的 8 位计数方式。工作方式 2 特别适合于用作较
精确的脉冲信号发生器。其结构图如下所示:

计数初值与计数个数的关系为:

X=28NX=2^8-N

方式 3

方式 3 只适用于定时/计数器 T0, 定时器 T1 处于方式 3 时相当于 TR1=0,停止计数。工作方式 3 将 T0 分成为 两个独立的 8 位计数器 TL0 和 TH0。其结构如下所示:

小结

这几种工作方式中应用较多的是方式 1 和方式 2。 定时器中通常使用定时器 方式 1,串口通信中通常使用方式 2

定时器配置(顺序可调换)

  • ①对 TMOD 赋值,以确定 T0 和 T1 的工作方式,如果使用定时器 0 即对 T0 配置,如果使用定时器 1 即对 T1 配置。
  • ②根据所要定时的时间计算初值,并将其写入 TH0、TL0 或 TH1、TL1。
  • ③如果使用中断,则对 EA 赋值,开放定时器中断。
  • ④使 TR0 或 TR1 置位,启动定时/计数器定时或计数。

如何计算定时/计数器初值?

12MHz晶振情况下,1个机器周期=1us,假如需要定时1ms则

1ms/1us=10001ms/1us=1000\text{次}

溢出是65536,则用

655361000=64536=FC18H65536-1000=64536=FC18H

所以初值为 THx=0XFC,TLx=0X18 或者使用小工具进行换算不用手动算。

注意

这种只能定时时间小的如果是定时500ms则需要另找办法(可以多设置一个变量来记录定时次数)

这里以定时器 0 为例介绍配置定时器工作方式 1、设定 1ms 初值,开启定时
器计数功能以及总中断,如下:

void time0_init(void)
{
TMOD|=0X01;//选择为定时器 0 模式,工作方式 1(为了不干扰T1定时器所以用|);如果是定时器1则把0x01改成0x10
TH0=0XFC; //给定时器赋初值,定时 1ms;如果是定时器1则把TH0改成TL0
TL0=0X18;//如果是定时器1则把TH1改成TL1
ET0=1;//打开定时器 0 中断允许
EA=1;//打开总中断
TR0=1;//打开定时器

代码:

/**********************************************************************************
****
实验名称:定时器 0 实验
接线说明:
实验现象:下载程序后,当按下 D1 指示灯间隔 1s 闪烁
注意事项:
***********************************************************************************
****/
# include "reg52.h"

typedef unsigned char u8;
typedef unsigned int u16;

sbit LED1=P2^0; 
void delay_ms(u16 ms)
{
	u16 i,j;
	for(i=ms;i>0;i--)
	{
		for(j=110;j>0;j--);
	}
}

void time0_init(void)
{
	TMOD|=0x01;//选择为定时器0模式,工作方式1
	TH0=0xFC;//给定时器赋初值,定时1ms
	TL0=0x63;
	ET0=1;//打开定时器0中断允许
	EA=1;//打开总中断
	TR0=1;//打开定时器
}

void main()
{
	time0_init();
	while(1)
	{
		
	}
}
void time0() interrupt 1//定时器0中断函数
{
	static u16 i;//定义静态变量i
	TH0=0xFC;//给定时器赋初值,定时1ms
	TL0=0x63;
	i++;
	if(i==1000)//1000ms=1s
	{
		i=0;//溢出然后重新赋0
		LED1=!LED1;
	}
}

实验现象