Chinaunix首页 | 论坛 | 博客
  • 博客访问: 805683
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: 嵌入式

2017-08-31 15:41:44

cortex-M3移植uCOS-II启动流程分析:

上电启动,分为两个阶段:

第一个阶段:(主要是CPU内核的初始化)
先运行启动代码(这个是ARM公司写的,跟具体的CPU相关)
1.1 初始化堆栈
1.2 初始化,规划好中断向量表
    跳转到Reset_Handler中,执行复位后的中断
1.3 在 Reset_Handler 中,调用C函数 SystemInit 来进行系统初始化
    主要是做跟CPU初始化相关的工作,比如时钟PLL的配置等
1.4 调用C函数 __main 将会跳转到主程序 main 函数中

第二个阶段:(主要是外设模块的初始化,并且完成启动操作系统调度)
2.1 初始化心跳时钟
2.2 初始化中断分组
2.3 初始化外设:串口,GPIO,等等

操作系统相关初始化:
2.4 调用OSInit: 初始化操作系统相关的全局变量,链表结构
    OSInitHookBegin();                                           /* Call port specific initialization code   */
    OS_InitMisc();                                               /* Initialize miscellaneous variables       */
    OS_InitRdyList();                                            /* Initialize the Ready List                */
    OS_InitTCBList();                                            /* Initialize the free list of OS_TCBs      */
    OS_InitEventList();                                          /* Initialize the free list of OS_EVENTs    */
    
    创建空闲任务,必须创建的任务!!!
    OS_InitTaskIdle();                                           /* Create the Idle Task                     */
    OSInitHookEnd();                                             /* Call port specific init. code            */
2.5 创建应用程序的任务(可以不创建)
2.6 调用函数 OSStart 启动操作系统
2.6.1 找到就绪列表中,任务优先级最高的任务,获取其任务控制块TCB
2.6.2 调用函数 OSStartHighRdy 启动优先级最高的任务
    OSStartHighRdy 函数是与具体的CPU架构相关的。
    在Cortex-M3架构的芯片中,是利用pendsv中断来进行任务间切换的。
    
=================================
汇编语言实现 OSStartHighRdy 函数:

OSStartHighRdy
    LDR     R0, =NVIC_SYSPRI14         ; 设置PendSV的异常中断优先级
    LDR     R1, =NVIC_PENDSV_PRI
    STRB    R1, [R0]

    MOVS    R0, #0                     ; 初始化PSP=0
    MSR     PSP, R0

    LDR     R0, =OS_CPU_ExceptStkBase  ; 初始化异常堆栈MSP地址
    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                          ; 开启中断,于是进入PendSV异常

OSStartHang
    B       OSStartHang                ; 正常情况下,不应运行到这
    

========================================================
当使能了中断:CPSIE   I,将会触发 PendSV 的中断服务程序:

;********************************************************************************************************
;                                         HANDLE PendSV EXCEPTION
;                                     void OS_CPU_PendSVHandler(void)
;
; Note(s) : 1) PendSV is used to cause a context switch.  This is a recommended method for performing
;              context switches with Cortex-M3.  This is because the Cortex-M3 auto-saves half of the
;              processor context on any exception, and restores same on return from exception.  So only
;              saving of R4-R11 is required and fixing up the stack pointers.  Using the PendSV exception
;              this way means that context saving and restoring is identical whether it is initiated from
;              a thread or occurs due to an interrupt or exception.
;
;           2) Pseudo-code is:
;              a) Get the process SP, if 0 then skip (goto d) the saving part (first context switch);
                首次切换到最高优先级的就绪任务时,不需要保存CPU的寄存器(因为前面还没有运行任务!!)

;              b) Save remaining regs r4-r11 on process stack;
;              c) Save the process SP in its TCB, OSTCBCur->OSTCBStkPtr = SP;
;              d) Call OSTaskSwHook();
;              e) Get current high priority, OSPrioCur = OSPrioHighRdy;
;              f) Get current ready thread TCB, OSTCBCur = OSTCBHighRdy;
;              g) Get new process SP from TCB, SP = OSTCBHighRdy->OSTCBStkPtr;
;              h) Restore R4-R11 from new process stack;
;              i) Perform exception return which will restore remaining context.
;
;           3) On entry into PendSV handler:
;              a) The following have been saved on the process stack (by processor):
;                 xPSR, PC, LR, R12, R0-R3 (这些寄存器是CPU在中断过程中自动保存的)
;              b) Processor mode is switched to Handler mode (from Thread mode) (CPU模式从用户模式自动切换到管理模式(特权模式))
;              c) Stack is Main stack (switched from Process stack)  (PSP: 进程堆栈,用于用户模式, MSP: 主堆栈,用于特权模式)
;              d) OSTCBCur      points to the OS_TCB of the task to suspend
;                 OSTCBHighRdy  points to the OS_TCB of the task to resume
;
;           4) Since PendSV is set to lowest priority in the system (by OSStartHighRdy() above), we
;              know that it will only be run when no other exception or interrupt is active, and
;              therefore safe to assume that context being switched out was using the process stack (PSP).
                注: PendSV设置成最低的中断优先级,所以只有所有的异常和中断完成后,才轮到pendsv执行,来完成任务切换,
                确保了在中断过程中,是不能进行任务级别的任务切换的(因为在实时操作系统中,中断总是优先于任务去
                处理的。)

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

OS_CPU_PendSVHandler
    CPSID   I                              ; 关中断
    MRS     R0, PSP                        ; 获得PSP
    CBZ     R0, OS_CPU_PendSVHandler_nosave; PSP为0跳到OS_CPU_PendSVHandler_nosave,即不保存上文,直接进入下文。
                                           ; 问什么呢,因为首次调用,是没有上文的。
                                    // CBZ表示跳转指令,判断PSP != 0,将直接往下执行,否则将跳转执行 OS_CPU_PendSVHandler_nosave
                                           
                                           ; 保存上文 (如果是第一次切换,则不执行下面的语句,直接执行OS_CPU_PendSVHandler_nosave)
    SUBS    R0, R0, #0x20                  ; 因为寄存器是32位的,4字节对齐,自动压栈的寄存器有8个,所以偏移为8*0x04=0x20
    STM     R0, {R4-R11}                   ; 除去自动压栈的寄存器外,需手动将R4-R11压栈

    LDR     R1, =OSTCBCur                  ; 保存上文的SP指针 OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]                       ; 注意:这里OSTCBCur的地址就是 OSTCBCur->OSTCBStkPtr 的地址
    STR     R0, [R1]                                            

                                                                
OS_CPU_PendSVHandler_nosave                ; 切换下文
    PUSH    {R14}                          ; LR压栈,下面要调用C函数, 因为LR调用C函数时,将用来保存返回的函数地址
                                           ; 所以这里必须把之前LR的值保存起来

    LDR     R0, =OSTaskSwHook              ; 调用OSTaskSwHook();
    BLX     R0                             ; 调用OSTaskSwHook()结束并返回
    POP     {R14}                          ; 恢复 LR 寄存器,与前面对应

    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中的值为新任务的SP; SP = OSTCBHighRdy->OSTCBStkPtr;
    LDM     R0, {R4-R11}                   ; 堆栈中的值手动弹出到寄存器:R4-R11
    ADDS    R0, R0, #0x20
   
        
    MSR     PSP, R0                        ; PSP = 新任务SP,将SP的指针指向堆栈的顶端
    ORR     LR, LR, #0x04                  ; 确保异常返回后使用PSP
    CPSIE   I                              ; 重新打开中断
    BX      LR                             ; 退出异常,从PSP自动弹出xPSR,PC,LR,R0-R3,进入新任务运行


阅读(2005) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~