中断系统、定时器/计数器工作原理及应用
文章目录
一、中断系统
(一)中断结构
AT89S51系列单片机提供了5个中断请求源,它们分别是:外部中断0请求、定时器/计数器0中断请求、外部中断1请求、定时器/计数器1中断请求、串行口中断请求。下图为AT89S51的中断系统结构:
(二)中断寄存器
1、中断优先级寄存器IP
中断请求源有两个中断优先级,每一个中断请求源可由软件设置为高优先级中断或低优先级中断,也可实现两级中断嵌套。
两级嵌套如下图所示:
IP寄存器格式如下:
(1) PS— 串行口中断优先级控制位,1一 高级; 0— 低级。
(2) PT1—T1中断优先级控制位,1一高级; 0— 低级。
(3) PX1—外部中断1中断优先级控制位,1一高级; 0—低级。
(4) PT0—T0中断优先级控制位,1一 高级; 0—低级。
(5) PX0—外部中断0中断优先级控制位,1一高级; 0—低级。
使用C语言编程,中断查询次序号就是中断号,例如:
2、串行口控制寄存器SCON
SCON
为串行口控制寄存器,SCON
的低二位锁存串口的发送中断和接收中断的中断请求标志TI
和RI
,SCON格式如下:
(1) TI一串口发送中断请求标志位。CPU将1字节的数据写入串口的发送缓冲器SBUF时,就启动一帧串行数据的发送,每发送完一帧串行数据后,硬件使TI自动置“1”。CPU响应串口发送中断时,并不清除TI中断请求标志,TI标志必须在中断服务程序中用指令对其清“0”。
(2) RI一串行口接收中断请求标志位。在串口接收完一个串行数据帧,硬件自动使RI中断请求标志置“1”。CPU在响应串口接收中断时,RI标志,并不清“0”,须在中断服务程序中用指令对RI清“0”。
二、外部中断的应用
(一)单一外中断
要求:
在单片机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仿真:
普中开发板实现:
(二)两个外中断
要求:在单片机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);
}
}
Proteus仿真:
普中开发板实现:
(三)中断嵌套
要求:设计一中断嵌套程序:要求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
Proteus仿真:
三、定时器/计数器工作原理
(一)定时器/计数器结构
AT89S51定时器/计数器结构如下图,定时器/计数器T0、T1由特殊功能寄存器TH0、TL0和TH1、TL1构成。
计数器模式是对加在T0和T1两个引脚上的外部脉冲进行计数。
定时器模式是对系统时钟信号经12分频后的内部脉冲信号(机器周期)计数。由于系统时钟频率是定值,可根据计数值计算出定时时间。两个定时器/计数器属于增1计数器,即每计一个脉冲,计数器增1。
(二)计算定时器的计数初值
*定时时间=(2^16-X)12/晶振频率
(三)定时/计数器应用
1、定时器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
。
Keil代码实现:
#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;
}
}
Proteus仿真:
普中开发板实现:
2、Keil软件中查看波形图
波形如下图:
3、计数器T0中断
要求:采用计数器中断,实现按4次按键开关后,P2口的8只LED闪烁不停。
计数器初值:由于每按1次按键开关,计数1次,按4次后,P2口的8只LED闪烁不停。因此计数器初值为2^16-4=65532
,转化为十六进制后为0XFFFC
,所以TH0=0XFF
,TL0=0XFC
。
Keil代码实现:
#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);
}
}
Proteus仿真:
四、中断服务程序优化
要求: 在上述的按键控制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()//外部中断0初始化
{
EA=1;
EX0=1;
IT0=1;//下降沿触发
}
unsigned char Key_Interrupt_Flag=0;//中断模式标志位
void Init0_Routine() interrupt 0
{
Key_Interrupt_Flag=1;//模式标志位置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;
}
}
}
Proteus仿真:
普中开发板实现:
五、总结
通过此次学习了解了中断和定时/计数器的工作机制,学会了合理设置中断优先级和定时器的工作参数,从而能够有效地节省系统资源,提高系统的性能和效率。
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;
}
}
}
**Proteus仿真**:
<video src="C:\Users\Administrator\Desktop\图片\8.mp4"></video>
**普中开发板实现**:
<video src="C:\Users\Administrator\Desktop\图片\9.mp4"></video>
# 五、总结
通过此次学习了解了中断和定时/计数器的工作机制,学会了合理设置中断优先级和定时器的工作参数,从而能够有效地节省系统资源,提高系统的性能和效率。