【C51】单片机中断

引言

     其实人的一生和单片机的运行很类似。就拿人的一生来说:有些事只需要做一次,比如得了水痘以后,体内产生免疫,以后就不会再生这个病了。有些事需要反复做,比如反复读书,反复工作,反复与困苦打交道,反复地与人相处。而有其他一些事,只有当它突然发生时我们才会去处理的,比如中彩票,钱包丢了......

这个运行机制早被Arduino借鉴了。

/*******Arduino代码框架********/
void setup() {

     //只执行一次的代码

}

void loop() {

    //反复执行的代码

}


//------------经过Arduino编译器转译后的实质代码(有删改)-----------------------
#include <Arduino.h>

int main(void)
{
       
        init();      //最先调用init,初始化arduino 的硬件

        setup();  //只执行一次的代码

        for (;;)      //反复执行的代码
        {
              loop();
             
        }

        return 0;
}

 

 回想一下写C51代码的模式,其实也是一样的。不过上述代码中,还有一种机制没体现出来:突然发生的事情。这就是下面要剖析的中断机制。

 

关于中断

无论是Arduino 还是 C51,还有其他硬件平台,都有中断机制。

中断是单片机处理突发性事件的一种机制。只执行一次的代码可以放在大循环外,循环执行的放在大循环内,突发性事件的代码,则通过中断方式处理。我们的PC机,鼠标的点击,键盘的按下,都是以中断的方式处理的。

 

若程序正常运行的某一时刻,中断发生了,当前执行流程就会暂停,CPU会转去处理中断服务程序(执行中断函数),当中断服务程序执行完后,再返回来接着执行原来的指令。

这个过程就是中断响应和中断返回。

 

 

 

中断源

引起中断的根源,叫中断源。
当有中断发生时,会向单片机申请中断处理。标准C51中有5个中断源,也就是有5种情况会引发中断。
 
 
中断源引发原因默认优先级中断序号(C语言)
入口地址(汇编用)
中断标号*8 +3得到
INT0引脚输入低电平或者下降沿引发。此时标志位IE0=1最高00003
T0T0对应的TF0溢出时发生。此时标志位TF0 = 1 1000B
INT1引脚输入低电平或者下降沿引发。此时标志位IE1=1 20013
T1T1对应的TF1溢出时发生。此时标志位TF1 = 1 3001B
串口中断串口完成一帧字符的接受/发送引发。 40023
T2(如果有的话)T2对应的TF2溢出时发生。标志位TF2 = 1最低5002B

 

 若配置好了相应的中断,当中断发生时,单片机就会自动去调用中断函数,来处理中断。

在执行中断函数前,除了串行口中断的标志位需要用代码指令软件归零外,其他的中断标志位都是硬件自动归零。

中断函数

//无返回值,无参数,不用声明

//注意:不要让中断事件处理太复杂耗时的任务。中断函数应该速战速决。
//格式如下: void functionName() interrupt 中断序号 { //..... }


//如下是计数器/定时器0中断函数
void interrupt_Timer0() interrupt 1
{
  
//这里写 计数器/定时器0发生中断时的处理代码
}



 

中断使能寄存器 IE(interrupt enable)

 

 

想要中断发生时,CPU能处理中断函数,就必须使能相应的中断,通过IE配置即可。

为1时使能,为0不使能
 
EA  (enable ALL ):中断使能总开关位。1开启,0关闭。若EA不开启,即便5个中断都开启,CPU也不会处理中断。
 
 
EX0:外部中断INT0使能
EX1:外部中断INT1使能
 
ET0:定时器0中断使能
ET1:定时器1中断使能
ET2:定时器2中断使能
 
ES   :  串口中断使能

 

 

 

中断优先级寄存器 IP (interrupt priority)

 

 

 

为1时表示为提升中断为高级中断,为0时表示为低级中断级别。
 
 
PX0  :外部中断0优先级提升 位
PX1  :外部中断1优先级提升 位
 
PT0 : 计时器0中断优先级提升 位
PT1:  计时器1中断优先级提升 位
pt2 : 计时器2中断优先级提升 位
 
PS:   串口中断优先级提升 位

 

 

被设置为1的中断,将提升位高级优先级,否则归纳为低级优先级。

      

 

 

 

同时发生的中断:

同优先级时,按照默认优先顺序执行

不同优先级时,高优先级 先 于 低优先级执行

 

验证:

 

#include<reg51.h>
typedef unsigned int uint;
typedef unsigned char uchar;

/**************函数声明******************/
void delay(uint t);
void enableALL(void);
void init_interrupt(void);
bit isKeyPressed() ;

/********************************/


/***********特殊功能位********************/
sbit redLED_TO = P0^0;
sbit blueLED_T1 = P0^5;
sbit key = P1^0;
/*********************************/


void main()
{
    init_interrupt();
    key = 1;
    redLED_TO = 0;
    blueLED_T1 = 0;

    //PT1 = 1;
    //取消这里的注释,来验证:同时发生的中断,不同优先级时,高优先级 先 于 低优先级执行

    while(1)
    {
         if(isKeyPressed())
         {
            enableALL();
            TR0 =  0;
            TR1 =  0 ;
         }

    }

}


void init_interrupt(void)
{
    EA = 0;         //关闭总中断使能,默认就是0
    ET0 = 1;     //使能定时器0 中断
    ET1 = 1;     //使能定时器1 中断

    TMOD = 17;    //T0 T1 都是16位存储计数器模式
   
    //T0
    TH0 = 255 ;      //都设置为255,以便程序运行后马上就能引发中断
    TL0 = 255;
    TR0 = 1;      //开启定时器,让定时器跑起来,引发中断

    //T1
    TH1 =255 ;
    TL1 =255 ;
    TR1 = 1;

}

void enableALL(void)
{
    //使能中断总开关
    EA =1;
}

//定时器0 的中断函数
void interrupt_Timer0()  interrupt 1
{
      redLED_TO = 1;   //点亮红色LED
      delay(1000);
}
//定时器1 的中断函数
void interrupt_Timer1()  interrupt 3
{
      blueLED_T1 = 1;    //点亮蓝色色LED
      delay(1000);
}


//按键检测函数,按下返回1,否则返回0
bit isKeyPressed()
{
    bit pressed = 0;
    if(key==0)
    {
        delay(5);
        if(key == 0)
        {
             pressed = 1;
             while(key==0);
        }
    }

    return pressed;
}



void delay(uint t)
{
    uchar j;
    uint i;
    for(i=t;i>0;--i)
     for(j=110;j>0;--j)
      ;
}

/**************************************
实验效果:当程序跑起来后,2个LED都是熄灭的。
按下按键后,红色LED先亮,1s后蓝色LED亮。

取消  PT1 = 1; 这行注释。
按下按键后,蓝色LED先亮,1s后红色LED亮。
***************************************/

 

 

 

 

 

先后发生的中断:

高优先级中断打断正在执行的低优先级程序,执行高优先级中断函数,形成中断嵌套。

低优先级中断 不会打断 正在执行的高优先级程序。

同级优先级中断不会相互打断(书上如是说,但是我实验结果却不是这样,同级看默认优先级,高优先级的还是会打断低优先级的,求解释 --!)。

 

连线图同上。

/***高优先级中断打断正在执行的低优先级程序***/
#include<reg51.h> typedef unsigned int uint; typedef unsigned char uchar; /**************函数声明******************/ void delay(uint t); void enableALL(void); void init_interrupt(void); bit isKeyPressed() ; /********************************/ /***********特殊功能位********************/ sbit redLED_TO = P0^0; sbit blueLED_T1 = P0^5; sbit key = P1^0; /*********************************/ void main() { init_interrupt(); key = 1; redLED_TO = 0; blueLED_T1 = 0; PT0 = 1; while(1) { if(isKeyPressed()) { enableALL(); TR0 = 0; TR1 = 0 ; } } } void init_interrupt(void) { EA = 0; //关闭总中断使能,默认就是0 ET0 = 1; //使能定时器0 中断 ET1 = 1; //使能定时器1 中断 TMOD = 17; //T0 T1 都是16位存储计数器模式 //T0 TH0 = 255 ; //T0比T1慢溢出 TL0 = 240; TR0 = 1; //开启定时器,让定时器跑起来,引发中断 //T1 TH1 =255 ; //T1先于T0溢出 TL1 =255 ; TR1 = 1; } void enableALL(void) { //使能中断总开关 EA =1; } //定时器0 的中断函数 void interrupt_Timer0() interrupt 1 { redLED_TO = 1; //点亮红色LED delay(1000); } //定时器1 的中断函数 void interrupt_Timer1() interrupt 3 { delay(10000); blueLED_T1 = 1; //点亮蓝色色LED } //按键检测函数,按下返回1,否则返回0 bit isKeyPressed() { bit pressed = 0; if(key==0) { delay(5); if(key == 0) { pressed = 1; while(key==0); } } return pressed; } void delay(uint t) { uchar j; uint i; for(i=t;i>0;--i) for(j=110;j>0;--j) ; } /************************************** 实验效果:当程序跑起来后,2个LED都是熄灭的。 按下按键后,红色LED先亮, 后蓝色LED亮。 由于T1的初始值大,会先发生中断,进入中断处理函数后,延时10s,在延时期间,T1对应的蓝色LED 来不及点亮,后来的T0 发生中断,由于T0设置了高优先级,打断了T1的中断,进入T0的中断,红色LED点亮。 然后回到T1的中断,最终点亮蓝色LED。 ***************************************/

 

 

使用中断的一个例子:数码管显示跑秒

 

 

#include<reg51.h>
typedef unsigned int uint;
typedef unsigned char uchar;

/**************函数声明******************/
void delay(uint t);
void showDigit(uint num);
void destroy_Timer0(void);
void init_Timer0(void);
/********************************/

uchar code TABLE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

#define DUAN_XUAN P0
sbit Add0 = P2^0;
sbit Add1 = P2^1 ;
sbit Add2 = P2^2 ;

uchar T0_interruptCount = 0;
uint times = 0;

void main()
{

    init_Timer0();
    while(1)
    {
       showDigit(times);

    }



}


void showDigit(uint num)
{
    uchar c = 0;
    do{

           switch(c)
           {
                case 0: Add2=1 ; Add1 = 1; Add0=1 ; break;
                case 1: Add2=1 ; Add1 = 1; Add0= 0; break;   
                case 2: Add2=1 ; Add1 = 0; Add0= 1; break;   
                case 3: Add2=1 ; Add1 = 0; Add0= 0; break;   
                case 4: Add2=0 ; Add1 = 1; Add0= 1; break;   
                case 5: Add2=0 ; Add1 = 1; Add0= 0; break;   
                case 6: Add2=0 ; Add1 = 0; Add0= 1; break;   
                case 7: Add2= 0; Add1 = 0; Add0= 0; break;   
                default :break;
           }

           DUAN_XUAN = TABLE[num%10] ;
           delay(5);
           DUAN_XUAN = 0;
           ++c;


    }while(num/=10);

}


void init_Timer0(void)
{
    TMOD = 1;
    TL0 = 0;
    TH0 =220 ;
    EA = 1;
    ET0  =1;

    TR0 =1;

}


void interrupt_Timer0() interrupt 1
{
    TL0 = 0;
    TH0 =220 ;
    ++T0_interruptCount   ;
    if(T0_interruptCount == 100)
    {
           ++times;
           T0_interruptCount = 0;
    }
}



void delay(uint t)
{
    uchar j;
    uint i;
    for(i=t;i>0;--i)
     for(j=110;j>0;--j)
      ;
}

 

 

 

计数器中断的运用

以后补。

 

INTx外部中断的运用 

 以后用到再补。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值