[ SDCC For 8051 ] 03-External_Interrupt
Interrupt 中斷是所有微處理器必備功能之一可以用來處理非預期事件。
本文章將介紹 8051 的外部中斷 ( External Interrupt ) 功能並以 VScode 搭配 Embedded IDE 與 SDCC 作為開發環境。
8051 的外部中斷
對於微處理器 ( Micro Controller Unit, MCU ) 這一類裝置而言都是針對某種功能獨立並且重複運行,中斷的作用在於讓處理器暫停目前的工作轉而處裡其他工作,例如使用者要輸入操作參數或是感測器偵測到危險必須發布警告…等。
微處理器在正常運行下會很忠實地重複循環程式內容,發生中斷才會放下當前工作去完成中斷工作後又回到先前的工作繼續往下走,因此中斷經常是非預期發生的。微處理器在發生中斷時會轉跳到中斷服務常式 ( Interrupt Service Routine, ISR ) 在程式中以函數的形式存在並配有專屬位址也稱為中斷向量,8051 共有 5 個中斷源分述如下:
中斷優先順序可以透過暫存器設定高與低兩種優先權,在多個中斷發生時決定誰先執行低優先權中斷不可岔斷高優先權中斷,反之在低優先權中斷執行時高優先權中斷則可以岔斷。另外有一個不可撼動的最優先中斷 Reset 在系統重置時也會產生中斷,其中斷向量為 0x0000使得程式跳至起點開始執行。
那麼,在程式中該如何使用中斷服務常式呢 ? 其格式如下
void 函式名稱 (void) interrupt (中斷編號) using (暫存庫編號)
例如以 SDCC 語法外部中斷 INT0 可以寫成
void Ext_0 (void) __interrupt (0) __using (0)
其中 Ext_0 是函式名稱,一般會命名好辨認的名稱提高程式閱讀性。__interrupt (0) 表示指定中斷編號 0 即是外部中斷 0 ,using (0) 表示使用暫存庫 BANK0 ,若不指定暫存庫預設指定 BANK 0。
中斷函式有幾點需要注意:
- 中斷函式不可傳入引數,也不會回傳任何值。
- 8051 只有兩種中斷優先順序分別是高與低,預設都是低順序。
- 中斷函式要盡快退出不要占用太久,因此避免在中斷內部使用延遲。
回到目錄
相關暫存器
使用中斷時必須設定暫存器來啟動功能,以下介紹相關暫存器。
- EA : 中斷總開關。EA=1 啟動中斷功能,EA=0 關閉中斷功能。
- ES :串列埠中斷開關。ES=1 啟動串列埠中斷,ES=0關閉串列埠中斷。
- ET1 :Timer 1 中斷開關。ET1 =1 啟動 Timer 1 中斷, ET1=0 關閉 Timer 1 中斷。
- EX1 :INT 1外部中斷開關。EX1=1 啟動INT_1 外部中斷, EX1=0 關閉INT_1 外部中斷。
- ET0 :Timer 0 中斷開關。ET0 =1 啟動 Timer 0 中斷, ET0=0 關閉 Timer 0 中斷。
- EX0 :INT 0外部中斷開關。EX0=1 啟動INT_0 外部中斷, EX0=0 關閉INT_0 外部中斷。
中斷優先暫存器可以設定各個中斷的優先權,設置 1 表示高優先權,設置 0 表示低優先權。
- PS : 串列埠中斷優先權。PS=1 高優先權,PS=0 低優先權。
- PT1 : Timer 1 中斷優先權。PT1=1 高優先權,PT1=0 低優先權。
- PX1 :INT 1 外部中斷優先權。PX1=1 高優先權,PX1=0 低優先權。
- PT0 : Timer 0 中斷優先權。PT0=1 高優先權,PT0=0 低優先權。
- PX0 :INT 0 外部中斷優先權。PX0=1 高優先權,PX0=0 低優先權。
TCON 暫存器高位四位元 TF1 ~ TR0 是計時 / 計數溢位旗標與啟動開關。這一次先把目光放在低位四位元 IE1 ~ IT0 上。
外部中斷有兩種觸發準位,負緣觸發與低準位觸發。負緣觸發是電位從高準位 ( 5v ,邏輯 1 ) 變化為低準位 ( 0v , 邏輯 0 ) 期間觸發中斷。低準位觸發是電位維持在 ( 0v ,邏輯 0 ) 時觸發中斷。
- IE1 :INT1 中斷旗標。發生 INT 1 中斷時自動設置 1,執行完中斷函式時自動清零。
- IT1 :INT1 中斷型態。INT1=1 負緣觸發,INT1=0 低準位觸發。
- IE0 :INT0 中斷旗標。發生 INT 0 中斷時自動設置 1,執行完中斷函式時自動清零。
- IT0 :INT1 中斷型態。INT0=1 負緣觸發,INT0=0 低準位觸發。
回到目錄
電路圖
這次將以 INT0 外部中斷作為中斷訊號來源,在 P3_2 腳位放置一個按鈕開關並且接上拉電阻到電源使得未按下開關時 P3_2 保持高電位,壓下開關後呈現低電位觸動 INT0 外部中斷,
利用中斷來控制 LED 燈閃爍方向。
回到目錄
新建專案
使用 8051 的 Port1 的低 4 位元控制外接 LED 燈,讓燈號依序亮滅並持續重複動作。
由於 LED 燈有外接電源因此要讓 LED 燈亮需輸出低電位,反之輸出高電位則 LED 燈滅。
觸發中斷時會改變 LED 燈亮燈順序,例如由左而右變成由右而左。
首先在 Embedded IDE 中點擊 New Project,選擇 Internal Template 內建模板,接著選擇 89C52 SDCC Quickstart 。
在 Embedded IDE 中點擊 New Project,選擇 Internal Template 內建模板,接著選擇 89C52 SDCC Quickstart 。
最後輸入專案名稱,例如本專案命名為 EXT_LED,輸入完成後按下 Enter 鍵並選擇存放資料夾即完成專案的建立。
回到目錄
流程圖
回到目錄
程式撰寫
首先引入標頭檔案 < lint.h > 與 < 8051.h> 。
宣告一個全域變數用於增減陣列索引。
#include <lint.h>
#include <8051.h>
int increment=1;
接著建立延遲副程式讓 LED 燈閃爍稍作停留。
voide Delay(unsigned int cnt)
{
while(cnt > 0) cnt--;
}
建立中斷服務函式,觸發中斷時將變數 increment 取負號。
void EX0_INT (void) __interrupt(0)
{
increment=-increment;
}
在 main 主程式中建立一個陣列作為輸出用途因為陣列內容是固定不變的因此可以加上修飾字 __code 將陣列內容存放在程式記憶體 ( ROM / Flash ) 如此可以不佔用資料記憶體 RAM 。
__code unsigned char LED_Array[4]={0x01,0x02,0x04,0x08};
再宣告一個變數作為陣列索引。
int index=0;
接著設定中斷,這裡不特別設定中斷先權因此 Interrupt Prioroty Register, IP 暫存器不做設定。
另外,因為設計成低電位觸發因此 Timer / Conter Control Register, TCON 暫存器的 IT0 不做設定。 ( 因為預設 IT0=0 )
EX0=1; // 啟動外部中斷 INT_0
EA=1; // 啟動中斷總開關
P1=0xFF; // 關閉全部 LED 燈
while 迴圈重複執行。
while (1)
{
P1=~LED_Array[index];
Delay(30000);
index=index+increment;
if(index>3) index=0;
if(index<0) index=3;
}
回到目錄
編譯與成果
完成程式撰寫後回到 Embedded IDE 環境中,滑鼠點擊上方的專案確認編譯器是否為 SDCC,之後按下編譯鍵 ( Build ) 進行編譯。
編譯過程螢幕下方會開啟終端機顯示訊息,編譯完成後提示按下任意鍵離開終端機。
回到專案資料夾,在 build \ Debug 資料夾內部有一個副檔名為 .hex 的檔案便可以使用燒錄軟體進行燒錄。
成果
回到目錄
總結
外部中斷是中斷功能的其中之一,常用來作為外部偵測用途提供喚醒或使用者輸入等,關於 8051 的外部中斷總結如下。
- 8051 有兩個外部中斷 INT0 與 INT1 。
- INT0 位於第12 腳位 P3_2,INT1 位於第 13 腳位 P3_3。
- 中斷致能暫存器 IE 用來啟動或關閉中斷功能。
- 中斷優先權暫存器可設定中斷優先權,高或低兩種設定。
- 計時/計數控制暫存器的 IT0 與 IT1 可分別設定 INT0 與 INT1 為負緣觸發或低準位觸發。
- INT0 的中斷旗標 IE0 或 INT1 的中斷旗標 IE1 在發生中斷時會自動設置 1 ,結束中斷服務函式會自動清零。
- SDCC 的中斷服務函式語法為 void 函式名稱 (void) __interrupt(中斷編號) using(暫存庫編號),其中暫存庫編號為特別聲明時預設為 BANK0。
- INT0 的中斷編號為 0 ,INT1 的中斷編號為 2 。
回到目錄
完整程式碼
#include <lint.h>
#include <8051.h>
int increment=1;
void Delay(unsigned int cnt)
{
while (cnt>0) cnt--;
}
void EX0_INT (void) __interrupt(0)
{
increment=-increment;
}
void main(void)
{
__code unsigned char LED_Array[4]={0x01,0x02,0x04,0x08};
int index=0;
EX0=1;
EA=1;
P1=0xFF;
while (1)
{
P1=~LED_Array[index];
Delay(30000);
index=index+increment;
if(index>3) index=0;
if(index<0) index=3;
}
}