一,中断系统
1.中断概念:
中断是一种硬件或软件引起的临时中止当前程序执行,转而去执行特殊事件或服务程序的机制。当发生中断时,CPU会暂停当前正在执行的程序,保存现场并跳转到中断服务程序,待中断服务程序执行完毕后再返回到原程序继续执行。
2.中断源:
51单片机中的中断源包括外部中断、定时器/计数器中断和串行口中断等。外部中断可以由外部信号触发,定时器/计数器中断可以根据定时器/计数器的计数值达到设定的阈值时触发,串行口中断则是由串行通信引发的。
3.中断优先级:
51单片机中的中断具有不同的优先级,高优先级的中断可以打断低优先级的中断执行。可以通过设置中断优先级来确定各个中断的执行顺序。
4.中断向量表:
51单片机中的中断向量表是一个存储中断服务程序入口地址的表格,当发生中断时,CPU会根据中断源的编号从中断向量表中找到对应的中断服务程序的入口地址。
5.中断处理程序:
中断处理程序是针对特定中断源编写的程序,用于处理中断事件。在51单片机中,中断处理程序一般是通过中断向量表来调用的。
6.中断控制寄存器:
51单片机中的中断控制寄存器用于控制中断的使能和禁止,可以通过设置特定的位来开启或关闭各个中断源。
各中断源开放或屏蔽,是由片内中断允许寄存器IE控制。IE字节地址为A8H,可进行位寻址。IE寄存器格式如图:
IE对中断开放和关闭实现两级控制。两级控制就是有一个总的中断开关。控制位EA (IE. 7位),当EA=0, 所有中断请求被屏蔽,CPU对任何中断请求都不接受;当EA=1时,CPU开中断,但5个中断源的中断请求是否允许,还要由IE中的低5位所对应的5个中断请求允许控制位的状态来决定。
EA—中断允许总开关控制位,EA=0/1,所有的中断请求被屏蔽/开放。
ES—串行口中断允许位,ES=0/1,禁止/允许串行口中断。
ET0/ET1—定时器/计数器T0/T1溢出中断允许位, ET0/ET1=0,禁止T0/T1溢出中断。ET0/ET1=1,允许T0/T1溢出中断。
EX0/EX1—外部中断0/1中断允许位,EX0/EX1=0,禁止外部中断0/1溢出中断。EX0/EX1=0,允许外部中断0/1溢出中断。
7.中断标志位:
51单片机中的中断标志位用于指示是否有中断发生,当某个中断源触发时,相应的中断标志位会被设置,程序可以通过检查这些标志位来判断是否有中断需要处理。
二,外部中断的应用
1,单外中断
需求:
在单片机P2口上接有8只LED.在外部中断0输入引脚(P3. 2)接一只按钮开关K1。要求将外部中断0设置为电平触发。程序启动时,P2口上的8只LED全亮。每按一次按钮开关K1,使引脚接地,产生一个低电平触发的外中断请求,在中断服务程序中,让低4位的LED与高4位的LED交替闪烁5次。然后从中断返回,控制8只LED再次全亮。
keil代码如下,
#include <REGX52.H>
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void main()
{
EA=1; //总中断允许
EX0=1; //允许外部中断0中断
IT0=1; //设置外部中断为下降沿触发方式
while(1)
{
P2=0x00;
}
}
void Int0_Routine() interrupt 0
{
unsigned char i;
EX0=0; //禁止外部中断0中断
for(i=0;i<5;i++)
{
P2=0xf0;
Delay(500);
P2=0x0f;
Delay(500);
}
EX0=1; //中断返回前,打开外部中断0中断
}
Proteus仿真:
2,两个外中断
需求:
在单片机P2口_上接有8只LED。在外部中断0输入引脚(P3.2) 接有一只按钮开关K1。在外部中断1输入引脚(P3.3) 接有一只按钮开关K2。要求K1和K2都未按下时,P2口的8只LED呈流水灯显示,仅K1 (P3.2)按下再松开时,上下各4只LED交替闪烁10次,然后再回到流水灯显示。如果按下再松开K2 (P3. 3)时,P2口的8只LED全部闪烁10次,然后再回到流水灯显示。设置两个外中断的优先级相同。
keil代码如下,
#include <REGX52.H>
void Delay(unsigned int xms) //@11.0592MHz延时函数
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void main()
{
EA=1; //总中断打开
EX0=1;
EX1=1; // 允许中断0、1中断
IT0=1;
IT1=1; //中断0、1设置下降沿中断
IP=0; //中断优先级控制寄存器低(可位寻址)
while(1)
{
P2=0xFE;//1111 1110
Delay(500);
P2=0xFD;//1111 1101
Delay(500);
P2=0xFB;//1111 1011
Delay(500);
P2=0xF7;//1111 0111
Delay(500);
P2=0xEF;//1110 1111
Delay(500);
P2=0xDF;//1101 1111
Delay(500);
P2=0xBF;//1011 1111
Delay(500);
P2=0x7F;//0111 1111
Delay(500);
}
}
void Int0_Routine() interrupt 0//外部中断0
{
unsigned char i;
for(i=0;i<10;i++)
{
P2=0xf0;
Delay(300);
P2=0x0f;
Delay(300);
}
}
void Int1_Routine() interrupt 2//外部中断1
{
unsigned char i;
for(i=0;i<10;i++)
{
P2=0xff;
Delay(300);
P2=0x00;
Delay(300);
}
}
仿真如下
3,中断嵌套
需求:
设计一中断嵌套程序:要求K1和K2都未按下时,P2口8只LED呈流水灯显示,当按一下K1时,产性一个低优先级外中断0请求(跳沿触发),进入外中断0中断服务程序,上下4只LED交替闪烁。此时按一下K2时,产生一个高优先级的外中断1请求(跳沿触发),进入外中断1中断服务程序,使8只LED全部闪烁。当显示5次后,再从外中断1返回继续执行外中断0中断服务程序,即P2口控制8只LED,上、 下4只LED交替闪烁。设置外中断0为低优先级,外中断1为高优先级。
keil代码
嵌套中断与两个外部中断不同之外在于中断优先级有高低之分,因此,只需在两个外部中断的代码的基础上,将优先级做调整即可实现:
//两个外部中断的优先级设置为同优先级
IP=0; //中断优先级控制寄存器低(可位寻址)
//中断嵌套按要求设置为外部中断1优先级高于外部中断0
PX0=0;
PX1=1; //中断1优先于中断0
三,定时器/计数器的工作原理
1,定时器/计数器结构
AT89S51定时器/计数器结构见图 7-1,定时器/计数器T0由特殊功能寄存器TH0、TL0构成,T1由特殊功能寄存器TH1、TL1构成。
2,定时器/计数器寄存器
定时器T0中断:
要求 :在 Prodeus 和普中单片机板上分别完成采用定时计数器控制 LED 灯每隔 1S 周期性亮灭的实验。要 求KEIL 仿真中的虚拟逻辑仪对 LED 管脚进行波形观察,测量真实的周期数,并与软件循环进行周期定时 的精度进行对比,看哪一种方式更加精准。
定时器初值 :设定时时间 1ms ( 即 5000 μ s) ,设 T0 计数初值为 X ,假设晶振的频率为 11.0592MHz, 则定时时间为:
1000=(2^16 - X)*12/11.0592 ==> X=64614 ,转换为十六进制为 0xFC66 ,其中 0XFC 装入 TH0 ,0X66 装入 TL0 。
相关代码:
#include <REGX52.H>
void Timer0_Init()
{
//设置定时器工作模式TMOD为方式1
//TMOD为不可位寻址,只能整体赋值,可采用&=、|=使其只改变某些位
TMOD&=0xf0;//高四位不变,低四位清零
TMOD|=0x01;//最低位置1,其余位不变 //TOMD=0x01
//定时时间=(2^16-X)*12/晶振频率
TL0=0x66;
TH0=0xfc;//定时1ms
//设置定时器控制寄存器TCON
TF0=0; //中断溢出标志位先清0,避免刚配置好就中断
TR0=1; //定时器开始工作
//中断
EA=1;//允许总中断
ET0=1;//T0的中断溢出允许位,T0=1,允许中断
}
void main()
{
Timer0_Init();
while(1)
{
}
}
void Timer0_Runtine() interrupt 1
{
unsigned int Count;//计数
TL0=0x66;
TH0=0xfc;
Count++;
if(Count>=1000)//计数累加到1000,定时器定时1000ms=1s
{
P2_0=~P2_0;//LED状态取反
Count=0;
}
}
计数器T0中断
要求 :
采用计数器中断,实现按 4 次按键开关后, P2 口的 8 只 LED 闪烁不停。
计数器初值 :由于每按 1 次按键开关,计数 1 次,按 4 次后, P2 口的 8 只 LED 闪烁不停。因此计数器初值为
2^16 - 4=65532 , 转化为十六进制后为 0XFFFC ,所以 TH0=0XFF , TL0=0XFC 。
相关编程代码如下:
#include <REGX52.H>
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void Count0_Init() //计数器
{
//设置计数器0工作模式1
TMOD&=0xf0;
TMOD|=0x05;// C/T置1为计数器模式
TL0=0xfc;
TH0=0xff;//计数4次,65536-4=65532
//设置计数器控制寄存器TCON
TF0=0;
TR0=1;
//中断
EA=1;
ET0=1;
}
void main()
{
Count0_Init();
while(1)
{
}
}
void Count0_Routine() interrupt 1
{
while(1)
{
P2=~P2;
Delay(500);
P2=~P2;
Delay(500);
}
}
四,中断优化
在上述的按键控制 LED 的实验中,中断函数采用了软件延时函数控制 LED 间隔周期,为了避免中
断服务程序执行较长耗时的代码,将外部中断程序进行优化:在中断服务程序中只改变中断模式标志 位,在主函数中根据不同的中断模式标志,选择不同的亮灯模式。
Keil代码实现:
#include <REGX52.H>
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void Init0()
{
EA=1;
EX0=1;
IT0=1;//下降沿触发
}
unsigned char Key_Interrupt_Flag=0;//中断模式标志位
void Init0_Routine() interrupt 0
{
Key_Interrupt_Flag=1;
}
void main()
{
unsigned char Mode=0;
Init0();
while(1)
{
if(Key_Interrupt_Flag)//如果中断标志位为1
{
Key_Interrupt_Flag=0;//标志位置0
Mode++;//切换亮灯模式
if(Mode>=3)
{
Mode=0;//循环切换
}
}
switch (Mode)//亮灯模式选择
{
case 0:
{P2=0xfe;}
break;
case 1:
{P2=0xf0;Delay(300);P2=0X0F;Delay(300);}
break;
case 2:
{P2=0XAA;Delay(300);P2=0x55;Delay(300);}
break;
default:
break;
}
}
}
5,总结
本次仿真实验,从多方面学习到了仿真实践,熟练掌握仿真、烧录流程,对汇编语言代码也进一步加深了理解,对51单片机的学习更进一步。