中断
对于刚上大一的小伙伴,应该和我一样第一次见到“中断”这个词。估计也困扰了许多小伙伴很久,今天以我的角度重新给大家说一下关于51单片机的中断。
(受惠于互联网,感恩于互联网)
定义
中断的意思很简单,以一个生活中的例子来说:
我在洗衣服,洗衣服可以认为是一个程序在运行中,这时候突然有人打来电话,我们肯定是去接电话,因为接电话更重要一些。接电话这个过程就可以认为是中断进行了,我们接完电话,然后就继续洗衣服,这个过程相当于进行完一次中断。
对于单片机来说,中断是指:CPU在处理某一件时间A时,发生了另一事件B,请求CPU迅速去处理(中断发生);CPU暂时停止当前的工作(中断响应),将事件B处理完后,再回到事件A被中断的地方继续处理事件A(中断返回)
部分来源于郭天祥老师的书
预备知识
单片机的几个周期
时钟周期(重要):也成为震荡周期,定义为时钟频率的倒数(就是我们单片机晶振频率的倒数,51单片机应该都是12MHz,所以对应时钟周期就是十二分之一微秒)
状态周期:时钟周期的2倍
机器周期(重要):单片机的基本操作周期,在一个操作周期内,单片机完成一项基本操作,如取指令、存储器读/写等。机器周期由12个时钟周期组成(也就是1微妙)
指令周期:指CPU执行一条指令所需的时间。指令周期一般包含1~4个机器周期组成
这边要记住机器周期,后面涉及。
大致的寄存器概念。(这个不懂的可以去上一篇文章看一下。链接如下:
如何入门51单片机
正文
其实中断很简单,只要你明白其中的几个寄存器以及它的作用。
现在我以定时器中断为例子,让大家明白中断在单片机如何使用
第一个知识点:
中断允许寄存器IE和中断优先寄存器IP
如同P2寄存器一样,都是可以放8个bit的小盒子组成的。只不过名字有一点奇怪,难免让没接触过的人难以接受。(不同的是这些小盒子可以控制的是单片机的状态)
中断允许寄存器IE:顾名思义,就是控制中断的打开与关闭。看一下每个小盒子的作用:
(这边我只以这一个为例子讲一下这样的寄存器如何使用)
这个寄存器是可以进行位寻址的(所谓位寻址,意思就是可以直接进行某一位的定义,而不用单独去定义。比如:可以直接使用EA=1),可以看到上图中从最低位D0到最高位D7总共8位,代表着不同的功能。这个寄存器控制着中断的打开与关闭。默认是全都关闭,也就是EA=0。
如果我们想让定时器1打开中断,代码如下:
EA = 1;
ET1 = 1;//或者IE = 0x88效果一样
寄存器功能就是这样的简单去使用,而不是让你去造轮子。
中断优先级寄存器IP:功能如下,用法和IE一样。
(这个功能大家可以了解一下,这里我们现在用不到)
第二个知识点:
加1计数器、定时器/计数器工作方式寄存器TMOD、定时器/计数器控制今寄存器TCON、定时器的运作流程
加1计数器:由两个寄存器组成,总共是16位这么大。
因为由两个定时器(定时器1和定时器0),所以由两个加1计数器。
作用:加1计数器的两个寄存器都满了(都为1)之后,触发中断。(后面会介绍)
两个来源:一个是我们人为控制的;一个是通过系统的时钟振荡器输出脉冲经12分频送过来的(简单来说就是一个机器周期自动加1,也就是1微妙)
定时器/计数器工作方式寄存器TMOD:就是选择定时器如何工作。
(书中写的有点复杂,这边我简单让大家理解大概内容)
GATE:一门控制位。当GATE=0,定时器/计数器启动与停止仅受TCON寄存器的TRX(X=0或1)来控制;GATE=1,定时器/计数器启动与停止由TCON寄存器中的TRX和外部中断引脚上的电平状态来共同控制。(这里我们默认为0,也就是只需要一个控制开始与结束)
C/T:选择定时器还是计数器。如果为0为定时器模式,如果为1时计数器模式
M0M1:工作方式的选择,如下图(我们选择方式1,这个理解其他的基本没问题):
定时器/计数器控制寄存器TCON:控制定时器或计数器的开始与停止的
(我简单介绍以下这几个位的作用)
TF1:定时器1的溢出标志位。也就是说加1计数器满了,TF1将会自动置1,并申请中断。进入中断后会自动清0。(不需要人为的操作)
TR1:定时器1的运行控制位。GATE=0时,我们将其置1,则定时器1开始工作,置0时关闭定时器1(人为操作),GATE=1不多介绍。
TF0:定时器0溢出标志,同定时器1
TR0:定时器0运行控制位,同定时器1
(剩下的对本次定时器中断无关紧要,有兴趣可以自己查看资料)
定时器1工作流程:
就是我们选择完定时器工作方式并且打开定时器,此时加1计数器开始在两个寄存器中经过1微妙(一个机器周期)进行加1,直到溢出从而触发中断。此时从主函数跳到中断函数中执行,中断函数执行完成后,再返回主函数继续执行主函数的内容。
加1计数器16位,如果从0一直加到溢出需要加216=65536,才能触发一次中断,也就是经过65535微秒就能从0把两个寄存器加满。
当然这个初值我们可以随便控制,我们可以让其加到6000微秒,5000微秒等。(这边大家可以用一些辅助软件去计算,然后直接复制TH1和TL1的值就好了)
配置过程
看看如何用程序来调整我们的定时器中断
1、首先我们打开总中断和定时器1中断
EA = 1;
ET1 = 1;
2、选择定时器1的工作方式1
TMOD = 0x10;
也就是GATE=0;C/T=0,M1=1,M2=0。对应上面TMOD寄存器。
3、设置加1计数器的初值(我们想计时500微妙)
TL1 = 0x66;
TH1 = 0xEA;
4、打开定时器
TR1 = 1;
5、中断函数中再装初值(因为进入中断函数后,加1计数器自动清0,我们需要再装初值)
void T1() interrupt 3
{
TL1 = 0x66;
TH1 = 0xEA;
}
简单设计
写一个简单的小实验。
我们让一个LED灯每隔500微秒亮一次。
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit LED1 = P2^0;
void mian()
{
EA = 1;
ET1 = 1;
TMOD = 0x10;
TL1 = 0x66;
TH1 = 0xEA; //初始化,让定时器500微秒溢出一次
TR1 = 1; //打开定时器
while(1); //让程序一直循环不做任何动作
}
void T1() interrupt 3
{
TL1 = 0x66;
TH1 = 0xEA; //再装初值
LED1 = ~LED1; //每经过一次中断,小灯取反一次
}
总结
本来想连着串口通信一块写的,太长了。
关于中断知道这几个寄存器的使用就行了。