定时器是单片机模块的一个资源,确确实实存在的一个模块,而中断,是单片机的一种运行机制。
标准51单片机中控制中断的寄存器有两个:一个是中断使能寄存器;另一个是中断优先级寄存器。
🟤中断使能寄存器
IE-中断使能寄存器的位分配(地址0xA8、可位寻址)
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
符号 | EA | - | ET2 | ES | ET1 | EX1 | ET0 | EX0 |
复位值 | 0 | - | 0 | 0 | 0 | 0 | 0 | 0 |
IE-中断使能寄存器的位描述
位 | 符号 | 描述 |
---|---|---|
7 | EA | 总中断使能位,相当于总开关 |
6 | - | - |
5 | ET2 | 定时器2中断使能 |
4 | ES | 串口中断使能 |
3 | ET1 | 定时器1中断使能 |
2 | EX1 | 外部中断1使能 |
1 | ET0 | 定时器0中断使能 |
0 | EX0 | 外部中断0使能 |
总开关相当于家里或者学生宿舍里的那个电源总闸门,而0~5位这6个位相当于每个分开关。也就是说,只要用到中断,就要写EA=1这一句,打开中断总开关,然后用到哪个分中断,再打开相对应的控制位就可以了。
🔸中断服务函数
书写格式:例如
void InterruptTimer0() interrupt 1{}
,首先中断函数前边void表示函数返回空,即中断函数不返回任何值,函数名是InterruptTimer0(),这个函数名在符合函数命名规则的前提下可以随便取,而后是interrupt
这个关键字,这是中断特有的关键字,紧跟在其后面的数字1
为中断函数编号,是根据中断向量得出的,其计算方法是x*8+3=向量地址。同时也解决了因为程序执行时间不一致而导致数码管显示抖动的视觉效果。
中断查询序列
中断函数编号 | 中断名称 | 中断标志位 | 中断使能位 | 中断向量地址 | 默认优先级 |
---|---|---|---|---|---|
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中断 | T1/R1 | ES | 0x0023 | 5 |
5 | T2中断 | TF2/EXF2 | ET2 | 0x002B | 6 |
🟣中断优先寄存器
中断优先级有两种:一种是
抢占优先级
,另一种是固有优先级
。IP这个寄存器的每一位,表示对应中断的抢占优先级。
当进入低优先级中断中执行时,如又发生了高优先级的中断,则立刻进入高优先级中断执行,处理完高优先级中断后,再返回处理低优先级中断,这个过程就称为
中断嵌套
,也称为抢占
。固有优先级又称非抢占优先级,其作用为多个中断同时存在时的仲裁。
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中断优先级控制位 |
#include "reg52.h"
sbit LSA=P2^3;
sbit LSB=P2^4;
sbit LSC=P2^5;
unsigned char code LedChar[]={ //数码管显示字符转换表
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
};
unsigned char LedBuff[8]={ //数码管显示缓冲区,初值0x00确保启动时都不亮
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
unsigned char i = 0; //动态扫描的索引
unsigned int cnt = 0; //记录T0中断次数
unsigned char flag1s=0; //1秒定时标志
void main()
{
unsigned long sec = 0; //记录经过的秒数
EA = 1; //使能总中断
TMOD = 0x01; //设置T0为模式1
TH0 = 0xFC; //为T0赋初值0xFC67,定时1ms
TL0 = 0x67;
ET0 = 1; //使能T0中断
TR0 = 1; //启动T0
while(1)
{
if(flag1s == 1) //判断T0是否溢出
{
flag1s = 0; //T0溢出后,清0中断标志
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];
LedBuff[6] = LedChar[sec/1000000%10];
LedBuff[7] = LedChar[sec/10000000%10];
}
}
}
/* 定时器0中断服务函数 */
void InterruptTimer0() interrupt 1
{
TH0 = 0xFC; //重新加载初值
TL0 = 0x67;
cnt++; //中断次数计数值加1
if(cnt >= 1000) //中断1000次即1秒
{
cnt = 0; //清0计数值以重新开始下1秒计时
flag1s=1; //设置1秒定时标志为1
}
//以下代码完成数码管动态扫描刷新
P0 = 0x00; //显示消隐
switch(i)
{
case 0:LSA=1;LSB=1;LSC=1;i++;P0=LedBuff[0];break;
case 1:LSA=0;LSB=1;LSC=1;i++;P0=LedBuff[1];break;
case 2:LSA=1;LSB=0;LSC=1;i++;P0=LedBuff[2];break;
case 3:LSA=0;LSB=0;LSC=1;i++;P0=LedBuff[3];break;
case 4:LSA=1;LSB=1;LSC=0;i++;P0=LedBuff[4];break;
case 5:LSA=0;LSB=1;LSC=0;i++;P0=LedBuff[5];break;
case 6:LSA=1;LSB=0;LSC=0;i++;P0=LedBuff[6];break;
case 7:LSA=0;LSB=0;LSC=0;i=0;P0=LedBuff[7];break;
default:break;
}
}
#include "reg52.h"
sbit LSA=P2^3;
sbit LSB=P2^4;
sbit LSC=P2^5;
unsigned char code LedChar[]={ //数码管显示字符转换表
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
};
unsigned char LedBuff[8]={ //数码管显示缓冲区,初值0x00确保启动时都不亮
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
unsigned char water_led1[8]={
0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f //正向流动
};
unsigned char water_led2[8]={
0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe //反向流动
};
unsigned char flag1s=0; //1秒定时标志
void main()
{
unsigned long sec = 0; //记录经过的秒数
static unsigned char j=0;
unsigned char buf[8];
char k;
EA = 1; //使能总中断
TMOD = 0x01; //设置T0为模式1
TH0 = 0xFC; //为T0赋初值0xFC67,定时1ms
TL0 = 0x67;
ET0 = 1; //使能T0中断
TR0 = 1; //启动T0
while(1)
{
if(flag1s == 1) //判断T0是否溢出
{
flag1s = 0; //T0溢出后,清0中断标志
sec++; //秒计数自加1
//以下代码将sec按十进制位从低到高依次提取并转为数码管显示字符
buf[0]=sec%10;
buf[1]=sec/10%10;
buf[2]=sec/100%10;
buf[3]=sec/1000%10;
buf[4]=sec/10000%10;
buf[5]=sec/100000%10;
buf[6]=sec/1000000%10;
buf[7]=sec/10000000%10;
for(k=7;k>=1;k--)
{
if(buf[k]==0)
LedBuff[k]=0x00;
else
break;
}
for(;k>=0;k--)
{
LedBuff[k]=LedChar[buf[k]];
}
if(j<8)
P1=water_led1[j];
if(j>=8)
P1=water_led2[j-8];
j++;
if(j==16)
j=0;
}
}
}
/* 定时器0中断服务函数 */
void InterruptTimer0() interrupt 1
{
static unsigned char i = 0; //动态扫描的索引
static unsigned int cnt = 0; //记录T0中断次数
TH0 = 0xFC; //重新加载初值
TL0 = 0x67;
cnt++; //中断次数计数值加1
if(cnt >= 1000) //中断1000次即1秒
{
cnt = 0; //清0计数值以重新开始下1秒计时
flag1s=1; //设置1秒定时标志为1
}
//以下代码完成数码管动态扫描刷新
P0 = 0x00; //显示消隐
switch(i)
{
case 0:LSA=1;LSB=1;LSC=1;i++;P0=LedBuff[0];break;
case 1:LSA=0;LSB=1;LSC=1;i++;P0=LedBuff[1];break;
case 2:LSA=1;LSB=0;LSC=1;i++;P0=LedBuff[2];break;
case 3:LSA=0;LSB=0;LSC=1;i++;P0=LedBuff[3];break;
case 4:LSA=1;LSB=1;LSC=0;i++;P0=LedBuff[4];break;
case 5:LSA=0;LSB=1;LSC=0;i++;P0=LedBuff[5];break;
case 6:LSA=1;LSB=0;LSC=0;i++;P0=LedBuff[6];break;
case 7:LSA=0;LSB=0;LSC=0;i=0;P0=LedBuff[7];break;
default:break;
}
}