中断概念
关键词:
中断源:(中断嵌套,中断优先级)
51有5个,51有6个(多一个T2定时器/计数器2);
默认优先级(由高到低):INT0——外部中断0,P3.2口,低电平或下降沿引起(序号:0;入口地址:0003H);
INT1——外部中断1,P3.3口,低电平或下降沿引起(序号:1;入口地址:000BH);
T0——定时器/计数器0中断,由T0计数器计满回零引起(序号:2;入口地址:0013H);
T1——定时器/计数器1中断,由T1计数器计满回零引起(序号:3;入口地址:001BH);
T2——定时器/计数器2中断,由T2计数器计满回零引起(序号:4;入口地址:0023H);
TI/IR——串行口中断,串行端口完成一帧字符发送/接收后引起(序号:5;入口地址:002BH);
中断允许寄存器IE:
设定各个中断源的打开和关闭,是特殊功能寄存器,字节地址:A8H,位地址:A8H~AFH,可进行位寻址(即可对寄存器的每一位进行单独操作,复位时,IE全被清零。
EA:全局中断允许位;
**–**:无效位;
ET2:定时器/计数器2中断允许位(为1时打开,为0时关闭);
ES:串行口中断允许位;
ET1:定时器/计数器1中断允许位;
EX1:外部中断1中断允许位;
ET0:定时器/计数器0中断允许位;
EX0:外部中断0中断允许位。
中断优先级寄存器IP:
特殊功能寄存器,B8H~BFH。可位寻址,复位时,全清零
**–**:无效位;
PS:串行口中断优先级控制位(为1定义为高优先级中断);
PT1:定时器/计数器1中断优先级控制位;
PX1:外部中断1中断优先级控制位;
PT0:定时器/计数器0中断优先级控制位;
PX0:外部中断0中断优先级控制位;
按优先级可形成中断嵌套。
单片机的定时器中断
定时器/计数器实质是加1计数器(16位),由高8位和低8位两个寄存器组成:TMOD(定时器/计数器工作方式寄存器)
TCON(控制寄存器,控制T0、T1的启动停止及设置溢出标志)
加1计数器输入计数脉冲两个来源;由系统的时钟振荡器输出脉冲经12分频后送来;T0或T1引脚输入的外部脉冲源,每来一个脉冲计数器加1,加到全1时,在输入一个脉冲,计数器回零,且计数器的溢出使TCON寄存器中TF0或TF1置1,向CPU发送中断请求(定时器/计数器中断允许时)
定时器/计数器工作方式寄存器TMOD
字节地址:89H,不能位寻址,用来确定定时器的工作方式及功能选择,复位时全清零
TMOD高四位设置定时器1,低四位用于设置定时器0;
GATE:门控制位
GATE=0,定时器/计数器启动与停止仅受TCON寄存器中的TRX(X=0,1)来控制;
GATE=1,定时器/计数器启动与停止由TCON寄存器中TRX和外部中断引脚(INT0或INT1)上的电平状态来共同控制。
C/T:定时器模式(=0)和计数器模式(=1)选择位;
M1M0:工作方式选择位
(每个定时器/计数器都有4种工作方式)
定时器/计数器控制寄存器TCON
字节地址:88H~8FH,可位寻址,TCON寄存器用来控制定时器的启、停,标志定时器溢出和中断情况。复位时全清零。
TF1、TR1、TF0和TR0位用于定时器/计数器;IE1、IT1、IE0和IT0位用于外部中断。
TF1:定时器1溢出标志(当定时器1溢出时,由硬件使TF1置1,并且申请中断。进入中断服务程序后,由硬件自动清0。)(如果使用定时器的中断,则该位完全不用人为去操作,但是如果用软件查询方式,当查询到该位置1后,就需要用软件清0。)
TR1:定时器1运行控制位。(由软件清0关闭定时器1.当GATE=1,且INT1为高电平时,TR1置1启动定时器1;当GATE=0 时,TR1置1启动定时器1。
TF0:定时器0溢出标志,功能及操作方法同TF1。
TR0:定时器0运行控制位,同TR1。
IE1:外部中断1请求标志。
。。。。。。。。。
**计算定时器的初值问题 **
默认TH0和TL0为0,当用定时器的方式1时,机器周期为Tcy(一个机器周期为12个时钟周期),定时器产生一次中断的时间为t,则需计数的个数N=t/Tcy,装入THX和TLX中的数分别为;
THX = (65536-N)/256 TLX = (65536-N)%256
应用:
补充:中断服务程序的写法(C51)
形式:
void 函数名 () interrupt 中断号 using 工作组
{
中断服务程序内容
}
说明:中断号:中断源的序号
using 工作组:指这个中断函数使用单片机内存中4组工作寄存器中的哪一组,一般不写,会自动分配。
写单片机的定时器程序时,再开始处需对定时器及中断寄存器做初始化配置:
1、对TMOD赋值,确定T0和T1的工作方式;
2、计算初值,并将初值写入TH0、TL0或TH1、TL1;
3、中断方式时,则对IE赋值,开放中断;
4、使TR0或TR1置位,启动定时器/计数器定时或计数。
/************************************************************
9、利用定时器0工作方式1,LED 1S闪烁
*/
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int
sbit led1 = P1^0;
uchar num;
void main()
{
//定时器初始化
TMOD = 0x01; //设置定时器0为工作方式1(M1M0为01)
TH0 = (65536-45872)/256; //装初值11.0592M晶振定时50ms数为45872
TL0 = (65536-45872)%256;
EA = 1; //开总中断
ET0 = 1; //开定时器0中断
TR0 = 1; //启动定时器0
while(1); //程序停止在这里等待中断发生
}
void T0_time()interrupt 1 //中断服务程序
{
TH0 = (65536-45872)/256; //重装初值
TL0 = (65536-45872)%256;
num++; //num每加1次判断一次是否到20次
if(num==20) //如果到了20次,说明1秒时间到
{
num = 0; //然后把num清0重新再计20次
led1 = ~led1;
}
}
能在主程序中完成的功能就不在中断函数中写,中断函数中要高效简洁:
优化后:
/************************************************************
9、利用定时器0工作方式1,LED 1S闪烁
*/
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int
sbit led1 = P1^0;
uchar num;
void main()
{
//定时器初始化
TMOD = 0x01; //设置定时器0为工作方式1(M1M0为01)
TH0 = (65536-45872)/256; //装初值11.0592M晶振定时50ms数为45872
TL0 = (65536-45872)%256;
EA = 1; //开总中断
ET0 = 1; //开定时器0中断
TR0 = 1; //启动定时器0
while(1)
{
if(num==20) //如果到了20次,说明1秒时间到
{
num = 0; //然后把num清0重新再计20次
led1 = ~led1;
}
} //程序停止在这里等待中断发生
}
void T0_time()interrupt 1 //中断服务程序
{
TH0 = (65536-45872)/256; //重装初值
TL0 = (65536-45872)%256;
num++; //num每加1次判断一次是否到20次
}
/*********************************************************
10、中断(TX-1C)定时器0的方式1实现第一个发光管以200ms间隔闪烁
用定时器1的方式1实现数码管前两位59s循环计时
*/
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int
sbit dula = P2^6;
sbit wela = P2^7;
sbit led1 = P1^0;
uchar code table[]={
0x3f , 0x06 , 0x5b , 0x4f ,
0x66 , 0x6d ,0x7d , 0x07 ,
0x7f , 0x6f , 0x77 , 0x7c ,
0x39 , 0x5e , 0x79 , 0x71 };
void delayms(uint);
void display(uchar,uchar);
uchar num,num1,num2,shi,ge;
void main()
{
TMOD = 0x11; //设置定时器0和1为工作方式1(0001 0001)
TH0 = (65535-45872)/256; //装初值
TL0 = (65536-45872)%256;
TH1 = (65536-45872)/256; //装初值
TL1 = (65536-45872)%256;
EA = 1; //开总中断
ET0 = 1; //开定时器0中断
ET1 = 1; //开定时器1中断
TR0 =1; //启动定时器0
TR1 = 1; //启动定时器1
while(1)
{
display(shi,ge);
}
}
void display(uchar shi,uchar ge) //显示子函数
{
dula = 1;
P0 = table[shi]; //送段选数据
dula = 0;
P0 = 0xff; //送位选数据前关闭所有显示,防止打开位选锁存时原来的段选数据通过位选锁存器造成混乱
wela = 1;
P0 = 0xfe; //送位选数据
wela = 0;
delayms(5);
dula = 1;
P0 = table[ge];
dula = 0;
P0 =0xff;
wela = 1;
P0 = 0xfd;
wela = 0;
delayms(5);
}
void delayms(uint xms)
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
void T0_time() interrupt 1
{
TH0=(65536-45872)/256; //重装初值
TL0=(65536-45872)%256;
num1++;
if(num1==4) //4次中断=200ms
{
num1 = 0; //清0
led1 = ~led1;
}
}
void T1_time() interrupt 3
{
TH1=(65536-45872)/256; //重装初值
TL1=(65536-45872)%256;
num2++;
if(num2==20) //20次中断=1秒
{
num2=0; //清0
num++;
if(num==60) //该数用来送数码管显示,到60后归0
num=0;
shi=num/10; //把一个2位数分离后分别送数码管显示
ge=num%10; //十位和个位
}
}
/*********************************************************
10、中断(BST-M51)定时器0的方式1实现第一个发光管以200ms间隔闪烁
用定时器1的方式1实现数码管前两位59s循环计时
*/
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int
sbit wei1 = P2^4;//定义第一位数码管
sbit wei2 = P2^5;//定义第二位数码管
sbit wei3 = P2^6;//定义第三位数码管
sbit wei4 = P2^7;//定义第四位数码管
sbit led1 = P1^0;
uchar code table[]={
0x3f , 0x06 , 0x5b , 0x4f ,
0x66 , 0x6d ,0x7d , 0x07 ,
0x7f , 0x6f , 0x77 , 0x7c ,
0x39 , 0x5e , 0x79 , 0x71 };
void delayms(uint);
void display(uchar,uchar);
uchar num,num1,num2,shi,ge;
void main()
{
TMOD = 0x11; //设置定时器0和1为工作方式1(0001 0001)
TH0 = (65535-45872)/256; //装初值
TL0 = (65536-45872)%256;
TH1 = (65536-45872)/256; //装初值
TL1 = (65536-45872)%256;
EA = 1; //开总中断
ET0 = 1; //开定时器0中断
ET1 = 1; //开定时器1中断
TR0 =1; //启动定时器0
TR1 = 1; //启动定时器1
while(1)
{
display(shi,ge);
}
}
void display(uchar shi,uchar ge) //显示子函数
{
P0 = table[shi]; //送段选数据
wei1 = 1;
wei2 = 0;
wei3 = 0;
wei4 = 0;
delayms(5);
P0 = table[ge];
wei1 = 0;
wei2 = 1;
wei3 = 0;
wei4 = 0;
delayms(5);
}
void delayms(uint xms)
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
void T0_time() interrupt 1
{
TH0=(65536-45872)/256; //重装初值
TL0=(65536-45872)%256;
num1++;
if(num1==4) //4次中断=200ms
{
num1 = 0; //清0
led1 = ~led1;
}
}
void T1_time() interrupt 3
{
TH1=(65536-45872)/256; //重装初值
TL1=(65536-45872)%256;
num2++;
if(num2==20) //20次中断=1秒
{
num2=0; //清0
num++;
if(num==60) //该数用来送数码管显示,到60后归0
num=0;
shi=num/10; //把一个2位数分离后分别送数码管显示
ge=num%10; //十位和个位
}
}
注意:不能将判断发光二极管亮灭时间是否达到的语句写在主程序中。