uCOS III完全解读之任务调度

  1. 裸机和多任务

    1.1. 裸机程序的执行流程
    首先介绍下裸机程序执行的一般流程, 裸机程序按照顺序执行 , 当执行到某处被中断打断时,执行中断服务程序前会保存现场,退出中断时会恢复现场,这样程序就会接着从回断前的位置开始执行。

    1.2. 多任务程序的执行流程
    现有两个任务A和任务B, 任务A在执行时被中断打断,执行中断服务程序前保存任务A被打断处的现场, 然后执行中断服务程序,执行完后会怎么样呢? 如果我们不处理的话,cpu会恢复任务A的现场回到任务A继续执行。我们想要的结果是执行任务B,显然,此时恢复任务B的现场,那么就会执行任务B。这就是由A任务切换到B任务的过程。

2.代码解读

2.1 pendsv中断
以cortex-m3内核为例,通过写内核寄存器主动触发pendsv中断来执行任务调度

OSCtxSw
    LDR     R0, =NVIC_INT_CTRL  ; 将NVIC控制寄存器的值加载到R0                                
    LDR     R1, =NVIC_PENDSVSET ; 将要设置的值加载到R1
    STR     R1, [R0]            ; 写NVIC控制寄存器
    BX      LR

OSIntCtxSw和OSCtxSw的实现方式一样,这里不再赘述。前者在中断中调用,后者在任务中调用。这里要指出的是cortex-m3的寄存器(xPSR,PC,LR,R12,R3-R0)由硬件压栈, 剩余的寄存器(R4-R11)由软件压栈。如果是cortex-m4内核,还需要将浮点运算的相关寄存器入栈 。

OSIntCtxSw
    LDR     R0, =NVIC_INT_CTRL                                  ; 
    LDR     R1, =NVIC_PENDSVSET   
    STR     R1, [R0]    
    BX      LR

2.2 首次启动任务

OSStartHighRdy
    CPSID   I                         ; 关中断          
                                
    MOV32   R0, NVIC_SYSPRI14         ; 设置PendSV中断的优先级为最低                                
    MOV32   R1, NVIC_PENDSV_PRI
    STRB    R1, [R0]                                            

    MOV32   R0, OS_CPU_ExceptStkBase   ; 设置主堆栈栈顶指针MSP                           
    LDR     R1, [R0]
    MSR     MSP, R1                                           

    MOV32   R0, OSPrioCur              ; 设置当前任务的优先级为任务就绪表中的最高优先级                               
    MOV32   R1, OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]                                            
    
    MOV32   R5, OSTCBCurPtr            ; 设置当前任务控制块指针为任务就绪表中优先级最高的任务
    MOV32   R1, OSTCBHighRdyPtr                                  
    LDR     R2, [R1]
    STR     R2, [R5]                                            
                                                               
    LDR     R0, [R2]                   ; 设置用户线程指针PSP为当前任务的栈顶指针                                      
    MSR     PSP, R0                                              
 ; 设置CONTROL寄存器,线程模式下的用户代码使用PSP,异常服务例程使用MSP
 ; 这两个堆栈指针的切换是全自动的,进入中断或异常时切换到MSP,退出中断或异常进入用户线程时切换到PSP。由CM3的硬件自动完成
    MRS     R0, CONTROL
    ORR     R0, R0, #2 
 ; CONTROL[0]=0表示线程模式下拥有特权级(能够访问SCS系统控制空间,该空间包含了配置寄存器组以及调试组件的寄存器组)
    MSR     CONTROL, R0
    ISB                                 ; 指令同步隔离                                         

    LDMFD    SP!, {R4-R11}              ; 按照后进先出原则,从当前任务的栈顶弹出寄存器                                    
    LDMFD    SP!, {R0-R3}                                        
    LDMFD    SP!, {R12, LR}                                      
    LDMFD    SP!, {R1, R2}                                       
    CPSIE    I                          ; 开中断
    BX       R1                         ; 跳转到当前任务开始执行

2.3 任务切换过程
OS_CPU_PendSVHandler是pendsv中断的入口。此函数是任务切换的核心。

OS_CPU_PendSVHandler
    CPSID   I                  ; 关中断,将当前任务的R4-R11压入它的栈                                            
    MRS     R0, PSP            ; R0 = PSP, 将R0指向当前任务的栈顶地址                                           
    STMFD   R0!, {R4-R11}      ; 将当前任务的R4-R11压入它的栈                                  

    MOV32   R5, OSTCBCurPtr    ; 加载任务控制块指针到R5                                 
    LDR     R6, [R5]           ; 加载任务控制块记录的栈顶指针到R6
    STR     R0, [R6]           ; 上述将R4-R11压栈后,栈顶地址R0发生了变化,所以更新R0到当前任务控制块
                               ; 执行到此处,当前任务的上下文已经全部保存到了它的栈
                                             
    MOV     R4, LR             ; 保存中断异常返回值到R4                                             
    BL      OSTaskSwHook       ; 执行任务切换钩子函数                                   
 
    MOV32   R0, OSPrioCur      ; 把就绪任务的最高优先级作为当前任务的优先级                                   
    MOV32   R1, OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]
    
    MOV32   R1, OSTCBHighRdyPtr;把任务就绪表中优先级最高的任务赋给当前任务                                 
    LDR     R2, [R1]
    STR     R2, [R5]
    
    ORR     LR, R4, #0xF4      ; exc_return的bit2设为1,保证异常退出时,从线程堆栈PSP做出栈操作,退出后使用PSP                                      
  
    LDR     R0, [R2]           ; 取出切换后的当前任务的栈顶指针                                         
    LDMFD   R0!, {R4-R11}      ; 弹出R4-R11寄存器                                      

    MSR     PSP, R0            ; 重新设置线程栈顶指针PSP为切换后的任务的栈顶指针                                        
    CPSIE   I                  ; 开中断
    
    BX      LR                 ; 退出异常, 进入线程模式                                          
                               ; 异常退出时,由硬件把xPSR,PC,LR,R12,R3-R0弹出到正在使用的堆栈PSP
    END
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值