天天看點

NXP的ARM7帶ucos中硬中斷與軟中斷響應詳細分析(轉)

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的響應過程是一緻的,對于快速中斷響應過程也是一樣的。