单片机中断系统
首先要明确一个概念,定时器是单片机模块的一个资源,确确实实存在的一个模块,而中断,只是单片机的一种运行机制。
1.定时器中断的应用
*****标准51单片机中控制中断的寄存器有两个,一个是中单使能寄存器,一个是中断优先级寄存器(当然现在一些增强型的51单片机可能不止这两个)。
图1: IE——中断使能寄存器的位分配(地址0xA8,可位寻址)
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
符号 | EA | _ | ET2 | ES | ET1 | EX1 | ET0 | EX0 |
复位值 | 0 | _ | 0 | 0 | 0 | 0 | 0 | 0 |
图2: IE——中断使能寄存器的位描述
位 | 符号 | 描述 |
---|---|---|
7 | EA | 总中断使能位 |
6 | _ | _ |
5 | ET2 | 定时器2中断使能 |
4 | ES | 串口中断使能 |
3 | ET1 | 定时器1中断使能 |
2 | EX1 | 外部中断1使能 |
1 | ET0 | 定时器0中断使能 |
0 | EX0 | 外部中断0使能 |
*中断使能寄存器IE的位0~5控制6哥中断使能,而第6位没用用到,第7位是总开关。
以下是数码管动态显示程序用中断实现的代码(加入消隐处理):
#include<reg52.h>
sbit ADDR0=P1^0;
sbit ADDR1=P1^1;
sbit ADDR2=P1^2;
sbit ADDR3=P1^3;
sbit ENLED=P1^4;
unsigned char code Led Char[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
unsigned char LedBuff[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF} //数码管显示缓冲区
unsigned char i=0; //动态扫描的索引
unsinged int cnt=0; //记录T0中断次数
unsigned char flagls=0; //1秒定时标志
void main()
{
unsigned long sec=0; //记录经过的秒数
EA=1; //使能总中断
ENLED=0;
ADDR3=1; //因为需要动态改变数码管的位选,所以ADDR0~2不需要初始化了
TMOD=0x01;
TH0=0xFC; //定时1ms
TL0=0x67;
ET0=1; //使能T0中断
TR0=1; //启动T0
while(1)
{
if(flagls==1) //判断1秒定时标志
{
flagls=0; //1秒定时标志清零
sec++; //秒计数自加1
//以下代码将sec按十进制从低到高依次提取并转为数码管显示字符
LedBuff[0]=LedChar[sec%10];
LedBuff[1]=LedChar[sec/10%10];
LedBuff[2]=LedChar[sec/100%10];
LedBuff[3]=LedChar[sec/1000%10];
LedBuff[4]=LedChar[sec/10000%10];
LedBuff[5]=LedChar[sec/100000%10];
}
}
}
/*定时器0中断服务函数*/
void interruptTimer0() interrupt 1
{
TH0=0xFC;
TL0=0x67;
cnt++;
if(cnt>=1000) //中断1000次即一秒
{
cnt=0; //清零计数值以重新开始下一秒计时
flagls=1; //设置1秒定时标志为1
}
//以下代码王城数码管的动态扫描刷新
P0=0xFF; //显示消隐
switch(i)
{
case 0:ADDR2=0;ADDR1=0;ADDR0=0;i++;P0=LedBuff[0];break;
case 1:ADDR2=0;ADDR1=0;ADDR0=1;i++;P0=LedBuff[1];break;
case 2:ADDR2=0;ADDR1=1;ADDR0=0;i++;P0=LedBuff[2];break;
case 3:ADDR2=0;ADDR1=1;ADDR0=1;i++;P0=LedBuff[3];break;
case 4:ADDR2=1;ADDR1=0;ADDR0=0;i++;P0=LedBuff[4];break;
case 5:ADDR2=1;ADDR1=0;ADDR0=1;i=0;P0=LedBuff[5];break;
drfault:break;
}
}
*****代码中有两个函数,第一个是主函数,第二个是定义的中断函数。
中断查询序列
中断函数编号 | 中断名称 | 中断标志位 | 中断使能位 | 中断向量地址 | 默认优先级 |
---|---|---|---|---|---|
0 | 外部中断0 | IE0 | EX0 | 0x0003 | 1(最高) |
1 | T0中断 | TF0 | ET0 | 0x000B | 2 |
2 | 外部中断1 | IE1 | EX1 | 0x0013 | 3 |
3 | T1中断 | TF1 | ET1 | 0x001B | 4 |
4 | UART中断 | TI/RI | ES | 0x0023 | 5 |
5 | T2中断 | TF2/EXF2 | ET2 | 0x002B | 6 |
程序中就用到了第二行的中断使能位ET0,当ET0置1,并且它的中断标志位TF0变为1时,就是触发T0中断,这时候就会执行中断函数,单片机认出中断函数的编号1(如程序中interrupt 1),这样就找到了中断函数,然后系统开始调用中断函数,中断函数执行完成后程序又会跑到原来主函数中被中断的语句继续执行下面的程序,也就是回到主函数的while(1)函数中继续循环执行。这样就保证了动态显示间隔时固定的1ms,不会因为程序执行时间不一致的原因导致数码管显示的抖动了。
3.中断的优先级
IP——中断优先级积存的的位分配(地址0xB8,可位寻址)
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
符号 | _ | _ | PT2 | PS | PT1 | PX1 | PT0 | PX0 |
复位值 | _ | _ | 0 | 0 | 0 | 0 | 0 | 0 |
IP——中断优先级的寄存器的位描述
位 | 符号 | 描述 |
---|---|---|
7 | _ | 保留 |
6 | _ | 保留 |
5 | PT2 | 定时器2中断优先级控制位 |
4 | PS | 串口中断优先级控制位 |
3 | PT1 | 定时器1中断优先级控制位 |
2 | PX1 | 外部中断1中断优先级控制位 |
1 | PT0 | 定时器0中断优先级控制位 |
0 | PX0 | 外部中断0中断优先级控制位 |
*中断优先级有两种:抢占优先级,固有优先级。
*IP这个寄存器的每一位,表示对应终端的抢占优先级,每一位的复位值都是0,当把某一位的值设置为1时,这意味的优先级就比其他位高。
*抢占优先级的概念:优先级高的中断可以打断优先级低的中断的执行,从而形成嵌套。即处理完高优先级后再返回处理低优先级的中断。
*固有优先级不具有抢占的特性,即使在低优先级中断执行过程中又发生了高优先级的中断,那么高优先级的中断也只能等到低优先级的中断执行完后才能得到响应。