这个内容总的来说就是中断,根据触发来源是内部还是外部,分为外部中断与定时器中断,(之前未区分定时器和计数器,而定时器中断的原理就是加一计数器,计数器是定时器的基础,后面只会介绍定时器)
目录
1.IE(interrupt enable):中断允许寄存器
2.TCON(Timer Control Register):定时器控制寄存器
3.TMOD(Timer Mode Register):定时器工作方式寄存器
中断
1.中断介绍
对于单片机来讲,在程序的执行过程中,由于某种外界的原因,必须终止当前执行的程
序,而去执行相应的处理程序,待处理结束后,再回来继续执行被终止的程序,这个过程叫
中断。
中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的,中断功能的存在,很大程度上提高了单片机处理外部或内部事件的能力。它也是单片机最重要的功能之一,是我们学习单片机必须要掌握的。中断的出现代表我们可以完成许多事情,排列一些程序发生的顺序。
2.中断函数的分类
每一个中断都有中断配置函数和终端服务函数;
中断配置函数:就像其他机器一样如果你不叫它,它不会响应你的,所以我们需要去启动它。如何启动?这里我们通过配置寄存器就在启动它。外部中断和定时器中断会有不一样,但是基本上都是差不多的,只是控制他们的寄存器不同而已。
void exti0_init(void)
{
IT0=1;//跳变沿触发方式(下降沿)
EX0=1;//打开INT0的中断允许
EA=1;//打开总中断
}
中断服务函数:就是你启动它要干嘛,你不能无缘无故启动它。你可以操控它,做一些定时的任务。
void 函数名 (void)interrupt <中断号>(using 工作组)
{
中断服务内容
}
3.中断的分类
根据触发来源是内部还是外部,分为外部中断与定时器中断。
51单片机的 6 个中断源我们简述如下:
INT0—外部中断 0,引入端口: P3.2,触发方式:低电平、下降沿。
INT1—外部中断 1,引入端口: P3.3,触发方式:低电平、下降沿。
T0—定时器/计数器 0 中断,触发方式: TO 计数器记满归零。
T1—定时器/计数器 1 中断,触发方式: T1 计数器记满归零。
T2—定时器/计数器 2 中断,触发方式: T2 计数器记满归零。
TI/RI—串口中断,触发方式:串口完成一帧字符发送或接收完。
我们之前介绍过按键,在51单片机的开发板,外部中断源就设置在P3.2和P3.3上,按键也在P3.2和P3.3上,所以我们在外部也能控制中断实现其他功能。但是我们很少在外部触发中断,我们用这概念就行。
定时器中断:单片机的定时器可以理解成一个杯子,假如一个杯子装满需要花费十秒的时间,但是我只需要5秒钟的定时,那么我们就可以先给已经有半杯水的杯子加水加满,那么我们就实现5秒钟的定时。时间一到我们就可以中断,就算你在睡觉,你也要给我起来完成中断(可以重新拿半杯水继续定时),所以这个叫做溢出中断。假如我们需要20秒的定时,那我们可以准备两个空杯子。这是不是很像一款游戏。
再比如说,定时器就像一个状态检测机,假如我们用热水壶烧水,需要我们时时刻刻的检测温度,我们就可以用定时器每隔一段时间检测一次,他就会在规定的时间间隔不断地测量温度。
官方点的话说:定时器其实就是单片机内部的硬件资源,用特殊的总线与 CPU 相连,实现着计数功能。
4.优先级与中断号
51单片机有 6 个中断源,具有二个中断优先级,可实现二级中断服务程序的嵌套。每个中断源均可软件编程为高优先级或低优先级中断,允许或禁止向 CPU 请求中断。例如你现在正接着普通朋友的电话,这时你女朋友来电话了,之后你会向你普通朋友说,我们就聊到这儿吧,之后你会去接你女朋友的电话,在通话中,你的 BOSS 又来电话了,这时你就会考虑那个的电话重要,这个你考虑重要的过程就是所谓的—中断优先级,那若单片机同时有两个中断产生,单片机又是怎么执行的呢?着就取决于单片机内部的一个特殊功能寄存器(中断优先级寄存器)的设置,通过设置它,就相当于告诉单片机那个优先级高,那个优先级低,若不操作,就是按单片机默认的设置来执行(单片机自己有一套默认的优先级)。见下图
中断号每一个中断会有一个中断号,而且在写中断服务函数的时候是必须要写的。
补充知识:
CPU 时序的有关知识:
(1)振荡周期
(2)状态周期:一个状态周期=两个振荡周期
(3)机器周期:一个机械周期=六个状态周期
(4)指令周期:一个指令周期=1~4个振荡周期
例如:外接晶振为 12MHz 时,51 单片机相关周期的具体值为: 振荡周期=1/12us; 状态周期=1/6us; 机器周期=1us; 指令周期=1~4us;
寄存器
1.寄存器介绍
寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。通俗点讲,寄存器就是存放着工具的工具箱,工具箱里的工具只能用于一个目的,不同的工具箱是有不同的作用的,完成一个工程(中断)也会需要用到各种工具。
寄存器有八位的(这里指的是bit位),32位的等等,51单片机经常用到的是8位的寄存器。每个位的状态只有0与1,总共八位就构成了一个八位的二进制数,对寄存器赋值就是在给寄存器里的八位赋值。还有一些是系统为了标注过程状态而有的系统配置位(系统自己配置),比如我完成了一个工程里的一个步骤,然后你就要把某位变成1,来证明自己完成,然后别人一检查这个位的状态,发现是1,就知道你没有摸鱼。
赋值方式:
1.对寄存器的名字赋值:TCON=0x00,或TMOD=0000 0000,我们通常写成十六进制,但是你想写成其他进制也是可以的。
2.对已声明的位直接赋值(是否声明要看头文件),官方点叫做位寻址(单片机中能被 8 整除的地址都可以位寻址,后面继续介绍)。IE0=1,IT0=0等等,
这个是REG51.h的部分
头文件只能提示你这个叫什么名字,而我们要做的就是记住寄存器的功能。下面介绍几种常见的中断寄存器。
2.常见中断寄存器
1.IE(interrupt enable):中断允许寄存器
中断允许寄存器就是控制各个中断是开是关,要使用哪个中断,就必须将其对应
位置 1,也即允许该中断。字节地址为: A8H,位地址分别是: AFH~A8H(由高到低),由于该字节地址(A8)能被 8 整除(单片机中能被 8 整除的地址都可以位寻址),因而该地址可以位寻址,即可对该寄存器的每一位进行单独操作。
EA:老大哥总中断,没有我的命令,你怎么配置都没有用。EA=1,总中断开启;EA=0,总中断关闭。
ET2:定时/计数器 T2 的溢出中断允许位。
ET2 = 1,允许 T2 中断; ET2 = 0,禁止 T2 中断。
ES:串行口中断允许位。(串行中断也属于中断,但是没有归在这一类就是怕你们弄混,所以后面介绍。)
ES = 1,允许串行口中断; ES = 0,禁止串行口中断。
ET1:定时/计数器 T1 的溢出中断允许位。
ET1 = 1,允许 T1 中断; ET1 = 0,禁止 T1 中断。
EX1:外部中断 1 允许位。
EX1 = 1,允许外部中断 1 中断; EX1 = 0,禁止外部中断 1 中断。
ET0:定时/计数器 T0 的溢出中断允许位。
可以结合下图理解:
2.TCON(Timer Control Register):定时器控制寄存器
TCON寄存器就是配置定时器中断的时间配置和外部中断的触发方式等等,字节地址为88H,位地址由高到低8FH~88H,由于该字节地址(A8)能被 8 整除(单片机中能被 8 整除的地址都可以位寻址),因而该地址可以位寻址,即可对该寄存器的每一位进行单独操作。
前四位TF1/TR1/TF0/TR0是与定时器有关的,后四位IE1/IT1/IE0/IT0与外部中断有关。
TF1 :定时/计数器 T1 溢出标志位。 T1 被允许计数以后,从初值(后面会有一个寄存器来配
置初值)开始加 1 计数。当最高位产生溢出时由系统置“1”,此时向 CPU 发出请求中
断,一直到 CPU 响应中断时,才由系统清“0”,如果用中断服务函数来写中断,该位完
全不用理睬;相反,若用编程查询方式来判断,则一定要编程清“0”。
TR1:定时器 1 运行控制位。该位有两种条件:
(a) 当 GATE(TMOD.7) = 0 时, TR1 = 1,就允许 T1 开始计数, TR1 = 0,禁止T1计数;
(b) 当 GATE(TMOD.7) = 1 时, TR1 = 1 且外部中断引脚 INT1 为高电平时,才允许T1计数。
TF0/TR0 同上,只是用来设置 T0。-------------------------------------------------------------------------------------------------------------------------
IE1: 外部中断 1 请求源(INT1/P3.3)标志。可以不用理睬
外部中断向 CPU 请求中断。当 CPU 响应(也就是进入外部中断服务函数)之后
由系统自动清“0”。外部中断以哪种方式申请由下面 IT1 决定。
IT1:外部中断 1 触发方式控制位。
IT1 = 1,外部中断 1(INT1)端口由“1”到“0”的下降沿,置位中断请求标志位 IE1;
IT1 = 0,外部中断 1(INT1)端口为低电平为“0”时,置位中断请求标志位 IE1。
IE0/IT0 同上,只是用来设置外部中断 0(INTO)。
3.TMOD(Timer Mode Register):定时器工作方式寄存器
TMOD寄存器就是对定时器的工作方式进行配置,其字节地址为: 89H,该寄存器不能位寻址。
前四位是控制定时器1的工作方式,后四位是控制定时器0的工作方式。
GATE:门控位
GATE = 0,定时/计数器的启动和禁止仅有 TRx(x = 0/1)决定。
GATE = 1,定时/计数器的启动和禁止由 TRx(x = 0/1)和外部中断引脚(INT0/INT1)上的电平(必须是高电平)决定共同决定。
C/ T: 计数器模式还是定时器模式选择位(通常为定时器模式)
C/ T = 1,设置为计数器模式;
C/ T = 0,设置为定时器模式。M1M0 — 工作方式选择位(后面在介绍定时器工作方式)
4.IP寄存器:中断优先级寄存器
前面说过 51 单片机具有两个中断优先级,即高级优先级和低级优先级,可以实现两级
中断嵌套。中断优先级在特殊功能寄存器中,可以通过设置实现各个中断属于中断的哪一级,
该寄存器字节地址为: B8H,也能位寻址,即可对每一位单独操作。
PS:串行口中断优先级控制位。
PS = 1,串行口中断为高优先级中断;
PS = 0,串行口中断为低优先级中断。-------------------------------------------------------------------------------------------------------------------------
PT1:定时/计数器 T1 中断优先级控制位。
PT1 = 1, T1 中断定义为高优先级中断;
PT1 = 0, T1 中断定义为低优先级中断。-------------------------------------------------------------------------------------------------------------------------
PX1:外部中断 1 中断优先级控制位。
PX1 = 1,外部中断 1 中断定义为高优先级中断;
PX1 = 0,外部中断 1 中断定义为低优先级中断。-------------------------------------------------------------------------------------------------------------------------
PT0:定时/计数器 T0 的溢出中断允许位。
PT0 = 1, T0 中断定义为高优先级中断;
PT0 = 0, T0 中断定义为低优先级中断。-------------------------------------------------------------------------------------------------------------------------
PX0:外部中断 1 中断优先级控制位。
PX0= 1,外部中断 1 中断定义为高优先级中断;
PX0 = 0,外部中断 1 中断定义为低优先级中断。
5.TLx/THx(x=0,1):定时器初值设置寄存器
TLx/THx(x=0,1)都是八位的寄存器,就是用来计数时的初值,其中TL是低四位,TH是高四位。(具体的见下面初值的计算介绍)
外部中断
外部中断,顾名思义就是当外面有一些激励的时候,会触发中断去处理一些事情,一般外部中断外面会链接一写按键来触发中断,或者还可以连接一些其他外部激励(NE555的脉冲),合理运用这两个外部中断,就可以减少许多代码,实现一些特殊的功能。
两个函数(外部中断配置函数与外部中断服务函数)
1.外部中断配置函数
第一步:中断源有中断请求;
第二步:中断源的中断允许位为 1;
第三步:CPU 开中断(即 EA=1)。
void exti0_init(void) //外部中断0配置函数
{
IT0=1;//跳变沿触发方式(下降沿)
EX0=1;//打开INT0的中断允许
EA=1;//打开总中断
}
2.外部中断服务函数
第一步:中断函数无返回值,所以前面为 void。
第二步:中断函数命名随便,但一定不能是 C 语言的关键字,如 if、 case 等。
第三步:中断函数不带任何参数,所以小括号内写了 void。
第四步:中断函数的关键词 interrupt,一定要写且要正确无误。
第五步:后面的 using 工作组(),通常不写,具体依个人习惯。
void 函数名 (void)interrupt <中断号>(using 工作组)
{
中断服务内容
}
3.软件编程
实验现象:下载程序后,当按下K3键可控制D1指示灯亮灭
#include "reg51.h"
typedef unsigned int u16; //对系统默认数据类型进行重定义
typedef unsigned char u8;
//当按下K3键可控制D1指示灯亮灭
//定义LED1管脚
sbit LED1=P2^0;
//定义独立按键K3控制脚
sbit KEY3=P3^2;
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void delay_ms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}
void exti0_init(void) //外部中断0配置函数
{
IT0=1;//跳变沿触发方式(下降沿)
EX0=1;//打开INT0的中断允许
EA=1;//打开总中断
}
void main()
{
exti0_init();//外部中断0配置
while(1)
{
}
}
void exti0() interrupt 0 //外部中断0中断函数
{
delay_10us(1000);//消斗
if(KEY3==0)//再次判断K3键是否按下
LED1=!LED1;//LED1状态翻转
}
定时器中断
1.方式一
结构分析
主要以方式 1 为主来讲述定时器,只要方式 1 掌握了,其它的方式自然而然也就掌握了,简单说说单片机定时/计数器的结构。
OSC 框表示晶振频率,一个机器周期等于 12 个时钟周期,因此那个 d 就是 12 了。下面 GATE 右边的那个门是非门,再右侧是一个或门,再往右是一个与门电路,关于这些读者可参照前面的内容, 现对其分析如下:
(1) TR0 和下面或门电路的结果要进行与运算,那 TR0 如果是 0 的话,与运算完了肯
定是 0,那么要让定时器工作, TR0 必须为 1;
(2)与门结果要想是 1,那或门出来的信号必须也得是 1。在 GATE 位为 1 的情况下,
经过一个非门变成 0,或门电路结果要想是 1 的话,那 INT0 即 P3.2 引脚必须是 1 的情况下,
定时器才会工作,而 INT0 引脚是 0 的情况下,定时器不工作,这就是 GATE 位的作用。
(3)当 GATE 位为 0 的时候,经过一个非门变成 1,不管 INT0 引脚是什么电平,经过
或门电路后则肯定是 1,定时就开始工作了。
(4)要想让定时器工作,就是计数器加 1,从图上看有两种方式,第一种方式时那个
开关打到上边的箭头,就是 C/T = 0 的时候,一个机器周期 TL 就会加 1 一次;当开关打到
下边的箭头,即 C/T = 1 的时候, T0 引脚即 P3.4 引脚(引脚关系如图 1-1)来一个脉冲,
TL 就会加 1 一次,这也就是计数器的功能。
(5)无论是在 OSC(定时器)的作用下,还是在 Tn 脚(计数器)的作用下,当 TL0/1、
TH0/1 都记满以后,就会有益处,从而在这个溢出上来做文章(是执行中断、还是判断溢出
标志位)。
其他的工作方式也是差不多的。
初值计算
以实验板上搭载的是 11.0592MHz 的晶振,且单片机为 12T 的 STC89C52为例,机器周期 T = 1 / F = 1 /(11.0592 / 12) = 12 / 11.0592 us。现若要定时 10ms(10 000us),那需要记多少次数了?设为 x 次,则 x *(12 / 11.0592) = 10 000,则 x = 9216,这个数是要在初值基础上累加的值,那初值如何算,前面说的够多了吧,初始值 = 65536 – 9216 = 56320(0xDC00),这样
TH0 = 0xDC, TL0 = 0x00,或者 TH0 =(65536 - 9216) / 256, TL0 =(65536 - 9216) % 256(很多时候会写成这个形式),以后读者要注意这两种版本的写法。 所以有初值寄存器里装各种版本的初值,则会有各种版本的定时基准(范围 0~71ms)。
再比如说搭载的是 11.0592MHz 的晶振,且单片机为 12T 的 STC89C52,机器周期 T = 1 / F = 1 /(12/ 12) = 1us。现若要定时 10ms(10 000us),那需要记多少次数了?设为 x 次,则 x *(12 / 12) = 10 000,则 x = 10 000, TH0 =(65536 - 10 000) / 256, TL0 =(65536 - 10 000) % 256(很多时候会写成这个形式)
其他时间就是短时间相应的倍数。
检测方法
理解并区别下面的两种方法:
标志位检测法
第一步:配置定时器的工作模式(对 TMOD 赋于相应值)
第二步:装定时器的初值,即赋值 TH0 和 TL0
第三步:通过设置 TCON 来启动定时器,让其开始计数
第四步:判断 TCON 寄存器的 TFx(x = 0/1)位,检测定时器的溢出情况
中断检测法
第一步:配置定时器的工作模式(对 TMOD 赋于相应值)
第二步:装定时器的初值,即赋值 TH0 和 TL0
第三步:设置中断允许寄存器(IE)
第四步:置位TR0或TR1,启动定时器计数,到时间会启动中断。
2.方式二:
结构分析
这是一个八位自动重装的定时器的八位定时器,它定时的范围非常小,所以我们一般不用来进行定时,而是用来产生的小的定时。一般会设置TL0=TH1,当TL0计时满了以后,TH0的值就会赋给TL0,TH0继续计时,而TH0的值不会变。就这样一直循环赋值,计时。
初值计算
因为这里只有八位计数位,所以不再是65535,而是255。后面串口通信的时候会用到,用来产生脉冲。然后具体的方法同上。
检测方法
检测方法同上。
3.软件编程
实验现象:下载程序后,D1指示灯间隔1s闪烁
#include "reg52.h"
typedef unsigned int u16; //对系统默认数据类型进行重定义
typedef unsigned char u8;
//定义LED1管脚
sbit LED1=P2^0;
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void time0_init(void)
{
TMOD|=0X01;//选择为定时器0模式,工作方式1
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
void main()
{
time0_init();//定时器0中断配置
while(1)
{
}
}
void time0() interrupt 1 //定时器0中断函数
{
static u16 i;//定义静态变量i
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
i++;
if(i==1000)
{
i=0;
LED1=!LED1;
}
}