NXP的ARM7帶ucos中硬中斷與軟中斷響應詳細分析(轉)
2009-08-25 14:39:29| 分類: ucos|字号 訂閱
一.帶UCOS系統的軟中斷響應過程 1
1.第一步: 2
2.第二步: 2
二.帶UCOS系統的硬中斷響應過程 6
下面的主要分析LPC系列ARM7的中斷響應,以周立公老師帶ucos移植程式為分析對象,對其他的ARM中帶UCOS的項目也有參考價值。
一.帶UCOS系統的軟中斷響應過程
UCOS作業系統是以任務為單元的執行塊,可以了解為linux中線程,任務需要進行切換,輪巡的執行,這種任務切換是通過ARM的軟中斷來實作的。ARM的軟中斷是在向量表中就存在,寫在startup.s檔案中,如:
;interrupt vectors
;中斷向量表
Reset
LDR PC, ResetAddr
LDR PC, UndefinedAddr
LDR PC, SWI_Addr
LDR PC, PrefetchAddr
LDR PC, DataAbortAddr
DCD 0xb9205f80
LDR PC, [PC, #-0xff0]
LDR PC, FIQ_Addr
這個向量表是ARM具備的,放在程式的開始位置,具體怎麼放置和定位,要在編譯時候設定分散定位表(這裡不仔細講),ARM複位啟動的時候就會自動跑到開 始行PC, ResetAddr。第三行LDR PC, SWI_Addr為軟中斷響應行。附帶說明,這個向量表程式在編譯後的位址(程式在運作時可以看到彙程式設計式顯示的每行位址,也就是常說的中斷向量表中所有 資料32位累加和為0)累加和為0,是以可以看到程式中為什麼有DCD 0xb9205f80這行,數字是手動可以調整的。
軟中斷和硬中斷如果從程式角度分析,其實是一樣的,都是響應的一種中斷,響應開始要求環境和其他變量入桟保護起來,然後調用到中斷響應的C程式中(就 是自己寫中斷響應程式),執行完中斷響應程式後,再進行堆棧的出桟,恢複之前保護的環境與變量,再掉轉程式到開始進入中斷前的位置,這樣程式接着運作。其 實這個過程,跟51一樣,軟中斷與硬中斷也一樣。那軟中斷和硬中斷到底有什麼不同,軟中斷是可以人為控制的,硬中斷是ARM晶片裡面控制的,自動反應,如 序列槽中斷,是序列槽收到資料後,序列槽自動産生中斷。軟中斷是通過手動調用程式後産生的中斷。以我的了解,不知道是否還有其他差別。
現在我們具體可以分析軟中斷響應過程了:
1.第一步:
在UCOS中,當要進行任務切換,直接手動調用OS_TASK_SW()函數的調用,調用之後,相當執行了os_cpu.h檔案中的
__swi(0x00) void OS_TASK_SW(void);
這是ADS編譯器可以認識的軟中斷執行程式。這樣系統會響應軟中斷,程式會跳到上面講過的startup.s檔案中斷向量表中LDR PC, SWI_Addr,
2.第二步:
在startup.s檔案中有SWI_Addr DCD SoftwareInterrupt,相當再跳到SoftwareInterrupt函數,這個函數在OS_cpu_a.s檔案中:
;軟體中斷
SoftwareInterrupt
LDR SP, StackSvc ; 重新設定堆棧指針
STMFD SP!, {R0-R3, R12, LR}
MOV R1, SP ; R1指向參數存儲位置
MRS R3, SPSR
TST R3, #T_bit ; 中斷前是否是Thumb狀态
LDRNEH R0, [LR,#-2] ; 是: 取得Thumb狀态SWI号
BICNE R0, R0, #0xff00
LDREQ R0, [LR,#-4] ; 否: 取得arm狀态SWI号
BICEQ R0, R0, #0xFF000000
; r0 = SWI号,R1指向參數存儲位置
CMP R0, #1
LDRLO PC, =OSIntCtxSw
LDREQ PC, =__OSStartHighRdy ; SWI 0x01為第一次任務切換
BL SWI_Exception
LDMFD SP!, {R0-R3, R12, PC}^
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
這個函數,是自己寫的,可以進行改動。這其中就是上面說過的環境的一些入桟,出桟,還有查詢是否有更高優先級任務的響應。軟中斷響應時因為有不同的中斷, 如關OS_ENTER_CRITICAL,開OS_EXIT_CRITICAL等很多軟中斷,是以程式執行上面中的SWI_Exception執行自己的 外部中斷C響應程式,裡面實作具體的軟中斷:
void SWI_Exception(int SWI_Num, int *Regs)
{
OS_TCB *ptcb;
switch(SWI_Num)
{
//case 0x00:
// break;
//case 0x01:
// break;
case 0x02:
__asm
{
MRS R0, SPSR
ORR R0, R0, #NoInt
MSR SPSR_c, R0
}
OsEnterSum++;
break;
case 0x03:
if (--OsEnterSum == 0)
{
__asm
{
MRS R0, SPSR
BIC R0, R0, #NoInt
MSR SPSR_c, R0
}
}
break;
#if OS_SELF_EN > 0
case 0x40:
Regs[0] = _OSFunctionAddr[Regs[0]];
break;
case 0x41:
Regs[0] = _UsrFunctionAddr[Regs[0]];
break;
case 0x42:
OSIntNesting++;
break;
case 0x43:
if (OSTCBHighRdy == OSTCBCur)
{
Regs[0] = 0;
}
else
{
Regs[0] = 1;
}
break;
#endif
case 0x80:
__asm
{
MRS R0, SPSR
BIC R0, R0, #0x1f
ORR R0, R0, #SYS32Mode
MSR SPSR_c, R0
}
break;
case 0x81:
__asm
{
MRS R0, SPSR
BIC R0, R0, #0x1f
ORR R0, R0, #USR32Mode
MSR SPSR_c, R0
}
break;
case 0x82:
if (Regs[0] <= OS_LOWEST_PRIO)
{
ptcb = OSTCBPrioTbl[Regs[0]];
if (ptcb != NULL)
{
ptcb -> OSTCBStkPtr[1] &= ~(1 << 5);
}
}
break;
case 0x83:
if (Regs[0] <= OS_LOWEST_PRIO)
{
ptcb = OSTCBPrioTbl[Regs[0]];
if (ptcb != NULL)
{
ptcb -> OSTCBStkPtr[1] |= (1 << 5);
}
}
break;
default:
break;
}
}
這部分程式也是移植時自己寫的,裡面有彙編。
其中的switch部分程式,對應的關系可以os_cpu.h檔案中:
__swi(0x00) void OS_TASK_SW(void);
__swi(0x01) void _OSStartHighRdy(void);
__swi(0x02) void OS_ENTER_CRITICAL(void);
__swi(0x03) void OS_EXIT_CRITICAL(void);
__swi(0x40) void *GetOSFunctionAddr(int Index);
__swi(0x41) void *GetUsrFunctionAddr(int Index);
__swi(0x42) void OSISRBegin(void);
__swi(0x43) int OSISRNeedSwap(void);
__swi(0x80) void ChangeToSYSMode(void);
__swi(0x81) void ChangeToUSRMode(void);
__swi(0x82) void TaskIsARM(INT8U prio);
__swi(0x83) void TaskIsTHUMB(INT8U prio);
注意:上面的程式OS_TASK_SW任務切換,_OSStartHighRdy優先級最高的任務切換,不是在void SWI_Exception(int SWI_Num, int *Regs)函數中實作,被屏蔽了,直接改在OS_cpu_a.s檔案中實作的:
;軟體中斷
SoftwareInterrupt
。。。。
為什麼要調整在OS_cpu_a.s檔案中實作,這個沒有深入研究,我想應該都是可以的。
到現在整個軟中斷過程分析完了。
注意:上面的各個部分,每個階段具體安排在哪裡和哪個檔案中,是可以認為改動的,甚至響應的形式也可以調整,但是每一個軟中斷的過程和要完成的工作是一樣的,因為在用lpc21xx 系列時上面的整個程式結構基本一緻的,現在用LPC23XX的時候有一些變化。
二.帶UCOS系統的硬中斷響應過程
硬中斷,就是我們常說的中斷,一般指的是外圍裝置中斷,如序列槽,I2C,TCP,USB等。
硬中斷一般分為普通和快速中斷,對應上面所說的中斷向量表中:
LDR PC, [PC, #-0xff0]
LDR PC, FIQ_Addr
如果發生了普通的硬中斷,程式會跳到LDR PC, [PC, #-0xff0]行,由于對于一個ARM7不同的硬體中斷響應時的入桟和出桟以及現場保護是相同的,是以寫一個彙編的宏定義就可以實作多個硬中斷響應共用,這個宏在IQR.INC有:
MACRO
$IRQ_Label HANDLER $IRQ_Exception_Function
EXPORT $IRQ_Label ; 輸出的标号
IMP ORT $IRQ_Exception_Function ; 引用的外部标号
$IRQ_Label
SUB LR, LR, #4 ; 計算傳回位址
STMFD SP!, {R0-R3, R12, LR} ; 儲存任務環境
MRS R3, SPSR ; 儲存狀态
STMFD SP, {R3, SP, LR}^ ; 儲存使用者狀态的R3,SP,LR,注意不能回寫
; 如果回寫的是使用者的SP,是以後面要調整SP
LDR R2, =OSIntNesting ; OSIntNesting++
LDRB R1, [R2]
ADD R1, R1, #1
STRB R1, [R2]
SUB SP, SP, #4*3
MSR CPSR_c, #(NoInt | SYS32Mode) ; 切換到系統模式
CMP R1, #1
LDREQ SP, =StackUsr
BL $IRQ_Exception_Function ; 調用c語言的中斷處理程式
MSR CPSR_c, #(NoInt | SYS32Mode) ; 切換到系統模式
LDR R2, =OsEnterSum ; OsEnterSum,使OSIntExit退出時中斷關閉
MOV R1, #1
STR R1, [R2]
BL OSIntExit
LDR R2, =OsEnterSum ; 因為中斷服務程式要退出,是以OsEnterSum=0
MOV R1, #0
STR R1, [R2]
MSR CPSR_c, #(NoInt | IRQ32Mode) ; 切換回irq模式
LDMFD SP, {R3, SP, LR}^ ; 恢複使用者狀态的R3,SP,LR,注意不能回寫
; 如果回寫的是使用者的SP,是以後面要調整SP
LDR R0, =OSTCBHighRdy
LDR R0, [R0]
LDR R1, =OSTCBCur
LDR R1, [R1]
CMP R0, R1
ADD SP, SP, #4*3 ;
MSR SPSR_cxsf, R3
LDMEQFD SP!, {R0-R3, R12, PC}^ ; 不進行任務切換
LDR PC, =OSIntCtxSw ; 進行任務切換
MEND
END
每個中斷要申明一個宏,如:
;
Timer0_Handler HANDLER Timer0_Exception
其中:Timer0_Exception為自己的C響應中斷函數。
說明:在上面的宏彙程式設計式中:
BL $IRQ_Exception_Function ; 調用c語言的中斷處理程式
是每次中斷響應時c語言的中斷處理程式,這個是每個中斷初始化時給每個中斷向量位址的(對應nxp晶片的寄存器)。中斷響應時會跳到中斷向量表 LDR PC, [PC, #-0xff0]行,然後系統根據中斷響應的通道号值(NXP的arm7每個外圍裝置對應唯一的中斷通道值,51裡也是這樣的,就是中斷号)執行對應的宏 彙程式設計式,在彙編裡執行
BL $IRQ_Exception_Function ; 調用c語言的中斷處理程式初始化時
這個IRQ_Exception_Function 對應與中斷初始化時的函數,如定時器的:VICVectAddr0 = (uint32)Timer0_Handler; //定時器0中斷函數位址配置設定
這樣可以響應中斷函數了。
注意:上面的硬體中斷過程,也有可能寫法不完全一緻,但是對應nxp的ARM7的響應過程是一緻的,對于快速中斷響應過程也是一樣的。