摘要:鑒于MCS-51系統只提供“二級中斷嵌套”,提出擴展51系統中斷優先級的純軟件方法。其利用51系統內建的中斷允許寄存器IE和中斷優先級寄存器IP,通過屏蔽字機制來實現;以C51的形式,給出這種擴展方法的函數庫實現,為該方法的使用賦予友好、簡潔的用戶接口。 關鍵詞:MCS-51單片機 中斷優先級 軟擴展 C51 引言 眾所周知,MCS-51系統只提供“二級中斷嵌套”,而大多數嵌入式系統希望有多于兩級的優先級別。因為一般來說,系統都有掉電中斷,且應置為最高優先級,這樣所有其它中斷只能共用一個最低優先級,如此,往往不能滿足實際的邏輯需求。為了使系統具有多于兩級的中斷優先級別,可以利用8259A之類的中斷控制芯片實現中斷優先級的硬擴展,但卻增加了系統的造價和復雜性。因復雜性的提高,系統的可靠性將受到影響。本文提出一種擴展MCS-51系統中斷優先級的純軟件方法,不需增加任何硬件,且所需的額外資源消耗也很小。實際應用表明這種方法是可行的和有效的。 1 MCS-51的中斷系統簡介 MCS-51系列單片機允許有五個中斷源,提供兩個中斷優先級,可實現二級中斷嵌套。這兩級優先級遵循下述規則:僅高優先級中斷源可中斷嵌套低優先級中斷源。為實現這一規則,中斷系統內部包含兩個不可尋址的優先級狀態觸發器。當特定優先級的某中斷源被響應時,相應的觸發器即被置位,直到執行了RETI指令后,這個觸發器才復位。在此期間,同級和低級中斷將被防止。中斷源的中斷請求能否得到響應,受中斷允許寄存器IE的控制。每個中斷源的優先級可通過對中斷優先級寄存器IP編程來設定:或最低,或最高。同一優先級中的各中斷源同時請求中斷時,由內部查詢邏輯確定響應次序。查詢次序依次為:外部中斷0(X0)、定時器中斷0(T0)、外部中斷1(X1)、定時器中斷1(T1)、串口中斷(S)。如果當前指令是RETI或是對IE、IP操作的指令,將封裝CPU對中斷的響應,且必須再執行完一條指令之后才會響應中斷。 2 中斷優先級軟擴展的方法 首先,給出軟擴展的第一種方法,并分析其特點,指出其存在的缺陷。然后,基于對方法一的不足之處,給出不斷完善的方法二、方法三。其中方法二是對方法一的完善,方法三是對方法二的完善,并最終解決了方法一、二中的缺陷,實現了真正的中斷優先級的軟擴展。 2.1 方法一 此法僅使用和系統的中斷允許寄存器IE,通過中斷屏蔽字機制,以使不同的中斷源具有不同的邏輯中斷優先級(下文中的“優先級”如不加說明即指“邏輯中斷優先級”)。 不失一般性,不妨令8051系統的五個中斷源——外中斷0(X0)、定時器中斷0(T0)、外中斷1(X1)、定時器中斷1(T1)及串口中斷(S),有如表1所列的優先級。(實際應用中,視具體情況,賦予不同中斷源以適當的優先級。) 其中,“0”代表最高優先級,“4”代表最低優先級。 首先,給設定了優先級的諸中斷源賦以二級“物理中斷優先級”:將優先級最高的中斷源(X1)在中斷優先級寄存器IP中的相應位(PX1)置1,而令IP中的其它相關位(PT1、PT0、PS、PX0)為0。 其次,給設定了優先級的各中斷源分配適當的“中斷屏蔽字”。其基本思想是屏蔽同級和低級中斷。具體分配過程如下:優先級為k(0≤k≤N-1,N為中斷源數量)的中斷源的“中斷屏蔽字”為:優先級為x(x∈[k,N-1],即同級和低級)的中斷源在IE中的對應位置0,優先級為y(y∈[0,k-1],即高級)的中斷源在IE中的相應位置1而得的位組字節。當然,IE的EA位(CPU中斷允許標志位)始終為1. 對于表1所列的中斷優先級分配情況,各中斷源的“中斷屏蔽字”配置如表2所列。
最后,給各中斷源的ISR(Interrupt Routine,中斷服務例程)加以如下所示的外殼(Assembly形式的)。不妨以定時器0(T0)為例: CSEG AT 8×1+3 ;定義絕對段,設置斷向量 JMP T0_ISR_SHELL ?PR?TO_ISR_SHELL?XX SEGMENT CODE ;聲明再定位段 T0_ISR_SHELL: PUSH IE ;保存IE MOV IE,#TO_INT_MASK ;設置當前中斷屏蔽字 CALL ResetIntSys:復位中斷系統 CALL T0_ISR:調用中斷服務例程的主體 POP IE ;恢復IE RET 這里,T0_ISR為定時器0(T0)的ISR的主體部分。其應以一般函數的形式,用匯編或C編寫。ResetIntSys為僅含一條中斷返回指令(IRET)的函數,即ResetIntSys:RETI。其用于復位中斷系統,以使在相應ISR執行過程中,系統仍可響應其它中端源提出的中斷請求,以便實現中斷嵌套。這樣就達到了防止同級和低級優先級中斷的目的。 高優先級的中斷源可以提出中斷請求,但未必會被立即響應。因為在當前策略下,尚不能實現真正的“中斷嵌套”(即高優先級的中斷服務例程可中斷低優先級的中斷服務例程而嵌套執行),而僅有最高優先級的中斷(X1)才可以實現這種真正的“中斷嵌套”。因為在8051系統里,中斷能否嵌套僅取決于其相應的“物理中斷優先級”(各中斷源的物理中斷優先級由中斷優先級寄存器IP中的相應位決定,且僅有二級)。下面分三種情況說明方法一的特點和不足: ①當外部中斷1(X1,其具有最高的邏輯中斷優先級和最高的物理中斷優先級)提出中斷請求時,系統將立即響應,而不管系統此時忙否。如果此時 系統正在執行其它中斷的ISR,X1的ISR將以嵌套形式執行,因為其它中斷湖泊的物理中斷優先級都為最低(51系統僅有兩級物理優先級:最高或最低)。 ②當定時器0(T0,其優先級為2)的中斷請求正被響應時,來自串口(S,其優先級為3)和外部中斷0(X0,其優先級為4)的中斷請求將被禁止;而只允許外部中斷1(X1,其優先級為0)和定時器1(T1,其優先級為1)提出中斷請求。如果是X1提出中斷請求,則X1的ISR將立即嵌套執行;如果是T1提出,盡管其優先級高于當前中斷T0,但因其物理中斷優先級與T0一樣(同為最低),故而將不會像X1那樣被系統立即響應,并嵌套執行,而只能等待,直到T0的中斷服務例程執行完畢。 ③如果在串口(S,其優先級為3)中斷正被響應過程中,定時器1(T1,其優先級為1)與定時器0(T0,其優先級為2)分別提出中斷請求。由于它們有高于S的優先級,所以系統允許它們提出中斷請求;但因其物理優先級與S一樣,故而直到S的中斷服務例程執行完畢,系統才會受理T1與T0的中斷請求。邏輯上,由于T1具有高于T0的優先級,所以T1應先為系統響應。但因物理優先級相同時,中斷請求的響應次序取決于內部查詢順序,而T0先于T1,所以實際上T0先 系統響應,即出現了“優先級反轉”的問題。 可見,方法一雖然可以部分地達到“擴充中斷優先級”的目的,但其存在兩個問題: *某些高優先級中斷不能中斷嵌套低優先級中斷; *會出現“優先級中反轉”。 方法二和方法三是針對方法一的這兩個不足而提出的,并最終實現對51系統的中斷優先級的真正擴展。 2.2 方法二 該方法是在方法一基礎上,為解決“優先級反轉”的問題,而實施的簡單策略而得。 根據方法一中對“優先級反轉”問題的分析可知,出現該問題的原因是:各中斷源的邏輯優先級與其內部查詢順序不一致。只要在系統設計時,兼顧中斷源相關事件的緊迫程度與中斷源的內部查詢邏輯:將最緊迫的事件(如掉電)賦以最高的優先級0,并使其與系統中的最先被查詢的中斷源(外部中斷0
如此,即可解決“優先級反轉”的問題。 2.3 方法三 本法是在方法一、二的基礎上,針對“某些高優先級中斷不能中斷嵌套低優級中斷”的問題,引入相應的策略,以實現對51系統中斷優先級的“真正”擴展。 為了真正擴展51系統的優先級,各中斷源的優先級、優先級屏蔽字、中斷屏蔽字應是確定的,如表3、4所列。C51編寫斷服務例程時,應給出相應的中斷源編號(中斷號)。特定中斷源有特定的中斷號,而此中斷號恰與各中斷應有的優先級一致。 本文用C51,以函數庫的形式實現方法三所述的策略,其包含兩個文件:ExtIntPri.H、ExtIntPri.C。須要指出,為使優先級的設置和恢復具有原子性以防出現混亂,應對SetPriority()和ResetPriority()作臨界處理,以使其不被“再入”訪問。另外,應對系統棧作調整。如圖1所示,其中“1”代表SetPriority()所作的調整,其將IP、IE保存于系統棧中;“2”代表ResetPriority()所作的調整,其從系統棧中恢復IE、IP;“HAddr”、“LAddr”分別代表當前函數返回地址的高位字節和低字節(棧中的地址是以小端字節序 //ExtIntPri.H extern void SetPriority(unsigned char); extern void SetPriority(unsigned char); extern void ResetPriority(void); //ExtIntPri.C #pragma src #include "ExtIntPri.H" #include //靜態(局部)函數聲明 static void ResetIntSys(void);//僅含一條指令:RETI //寬兩個宏用作“臨界區”的進入區和退出區 #define ENTER_CRITICAL()EA=0//關中斷,以防臨界再入 #define EXIT_CRITICAL() EA=1 //中斷屏蔽字和優先級屏蔽字的宏定義,如表3所列。 #define S_INT_MASK 0x8F//;1-01111B //… #define S_PRI_MASK 0x0F//;---01111B //… //先調整系統棧以保存IP、IE,其過程如圖1所示,再為給定中斷 //(prio也是中斷號)設置優先級 void SetPriority(unsigned char prio){ ENTER_CRITICAL();//關中斷 #pragma asm POP ACC //彈出返回地址的高位字節HAddr POP B //彈出返回地址的低位字節Laddr PUSH IP PUSH IE //EA= =0 PUSH B //LAddr進棧 PUSH ACC //HAddr進制 #pragma endasm switch(prio){ case 0:IP=X0_PRI_MASK;IE=X0_INT_MASK; break; //… case4:IP=S_PRI_MASK;IE=S_INT_MASK;break; } ENTER_CRITICAL();//這里中斷被打開,故再關中斷 ResetIntSys(); EXIT_CRITICAL();//開中斷 } //從系統棧中恢復IE、IP,其過程如圖1所示。該函數應在退出ISP時調用 void ResetPriority(void){ ENTER_CRITICAL(); #pragma asm POP ACC //彈出返回地址的高位字節HAddr POP B //彈出返回地址的低位字節LAddr POP IE //EA= =0 POP IP PUSH B //LAddr進棧 PUSH ACC //Haddr進棧 #pragma endasm EXIT_CRITICAL();//開中斷 } //僅含一條指令:RETI,用以復位中斷系統,以便系統在ISR執行過程中可響應其它中斷 void ResetIntSys(void){ char code reti=0x32; //32H為RETI的機器碼 (((void)(code*)(void))(&reti))();//將reti的地址強制轉化為函數指針 } 使用時,只需將ExtIntPri.H頭文件用#include加入相應源文件(當然,應將ExtIntPri.C的目標文件<*.Obj>、庫文件<*.lib>或匯編源文件<*.src>加入當前工程)。不妨以定時器0(T0)為例,其中斷號為1,故優先級亦應為1,如下所示: ////Test.C #include "ExtIntPri.H" //… void T0_ISR(void)interrupt 1 using 2{ SetPriority(1); //… ResetPriority(); } 如此,T0即有了次最高優先級——1。 結語 使用本文所述的“軟擴展”方法,可以將MCS-51系統的中斷優先級擴展到5級。如果所用51系統的中斷源個數為N(N≤8),只須對上述方法稍作修改即可將其優先級擴展到N級。該方法不需增加任何硬件,且所需的額外資源消耗很小,使用也非常簡單,不會給用戶增加編程負擔。實際應用表明,這種方法是可行的和有效的。 |
- MCS-(14032)
相關推薦
評論
查看更多