【UCOSIII源码阅读笔记】第三篇——PendSV异常处理分析

前言

本文对ucos中为什么使用PendSV,以及如何使用PendSV进行分析。

正文

ucos任务切换的核心工作是在PendSV异常服务函数中完成的。
如果通过SysTick中断进行上下文切换,理想情况如下:
在这里插入图片描述
现实情况是SysTick中断被触发时,系统有可能正在处理另一个中断请求。此时SysTick中断打断了正在处理的中断请求,情况如下:
在这里插入图片描述
IRQ正在执行时,如果SysTick完成了上下文切换,并且要回到线程模式,这是CPU会触发用法fault异常。
所以完成上下文切换并回到线程模式这件事,需要一个优先级很低的中断完成,低到不会打断其他任何中断,这就用到了PendSV中断。
首先需要把PendSV编程为最低优先级异常,如果某IRQ被SysTick抢占,SysTick只是会悬起一个PendSV中断,然后继续执行IRQ,等IRQ执行完毕再执行PendSV进行任务切换,如下:
在这里插入图片描述
这样就解决了会有在IRQ中返回线程模式的情况。

从以上分析可以看出,PendSV要保证中断优先级最低。
系统异常优先级寄存器如下:
在这里插入图片描述
可以看到设置PendSV优先级的寄存器为0xE000ED22
中断控制及状态寄存器如下:
在这里插入图片描述
可以看到对寄存器0xE000ED04的第28位写入1可以悬起PendSV。
对应ucos代码在os_cpu_a.s文件中,起始位置有一段宏定义如下:

在这里插入图片描述
NVIC_SYSPRI14写入NVIC_PENDSV_PRI可以设置PendSV中断优先级为0xff(最低)。
NVIC_INT_CTRL写入0x10000000可以悬起PendSV。
之后有如下一段代码:

OSStartHighRdy
    LDR     R0, =NVIC_SYSPRI14                                  ; Set the PendSV exception priority
    LDR     R1, =NVIC_PENDSV_PRI
    STRB    R1, [R0]

    MOVS    R0, #0                                              ; Set the PSP to 0 for initial context switch call
    MSR     PSP, R0

    LDR     R0, =OS_CPU_ExceptStkBase                           ; Initialize the MSP to the OS_CPU_ExceptStkBase
    LDR     R1, [R0]
    MSR     MSP, R1    

    LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    
    CPSIE   I                                                   ; Enable interrupts at processor level

OSStartHang
    B       OSStartHang                                         ; Should never get here

其中的
在这里插入图片描述
用于设置0xE000ED22寄存器内容为0xFF。

在这里插入图片描述
用于设置PSP的值为0。
在这里插入图片描述
用于设置MSP的值为OS_CPU_ExceptStkBase

  • MSP为主堆栈指针:复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程(包
    括中断服务例程)。
  • PSP为进程堆栈指针:由用户的应用程序代码使用。
    在这里插入图片描述
    用于将0xE000ED04寄存器赋值为0x10000000,即悬起PendSV中断。

OSStartHighRdyOSStart()函数调用。

接下来是PendSV中断服务函数的内容,如下:

OS_CPU_PendSVHandler
    CPSID   I                                                   ; Prevent interruption during context switch
    MRS     R0, PSP                                             ; PSP is process stack pointer
    CBZ     R0, OS_CPU_PendSVHandler_nosave                     ; Skip register save the first time

    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack
    STM     R0, {R4-R11}

    LDR     R1, =OSTCBCurPtr                                    ; OSTCBCurPtr->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]                                            ; R0 is SP of process being switched out

                                                                ; At this point, entire context of process has been saved
OS_CPU_PendSVHandler_nosave
    PUSH    {R14}                                               ; Save LR exc_return value
    LDR     R0, =OSTaskSwHook                                   ; OSTaskSwHook();
    BLX     R0
    POP     {R14}

    LDR     R0, =OSPrioCur                                      ; OSPrioCur   = OSPrioHighRdy;
    LDR     R1, =OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]

    LDR     R0, =OSTCBCurPtr                                    ; OSTCBCurPtr = OSTCBHighRdyPtr;
    LDR     R1, =OSTCBHighRdyPtr
    LDR     R2, [R1]
    STR     R2, [R0]

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdyPtr->StkPtr;
    LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                                             ; Load PSP with new process SP
    ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack
    CPSIE   I
    BX      LR                                                  ; Exception return will restore remaining context

    END

其中的
在这里插入图片描述
用于判断PSP的值是否为0,如果是0说明是系统刚启动,不用保存就得任务信息,直接跳转到OS_CPU_PendSVHandler_nosave执行,nosave意思是不进行压栈保存数据的处理。

在这里插入图片描述
用于将R4-R11寄存器的内容压栈保护起来,R4-R11被称为“被调用者保护寄存器”,R0-R3R12LRxPSR被称为“调用者保护寄存器”,调用者保护寄存器在触发中断时,CPU就会自动压栈,所以ucos只负责将被调用者保护寄存器内容压栈,这部分内容在【Cortex-M3】中断处理时栈空间操作过程分析一文中有详细讲解。
在这里插入图片描述
用于将OSPrioCur赋值为OSPrioHighRdy
在这里插入图片描述
用于将OSTCBCurPtr赋值为OSTCBHighRdyPtr
OSTCBCurPtr指向的一个os_tcb的结构体的第一项为该任务的栈指针:
在这里插入图片描述
接下来的
在这里插入图片描述
用于将新的任务的R4-R11出栈。
在这里插入图片描述
用于打开中断,跳转到新的任务开始执行代码。

  • 26
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值