中断与定时计数
一、实验目的
深入了解51单片机中断原理。掌握外部中断、定时器/计数器中断的编程方式;
了解中断响应、中断优先级、中断标志位复位等原理,掌握二级中断、中断嵌套的具体应用编程;
了解定时器/计数器的工作原理,掌握计数器初值计算方法,定时/计数中断服务的编程开发。
二、 具体任务
1、在Proteus和普中单片机板上分别完成采用定时计数器控制LED灯每隔1s周期性亮灭的实验。要求Keil仿真中的虚拟逻辑仪对LED管脚进行波形观察,测量真实的周期数。
代码如下:
#include <reg51.h>
#define uchar unsigned char
void Delay(unsigned int i)
{
unsigned int j;
for(;i>0;i--)
for(j=0;j<333;j++)
{;}
}
void main( )
{
EA=1;
EX0=1;
IT0=1;
while(1)
{P1=0;}
}
void int0( ) interrupt 0 using 0
{
uchar m;
EX0=0;
for(m=0;m<5;m++)
{
P1=0x0f;
Delay(400);
P1=0xf0;
Delay(400);
EX0=1;
}
}
在proteus中仿真如下:
2、采用计数器中断,实现 按4次按钮开关后,P1口的8只LED闪烁不停。
代码如下:
#include <reg51.h>
#define uchar unsigned char
void Delay(unsigned int i)
{
uchar j;
for(;i>0;i--)
for(j=0;j<125;j++)
{;}
}
void main( )
{
uchar display[9]={0xff,0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf, 0x7f};
unsigned int a;
for(;;)
{
EA=1;
EX0=1;
EX1=1;
IT0=1;
IT1=1;
IP=0;
for(a=0;a<9;a++)
{
Delay(500);
P1=display[a];
}
}
}
void int0_isr(void) interrupt 0 using 1
{
uchar n;
for(n=0;n<10;n++)
{
P1=0x0f;
Delay(500);
P1=0xf0;
Delay(500);
}
}
void int1_isr (void) interrupt 2 using 2
{
uchar m;
for(m=0;m<10;m++)
{
P1=0xff;
Delay(500);
P1=0;
Delay(500);
}
}
在proteus中仿真如下:
3、一般来说,中断函数中要尽量避免使用执行时间较长(耗时)的代码,以避免中断服务影响到主程序代码的执行效率。但是在上面外部中断的实验中,中断函数采用了软件延时函数去控制LED亮灭的间隔周期。这是一种不好的编程。换一种更合理的方式,不在中断函数使用延时循环,实现同样的功能。
#include <reg51.h>
sbit LED = P1^0;
volatile unsigned char mode = 0;
void Delay(unsigned int i) {
unsigned int j;
for(;i>0;i--)
for(j=0;j<125;j++)
;
}
void main() {
TMOD = 0x50;
TH1 = 0xff;
TL1 = 0xfc;
EA = 1;
ET1 = 1;
TR1 = 1;
while(1) {
switch(mode) {
case 0: //
LED = 0;
break;
case 1: //
LED = 1;
break;
case 2: //
LED = ~LED;
Delay(500);
break;
}
}
}
void T1_int(void) interrupt 3 {
static unsigned int counter = 0;
counter++;
if(counter >= 1000) {
counter = 0;
mode = (mode + 1) % 3;
}
}
在修改后的代码中,我们使用了一个全局变量mode来表示当前的LED模式。这样,中断服务程序就非常快,不会占用太多时间,而LED的控制逻辑则交由主循环处理。这种设计更加合理,能够保证中断的快速响应,并且不会影响到主循环的执行。