移植uCOS-II到Cortex-M3平台(補遺)

來源:互聯網
上載者:User

去年寫過一篇介紹 uCOS-II 在Cortex-M3平台移植的文章:

http://blog.csdn.net/liyuanbhu/article/details/9082767

最近閑下來,研究了一下 FreeRTOS 官方的Cortex-M3平台的移植代碼,很有收穫,發現了幾處比 uCOS-II 移植代碼寫的好的地方。這裡簡單總結一下,算是給自己做個備忘。

對臨界區的處理

uCOS-II 移植代碼中對臨界區的處理是這樣的。

#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}  #define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}  __asm OS_CPU_SR OS_CPU_SR_Save(void)  {  MRS     R0, PRIMASK ; Set prio int mask to mask all (except faults)  CPSID   I  BX      LR  }  __asm void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr)  {  MSR     PRIMASK, R0  BX      LR  }  

這種處理沒有什麼錯,但是處理的有點簡單。實際上 Cortex M3 核心支援對優先順序分級,最多可以分255級。像STM32系列的Cortex M3 核心單片機支援16個優先順序。uCOS-II 移植代碼中這樣已進入臨界區就將所有的中斷都給屏蔽掉會導致有些很緊急的事情無法及時處理。FreeRTOS移植代碼處理的就聰明的多。FreeRTOS移植代碼要求受作業系統管理的中斷使用較低優先順序,不受作業系統管理的中斷可以使用任意優先順序。臨界區中只屏蔽掉可以產生任務切換的中斷,也就是較低優先順序的中斷,而高優先順序不受影響。這樣可以提高對重要中斷的響應速度。

FreeRTOS 中定義了一個宏 configMAX_SYSCALL_INTERRUPT_PRIORITY

所有用到作業系統功能的中斷優先順序數字都要大於這個宏定義的值。(也就是優先順序比它低,Cortex M3中低優先順序對應的數字大)

進入臨界區只是屏蔽比 configMAX_SYSCALL_INTERRUPT_PRIORITY優先順序低的中斷。對應的代碼如下。

#define portENTER_CRITICAL()vPortEnterCritical()#define portEXIT_CRITICAL()vPortExitCritical()#define portDISABLE_INTERRUPTS()ulPortSetInterruptMask()#define portENABLE_INTERRUPTS()vPortClearInterruptMask( 0 )void vPortEnterCritical( void ){portDISABLE_INTERRUPTS();uxCriticalNesting++;__dsb( portSY_FULL_READ_WRITE );__isb( portSY_FULL_READ_WRITE );}void vPortExitCritical( void ){uxCriticalNesting--;if( uxCriticalNesting == 0 ){portENABLE_INTERRUPTS();}}__asm unsigned long ulPortSetInterruptMask( void ){PRESERVE8mrs r0, baseprimov r1, #configMAX_SYSCALL_INTERRUPT_PRIORITYmsr basepri, r1bx r14}__asm void vPortClearInterruptMask( unsigned long ulNewMask ){PRESERVE8msr basepri, r0bx r14}

這裡可能有些人會覺得代碼有問題。退出臨界區時應該恢複進入臨界區前的狀態。如果進入臨界區之前就屏蔽了一小部分優先順序很低的中斷,那麼退出臨界區後這一小部分中斷還是應該被屏蔽的。FreeRTOS 網站對此的解釋如下:“Many bug reports are received that claim BASEPRI should be returned to its original value on exit, and not just set to zero, but the Cortex-M NVIC will never accept an interrupt that has a priority below that of the currently executing interrupt - no matter what BASEPRI is set to. An implementation that always sets BASEPRI to zero will result in faster code execution than an implementation that stores, then restores, the BASEPRI value (when the compiler's optimiser is turned on). ”

說的有些道理,其實將PRIMASK的值儲存下來也增加不了多少負擔。因此,我覺得可以這麼寫:

__asm OS_CPU_SR OS_CPU_SR_Save(void)  {  MRS     R0, PRIMASK ;     MOV R1, #configMAX_SYSCALL_INTERRUPT_PRIORITY MSR BASEPRI, R1  BX      LR  }  __asm void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr)  {  MSR     PRIMASK, R0  BX      LR  }  

再多說一句,如果使用的是STM32系列單片機,在啟動核心之前,應該如下設定:

NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );

保證所有的優先順序都是搶佔式優先順序。

任務切換

uCOS-II 移植代碼中任務切換的代碼(虛擬碼)如下。

OS_CPU_PendSVHandler  {     if (PSP != NULL)   {  Save R4-R11 onto task stack;   OSTCBCur->OSTCBStkPtr = SP;   }  OSTaskSwHook();   OSPrioCur = OSPrioHighRdy;   OSTCBCur = OSTCBHighRdy;   PSP = OSTCBHighRdy->OSTCBStkPtr;  Restore R4-R11 from new task stack;   Return from exception;   }  

代碼中需要根據PSP 的值來判斷是否是從 OSStartHighRdy() 函數過來的。因為OSStartHighRdy()的最後無法類比一次中斷返回,所以用了這麼個法子。FreeRTOS 移植代碼就聰明的多。反正 Cortex M3 系列支援的中斷很多,根本用不完,浪費點也無所謂。OSStartHighRdy() 的最後不觸發 PendSV 中斷,換個其他不用的中斷用不就可以了。FreeRTOS 移植代碼用的是 SVC 中斷。

下面是採用這種方法重寫的OSStartHighRdy() 函數,其中注釋掉的幾行是原始的uCOS-II 移植代碼採用的方法。

OSStartHighRdy    LDR     R0, =NVIC_SYSPRI14    ; Set the PendSV exception priority    LDR     R1, =NVIC_PENDSV_PRI    STRB    R1, [R0];    MOVS    R0, #0                ; Set the PSP to 0 for initial context switch call;MSR     PSP, R0    LDR     R0, =OSRunning        ; OSRunning = TRUE    MOVS    R1, #1    STRB    R1, [R0]SVC     0x00;    LDR     R0, =NVIC_INT_CTRL    ; Trigger the PendSV exception (causes context switch);    LDR     R1, =NVIC_PENDSVSET;    STR     R1, [R0]    CPSIE   I                     ; Enable interrupts at processor levelOSStartHang  B       OSStartHang           ; Should never get here

這樣的話,任務切換就分配到了兩個中斷中,好像有點浪費,不夠SVC 中斷不用更是浪費。

PendSV_Handler    CPSID   I                         ; Prevent interruption during context switch    MRS     R0, PSP                   ; PSP is process stack pointer;    CBZ     R0, PendSVHandler_nosave  ; Skip register save the first time    SUBS    R0, R0, #0x20             ; Save remaining regs r4-11 on process stack    STM     R0, {R4-R11}    LDR     R1, =OSTCBCur             ; OSTCBCur->OSTCBStkPtr = SP;    LDR     R1, [R1]    STR     R0, [R1]                  ; R0 is SP of process being switched out                                      ; At this point, entire context of process has been savedPendSVHandler_nosave    PUSH    {R14}                     ; Save LR exc_return value    LDR     R0, =OSTaskSwHook         ; OSTaskSwHook();    BLX     R0    POP     {R14}    LDR     R0, =OSPrioCur            ; OSPrioCur = OSPrioHighRdy;    LDR     R1, =OSPrioHighRdy    LDRB    R2, [R1]    STRB    R2, [R0]    LDR     R0, =OSTCBCur             ; OSTCBCur  = OSTCBHighRdy;    LDR     R1, =OSTCBHighRdy    LDR     R2, [R1]    STR     R2, [R0]    LDR     R0, [R2]                  ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;    LDM     R0, {R4-R11}              ; Restore r4-11 from new process stack    ADDS    R0, R0, #0x20    MSR     PSP, R0                   ; Load PSP with new process SP    ORR     LR, LR, #0x04             ; Ensure exception return uses process stack    CPSIE   I    BX      LR                        ; Exception return will restore remaining contextSVC_HandlerCPSID   I                         ; Prevent interruption during context switch    LDR     R0, =OSPrioCur            ; OSPrioCur = OSPrioHighRdy;    LDR     R1, =OSPrioHighRdy    LDRB    R2, [R1]    STRB    R2, [R0]    LDR     R0, =OSTCBCur             ; OSTCBCur  = OSTCBHighRdy;    LDR     R1, =OSTCBHighRdy    LDR     R2, [R1]    STR     R2, [R0]    LDR     R0, [R2]                  ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;    LDM     R0, {R4-R11}              ; Restore r4-11 from new process stack    ADDS    R0, R0, #0x20    MSR     PSP, R0                   ; Load PSP with new process SP    ORR     LR, LR, #0x04             ; Ensure exception return uses process stack    CPSIE   I    BX      LR                        ; Exception return will restore remaining context

這樣的話,任務切換中斷中少了一個條件跳躍陳述式,可以節省幾個刻度,代碼也稍微簡單一些,這樣做還是值得的。

加上這些改進,這個移植代碼應該就比較完美了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.