helper2416上移植ucosii V290

来源:互联网 发布:武清房产网楚天网络 编辑:程序博客网 时间:2024/05/14 10:16

 

首先到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,还不是很清楚,求了解的大大告之:eagleqingluo@gmail.com。

  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或者其他什么名字。

 

 

还有一个错误是我自己引起的,将软定时器当作了系统定时器。

 

 

 

本文出自 “万古一青莲” 博客,请务必保留此出处http://qingluo.blog.51cto.com/4582274/1189004

原创粉丝点击