“探索中断技术:深入理解51单片机中断原理与应用”

概述

中断系统的设置,是为了让CPU能对外界紧急事件进行实时处理

中断的过程是,当中断请求源发出中断请求时,CPU响应中断后,CPU先暂停当时正在执行的主程序,转而去处理中断服务程序,处理完后继续原来的工作

微型机一般允许多个中断源,当几个中断源同时发出中断时,CPU先响应最高优先级的中断

中断嵌套:CPU在处理中断时,外界产生了更高优先级的中断,CPU暂停当前中断,处理了更高优先级的中断后,在回来处理之前的中断,具有中断嵌套功能的系统叫多级中断系统,不具有的叫单级中断系统

如何使用中断

此52单片机一般有8个中断请求源,分别对应8个中断查询次序号(中断号),这些中断被硬件所调用,通过编写中断查询次序号的中断服务程序,就可以让CPU在响应中断时,执行具体的功能代码;4个外部中断INT0,INT1,INT2,INT3,3个定时器中断,定时器0中断,定时器1中断,定时器3中断,1个串口中断。

每一个中断源都可用软件独立地控制为允许中断或关闭中断,每一个中断源的中断优先级均可用软件设置

中断相关寄存器

中断相关的寄存器有:中断请求标志寄存器TCON和SCON,中断允许寄存器IE,中断优先级寄存器IP

TCON寄存器中IE0和IE1位是外部中断请求0和1的中断请求标志位,IT0和IT1位用于选择外部中断请求是负跳变触发,还是电平触发,IT置0为电平触发,即当加到INT上的外部中断请求输入信号为低电平时,将IE位置1,发出中断,CPU介入后,硬件将IE置0

IE寄存器对中断的允许和禁止实现两级控制,可位寻址,总的中断开关位EA(IE^7),EA置0,所有中断都无效,EA置1,中断源是否运行还要由低位的中断请求允许控制位的状态决定,置1对应中断源中断允许,置0中断无效,其他位ET0到ET2控制定时器0到2的溢出中断,ES控制串口,EX0到EX1控制外部中断0到1

中断开关如图

IP寄存器用于配置中断请求源的优先级,不同位配置不同中断请求源

优先级相同的多个中断,CPU的响应次序按照中断查询次序号来响应,0为最先,7为最低

中断请求源顺序如图

案例一:定时器中断控制闪灯

#include "reg52.h"

sbit ledblue =P3^7;
sbit ledyellow =P3^6;
void delay1000(){
	int cnt=0;
	TR0=1;
	while(1){
	  if(TF0==1){ 
		 cnt++;
		 TF0=0; //定时器0爆表不中断,那么手动将TF0位置0
	   TL0=0x00;
	   TH0=0xDC;
	 }
	 if(cnt==100)break;
	}
	TR0=0;
}
void ClockInit(){
  TMOD&=0x00;
	TMOD|=0x11;
	TL0=0x00;
	TH0=0xDC;
	TL1=0x00;
	TH1=0xDC;
}
void main(){
	 ledblue=1;
	 ledyellow=0;
	 ClockInit();

	 EA=1;
	 ET1=1;
   TR1=1;
   while(1){
    ledblue=!ledblue;
		delay1000(); 
	 }
}

void Lightyellow() interrupt 3 //定时器1每10ms爆表一次,爆表时TF1位置1,进入中断,TF1位自动置0,然后将cnt++,加到50次,即0.5s后,黄灯电平翻转
{    static int cnt=0;
	   cnt++;
	   TL1=0x00;
	   TH1=0xDC; 
	   if(cnt==50){
       ledyellow=!ledyellow;
			 cnt=0;
     }
}

案例二:定时器中断控制SG90舵机切换角度转动

SG90舵机

SG90舵机需要的控制信号与一般的电平信号不同,他需要的控制信号是PWM信号,即占空比不同的方波信号

舵机接线为红线VCC,黑线GND,黄线PWM信号接收

SG90舵机有两个规格,180°类型的360°类型,对应PWM波的控制情况如图

180°版本 占空比越高,转动的角度越大,但是转动角度有些误差,需要微调,高电平时间大概90°要1570us,180°要2570us,2720us为最大角度(略微大于180°),220us为最小角度(略微小于0°);超过2730us,先转动到最大角度,然后按超出量多少,转动速度就是多少的转动速度转动一小会,然后再快速转动回最大角度,如此循环,小于220us同理,

360°版本

0到2.5%占空比,维持最快顺时针转速不变;2.5%到7.5%占空比,占空比越高,顺时针转速越慢;7.5%完全暂停;7.5%到12.5%占空比,占空比越高,逆时针转速越快;12.5%占空比往上,维持最快逆时针转速不变

SG90舵机还分为数字电机和模拟电机,数字电机,给一次PWM信号就能转动,模拟电机需要在一段时间内持续给PWM信号才能转动

我们采用的是180类型的模拟信号SG90舵机

注意:一般信号周期是固定的,对于控制不同仪器,可能信号周期会有所变化;而对于相应信号周期占空比可以变化,用来控制程度

什么是PWM

PWM是脉冲宽度调制,通过对一系列脉冲的宽度进行调制,通过调节占空比,等效出需要的模拟信号波形,相当于用调节高低电平的时间长度来等效模拟信号,PWM给出的信号仍然是数字的,简而言之,PWM是一种对模拟信号电平进行数字编码的方法,比如1s内给持续5v的电压信号,占空比60%,相当于是1s内给持续3v的电压信号,占空比20%,相当于1s内给持续1v的电压信号

PWM波有两个决定条件,一个是信号周期,另一个是占空比

信号周期:PWM波是由周期的占空比信号组成,信号周期就是一个占空比信号所占的时间长度

占空比:一个信号周期内,信号处于高电平状态的时间占整个周期的时间,比如方波的占空比就是50%

占空比不同,表示的模拟信号不同,输出的电压就不同,对应的舵机旋转角度就不同

PWM信号可以通过C52自带的硬件产生,也可以通过IO口软件模拟产生,精准度略差

如何控制舵机

PWM波的频率不能太高,一般50hz,即信号周期0.02s,信号周期用定时器来控制

频率高的话,周期就小,固定时间内接收到的信号数量就越多,频率低的话,周期就大,固定时间内接收到的信号数量就越少,但是固定时间内二者高低电平所占时间的比例是相同的,为占空比

占空比每提升2.5%,舵机就增加45°

在大于周期的一段时间内持续输出对应占空比的PWM波,电机就会转动相应角度

如何设定定时器定时的初值

因为定时器的初值由TH位和TL位设置,所以TH位可以等于(65536-定时时长/机器周期)/256得出,

TL位可以等于(65536-定时时长/机器周期)%256得出

注意:取模运算不可以用浮点数

PWM方波如何输出

方案1

用定时器0来输出周期占空比的高电平,定时器1来输出剩下周期时间的低电平。在电平跳变点(当前即定时器爆表)进入中断程序,来配置下一个定时器

将PWM波分为一段时间的高电平,一段时间的低电平,不断重复,起初打开定时器1,爆表后,进入中断程序,在中断程序中,设定输出高电平和定时器0的定时初值后,打开定时器0,结束中断程序,这样就输出了对应时间的高电平,等定时器0爆表后,进入中断程序,在中断程序中又设置输出低电平和定时器1的定时初值,然后结束中断程序

进入中断后要及时关闭当前定时器,再打开下一个定时器,这样保持定时器0运行一段时间,定时器1运行一段时间

代码

#include "reg52.h"
#include<intrins.h>
sbit ledb=P3^7;
sbit ledy=P3^6;
sbit sg90 =P1^6;

int flag=0;
void Delay2000()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void ClockInit(){
  TMOD&=0x00;
	TMOD|=0x11;
	TL0=0x66;
	TH0=0xbd;
	TH1=0xfa;
	TL1=0x9a;
	EA=1;
	ET0=1;
	ET1=1;	
}
void do1() interrupt 1
{
	
	TR0=0;
    sg90=0;
	ledy=0;
	ledb=1;
  if(flag==0){
    //TL1=0xcd;
	//TH1=0xb9; 
	  TH1=(65536-19500/1.085)/256;
	  TL1=(int)(65536-19500/1.085)%256;
  }
	if(flag==1){
	//TL1=0x66;
	//TH1=0xbd;
	  TH1=(65536-18500/1.085)/256;
	  TL1=(int)(65536-18500/1.085)%256;  //取模不能用浮点数,需要强转

  }
	TR1=1;
}
void do2() interrupt 3
{
	
	TR1=0;
	sg90=1;
	ledy=1;
	ledb=0;
	if(flag==0){//360°sg90舵机 2.5%占空比顺时针全速转动
//  TL0=0x33;
//	TH0=0xfe;
	TH0=(65536-500/1.085)/256;
	TL0=(int)(65536-500/1.085)%256;
  }
	if(flag==1){ //360°sg90舵机 7.5%占空比停止转动
//	TL0=0x9a;
//	TH0=0xfa;
	TH0=(65536-1500/1.085)/256;
	TL0=(int)(65536-1500/1.085)%256;	
  }
	TR0=1;
}
void main(){
	  ledy=1;
	  ledb=0;

	  ClockInit();
	  TR1=1;
	  while(1){
     flag=!flag; //切换占空比
     Delay2000();
    }
    
}


	

方案2

由于占空比每提升2.5%,舵机就增加45°,如果以45度为转动单位,那么可以将一个周期20ms秒分成40等分,每等分占0.5ms,每等分输出高电平或低电平

用一个变量cnt记录次数,爆表一次cnt++,将定时器爆表前可计时的时长定为0.5ms,每次爆表前持续输出高电平或者低电平,总共输出40次,代表一个周期;cnt自加到40后,cnt置0,代表循环

每增加2.5%的占空比,40次中,输出高电平的次数就多一次

代码

#include "reg52.h"
#include<intrins.h>

sbit sg90 =P1^6;
sbit ledb =P3^7;
sbit ledy =P3^6;
int cnt=0;
int jd;
void Delay2000()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void ClockInit(){
  TMOD&=0x00;
	TMOD|=0x11;
//	TL0=0xff;
//	TH0=0xb7;
	TL1=0x33;
	TH1=0xfe;
	EA=1;
//	ET0=1;
	ET1=1;
}
void main(){
    Delay2000();
	  ClockInit();
    TR1=1;	
	  while(1){
     jd=3;
		 cnt=0;
		 TL1=0x33;
	   TH1=0xfe;
		 ledb=0;
		 ledy=1;
		 Delay2000();
		 jd=4;
		 cnt=0;
		 TL1=0x33;
	   TH1=0xfe;
		 ledb=1;
		 ledy=0;
		 Delay2000();
    }

	
}

void do2() interrupt 3
{
	cnt++;
	if(cnt<=jd)sg90=1;
	else sg90=0;
	TL1=0x33;
	TH1=0xfe;
	if(cnt==40){
   cnt=0;
  }
}

	

案例三:超声波模块测距控制亮灯

超声波模块HC-SR04

通过发送和接收超声波,利用时间和声音传播速度计算出障碍物与模块的距离,测距范围2cm到400cm,精度为3mm

TRIG引脚,给予至少10us的高电平,产生发波指令,经过一小段时间产生8个40khz的超声波方波并发送出去,发送时,ECHO引脚由低电平跳转到高电平,当超声波反弹回来被接收时,ECHO引脚又高电平跳转到低电平

计算时间,ECHO引脚进入高电平时,打开定时器,ECHO引脚跳转会低电平时,关闭定时器,通过定时器的差值计算出超声波传播的时间,距离等于时间*340/2,定时器一次可计时71106us,最大测距为1208cm,所以满足400cm的测距范围

代码

#include "reg52.h"

sbit ledblue =P3^7;
sbit ledyellow =P3^6;
sbit Trig=P2^5;
sbit Echo=P2^6;
void Delay10us()		//@11.0592MHz
{
	unsigned char i;

	i = 2;
	while (--i);
}

void ClockInit(){
    TMOD&=0xf0;
	TMOD|=0x01;
	TH0=0x00;
	TH0=0x00;
}

void startHC(){
  Trig=1;
	Delay10us();
	Trig=0;
}
void main(){
    double dis=0;
	double time=0;
	ClockInit();
	while(1){
     startHC();
	 while(Echo==0);
	 TR0=1;
     while(Echo==1);
 	 TR0=0;
     time=(TH0*256+TL0)*1.085;
	 dis=time*0.017;
	 if(dis<10)ledblue=0;
	 else ledblue=1;
	 TH0=0x00;
	 TL0=0x00;
    }
}

终项目:感应开关盖垃圾桶

目标实现

1、测距控制垃圾桶开关盖

2、按键控制垃圾桶开关盖

3、振动控制垃圾桶开关盖

4、开关盖时蜂鸣器响应

需要的技术

1、垃圾桶开关盖需要舵机转动角度来支撑,舵机转动不同的角度需要不同占空比的PWM波,实现PWM波,则需要定时器0和定时器1分别产生相应时间的高低电平循环,需要用到定时器中断

2、振动控制开关盖,如果只是把振动产生的信号或到测距控制开关盖中,则有可能发生振动已经产生,信号也产生了,但是代码没有捕获到这个信号,当运行到判断部分时,振动信号已经消失了,所以需要用到外部中断,信号产生时同时发起外部中断,用一个变量记录下这个信号,下次运行到判断部分时,能检测到这个变量,则会控制开关盖

外部中断IT0如果置0表示低电平触发,对于模块发出信号为低电平时会发出中断;置1表示下降沿触发,对于模块发出信号与高电平脉冲有效,因为发出脉冲后会跳变回低电平,跳回时发出中断

代码

#include "reg52.h"
#include<intrins.h>

sbit ledblue   =P3^7;
sbit ledyellow =P3^6;
sbit Trig      =P2^5;
sbit Echo      =P2^6;
sbit sg90      =P1^6;
sbit key1      =P2^1;
sbit zd        =P3^2;  //振动
sbit beep      =P2^4;  //继电器开关,用于控制喇叭
int jd=0;
int jdbake=0;
int flag=1;
int soundflag=0;
int zd_mark=0;
void Delay2000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void Delay100ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 180;
	j = 73;
	do
	{
		while (--j);
	} while (--i);
}
void Delay10us()		//@11.0592MHz
{
	unsigned char i;

	i = 2;
	while (--i);
}

void startHC(){
    Trig=1;
	Delay10us();
	Trig=0;
}

double getDis(){
	 double time=0;
	 TH2=0x00;
	 TL2=0x00;
	 startHC();
	 while(Echo==0);
	 TR2=1;
   while(Echo==1);
 	 TR2=0;
   time=(TH2*256+TL2)*1.085;
	 return (time*0.017);
}

void ClockInit(){
  TMOD&=0x00;
	TMOD|=0x11;
	EA=1;
	ET0=1;
	ET1=1;	
	TR1=1;
}
void Sound(){
  int i=0;
	beep=0;
	for(i;i<3;i++){
  Delay100ms();
  }
  beep=1;
	i=0;
	for(i;i<3;i++){
  Delay100ms();
  }

}
void Clock2Init(){
	TH2=0x00;
	TL2=0x00;
}
void EX0Init(){
    EX0=1;//开启外部中断
	IT0=0;//中断触发为低电平

}
void main(){
    double dis=0;
	Clock2Init();
	ClockInit();
	EX0Init();
    while(1){
	   dis=getDis();
	   if(key1==0){
       Delay100ms();
       flag=!flag;
	   if(soundflag==0){
		 soundflag=0;
	   }else {
         soundflag=!soundflag;
      }
     }
	 if(flag==1){
		 EX0=1;
		 ledyellow=1;
	     if(dis<10||zd_mark==1){ 
			 EX0=0;
			 jd=180;
		     ledblue=0;
             if(jdbake!=jd){
			 Sound();
             }
             jdbake=jd;
		     Delay2000ms();
            zd_mark=0;
			 EX0=1;
        }
	    else {
		   jd=0;
           jdbake=jd;
		   ledblue=1;
		   Delay100ms();
	   }
     }else{
		   EX0=0;
           jd=180;
		   if(soundflag==0){
		     Sound(); //按键后只响一声
		   }
		   soundflag=1;
		   Delay100ms();
		   ledyellow=0;
     }
   }
}
void Low() interrupt 1
{
	TR0=0;
    sg90=0;
    if(jd==0){
    //	TL1=0xcd;
	//  TH1=0xb9;
	  TH1=(65536-(20000-500)/1.085)/256;  
	  TL1=(int)(65536-(20000-500)/1.085)%256;
  }
	if(jd==180){
	//TL1=0x66;
	//TH1=0xbd;
		TH1=(65536-(20000-2570)/1.085)/256;
		TL1=(int)(65536-(20000-2570)/1.085)%256;  //¼Çסȡģ²»¿ÉÒÔÓø¡µãÊý
  }
	TR1=1;
}
void High() interrupt 3
{
	TR1=0;
	sg90=1;
	if(jd==0){//ת¶¯
//  TL0=0x33;
//	TH0=0xfe;
	TH0=(65536-500/1.085)/256;    //1570 90°  210最小度数
	TL0=(int)(65536-500/1.085)%256;
  }
	if(jd==180){ //ÔÝÍ£
//	TL0=0x9a;
//	TH0=0xfa;
	TH0=(65536-2570/1.085)/256;   //2570 180°   2720最大度数 
	TL0=(int)(65536-2570/1.085)%256;	
  }
	TR0=1;
}
void get_zdsignal() interrupt 0
{
    zd_mark=1;

}

物联沃分享整理
物联沃-IOTWORD物联网 » “探索中断技术:深入理解51单片机中断原理与应用”

发表评论