專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機教程網(wǎng) >> MCU設(shè)計實例 >> 瀏覽文章

UCOS-II中OS_CPU_IRQ_ISR移植過程分析

作者:龔平   來源:本站原創(chuàng)   點擊數(shù):  更新時間:2014年03月14日   【字體:

在uc/os-II的移植過程中存在一個通用的irq中斷處理函數(shù),其中的實現(xiàn)過程如下:

 

    OS_CPU_IRQ_ISR
    STMFD SP!, {R1-R3} ; We will use R1-R3 as temporary registers
    MOV R1, SP
    ADD SP, SP, #12 ;Adjust IRQ stack pointer
    SUB R2, LR, #4 ;Adjust PC for return address to task
    MRS R3, SPSR ; Copy SPSR (Task CPSR)
    MSR CPSR_cxsf, #SVCMODE|NOINT ;Change to SVC mode
    ; SAVE TASK''S CONTEXT ONTO OLD TASK''S STACK
    STMFD SP!, {R2} ; Push task''s PC
    STMFD SP!, {R4-R12, LR} ; Push task''s LR,R12-R4
    LDMFD R1!, {R4-R6} ; Load Task''s R1-R3 from IRQ stack
    STMFD SP!, {R4-R6} ; Push Task''s R1-R3 to SVC stack
    STMFD SP!, {R0} ; Push Task''s R0 to SVC stack
    STMFD SP!, {R3} ; Push task''s CPSR
    LDR R0,=OSIntNesting ;OSIntNesting++
    LDRB R1,[R0]
    ADD R1,R1,#1
    STRB R1,[R0]
    CMP R1,#1 ;if(OSIntNesting==1){
    BNE %F1
    LDR R4,=OSTCBCur ;OSTCBHighRdy->OSTCBStkPtr=SP;
    LDR R5,[R4]
    STR SP,[R5] ;}
    1 MSR CPSR_c,#IRQMODE|NOINT ;Change to IRQ mode to use IRQ stack to handle interrupt
    LDR R0, =INTOFFSET
    LDR R0, [R0]
    LDR R1, IRQIsrVect
    MOV LR, PC ; Save LR befor jump to the C function we need return back
    LDR PC, [R1, R0, LSL #2] ; Call OS_CPU_IRQ_ISR_handler();
    MSR CPSR_c,#SVCMODE|NOINT ;Change to SVC mode
    BL OSIntExit ;Call OSIntExit
    LDMFD SP!,{R4} ;POP the task''s CPSR
    MSR SPSR_cxsf,R4
    LDMFD SP!,{R0-R12,LR,PC}^ ;POP new Task''s context
    IRQIsrVect DCD HandleEINT0

這個函數(shù)是irq中斷的通用處理形式,我對其中的代碼做一下簡要的分析和討論。
首先我需要簡要的分析一下中斷處理過程中我們應(yīng)該完成的任務(wù),首先cpu會自動完成一些操作,其中包括中斷的關(guān)閉已經(jīng)返回地址的保存,還有模式的切換等。我們程序員則需要完成一些寄存器的保存工作以及跳轉(zhuǎn)到具體的處理函數(shù)中,最后完成返回控制。
需要注意的是,本文中的UC/OS-II任務(wù)都運行在SVC模式下,而不是SYS模式下。任務(wù)棧中保存的寄存器也是這種模式下對應(yīng)的值。
我按照注釋號對代碼進行解釋:
2、STMFD   SP!, {R1-R3}; 主要是完成幾個寄存器的壓棧操作,為什么這么做呢?因為我們接下來將要使用這寫寄存器。為什么需要呢?這是因為當(dāng)前CPU工作的irq模式下,因此這里的SP并不是SVC模式下的SP指針,但是在UC/OS-II的移植過程中通常將各個任務(wù)工作在SVC模式下,同時還需要對寄存器的保存,在中斷發(fā)生以后,需要保存所有的寄存器以及CPSR的值,而當(dāng)前的SP并不是任務(wù)的棧,此時除了SP、R14的值發(fā)生了變化以外,其他的寄存器并沒有發(fā)生變化。但是由于后面需要使用這些寄存器,因此需要壓棧。
3、MOV     R1, SP; 將R14_irq的值保存到R1中,這個SP的保存主要是為了通過這個值訪問IRQ模式下的堆?臻g,實現(xiàn)對數(shù)據(jù)的訪問。
4、ADD     SP, SP, #12; 調(diào)整IRQ模式下的堆棧指針SP_irq,將這個指針指向IRQ堆棧的開始位置,方便下一次中斷的處理操作。
5、SUB R2, LR, #4 ;這個操作主要是調(diào)整返回地址,了解異常返回地址的理解其中的含義,這時候的返回地址實質(zhì)上就是任務(wù)的返回地址,也就是將來需要加載到PC中的值。
6、MRS     R3, SPSR;因為發(fā)生了IRQ中斷,此時CPU進入IRQ模式中,這時的SPSR_irq中保存了svc模式下的CPSR狀態(tài)。而任務(wù)堆棧中保存的剛好是SVC模式下的狀態(tài)寄存器,因此需要將SVC模式下的狀態(tài)寄存器首先讀出來,然后保存進任務(wù)的堆棧中,因此用R3來保存CPSR值。
7、MSR     CPSR_cxsf, #SVCMODE|NOINT; 因為任務(wù)的堆?臻g位于SVC模式下,因此首先需要將CPU的狀態(tài)切換到SVC模式下,然后進行任務(wù)情景的切換操作。
 
9、STMFD   SP!, {R2}; 這時候的SP是指在SVC模式下的R13_svc。中斷發(fā)生以后,SVC模式下的SP并沒有發(fā)生改變,返回以后,該值仍然存在,仍然指向任務(wù)的棧頂位置。根據(jù)任務(wù)?臻g的分布,首先需要保存PC值,然后是R14-R0,CPSR的值,最后是保存SP到任務(wù)中,這種分布狀態(tài)是和堆棧初始化過程的分布一致的。而通過調(diào)整的返回地址剛好就保存在了R2中,因此需要壓棧保存PC值。
10、STMFD   SP!, {R4-R12, LR};因為在之前的一系列操作中,并沒有對R4-R12,R14的值進行破壞,因此可以直接進行壓棧操作,實現(xiàn)任務(wù)堆棧中R4-R12,LR的保存操作。
 
接下來的幾句代碼是重點:實現(xiàn)了在對其他模式下堆?臻g的訪問問題。
11、LDMFD   R1!, {R4-R6};是指將R1地址處加載一些數(shù)據(jù)到R4-R6,這三個寄存器的值已經(jīng)被壓入棧中,對他們的修改并不會導(dǎo)致錯誤的產(chǎn)生,因此這三個寄存器實質(zhì)上是作為中間地址。其中加載的順序滿足高地址對應(yīng)高的高編號的寄存器值。R1我在前面就強調(diào)了用來訪問IRQ模式堆?臻g的參考地址。從這個地址向上分別保存了壓入棧中的R1-R3寄存器的值,也就是被中斷任務(wù)R1-R3的值。
12、STMFD   SP!, {R4-R6};也就是完成了對任務(wù)寄存器R1-R3的壓棧操作。
13、STMFD   SP!, {R0} ;前面的代碼中并沒有修改R0的值,因此其中的值仍然是任務(wù)的R0值,因此也需要壓棧操作。通過上面的幾句代碼就完成了任務(wù)的R0-R15所有寄存器的保存操作。接下的就應(yīng)該完成CPSR的保存。
14、STMFD   SP!, {R3};當(dāng)前的R3中保存了實際上是前面所說的SPCR_irq中的值,也就是SVC模式下的狀態(tài)寄存器的值,因此可以認為就是完成了任務(wù)的狀態(tài)寄存器的保存。
 
16-19、接下來的操作本應(yīng)該是完成將SP的值保存到任務(wù)堆棧空間的,但是在UC/OS-II中存在一個全局變量OSIntNesting,它表明了中斷嵌套的次數(shù),因此需要對這個值進行一次加1操作。
 
21、接下來的操作就是判斷是否在中斷嵌套中,也就是對全局變量進行比較操作,如果這個值是1,則認為只有一個中斷產(chǎn)生,如果不等于1,則認為實在中斷嵌套中。

CMP     R1,#1                   ;if(OSIntNesting==1){

 

22、BNE     %F1;如果是在中斷嵌套中,則直接跳轉(zhuǎn)到下面的中斷處理函數(shù)中

 
23-25、

LDR     R4,=OSTCBCur            ;OSTCBHighRdy->OSTCBStkPtr=SP;

       LDR     R5,[R4]
       STR     SP,[R5]
說明是從任務(wù)到中斷的過程,也就是只有一個中斷產(chǎn)生,不是在中斷嵌套中,這時就需要將SP的值保存到任務(wù)控制塊中。 以上的操作也就完成了任務(wù)情景的保存操作,接下來的操作就應(yīng)該是真正的中斷處理函數(shù)啦。
 
27、1 MSR CPSR_c,#IRQMODE|NOINT;實質(zhì)上是完成CPU模式的切換操作,進入到IRQ模式下。
 
接下來的實際處理過程就如前面兩篇文章中討論的中斷處理過程。

29-30、

    LDR     R0, =INTOFFSET

    LDR     R0, [R0]

得到INTOFFSET的值,實際上就是得到偏移量,實質(zhì)上就是中斷號產(chǎn)生中斷,通過這個寄存器可以快速的確定在二級向量表中該中斷的向量位置,該向量表中就保存了對應(yīng)中斷處理函數(shù)的函數(shù)地址。

 

32、LDR R1, IRQIsrVect

43、IRQIsrVect DCD HandleEINT0

這兩句說明了我前面的分析,IRQIsrVect實際上就是一個標(biāo)號,其中存儲了HandleEINT0,HandleEINT0又是我們IRQ中斷向量的入口地址(前一篇文章已經(jīng)說明),也就是說HandleEINT0是二級向量表的開始地址。因此此時R1中實際上就保存了HandleEINT0。

33、MOV     LR, PC;就是完成簡單的保存過程,這個過程實質(zhì)上就是保存了函數(shù)調(diào)用的返回地址。

34、LDR     PC, [R1, R0, LSL #2];這句代碼的意義是將地址R1+R0*4處的內(nèi)容加載到PC中,也就是實現(xiàn)函數(shù)的跳轉(zhuǎn),即函數(shù)的調(diào)用過程。其中R1=HandleEINT0,而R0恰好又是一個偏移量,每一個指針的空間是4個字節(jié),那么R1+R0*4地址處剛好就是對應(yīng)中斷號的向量。其中就保存了對應(yīng)中斷函數(shù)的地址。因此PC就保存了這個調(diào)用函數(shù)的入口地址。也就是實現(xiàn)了處理函數(shù)調(diào)用過程。

 

35、 MSR            CPSR_c,#SVCMODE|NOINT; 執(zhí)行這句代碼的前提就是被調(diào)用的函數(shù)執(zhí)行完畢了,相關(guān)的入棧出棧操作也已經(jīng)完成,恢復(fù)到了調(diào)用前的狀態(tài)。此時需要將CPU的模式切換到SVC模式下。

 

36、BL          OSIntExit  ;這個操作完成了中斷的切換,如果不是在中斷嵌套中,那么最高優(yōu)先級的任務(wù)就會被執(zhí)行,進入最高優(yōu)先的任務(wù)之后就不會再返回了,這是UC/OS-II中任務(wù)的特點,之后的代碼也就不會執(zhí)行了。這是特別需要注意的。但是如果任務(wù)處在中斷嵌套中,那么OSIntExit只是減少中斷嵌套的次數(shù),并不完成其他的操作。那么這時候就需要恢復(fù)之前被中斷的任務(wù)了,也就是需要完成任務(wù)堆棧的彈出操作。

 

39-41、

 LDMFD   SP!,{R4}               ;POP the task''s CPSR

 MSR              SPSR_cxsf,R4

 LDMFD   SP!,{R0-R12,LR,PC}^

這幾句代碼的實現(xiàn)實質(zhì)上是完成了在中斷嵌套中時的任務(wù)切換操作。

 

 

討論:

不知道我理解的對不對,我認為這段代碼存在一定的問題,具體的問題如下,因為在中斷嵌套中,CPU執(zhí)行的肯定就是中斷服務(wù)函數(shù),此時的任務(wù)處于低優(yōu)先級的,并不需要我們保存任務(wù)的信息。為什么這段代碼能夠運行的原因我認為主要是因為這種處理的方式是不可能導(dǎo)致中斷嵌套問題產(chǎn)生的。因為我們在進入中斷以后關(guān)閉了中斷使能位,不會產(chǎn)生中斷嵌套也就看不出問題所在。我認為如果在支持中斷嵌套的CPU中,應(yīng)該首先檢測是否在中期嵌套中,如果在中斷嵌套中,則不需要任務(wù)寄存器的保存,如果不在,則需要保存。

關(guān)閉中斷的方式避免了中斷嵌套產(chǎn)生的可能,這也說明一直需要保存任務(wù)的情景,使得這段代碼是有效的。

 

總結(jié):

在討論ARM的移植過程中,我覺得首先應(yīng)該搞清楚每一種情況下CPU的工作模式,同時搞清楚寄存器的特殊性,同時搞清楚中斷處理的一般過程。

關(guān)閉窗口

相關(guān)文章