

在讲授中断这一概念时,人们总是喜欢举洗衣服烧水的例子:
话说,一天“你”独自在家,为了泡脚给自己烧上了一壶水,然后想着明天没有衣服穿了,就去阳台洗起了衣服。过了十几分钟,“你”在阳台洗着衣服,突然听到了厨房烧水壶沸腾的鸣叫,所以,“你”放下洗了一半的衣服,去关闭了煤气灶,接着又返回阳台继续洗衣服。在这个小故事中,如果我们把烧水和洗衣服分别看作两个进程,那么显然,烧水壶沸鸣对应的是中断请求,洗衣服则是正常运行的主进程,高优先级进程通过向CPU提交中断请求的方式,暂时中断主进程转而运行其他进程,利用有限的CPU资源,尽可能的做到了对效率和实时性的兼顾。
在第一天的教程中曾经提到过,我们会用三天的时间分别学习外部中断、定时器/计数器中断以及串口中断,占据全部八天课程的三分之一还多,单单从课程安排上,大家就可以体会到中断在单片机学习中的重要地位。

中断的优先级
当有多个中断同时被提交,那么CPU该选择响应哪个中断呢?这要从中断源的类型来分
单片机的中断源分为三类:
外部中断
定时器/计数器中断
串口中断
但单片机最基本的中断源却有5个:
外部中断0
定时器/计数器中断0
外部中断1
定时器/计时器中断1
串口中断
(排列即为他们的优先级)
丰富的中断源可以大大提高单片机的便捷性,有些较新的单片机已经拥有了8个中断源,但本教程中只讲单片机必备的5个中断源的使用.
在洗衣烧水的小故事中,我们已经意识到了优先级这一概念的存在,高优先级的中断将被优先执行,且可以打断低优先级的中断,反之则不能。5个中断源的优先级(或查询次序)已经被指定,在软件编程中又有着相对应的中断号(0-4),软件编程里的中断号与中断优先级匹配正确,程序才能正常进入中断(程序中会再次讲解)。





特殊功能寄存器
前面也曾讲过,对单片机的编程,基本可以说是对单片机内部寄存器的操作,五种中断所涉及到的特殊寄存器以及各个位的功能讲解如下:
IE寄存器(中断允许控制寄存器)
IE:EAXXESET1EX1ET0EX0
其中,EA为总中断允许位,ES为串口中断允许位。
ET1、ET0分别是定时器/计数器1与定时、计数器0中断允许位。
EX1、EX0为外部中断1与外部中断0中断允许位。
可以看到,IE寄存器可以控制所有中断的开关,而且支持位寻址。
TCON寄存器(定时器/计数器控制寄存器)TCON:TF1TR1TF0TR0IE1IT1IE0IT0
其中,TF1、TF0分别为定时器T1/T0溢出标志位,当定时器溢出时状态改变,无需手动设置。
TR1/TR0分别为定时器1/定时器0中断允许位
IE1/IE0分别为外部中断请求标志位,IT1/IT0分别设置两个外部中断源的触发方式(0上升沿触发,1下降沿触发)。
TCON寄存器支持位寻址。
TMOD寄存器(定时器/计数器模式控制寄存器)
TMOD:GATEC/TM1M0GATEC/TM1M0
其中,GATE为门控制位,当此位为1时,外部中断被触发与定时器中断允许位同时为1,才会开启定时器/计数器。
两个C/T位,分别是定时器/计数器1模式选择位,0为定时器模式,1为计数器模式。
M1M0,设置定时器/计数器1/0工作方式,00为13位定时/计数器模式,01为16位定时器/计数器模式,10为8位定时/计数器自重装模式(就是自动归零),11则是指将T0分为两个8位定时/计数器,T1停止计数。
要注意的是,TMOD不支持位寻址,只能对整体赋值
T0寄存器组:TH0TL0
T1寄存器组:TH1TL1
以上两种寄存器分别是定时/计数器的计数初值设定,TH是高八位,TL指低八位,这两者共同控制定时器/计数器溢出的时间(在定时/计数器中断一节会详细讲解)
从上面的总体介绍中,我们单独提取外部中断需要设置的寄存器:IE寄存器中的EA、EX,TCON中的IT1、IT0,同时需要接线P3^2、P3^3外部中断引脚
接下来,我们将会在上次流水灯的项目基础上,使用外部中断的方式让按钮控制流水灯的切换
原理图
本项目新增Button元件(按钮),原理图如下:

说明:
emm,没什么特别好说的,看图连接就行了,注意整洁美观。





keli中的代码
#include
#include
sbit k0 = P3^2;
sbit k1 = P3^3;
sbit LED = P2^0;
void IntInit()
{
EA = 1;//打开总中断
IT0 = 1;//设置触发方式为下降沿
EX0 = 1;
IT1 = 1;
EX1 = 1;
P0 = 0x01;
k0 = k1 = 1;
}
void main()
{
IntInit();
while(1);
}
void Int0() interrupt 0
{
if(k0 == 0)
{
P0=P0>>1;
}
if(P0==0x00)
{
P0 = 0x80;
}
}
void Int1() interrupt 2
{
LED = 0;
if(k1 == 0)
{
P0=P0<<1;
}
if(P0==0x00)
{
P0 = 0x01;
}
}
说明
说明:
1.头文件中包含了很多汇编会用的操作,比如这个项目中使用的左移右移运算,可以大大简化代码
2.IntInit()函数主要用来初始化外部中断,其中语句的顺序可以调换,可以依照自己的习惯或者记忆方式,自行设计安排。
3.void Int0() interrupt 0 、void Int1() interrupt 2分别对应外部中断0与外部中断1,interrupt之后跟的数字就是我们之前说的优先级标号,格式不能错,编号一定一一对应,名字各自随便修改(依照C语言命名规范),函数里面的内容就是中断后执行的程序。
实验现象

分别按下两个按钮,可以观察到LED灯向左/右接力闪烁,证明实验成功。

另外:
剩下两天基础课程的学习难度将会提升,定时器/计数器中断以及串口中断的学习,将要比今天的课程难度提升不少
不过,稳扎稳打,多做实验,你会发现也没有什么大不了。加油!

