天天看點

初學者EFM32上移植uC/OSII

前言

之前大學接觸過uc/os,大學開了ARM這門課程,用的是周立功的教材,好像是大四開的,你懂的,實驗隻帶手不帶腦,複制–編譯–下載下傳–嘿,燈亮了–走人。

最近在接觸EFM32系列MCU,就想彌補一下大學的遺憾。為時間4天的準備和移植工作終于完成,還是,嘿燈亮了。

硬體:EFM32G890F128

IDE : IAR

準備工作

兩本書:Cortex-M3權威指南,uc/OSII作業系統原理(邵貝貝)。

因為核心之前已看過了,就琢磨了下uc,大學的時候接觸過點點還有點印象,是以看起來不費什麼勁,确實挺小的一個核。裡面關于移植的過程也挺詳細的。不過那裡關于移植的是ARM7核心。大同小異。原理性的東西是一樣的。

其餘,關于移植部分,各大晶片廠商都已經很完善了,對于求知的同學,這是一個了解CPU,作業系統的好地方。

建立模闆

  1. 官網下載下傳源代碼

    http://micrium.com/ ->downloads -> Application Notes ->

    初學者EFM32上移植uC/OSII
    壓縮包下載下傳下來解壓最主要的就是這幾個檔案夾了。
    初學者EFM32上移植uC/OSII
    當然還有一個PDF文檔,這個文檔主要是教你怎樣使用它們的官方工程模闆。英文好的可以照着上面的步驟一步一步來,沒什麼說的,當然你要有耐心看英語。我試了下:确實規範,一目了然。
    初學者EFM32上移植uC/OSII

    個人覺得學習的話還是自己建一個模闆吧,這樣有利于了解各個子產品之間的關系。是以我就以自己的習慣加參考官方的自己操作了。

    其實我們自己移植的話就隻要uc/OSII的核心源碼和三個移植檔案啦。最難的移植部分人家幫我們做好了,那就是那三個移植檔案了,即os_cpu.h,os_cpu_a.asm, os_cpu.c當然我們還是要看懂的。

  2. 接下來就是建工程,這是我自己的工程:
    初學者EFM32上移植uC/OSII

代碼分析

這裡的代碼分析就是與處理器相關的代碼了,最接近處理器的代碼:彙編。

主要幹了兩個個事情:

  1. 任務級和中斷級排程,這兩個函數的内容是一樣的,真正的排程則放到了PENDSVHandler。
  2. 系統開始啟動時,啟動最高優先級任務。
  3. PENDVHandler儲存上下文切換環境。

    下面分析整個OS_CPU_A.ASM檔案

EXTERN  OSRunning                   ; External references,外部标号
EXTERN  OSPrioCur
EXTERN  OSPrioHighRdy
EXTERN  OSTCBCur
EXTERN  OSTCBHighRdy
EXTERN  OSIntExit
EXTERN  OSTaskSwHook
EXTERN  OS_CPU_ExceptStkBase


PUBLIC  OS_CPU_SR_Save               ; Functions declared in this file,全局函數,可被其他子產品引用
PUBLIC  OS_CPU_SR_Restore
PUBLIC  OSStartHighRdy
PUBLIC  OSCtxSw
PUBLIC  OSIntCtxSw
PUBLIC  PendSV_Handler

NVIC_INT_CTRL   EQU     xE000ED04         ; Interrupt control state register. 中斷控制寄存器
NVIC_SYSPRI14   EQU     xE000ED22         ; System priority register (priority ).優先級寄存器
NVIC_PENDSV_PRI EQU           xFF         ; PendSV priority value (lowest).  PENDSV優先級(最低)
NVIC_PENDSVSET  EQU     x10000000         ; Value to trigger PendSV exception.  觸發PENDSV的值
           

;**********************************************************************************************

; 代碼部分

;**********************************************************************************************

RSEG CODE:CODE:NOROOT(2)

THUMB

;RSEG 是段選擇指令。RSEG CODE:選擇段 code。第二個 CODE 表示代碼段的意思,隻讀。

;NOROOT 表示:如果這段中的代碼沒調用,則允許連接配接器丢棄這段.(2)表示:4 位元組對齊。假如是(n),則表示 2^n 對

;齊,更多關于IAR編譯指令參考IAR編譯手冊。

;THUMB則為代碼為THUMB指令,M3為THUMB指令集。

OS_CPU_SR_Save
    MRS     R0, PRIMASK                          ; 儲存PRIMASK值至R0,OS_CPU_SR_Save傳回時,R0中值送入
                                                 ; cpu_sr.
    CPSID   I                                    ; PRIMASK =1 ,關中斷.
    BX      LR

OS_CPU_SR_Restore
    MSR     PRIMASK, R0                         ;把R0的值加載到PRIMASK,R0為調用OS_CPU_SR_Restore的函
                                                ;數的參數cpu_sr.                                        
    BX      LR
           
/*-----啟動最高優先級任務--作業系統啟動第一個任務時------*/
OSStartHighRdy
    LDR     R0, =NVIC_SYSPRI14                         ; 設定PENDSV為最低優先級
    LDR     R1, =NVIC_PENDSV_PRI
    STRB    R1, [R0]                                      

    MOVS    R0, #0                                     ; 把從堆棧置0,在排程器排程時通過判斷PSP是否 
    MSR     PSP, R0                                    ; 0來判斷系統是否為首次排程。
    STRB    R1, [R0]

    LDR     R0, =OS_CPU_ExceptStkBase                  ; 初始化主堆棧 
    LDR     R1, [R0]
    MSR     MSP, R1                                            

    LDR     R0, =OSRunning                             ; OSRunning = TRUE
    MOVS    R1, #1                                             
    STRB    R1, [R0]

    LDR     R0, =NVIC_INT_CTRL                         ; 觸發一次PENDSV 
    LDR     R1, =NVIC_PENDSVSET                        
    STR     R1, [R0]

    CPSIE   I                                          ; 打開中斷                                                          
OSStartHang
    B       OSStartHang                                ; 程式到這裡就崩了。
           
初學者EFM32上移植uC/OSII
;************************************************************************************************
                                        任務級切換與中斷級切換
;************************************************************************************************

OSIntCtxSw
    LDR     R0, =NVIC_INT_CTRL                                 ; 觸發一次PENDSV 
    LDR     R1, =NVIC_PENDSVSET                                 
    STR     R1, [R0]
    BX      LR  
;********************************************************************************************************
;                                     void OS_CPU_PendSVHandler(void)
;

;              a) 擷取SP, 通過判斷sp是否為第一次任務切換,如果不是則跳過如果是則跳轉到
               OS_CPU_PendSVHandler_nosave,第一次任務不需要進行現場保護。
;              b) 如果不是第一次任務切換,需儲存R4-R11的值至PSP;
;              c) 将上一任務的SP儲存在OSTCBCur->OSTCBStkPtr = SP;
;              d) 調用鈎子函數 OSTaskSwHook();
;              e) 擷取最高優先級任務, OSPrioCur = OSPrioHighRdy;
;              f) 獲得最高優先級任務控制塊, OSTCBCur = OSTCBHighRdy;
;              g) 從任務快做擷取其SP堆棧指針, SP = OSTCBHighRdy->OSTCBStkPtr;
;              h) 從起任務堆棧SP中恢複R4-R11;
;              i) 啟動異常傳回序列xPSR, PC, LR, R12, R0-R3
;
;           3)  PendSV handler中斷發生時:
;              a) xPSR, PC, LR, R12, R0-R3自動入棧程序堆棧PSP
;              b) 處理器模式從線程模式轉換到異常模式
;              c) 此時堆棧使用主堆棧MSP
;              d) OSTCBCur      指針指向的為被挂起的任務
;                 OSTCBHighRdy  指針指向将被排程的任務
;
;           4) 因為PendSV為最低優先級,是以隻有當沒有任何異常中斷的時候PendSV才會相應。
;               
;              
;************************************************************************************************

PendSV_Handler
           
CPSID   I                                    ; 關中斷

    MRS     R0, PSP                              
    CBZ     R0, OS_CPU_PendSVHandler_nosave      ; 第一次任務開始時,PSP為0,故跳過對R4-R11的儲存              

    SUBS    R0, R0, #0x20                        ; 總共需要儲存8個寄存器的值,即 8個字*4=32位元組=0x20,将
                                                 ;r4-r11存儲到psp中
    STM     R0, {R4-R11}
    LDR     R1, =OSTCBCur                       ; 儲存被中斷任務的堆棧
    LDR     R1, [R1]                            ; 擷取堆棧指針,任務堆棧指針在TCB的頂部。                  
    STR     R0, [R1]                                       ```CPU_PendSVHandler_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]                             ; 獲得最高優先級任務堆棧指針;
    LDM     R0, {R4-R11}                         ; 從PSP從恢複r4-11
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                              ;更新PSP,已啟動正确的傳回序列
    ORR     LR, LR, #0x04                        ;確定異常傳回後使用PSP(詳見M3異常傳回) 
    CPSIE   I                                    ;打開中斷
    BX      LR                                   ;啟動中斷傳回序列,擷取PC到任務斷點處。                                          
           
初學者EFM32上移植uC/OSII

代碼修改

這裡的代碼修改主要是開發闆上的啟動代碼和OS核心的挂接了。因為我們下載下傳下來的代碼本身是IAR下的代碼,是以很多關于編譯器的代碼我們就可以省了,至于什麼是與編譯器相關的代碼,那隻能翻書了。

ARM晶片都有一個啟動代碼.S檔案,就是放置向量表的地方。

1、函數名稱的對應

在OS核心os_cpu_a_asm中PENDSV中斷的名稱為OS_CPU_PendSvHandler.。而開發闆啟動代碼的PENDSV中斷标号為PendSV_Handler,保證兩者标号一直即可,我這裡将OS_CPU_PendSvHandler.改為PendSV_Handler。

Os_cpu_a_asm:

初學者EFM32上移植uC/OSII
初學者EFM32上移植uC/OSII

因為我自己開發闆工程目錄下core_cm3.h下有關于SysTick的函數:是以我們得把os_cpu_c.c關于SystemTick的函數和宏定義都注釋掉:

自己工程下的SysTick_Config()

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - ) > SysTick_LOAD_RELOAD_Msk)  return ();     

  SysTick->LOAD  = ticks - ;                                 
  NVIC_SetPriority (SysTick_IRQn, (<<__NVIC_PRIO_BITS) - );  
  SysTick->VAL   = ;                                         
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                    
  return ();                                                 
}
           

将os_cpu_c.c關于SystemTick的函數和宏定義都注釋掉:

#if  //-------注釋掉
void  OS_CPU_SysTickInit (INT32U  cnts)
{
    OS_CPU_CM3_NVIC_ST_RELOAD = cnts - 1u;
    OS_CPU_CM3_NVIC_PRIO_ST   = OS_CPU_CM3_NVIC_PRIO_MIN;
    OS_CPU_CM3_NVIC_ST_CTRL  |= OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC | OS_CPU_CM3_NVIC_ST_CTRL_ENABLE;
    OS_CPU_CM3_NVIC_ST_CTRL  |= OS_CPU_CM3_NVIC_ST_CTRL_INTEN;
}
#endif 

#if  //-------注釋掉
#define  OS_CPU_CM3_NVIC_ST_CTRL    (*((volatile INT32U *)xE000E010uL)) 
#define  OS_CPU_CM3_NVIC_ST_RELOAD  (*((volatile INT32U *)xE000E014uL)) 
#define  OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U *)xE000E018uL)) 
#define  OS_CPU_CM3_NVIC_ST_CAL     (*((volatile INT32U *)xE000E01CuL)) 
#define  OS_CPU_CM3_NVIC_PRIO_ST    (*((volatile INT8U  *)xE000ED23uL)) 


    #define  OS_CPU_CM3_NVIC_ST_CTRL_COUNT                    x00010000uL   
    #define  OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC                  x00000004uL   
    #define  OS_CPU_CM3_NVIC_ST_CTRL_INTEN                    x00000002uL   
    #define  OS_CPU_CM3_NVIC_ST_CTRL_ENABLE                   x00000001uL   
    #define  OS_CPU_CM3_NVIC_PRIO_MIN                               xFFu    
    #endif
           

總結

1、光知道移植這個操作肯定是不行的,還要知道為什麼這樣移植。這就要參考很多資料了,我是讀了邵貝貝的那本書,其實也不難,當然,我是懷着一股好奇的态度去閱讀的,我就想搞清楚,作業系統到底是個啥玩意。
  2、了解Cortex-M3核心,隻要接觸過單片機,不管是大學裡微機原理中的8086還是C51,隻要仔細研究過單片機的工作原理,其實也無非就是指令集,中斷,記憶體映射。。。至于外設操作大同小異。對于M3應該是很簡單的。
  3、此次移植的收獲很大,人家的程式設計方式,人家的代碼一看就舒服,人家的檔案structure。當然以前許多C語言教程微微提到過的東西,也終于見到他的影子了,總之,學習一個作業系統的源碼,對自己的程式設計能力和見識是很有幫助的。
  4、英文真的很重要,其實向這種移植過程micrim的網站上都有其應用文檔。自己能看懂,就不必去羨慕那些輔導機構的人為什麼知道的那麼多了哦。。。他們也是從國外的網站上借鑒過來的。做技術的,英語無處不在啊!
  5、中國的科技道路還有很長的路要走啊。
  最後,說一下,關于ucos方面的移植,網上的文章講爛了都,但扪心自問一下,自己是否真正徹底弄明白過!