一、中斷基礎概念
內核與外設之間的主要交互方式有兩種:輪詢和中斷。中斷系統使得內核具備了應對突發事件的能力。
在執行CPU當前程序時,由於系統中出現了某種急需處理的情況,CPU暫停正在執行的程序,轉而去執行另外一段特殊程序來處理出現的緊急事務,處理結束后,CPU自動返回到原來暫停的程序中去繼續執行。 這種程序在執行過程中由於外界的原因而被中間打斷的情況,稱為中斷。
采用中斷技術后,可以為計算機系統帶來以下好處:
1)實現分時操作
速度較快的CPU和速度較慢的外設可以各做各的事情,外設可以在完成工作后再與CPU進行交互,而不需要CPU去等待外設完成工作,能夠有效提高CPU的工作效率。
2)實現實時處理
在控制過程中,CPU能夠根據當時情況及時做出反應,實現實時控制的要求。
3)實現異常處理
系統在運行過程中往往會出現一些異常情況,中斷系統能夠保證CPU及時知道出現的異常,以便CPU去解決這些異常,避免整個系統出現大的問題。
兩個重要的概念:
<1> 中斷服務函數: 內核響應中斷后執行的相應處理程序。例如ADC轉換完成中斷被響應后,CPU執行相應的中斷服務函數,該函數實現的功能一般是從ADC結果寄存器中取走並使用轉換好的數據。
<2> 中斷向量:中斷服務程序的入口地址,當CPU響應中斷請求時,會跳轉到該地址去執行代碼。
二、 CC2530中斷
CC2530具有18個中斷源,每個中斷源都有它自己的位於一系列SFR 寄存器中的中斷請求標志。相應標志位請 求的每個中斷可以分別使能或禁用。
Tip:
(1)當調用中斷服務例程時清除硬件。
(2)另外的 IRQ 掩碼和 IRQ 標志位存在。
三、CC2530中斷處理函數格式書寫
中斷服務函數與一般自定義函數不同,有特定的書寫格式:
#pragma vector = 中斷向量
__interrupt void 函數名稱 (void)
{
/*此處編寫中斷處理函數的具體程序*/
PxIFG = 0; //先清除Px引腳的中斷狀態標志位
PxIF = 0; //再清除Px端口組的中斷狀態標志位
}
<1> 在每一個中斷服務函數之前,都要加上一句起始語句:
#pragma vector = <中斷向量>
<中斷向量>表示接下來要寫的中斷服務函數是為那個中斷源服務的,該 語句有兩種寫法:
#pragma vector = 0x7B或者#pragma vector = P1INT_VECTOR
前者是中斷向量的入口地址,后者是頭文件“ioCC2530.h”中的宏定義。
<2> _ _interrupt關鍵字表示該函數是一個中斷服務函數,<函數名稱>可以 自定義,函數體不能帶有參數,也不能有返回值。
四、外部中斷
(1)CC2530的P0、P1和P2端口中的每個引腳都具有外部中斷輸入功能,要使某些引腳具有外部中斷功能,需要對IENx寄存器、PxIEN寄存器和PICTL寄存器進行適當的設置。 除了各個中斷源都有自己的中斷使能開關之外,中斷系統還有一個總開關,可以同“EA = 1;”來打開總中斷。
IENx寄存器 使能端口組的中斷功能。
PxIEN寄存器 使能具體的外部中斷引腳。
PICTL寄存器 設置中斷觸發方式。
(2)P0、P1和P2端口分別使用P0IF、P1IF和P2IF作為中斷標志位,任何一個端口組上的引腳產生外部中斷時,都會將對應端口組的中斷標志自動置位。注意,外部中斷標志必須在中斷服務函數中手工清除,否則CPU會反復進入中斷。 端口狀態標志寄存器P0IFG、P1IGF和P2IFG,分別對應3個端口中各引腳的中斷觸發狀態,當某引腳發生外部中斷觸發時,對應的標志位會自動置位,這個標志同樣需要手工清除。
題目:
將Zigbee小模塊上SW1按鍵設置為外部中斷輸入引腳。在中斷服務函數中,控制一個LED6燈的開關切換,也就是原來LED是點亮的熄滅,原來熄滅的點亮。同時在主程序中,運行一段跑馬燈程序,使LED3和LED4輪流點亮和熄滅。
思路:
(1)端口初始化和跑馬燈程序
(2)定義一個外部中斷初始化函數,將SW1引腳,即P1_ _2引腳配置成外部中斷輸入端口,將其中斷觸發方式設置為下降沿觸發。
(3)為外部中斷定義-一個中斷服務函數,要照中斷服務函數的書寫格式編程,注意要在中斷服務函數中把相應的中斷標志位清除。必須先清除引腳的中斷標志,再清除端口組的中斷標志。
解題:
【1】設計外部中斷初始化函數Init_INTP()
外部中斷初始化函數,主要是完成跟中斷相關的特殊功能寄存器配置工作:
<1> 配置IENx寄存器,使能端口組的中斷功能。
<2> 配置PxIEN寄存器,使能具體的外部中斷引腳。
<3> 配置PICTL寄存器,設置中斷觸發方式。
1 void Init_INTP() 2 { 3 IEN2 |= 0x10; //端口1中斷使能 0001 0000 端口1 中斷使能 4 P1IEN |= 0x04; //端口P1_2外部中斷使能 0000 0100 P1_2 使能具體的外部中斷引腳。 5 PICTL |= 0x02; //端口P1_0到P1_3下降沿觸發 0000 0010 P1_0和P1_1 下降沿觸發 6 EA = 1; //使能總中斷 7 }
【2】設計外部中斷服務函數Int1_Sevice()
在編寫中斷服務函數的時候,書寫格式要正確,中斷向量不能搞錯。特別要注意:在函數里面把端口組和引腳的標志位清除,否則CPU將會反復進入中斷,必須先清除引腳標志位PxIFG,再清除端口組標志位PxIF。
#pragma vector = P1INT_VECTOR //外部中斷1的向量入口 __interrupt void Int1_Sevice() { LED6 = ~LED6; /*先清除引腳標志位,再清除端口標志位,否則會不斷進入中斷*/ P1IFG &= ~ 0x04; //軟件清除P1_2引腳的標志位 P1IF = 0; //軟件清除P1端口組的標志位 }
最終代碼:

1 #include "ioCC2530.h" 2 3 #define LED6 P1_4 4 #define LED3 P1_0 5 #define LED4 P1_1 6 /*===================延時函數=========================*/ 7 void Delay(unsigned int t) 8 { 9 while(t--); 10 } 11 /*==================端口初始化函數=====================*/ 12 void Init_Port() 13 { 14 //將P1_0、P1_1和P1_4設置為通用I/O端口功能 15 P1SEL &= ~0x13; 16 //將P1_0、P1_1和P1_4的端口傳輸方式設置為輸出 17 P1DIR |= 0x13; 18 19 P1SEL &= ~0x04; //P1_2作為通用I/O端口 20 P1DIR &= ~0x04; //P1_2端口輸入 21 P1INP &= ~0x04; //P1_2設置為上拉/下拉模式 22 P2INP &= ~0x40; //P1_2設置為上拉 23 24 LED6 = 0; 25 LED3 = 0; 26 LED4 = 0; 27 } 28 /*==================跑馬燈子函數=====================*/ 29 void LED_Running() 30 { 31 LED3 = 1; 32 Delay(50000); 33 LED4 = 1; 34 Delay(50000); 35 LED3 = 0; 36 Delay(50000); 37 LED4 = 0; 38 Delay(50000); 39 } 40 /*===============外部中斷初始化函數==================*/ 41 void Init_INTP() 42 { 43 IEN2 |= 0x10; //端口1中斷使能 0001 0000 端口1 中斷使能 44 P1IEN |= 0x04; //端口P1_2外部中斷使能 0000 0100 P1_2 使能具體的外部中斷引腳。 45 PICTL |= 0x02; //端口P1_0到P1_3下降沿觸發 0000 0011 P1_0和P1_1 下降沿觸發 46 EA = 1; //使能總中斷 47 } 48 /*================外部中斷1服務函數====================*/ 49 #pragma vector = P1INT_VECTOR //外部中斷1的向量入口 50 __interrupt void Int1_Sevice() 51 { 52 LED6 = ~LED6; 53 /*先清除引腳標志位,再清除端口標志位,否則會不斷進入中斷*/ 54 P1IFG &= ~ 0x04; //軟件清除P1_2引腳的標志位 55 P1IF = 0; //軟件清除P1端口組的標志位 56 } 57 /*====================主函數==========================*/ 58 void main() 59 { 60 Init_Port(); //初始化通用I/O端口 61 Init_INTP(); //初始化外部中斷 62 while(1) 63 { 64 LED_Running(); //跑馬燈 65 } 66 }
總結
void Init_INTP() //中斷初始化函數
{
EA = 1; //開總中斷
IENx = ? ; //要用哪一個端口組?P0,P1還是P2?
/*
IEN1 |= 0x20;開P0端口組中斷
IEN2 |= 0x10;開P1端口組中斷
IEN2 |= 0x01;開P2端口組中斷
以上都為固定開各個端口組中斷的寫法,至於為什么是這樣子才能開P0,P1,或P2端口組的中斷,翻看手冊就知道了
*/
PxIEN = ? ; //開端口組中斷之后,要選擇用哪一個引腳?
PICTL = ? ; //開引腳中斷之后,選擇沿觸發方式,上升沿還是下降沿?
}
#pragma vector = 中斷向量
__interrupt void 函數名稱 (void)
{
/*此處編寫中斷處理函數的具體程序*/
PxIFG = 0; //先清除Px引腳的中斷狀態標志位
PxIF = 0; //再清除Px端口組的中斷狀態標志位
}