helper2416上移植ucosii V290

 

首先到ucos的官網上下載ucosii的源碼(實際上是很多已經移植好的目標板,尋找下你說需要的板子是否在其上),找到一個相似的板子的源碼。我們的設備是開發板Help2416;採用的源碼是Micrium-uCOS-II-V290,參考源碼Micrium_STM32xxx_uCOS-II。在Micrium\AppNotes\AN1xxx-RTOS\AN1018-uCOS-II-Cortex-M3\這個路徑下的AN-1018.pdf非常重要,這文檔詳細的介紹了文件夾結構,關係,以及移植的細節。下面我們來慢慢分析。

首先參考STM32工程建立Ports\arm2416\Generic\MDK路徑,此路徑主要存放移植相關的代碼。新建

  1. OS_CPU.H  
  2. OS_CPU_C.C  
  3. OS_CPU_A.ASM  
  4. OS_DBG.C //needless 

OS_CPU.h中一般聲明瞭常量,宏和基本類型定義。先按照AN1018.pdf中的步驟來一步一步製作:

  先定義全局變量與外部定義符號

 

  1. #ifdef  OS_CPU_GLOBALS  
  2. #define OS_CPU_EXT  
  3. #else  
  4. #define OS_CPU_EXT  extern  
  5. #endif  

 

這是一個很智能的宏,在一些必要函數前加上,宏的定義位置就取決於文件中是否定義了OS_CPU_GLOBALS.

 

2.定義了基本數據

  1. typedef unsigned char  BOOLEAN;  
  2. typedef unsigned char  INT8U;   
  3. typedef signed   char  INT8S;   
  4. typedef unsigned short INT16U;              
  5. typedef signed   short INT16S;   
  6. typedef unsigned int   INT32U;  
  7. typedef signed   int   INT32S;   
  8. typedef float          FP32;               
  9. typedef double         FP64;   
  10. typedef unsigned int   OS_STK;             
  11. typedef unsigned int   OS_CPU_SR;         

由於都是32位小端,基本上基本類型都沒有什麼可修改的,繼續往下看。(在實際的工作中,需要按照目標CPU的位寬修改基本類型,以滿足有/無符號及位寬。

 

1.設置臨界區模式

這裏我們用常用的模式3,具體爲什麼不用模式1, 模式2,還不是很清楚,求瞭解的大大告之:[email protected]

  1. #define  OS_CRITICAL_METHOD    3    
  2. #define  OS_ENTER_CRITICAL()  {cpu_sr = OS_CPU_SR_Save();}  
  3. #define  OS_EXIT_CRITICAL()   {OS_CPU_SR_Restore(cpu_sr);} 
這裏的實質就是將CPSR暫存於cpu_sr這個unsigned long型的臨時變量裏面(內核使用到的時候已經自動添加了定義)。

2.設置堆棧增長方向

  1. #define  OS_STK_GROWTH        1  
1表示從高地址到低地址(實際上,除了個別8位單片機及特殊用途的芯片,大多數都是這樣的)。

3.定義上下文切換函數

  1. #define  OS_TASK_SW()         OSCtxSw()  
OSCtxSw()稍後我們會在os_cpu_a.s中實現。

4.定義函數原型

  1. #if OS_CRITICAL_METHOD == 3  
  2. OS_CPU_SR  OS_CPU_SR_Save(void);  
  3. void       OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);  
  4. #endif  
  5. void       OSCtxSw(void);  
  6. void       OSIntCtxSw(void);  
  7. void       OSStartHighRdy(void);   

接下來是OS_CPU_C.C這個文件的編寫

  1. OSInitHookBegin()  
  2. OSInitHookEnd()  
  3. OSTaskCreateHook()  
  4. OSTaskDelHook()  
  5. OSTaskIdleHook()  
  6. OSTaskStatHook()  
  7. OSTaskStkInit()  
  8. OSTaskSwHook()  
  9. OSTCBInitHook()  
  10. OSTimeTickHook()  

看似有很多需要編寫,實際上最主要的只需要一個 OSTaskStkInit(),那我們首先來編寫他

  1. OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) 
  2.     INT32U *stk; 
  3.         opt    = opt;                           /* 'opt' is not used, prevent warning                      */ 
  4.         stk    = (INT32U *)ptos;                /* Load stack pointer                                      */ 
  5.     *(--stk) = (INT32U)task;                /* Entry Point .. -> PC                                    */ 
  6.         *(--stk) = (INT32U)0;                   /* r14                                                     */ 
  7.         *(--stk) = (INT32U)0;                   /* r12                                                     */ 
  8.         *(--stk) = (INT32U)0;                   /* r11                                                     */ 
  9.         *(--stk) = (INT32U)0;                   /* r10                                                     */ 
  10.         *(--stk) = (INT32U)0;                   /* r9                                                      */ 
  11.         *(--stk) = (INT32U)0;                   /* r8                                                      */ 
  12.         *(--stk) = (INT32U)0;                   /* r7                                                      */ 
  13.         *(--stk) = (INT32U)0;                   /* r6                                                      */ 
  14.         *(--stk) = (INT32U)0;                   /* r5                                                      */ 
  15.         *(--stk) = (INT32U)0;                   /* r4                                                      */ 
  16.         *(--stk) = (INT32U)0;                   /* r3                                                      */ 
  17.     *(--stk) = (INT32U)0;                   /* r2                                                      */ 
  18.     *(--stk) = (INT32U)0;                   /* r1                                                      */ 
  19.     *(--stk) = (INT32U)pdata;               /* r0 : argument                                           */ 
  20.     *(--stk) = (INT32U)0x0;                 /* CPSR     model tel here 0x00000013L SVC mode  */ 
  21.     return ((void *)stk); 
  22. }  

注意:爲了演示各種各樣有可能的錯誤,我會在代碼中將正確選項標識或註解的方式給出。

在網上搜索這個壓棧順序,說是在arm核心pdf上有,但是實際上我只在cortexM3上看到過,其他芯片上沒有具體說,但是,我們只需要瞭解這裏的壓棧順序必須和OSCtxSw()的彈棧順序一一對應就OK了。

其他都是些預留的暫時不用的鉤子函數,只需要定義一個空的執行體就行了。

文件OS_CPU_A.S

需要我們編寫的代碼:

OS_CPU_SR_Save() 

OS_CPU_SR_Restore() 

OSStartHighRdy() 

OSCtxSw() 

OSIntCtxSw() 

  1. OSStartHighRdy 
  2.     MSR CPSR_cxsf, #SVCMODE:OR:NOINT    ;//switch to svcmode& disable irq&fiq 
  3.     BL  OSTaskSwHook 
  4.     LDR R0, =OSRunning 
  5.     MOV R1, #1 
  6.     STRB    R1, [R0] 
  7.     LDR R0, =OSTCBHighRdy 
  8.     LDR R0, [R0] 
  9.     LDR SP, [R0] 
  10.     LDMFD   SP!, {R0} 
  11.     MSR SPSR_cxsf, R0 
  12.     LDMFD   SP!, {R0-R12, LR, PC}^ 

這裏就只是將系統運行打開,然後將最高優先級任務的棧指針賦值給SP,然後彈棧。

  1. OSCtxSw 
  2.     STMFD   SP!, {LR} 
  3.     STMFD   SP!, {R0-R12, LR} 
  4.     MRS R0, CPSR 
  5.     STMFD   SP!, {R0} 
  6.     LDR R0, =OSTCBCur 
  7.     LDR R0, [R0] 
  8.     STR SP, [R0] 
  9. OSIntCtxSw 
  10.     BL  OSTaskSwHook 
  11.     LDR R0, =OSTCBHighRdy 
  12.     LDR R1, =OSTCBCur 
  13.     LDR R0, [R0] 
  14.     STR R0, [R1] 
  15.     LDR R0, =OSPrioHighRdy 
  16.     LDR R1, =OSPrioCur 
  17.     LDRB    R0, [R0] 
  18.     STRB    R0, [R1] 
  19.     LDR R0, =OSTCBCur 
  20.     LDR R0, [R0] 
  21.     LDR SP, [R0] 
  22.     LDMFD   SP!, {R0} 
  23.     MSR SPSR_cxsf, R0 
  24.     LDMFD   SP!, {R0-R12, LR, PC}^ 

這裏我偷了一個懶,將OSCtxSw的下半部同時給OSIntCtxSw使用。

上下文切換的實質就是OSCurTCB中開始地址存放了當前將要壓棧的任務的棧地址。OSTCBHighRdy中存放的是將要運行的任務的張地址。我們要先將當前的寄存器 PC CPSR壓棧進去,就完成了對原任務的保護工作;然後調用用戶自定義鉤子函數OSTaskSwHook,完成後再把當前最高優先級的任務賦值給OSTCBCur, 將當前優先級改爲新的最高優先級任務的優先級。然後讀取出TCB結構體中的棧地址給SP,完成彈棧操作。

因爲OSIntCtxSw是在中斷處理完成後調用的,進入中斷時已經完成了壓棧保護工作,所以可以偷懶將上述代碼的後半部給它。

當然 還有一個可選的函數,可選是指你可以在這個彙編文件中實現,也可以在C中去實現,這裏給出彙編的實現,C的實現就對應很簡單了。

  1. OS_CPU_IRQ_ISR 
  2.     STMFD   SP!, {R1-R3} 
  3. ;//---------------------------------------------------------------------------- 
  4. ;//     R1--SP  (IRQ MODE) 
  5. ;//     R2--PC   (TASK POINTER)  
  6. ;//     R3--SPSR  (TASK CPSR) 
  7. ;//---------------------------------------------------------------------------- 
  8.     MOV R1, SP 
  9.     ADD SP, SP, #12 
  10.     SUB R2, LR, #4 
  11.     MRS R3, SPSR 
  12.     MSR CPSR_cxsf, #SVCMODE:OR:NOINT 
  13.     ;//take care! this sp is not the one before!svc mode's SP 
  14.     STMFD   SP!, {R2} 
  15.     STMFD   SP!, {R4-R12, LR} 
  16.     LDMFD   R1!, {R4-R6} 
  17.     STMFD   SP!, {R4-R6} 
  18.     STMFD   SP!, {R0} 
  19.     STMFD   SP!, {R3} 
  20.     ;LDR    R0, =OSIntNesting 
  21.     ;LDRB   R1, [R0] 
  22.     ;ADD    R1, R1, #1 
  23.     ;STRB   R1, [R0] 
  24.      
  25.     ;CMP    R1, #1 
  26.     ;BNE    %F1 
  27.     LDR R4, =OSTCBCur 
  28.     LDR R5, [R4] 
  29.     LDR SP, [R5] 
  30.     BL  OSIntEnter 
  31.   
  32.     MSR CPSR_c, #IRQMODE:OR:NOINT 
  33.     ;/** 
  34.     ;LDR    R0, =INTOFFSET 
  35.     ;LDR    R0, [R0] 
  36.     ;LDR    R1, IRQIsrVect 
  37.     ;MOV    LR, PC 
  38.     ;LDR    PC, [R1, R0, LSL #2] 
  39.     ;*/ 
  40.     BL  irq_process 
  41.     MSR CPSR_c, #SVCMODE:OR:NOINT 
  42.     BL  OSIntExit 
  43.     LDMFD   SP!, {R4} 
  44.     MSR SPSR_cxsf, R4 
  45.     LDMFD   SP!, {R0-R12, LR, PC}^ 

其大致過程是:SVC模式下原環境保護壓棧,調用OSIntEnter, 進入中斷模式,調用irq_process, 調用OSIntExt。

 

 

上述步驟大家可以在所有ucos的移植教程或步驟中找到。

接下來的一些列步驟有點類似於一直uboot,希望大家先行閱讀相關的板子啓動介紹文檔。

除去nand與SD啓動的BL0 與BL1這些初始化步驟,我們ucosii的引導步驟實際上是在uboot已經爲我們初始化好了一些必要設備後進行的,所以相對簡單,我們可以參考uboot的start.S文件來製作我們自己的start.S

IMPORT 與EXPORT 符號

  1. ;******************************************************  
  2.      PRESERVE8                      ;//字節對齊 
  3.      CODE32                         ;//ARM代碼 
  4.      AREA RESET,CODE,READONLY       ;//reset名,代碼段,只讀 
  5.      ENTRY                          ;//程序入口 

這裏的erea 取名是根據2416.sct中的名字來的,這個文件存儲了鏈接時各個文件的存儲位置與順序等,詳細的介紹請看uboot的相關介紹,如果想要一個最純淨的,請到u-boot/arch/arm/cpu對應的路徑下找u-boot.lds文件。

在entry後緊跟

  1. b   HANDLE_ResetInit     

這個就是我們的第一個函數,也是我們的第一個初始化函數。這個函數裏面的很多操作都可以直接拷貝armv5.pdf上的例子,因爲涉及到太多的協處理器的操作。

  1. mov     r0, #0 
  2.     mcr     p15, 0, r0, c7, c7, 0   ;flush v3/v4 cache 
  3.     mcr     p15, 0, r0, c8, c7, 0   ; flush v4 TLB  

1.關閉Icache與Dcache

  1. mov         r1, #0xd3           ;//svc mode 
  2. msr     cpsr_cxsf,r1 
  3. ldr     sp,=0x31000000 

2.關閉IRQ FIQ, 設置工作模式SVC模式, 初始化堆棧地址。

PS:說實話,這個start.S也是我在2440中找到拿來改的,但是建議參考u-boot的順序來寫,比如這裏應該先修改運行模式,再設置cache相關的東西。

 

  1. ;set to high vector address 
  2. ;read c1 to r5 
  3. MRC     p15,0,r5,c1,c0,0 
  4. ;set bit 13 of c1 
  5. orr     r5, r5, #0x2000 
  6. ;write r5 to c1 
  7. mcr     p15, 0, r5, c1, c0, 0 

這幾步是將中斷向量表的入口地址改爲高地址 0xFFFF0000開始,因爲我使用的是SD卡啓動模式,低地址0被物理映射到了BL0所在的IROM中,這裏面是隻讀的,所以我們系統啓動之後,無法將中斷向量表寫入這個地址,所以將中斷向量表的地址改爲高地址以便寫入。實際上u_boot與linux都是做了類似的操作。用nand啓動方式似乎可以寫入(未進行測試,有朋友測試後求告知結果)。

接下來就是建立mmu映射表,原因同上

 

  1. bl  make_mmu_table 
  2. ;enable domain access 
  3.     ldr     r5, =0x0000ffff 
  4.     mcr     p15, 0, r5, c3, c0, 0       ;load domain access register 
  5.     ldr     r1,=0x37ff4000 
  6.     mcr     p15, 0, r1, c2, c0, 0 

這幾句實際上是打開MMU寄存器的訪問許可權,同時將內存映射表的開始地址賦給MMU的寄存器。

 

  1. mrc     p15, 0, r0, c1, c0, 0 
  2.     orr     r0, r0, #1          ;Set CR_M to enable MMU 
  3.     mcr     p15, 0, r0, c1, c0, 0 
  4.     nop 
  5.     nop 
  6.     nop 
  7.     nop 

使能MMU

接下來針對每個模式初始化設置他們的棧地址

 

  1. mov     r1, #0x11           ;//FIQ mode 
  2.     msr     cpsr_cxsf,r1 
  3.     ldr     sp,=0xc3ff4000 
  4.      
  5.     mov     r1, #0x12           ;//IRQ mode 
  6.     msr     cpsr_cxsf,r1 
  7.     ldr     sp,=0xc3ff0000 
  8.     mov     r1, #0x1f           ;//sys mode 
  9.     msr     cpsr_cxsf,r1 
  10.     ldr     sp,=0xc3fec000   
  11.          
  12.     mov     r1, #0x17           ;//abt mode 
  13.     msr     cpsr_cxsf,r1 
  14.     ldr     sp,=0xc3fe8000 
  15.     mov     r1, #0x1b           ;//undef mode 
  16.     msr     cpsr_cxsf,r1 
  17.     ldr     sp,=0xc3fe0000 
  18.      
  19.     mov     r1, #0xd3           ;//svc mode 
  20.     msr     cpsr_cxsf,r1 
  21.     ldr     sp,=0xc3fe4000 

最後將bss段清零然後跳轉到主函數

  1. BL      InitRORWZI 
  2.     LDR         PC,=main 

在app.c這個文件中添加一個 

Int main(void) 函數

{

  Timer_init();

  Uart_init();

  OSinit();

   App();

   OSstart();

}

接下來就是編譯調試錯誤, 像缺少頭文件這種錯誤,請自行修改頭文件名稱。

有2點直接說,工程文件中不能加入ucos_ii.c這個文件;os_cfg_r.h與os_dbg_r.c

要修改爲os_cfg.h, os_dbg.c

編譯

錯誤

.\output\obj\ucos2416prob.axf: Warning: L6305W: Image does not have an entry point. (Not specified or not set due to multiple choices.)

 

 

引起原因 app.c文件中出現了main被當作默認C入口,而start.S中又指定了ENTRY

 

將start.S中的mian都改爲Main或者其他什麼名字。

 

 

還有一個錯誤是我自己引起的,將軟定時器當作了系統定時器。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章