51單片機之外部中斷應用實例(電平觸發、邊沿觸發)

  • 硬件:STC89C52RC
  • 開發工具:Keil uVision4

前言:8051是一款很經典的、歷史悠久的單片機,作爲一款入門級的單片機8051受到很多初學者的歡迎。89c52是8051系列的成員之一,擁有8K字節程序存儲空間,512字節隨機數據存儲空間;I/O口控制端口、中斷功能、定時器及串行接口。下面詳細講述外部中斷功能的使用。

外部中斷:單片機提供的系統緊急事件的輸入控制。事件觸發的方式包括輸入信號的下降沿觸發、低電平觸發。當觸發中斷後,單片機會跳到某一個固定的地址去執行中斷服務程序。

外部中斷信號由INT0、INT1引腳傳送進來,如圖所示:

有關中斷處理的相關控制寄存器如下:

  • 計時計數器控制寄存器 TCON
  • 中斷允許控制寄存器 IE
  • 中斷優先權寄存器 IP

寄存器並不算多,配置起來也不復雜。先對各個寄存器進行說明:

TCON寄存器:

“T”開頭的是計數/定時器相關位,“I”開頭的是外部中斷相關位,我們需要看的是後者:

IE寄存器:

IP寄存器:

CPU接到中斷信號發生時會停止當前的工作跳轉到中斷服務程序。那麼當CPU同時接到多箇中斷信號時,該怎麼選擇?當CPU正在中斷函數時又接受到另一箇中斷信號,該怎麼處理?

關於中斷的優先級有一下原則:
1、CPU同時接收到幾個中斷時,首先響應優先級最高的中斷請求,低優先的進入隊列等待;
2、正在進行的中斷過程不能被新的同級或低優先級的中斷請求所中斷;
3、正在進行的低優先級中斷服務,能被高優先級中斷請求中斷;

那麼,IP寄存器的某一中斷配置爲1就成爲高優先級。每一箇中斷在IP裏面只佔一位配置位(IP.x=0或OP.x=1),也就是說系統裏只存在兩種優先級,要麼是高優先級,要麼是低優先級。

如果,任何中斷都不配置IP寄存器的優先級,也等同於系統上電時,默認的優先級順序如下:

外部中斷0 > 定時/計數器0 > 外部中斷1 > 定時/計數器1 > 串行中斷

關於外部中斷的寄存器已經瞭解清楚了,接下來看代碼設計:

外部中斷0(下降沿觸發)

/*-----------------------------------------------
  功能:外部中斷0邊沿觸發
  現象:首先將P3.2口通過上拉電阻接到電源,保證在空閒時P3.2處於高電平;
       當外部中斷信號輸出口P3.2接到GND時,產生了一個下降沿信號,接到P0.0
       口的LED燈反轉;若此後P3.2持續接到GND,LED只反轉一次,這與電平觸發
       有區別。
------------------------------------------------*/

#include<reg52.h> 

sbit LED=P0^0; //定義LED端口

void DelayMs(unsigned char t)     //大致延時1mS
{
 unsigned short T=500;
 while(t--)
 {
     while(--T);
 }
}
void INT0_init(void) //外部中斷0初始化
{
  LED=1;       //LED口初始值
  EA=1;          //全局中斷開
  EX0=1;         //外部中斷0開
  IT0=1;         //邊沿觸發
}

main()
{
  INT0_init();
  while(1){
     //主循環
  }
}

//中斷服務程序  interrupt 0 指明是外部中斷0的中斷函數
/*
interrupt 0  指明是外部中斷0;
interrupt 1  指明是定時器中斷0; 
interrupt 2  指明是外部中斷1;
interrupt 3  指明是定時器中斷1;
interrupt 4  指明是串行口中斷;
*/
void ISR_Key(void) interrupt 0 using 1
{
 if(!INT0){
    DelayMs(10);       //防抖動
    if(!INT0){         
     LED=!LED;         //按下觸發一次,LED取反一次
    }
 }
}

外部中斷0(電平觸發)

/*-----------------------------------------------
  功能:外部中斷0電平觸發
  現象:首先將P3.2口通過上拉電阻接到電源,保證在空閒時P3.2處於高電平;
       當外部中斷信號輸出口P3.2接到GND時,產生了一個低電平信號,接到P0.0
       口的LED燈反轉;若此後P3.2持續接到GND,LED會反覆反轉,這與邊沿觸
       發有區別。
------------------------------------------------*/

#include<reg52.h> 

sbit LED=P0^0; //定義LED端口

void DelayMs(unsigned char t)     //大致延時1mS
{
 unsigned short T=500;
 while(t--)
 {
     while(--T);
 }
}
void INT0_init(void) //外部中斷0初始化
{
  LED=1;       //LED口初始值
  EA=1;          //全局中斷開
  EX0=1;         //外部中斷0開
  IT0=0;         //電平觸發
}

main()
{
  INT0_init();
  while(1){
     //主循環
  }
}

//中斷服務程序  interrupt 0 指明是外部中斷0的中斷函數
/*
interrupt 0  指明是外部中斷0;
interrupt 1  指明是定時器中斷0; 
interrupt 2  指明是外部中斷1;
interrupt 3  指明是定時器中斷1;
interrupt 4  指明是串行口中斷;
*/
void ISR_Key(void) interrupt 0 using 1
{
  if(!INT0){
    DelayMs(20);       //防抖動
    if(!INT0){         
     LED=!LED;         //按下觸發一次,LED取反一次
    }
 }
}

外部中斷1(下降沿觸發)

/*-----------------------------------------------
  功能:外部中斷1邊沿觸發
  現象:首先將P3.3口通過上拉電阻接到電源,保證在空閒時P3.3處於高電平;
       當外部中斷信號輸出口P3.3接到GND時,產生了一個下降沿信號,接到P0.0
       口的LED燈反轉;若此後P3.3持續接到GND,LED只反轉一次,這與電平觸發
       有區別。
------------------------------------------------*/

#include<reg52.h> 

sbit LED=P0^0; //定義LED端口
void DelayMs(unsigned char t)     //大致延時1mS
{
 unsigned short T=500;
 while(t--)
 {
     while(--T);
 }
}
void INT1_init(void) //外部中斷0初始化
{
  LED=1;       //LED口初始值
  EA=1;          //全局中斷開
  EX1=1;         //外部中斷1開
  IT1=1;         //邊沿觸發
}

main()
{
  INT1_init();
  while(1){
     //主循環
  }
}

/*
interrupt 0  指明是外部中斷0;
interrupt 1  指明是定時器中斷0; 
interrupt 2  指明是外部中斷1;
interrupt 3  指明是定時器中斷1;
interrupt 4  指明是串行口中斷;
*/
void ISR_Key(void) interrupt 2 using 1
{
 if(!INT1){
    DelayMs(10);       //防抖動
    if(!INT1){         
     LED=!LED;         //按下觸發一次,LED取反一次
    }
 }
}

#endif

外部中斷1(電平觸發)

/*-----------------------------------------------
  功能:外部中斷1電平觸發
  現象:首先將P3.3口通過上拉電阻接到電源,保證在空閒時P3.3處於高電平;
       當外部中斷信號輸出口P3.3接到GND時,產生了一個低電平信號,接到P0.0
       口的LED燈反轉;若此後P3.3持續接到GND,LED會反覆反轉,這與邊沿觸
       發有區別。
------------------------------------------------*/

#include<reg52.h> 

sbit LED=P0^0; //定義LED端口
void DelayMs(unsigned char t)     //大致延時1mS
{
 unsigned short T=500;
 while(t--)
 {
     while(--T);
 }
}
void INT1_init(void) //外部中斷1初始化
{
  LED=1;       //LED口初始值
  EA=1;          //全局中斷開
  EX1=1;         //外部中斷1開
  IT1=0;         //電平觸發
}

main()
{
  INT1_init();
  while(1){
     //主循環
  }
}

/*
interrupt 0  指明是外部中斷0;
interrupt 1  指明是定時器中斷0; 
interrupt 2  指明是外部中斷1;
interrupt 3  指明是定時器中斷1;
interrupt 4  指明是串行口中斷;
*/
void ISR_Key(void) interrupt 2 using 1
{
 if(!INT1){
    DelayMs(10);       //防抖動
    if(!INT1){         
     LED=!LED;         //按下觸發一次,LED取反一次
    }
 }
}

以上四種模式,代碼都是大同小異,比較一下就知道哪些是關鍵點了。

注意點:

  • 上面的程序已經是測試過沒有問題的,如果出現led不反轉,那麼檢測一下電路,有一些集成了很多元件的開發板裏面電路複雜,幾個外圍元件可能共用一個IO口,容易被幹擾,以至於達不到想要的效果。最好還是買一個最小系統的單片機,所有IO獨立出來。
  • 在選擇“電平觸發”模式下,因爲低電平的持續時間比較長(雖然只是按一下,對於單片機來說已經持續很長),會出現反覆進入中斷,導致LED不會反轉,解決方法就是在進入第一次中斷後,先把該中斷關閉掉並且用while循環,直到中斷信號引腳退出低電平狀態再打開中斷並退出while循環,這麼做缺點就是會阻塞在中斷裏面,可能導致主函數裏面的程序不能及時運行。
  • 上面的代碼比較簡單,需要根據實驗出現的問題進一步優化。
  • 外部中斷還可以應用到檢測波形的週期、佔空比、頻率以及紅外接收處理,有興趣可以試一下。

僅供參考,錯誤之處以及不足之處還望多多指教。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章