ucosi操作系统移植到STM32

这个工作主要是根据网友的经验资料来学习移植的。总的来说需要下面几个参考资料。

ARM Cortex-m3权威指南

官方移植文档资料

网友移植成功经验资料

这三种资料在我的资源上传里面都能找到。

我在官网上下的是官方已经移植好的到STM32F103评估板的资料,很多部分已经实现了,所以需要改动的地方很少,不同的地方可以参考第三种网友移植成功经验资料,里面有详细说明。我下的是ucosii2.86版本。

下面说说移植过程:

移植主要涉及到两个源文件,os_cpu_c.c和os_cpu_a.asm,os_cpu.h。其他文件是ucosii核心文件,不需要修改。还有两个配置文件app_cfg.h和os_cfg.h。

整个文件框架构成图如下,用的是IAR。

这就是对照上面的说明而设置的目录结构。

下面谈主要移植的部分。主要集中在os_cpu_a.asm这个文件中,os_cpu.h主要涉及到一些宏的配置。os_cpu_c.c中主要有一个函数必须写出,就是堆栈初始化函数。

先说os_cpu.h中需要注意的地方。

下面是开关中断的宏

  1. #define  OS_CRITICAL_METHOD   3  
  2.   
  3. #if OS_CRITICAL_METHOD == 3  
  4. #define  OS_ENTER_CRITICAL()  {cpu_sr = OS_CPU_SR_Save();}  
  5. #define  OS_EXIT_CRITICAL()   {OS_CPU_SR_Restore(cpu_sr);}  
  6. #endif  


CM3堆栈方向高到底递减,设置为1,还有一个任务切换的宏。

  1. #define  OS_STK_GROWTH        1                   /* Stack grows from HIGH to LOW memory on ARM        */  
  2.   
  3. #define  OS_TASK_SW()         OSCtxSw()  


还有几个关于systick的函数需要注释掉,下面的os_cpu_c.c会说道原因。

  1. //                                                  /* See OS_CPU_C.C                                    */  
  2. //void       OS_CPU_SysTickHandler(void);  
  3. //void       OS_CPU_SysTickInit(void);  
  4. //  
  5. //                                                  /* See BSP.C                                         */  
  6. //INT32U     OS_CPU_SysTickClkFreq(void);  

 

看os_cpu_c.c。

里面有些函数是钩子函数,根据需要写。如果不需要可以不写。

下面是堆栈初始化函数。

  1. /* 
  2. ********************************************************************************************************* 
  3. *                                        INITIALIZE A TASK'S STACK 
  4. * 
  5. * Description: This function is called by either OSTaskCreate() or OSTaskCreateExt() to initialize the 
  6. *              stack frame of the task being created.  This function is highly processor specific. 
  7. * 
  8. * Arguments  : task          is a pointer to the task code 
  9. * 
  10. *              p_arg         is a pointer to a user supplied data area that will be passed to the task 
  11. *                            when the task first executes. 
  12. * 
  13. *              ptos          is a pointer to the top of stack.  It is assumed that 'ptos' points to 
  14. *                            a 'free' entry on the task stack.  If OS_STK_GROWTH is set to 1 then 
  15. *                            'ptos' will contain the HIGHEST valid address of the stack.  Similarly, if 
  16. *                            OS_STK_GROWTH is set to 0, the 'ptos' will contains the LOWEST valid address 
  17. *                            of the stack. 
  18. * 
  19. *              opt           specifies options that can be used to alter the behavior of OSTaskStkInit(). 
  20. *                            (see uCOS_II.H for OS_TASK_OPT_xxx). 
  21. * 
  22. * Returns    : Always returns the location of the new top-of-stack once the processor registers have 
  23. *              been placed on the stack in the proper order. 
  24. * 
  25. * Note(s)    : 1) Interrupts are enabled when your task starts executing. 
  26. *              2) All tasks run in Thread mode, using process stack. 
  27. ********************************************************************************************************* 
  28. */  
  29.   
  30. OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)  
  31. {  
  32.     OS_STK *stk;  
  33.   
  34.   
  35.     (void)opt;                                   /* 'opt' is not used, prevent warning                 */  
  36.     stk       = ptos;                            /* Load stack pointer                                 */  
  37.   
  38.                                                  /* Registers stacked as if auto-saved on exception    */  
  39.     *(stk)    = (INT32U)0x01000000L;             /* xPSR                                               */  
  40.     *(--stk)  = (INT32U)task;                    /* Entry Point                                        */  
  41.     *(--stk)  = (INT32U)0xFFFFFFFEL;             /* R14 (LR) (init value will cause fault if ever used)*/  
  42.     *(--stk)  = (INT32U)0x12121212L;             /* R12                                                */  
  43.     *(--stk)  = (INT32U)0x03030303L;             /* R3                                                 */  
  44.     *(--stk)  = (INT32U)0x02020202L;             /* R2                                                 */  
  45.     *(--stk)  = (INT32U)0x01010101L;             /* R1                                                 */  
  46.     *(--stk)  = (INT32U)p_arg;                   /* R0 : argument                                      */  
  47.   
  48.                                                  /* Remaining registers saved on process stack         */  
  49.     *(--stk)  = (INT32U)0x11111111L;             /* R11                                                */  
  50.     *(--stk)  = (INT32U)0x10101010L;             /* R10                                                */  
  51.     *(--stk)  = (INT32U)0x09090909L;             /* R9                                                 */  
  52.     *(--stk)  = (INT32U)0x08080808L;             /* R8                                                 */  
  53.     *(--stk)  = (INT32U)0x07070707L;             /* R7                                                 */  
  54.     *(--stk)  = (INT32U)0x06060606L;             /* R6                                                 */  
  55.     *(--stk)  = (INT32U)0x05050505L;             /* R5                                                 */  
  56.     *(--stk)  = (INT32U)0x04040404L;             /* R4                                                 */  
  57.   
  58.     return (stk);  
  59. }  


因为CM3支持的堆栈地址是从高到低递减的,所以里面都是--。而且CM3中断自动入栈顺序为xPSR,PC,R14,R12,R3~R0,这个可以参考CM3权威指南。所以放在前面。后面R11~R4需要我们手动入栈。最后返回栈顶指针。

OS_CPU_C.c中还有一些东西需要我们注释掉,因为官方的资料是根据评估板来写的,如果移植到我们有STM32固件库的平台上,则需要做些改变,这是参考网友移植经验知道的。这些需要注释掉的东西主要是跟systic这个定时器有关。

下面是需要注释掉的地方。

  1. //void  OS_CPU_SysTickInit (void)  
  2. //{  
  3. //    INT32U  cnts;  
  4. //  
  5. //  
  6. //    cnts = OS_CPU_SysTickClkFreq() / OS_TICKS_PER_SEC;  
  7. //  
  8. //    OS_CPU_CM3_NVIC_ST_RELOAD = (cnts - 1);  
  9. //                                                 /* Enable timer.                                      */  
  10. //    OS_CPU_CM3_NVIC_ST_CTRL  |= OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC | OS_CPU_CM3_NVIC_ST_CTRL_ENABLE;  
  11. //                                                 /* Enable timer interrupt.                            */  
  12. //    OS_CPU_CM3_NVIC_ST_CTRL  |= OS_CPU_CM3_NVIC_ST_CTRL_INTEN;  
  13. //}  


 

  1. //void  OS_CPU_SysTickHandler (void)  
  2. //{  
  3. //    OS_CPU_SR  cpu_sr;  
  4. //  
  5. //  
  6. //    OS_ENTER_CRITICAL();                         /* Tell uC/OS-II that we are starting an ISR          */  
  7. //    OSIntNesting++;  
  8. //    OS_EXIT_CRITICAL();  
  9. //  
  10. //    OSTimeTick();                                /* Call uC/OS-II's OSTimeTick()                       */  
  11. //  
  12. //    OSIntExit();                                 /* Tell uC/OS-II that we are leaving the ISR          */  
  13. //}  


 

  1. //#define  OS_CPU_CM3_NVIC_ST_CTRL    (*((volatile INT32U *)0xE000E010))   /* SysTick Ctrl & Status Reg. */  
  2. //#define  OS_CPU_CM3_NVIC_ST_RELOAD  (*((volatile INT32U *)0xE000E014))   /* SysTick Reload  Value Reg. */  
  3. //#define  OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U *)0xE000E018))   /* SysTick Current Value Reg. */  
  4. //#define  OS_CPU_CM3_NVIC_ST_CAL     (*((volatile INT32U *)0xE000E01C))   /* SysTick Cal     Value Reg. */  
  5. //  
  6. //#define  OS_CPU_CM3_NVIC_ST_CTRL_COUNT                    0x00010000     /* Count flag.                */  
  7. //#define  OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC                  0x00000004     /* Clock Source.              */  
  8. //#define  OS_CPU_CM3_NVIC_ST_CTRL_INTEN                    0x00000002     /* Interrupt enable.          */  
  9. //#define  OS_CPU_CM3_NVIC_ST_CTRL_ENABLE                   0x00000001     /* Counter mode.              */  


os_cpu.c.c中需要注意到的地方差不多就是这些了。

下面是os_cpu_a.asm.这里面的东西最多了,也相对难理解,这就需要仔细看CM3权威指南这个资料了。

这是上面开关中断宏的汇编语言实现。

  1. OS_CPU_SR_Save  
  2.     MRS     R0, PRIMASK                                         ; Set prio int mask to mask all (except faults)  
  3.     CPSID   I  
  4.     BX      LR  
  5.   
  6. OS_CPU_SR_Restore  
  7.     MSR     PRIMASK, R0  
  8.     BX      LR  


启动最高优先级函数,只调用一次,在OSStart()中被调用。

[plain]  view plain copy print ?
  1. ;********************************************************************************************************  
  2. ;                                          START MULTITASKING  
  3. ;                                       void OSStartHighRdy(void)  
  4. ;  
  5. ; Note(s) : 1) This function triggers a PendSV exception (essentially, causes a context switch) to cause  
  6. ;              the first task to start.  
  7. ;  
  8. ;           2) OSStartHighRdy() MUST:  
  9. ;              a) Setup PendSV exception priority to lowest;  
  10. ;              b) Set initial PSP to 0, to tell context switcher this is first run;  
  11. ;              c) Set OSRunning to TRUE;  
  12. ;              d) Trigger PendSV exception;  
  13. ;              e) Enable interrupts (tasks will run with interrupts enabled).  
  14. ;********************************************************************************************************  
  15.   
  16. OSStartHighRdy  
  17.     LDR     R0, =NVIC_SYSPRI14                                  ; Set the PendSV exception priority  
  18.     LDR     R1, =NVIC_PENDSV_PRI  
  19.     STRB    R1, [R0]  
  20.   
  21.     MOVS    R0, #0                                              ; Set the PSP to 0 for initial context switch call  
  22.     MSR     PSP, R0  
  23.   
  24.     LDR     R0, =OSRunning                                      ; OSRunning = TRUE  
  25.     MOVS    R1, #1  
  26.     STRB    R1, [R0]  
  27.   
  28.     LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)  
  29.     LDR     R1, =NVIC_PENDSVSET  
  30.     STR     R1, [R0]  
  31.   
  32.     CPSIE   I                                                   ; Enable interrupts at processor level  
  33.   
  34. OSStartHang  
  35.     B       OSStartHang                                         ; Should never get here  


主要是手动悬起PendSV中断,设置OSRunning为1,然后开启中断之后好进入PendSV中断处理函数中进行任务切换,开始运行ucosii。PendSV这个是需要掌握的,才能理解CM3是如何实现操作系统的管理的。

下面是两个任务切换函数,一个是任务级间切换,一个是中断与任务间切换,虽然代码相同,但意义不同,这主要就跟上面说道的PendSV中断有关了。因为这个中断的存在,我们的任务切换中实现的主要工作就是触发PendSV中断,让这个中断去处理任务切换相关细节。

[plain]  view plain copy print ?
  1. ;********************************************************************************************************  
  2. ;                               PERFORM A CONTEXT SWITCH (From task level)  
  3. ;                                           void OSCtxSw(void)  
  4. ;  
  5. ; Note(s) : 1) OSCtxSw() is called when OS wants to perform a task context switch.  This function  
  6. ;              triggers the PendSV exception which is where the real work is done.  
  7. ;********************************************************************************************************  
  8.   
  9. OSCtxSw  
  10.     LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)  
  11.     LDR     R1, =NVIC_PENDSVSET  
  12.     STR     R1, [R0]  
  13.     BX      LR  
  14.   
  15. ;********************************************************************************************************  
  16. ;                             PERFORM A CONTEXT SWITCH (From interrupt level)  
  17. ;                                         void OSIntCtxSw(void)  
  18. ;  
  19. ; Notes:    1) OSIntCtxSw() is called by OSIntExit() when it determines a context switch is needed as  
  20. ;              the result of an interrupt.  This function simply triggers a PendSV exception which will  
  21. ;              be handled when there are no more interrupts active and interrupts are enabled.  
  22. ;********************************************************************************************************  
  23.   
  24. OSIntCtxSw  
  25.     LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)  
  26.     LDR     R1, =NVIC_PENDSVSET  
  27.     STR     R1, [R0]  
  28.     BX      LR  


下面就是非常重要的PendSV中断处理函数了。

[plain]  view plain copy print ?
  1. ;********************************************************************************************************  
  2. ;                                         HANDLE PendSV EXCEPTION  
  3. ;                                     void OS_CPU_PendSVHandler(void)  
  4. ;  
  5. ; Note(s) : 1) PendSV is used to cause a context switch.  This is a recommended method for performing  
  6. ;              context switches with Cortex-M3.  This is because the Cortex-M3 auto-saves half of the  
  7. ;              processor context on any exception, and restores same on return from exception.  So only  
  8. ;              saving of R4-R11 is required and fixing up the stack pointers.  Using the PendSV exception  
  9. ;              this way means that context saving and restoring is identical whether it is initiated from  
  10. ;              a thread or occurs due to an interrupt or exception.  
  11. ;  
  12. ;           2) Pseudo-code is:  
  13. ;              a) Get the process SP, if 0 then skip (goto d) the saving part (first context switch);  
  14. ;              b) Save remaining regs r4-r11 on process stack;  
  15. ;              c) Save the process SP in its TCB, OSTCBCur->OSTCBStkPtr = SP;  
  16. ;              d) Call OSTaskSwHook();  
  17. ;              e) Get current high priority, OSPrioCur = OSPrioHighRdy;  
  18. ;              f) Get current ready thread TCB, OSTCBCur = OSTCBHighRdy;  
  19. ;              g) Get new process SP from TCB, SP = OSTCBHighRdy->OSTCBStkPtr;  
  20. ;              h) Restore R4-R11 from new process stack;  
  21. ;              i) Perform exception return which will restore remaining context.  
  22. ;  
  23. ;           3) On entry into PendSV handler:  
  24. ;              a) The following have been saved on the process stack (by processor):  
  25. ;                 xPSR, PC, LR, R12, R0-R3  
  26. ;              b) Processor mode is switched to Handler mode (from Thread mode)  
  27. ;              c) Stack is Main stack (switched from Process stack)  
  28. ;              d) OSTCBCur      points to the OS_TCB of the task to suspend  
  29. ;                 OSTCBHighRdy  points to the OS_TCB of the task to resume  
  30. ;  
  31. ;           4) Since PendSV is set to lowest priority in the system (by OSStartHighRdy() above), we  
  32. ;              know that it will only be run when no other exception or interrupt is active, and  
  33. ;              therefore safe to assume that context being switched out was using the process stack (PSP).  
  34. ;********************************************************************************************************  
  35.   
  36. OS_CPU_PendSVHandler  
  37.     CPSID   I                                                   ; Prevent interruption during context switch  
  38.     MRS     R0, PSP                                             ; PSP is process stack pointer  
  39.     CBZ     R0, OS_CPU_PendSVHandler_nosave                     ; Skip register save the first time  
  40.   
  41.     SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack  
  42.     STM     R0, {R4-R11}  
  43.   
  44.     LDR     R1, =OSTCBCur                                       ; OSTCBCur->OSTCBStkPtr = SP;  
  45.     LDR     R1, [R1]  
  46.     STR     R0, [R1]                                            ; R0 is SP of process being switched out  
  47.   
  48.                                                                 ; At this point, entire context of process has been saved  
  49. OS_CPU_PendSVHandler_nosave  
  50.     PUSH    {R14}                                               ; Save LR exc_return value  
  51.     LDR     R0, =OSTaskSwHook                                   ; OSTaskSwHook();  
  52.     BLX     R0  
  53.     POP     {R14}  
  54.   
  55.     LDR     R0, =OSPrioCur                                      ; OSPrioCur = OSPrioHighRdy;  
  56.     LDR     R1, =OSPrioHighRdy  
  57.     LDRB    R2, [R1]  
  58.     STRB    R2, [R0]  
  59.   
  60.     LDR     R0, =OSTCBCur                                       ; OSTCBCur  = OSTCBHighRdy;  
  61.     LDR     R1, =OSTCBHighRdy  
  62.     LDR     R2, [R1]  
  63.     STR     R2, [R0]  
  64.   
  65.     LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;  
  66.     LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack  
  67.     ADDS    R0, R0, #0x20  
  68.     MSR     PSP, R0                                             ; Load PSP with new process SP  
  69.     ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack  
  70.     CPSIE   I  
  71.     BX      LR                                                  ; Exception return will restore remaining context  
  72.   
  73.     END  


 

下面的说的就是上面忽略的部分systick,作为操作系统的心脏,我们需要自己来组织这个中断。

在STM32库中的终端处理文件stm32f10x_it.c中有个Systick_Handler()函数,在里面添加处理。

 

  1. /** 
  2.   * @brief  This function handles SysTick Handler. 
  3.   * @param  None 
  4.   * @retval None 
  5.   */  
  6. void SysTick_Handler(void)  
  7. {  
  8.     OSIntEnter();  
  9.     OSTimeTick();  
  10.     OSIntExit();  
  11. }  


在主函数main.c中添加systick初始化函数

  1. static void Systick_init(void)  
  2. {  
  3.     RCC_ClocksTypeDef rcc_clocks;  
  4.     RCC_GetClocksFreq(&rcc_clocks);  
  5.     SysTick_Config(rcc_clocks.HCLK_Frequency / OS_TICKS_PER_SEC);  
  6. }  


最后还有一个需要改动的就是要把STM32启动文件中的所有PendSV_Handler替换成OS_CPU_ PendSVHandler,因为UCOS默认移植文件中使用的是OS_CPU_ PendSVHandler。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值