在这篇文章中分析一下不同优先级的任务是如何在PendSV中断中进行切换的,为后续写一个简易的切换程序做铺垫。目前互联网上学习资料最多的两种RTOS是FreeRTOS和Ucos,他们二者的任务切换程序不同之处主要有两点,第一点为如何启动第一个任务,FreeRTOS时通过软件触发SVC中断进而来启动第一个任务,Ucos则是对进程堆栈指针(PSP)与0做比较来确认是否为系统在启动第一个任务。第二点为是否手动为MSP(主堆栈指针)进行初始化。在这里以Ucos为例来分析一下任务的切换过程。
一、任务切换的源代码
Ucos的内核中任务切换函数放在了os_cpu_a.s这个汇编文件中,源代码如下。在 ARM 编程领域中,凡是打断程序顺序执行的事件,都被称为异常(exception),下文中不对中断和异常进行区分,都可以理解为中断。
;********************************************************************************************************
; PUBLIC FUNCTIONS
;********************************************************************************************************
IMPORT OSRunning ; 外部声明的变量,IMPORT类似c语言的extern
IMPORT OSPrioCur
IMPORT OSPrioHighRdy
IMPORT OSTCBCurPtr
IMPORT OSTCBHighRdyPtr
IMPORT OSIntExit
IMPORT OSTaskSwHook
IMPORT OS_CPU_ExceptStkBase
EXPORT OSStartHighRdy ; 使用EXPORT定义的标号可以被其他文件中的代码访问。
EXPORT OS_CPU_PendSVHandler
;********************************************************************************************************
; EQUATES
;********************************************************************************************************
;定义中断相关的常量
NVIC_INT_CTRL EQU 0xE000ED04 ; 中断控制及状态寄存器ICSR
NVIC_SYSPRI14 EQU 0xE000ED22 ; System priority register (priority 14).
NVIC_PENDSV_PRI EQU 0xFF ; PendSV priority value (lowest).
NVIC_PENDSVSET EQU 0x10000000 ; Value to trigger PendSV exception.
;********************************************************************************************************
; CODE GENERATION DIRECTIVES
;********************************************************************************************************
PRESERVE8 ; 当前文件保持堆栈八字节对齐
THUMB ; 兼容Thumb指令
AREA CODE, CODE, READONLY ; 定义代码段,只读属性
;********************************************************************************************************
; 启动多任务运行START MULTITASKING
; void OSStartHighRdy(void)
;
; Note(s) : 1) 这个函数通过触发一个PendSV中断(也就是引起一个上下文切换)来启动第一个任务
; 2) OSStartHighRdy() MUST:
; a) 将PendSV的中断优先级设置为最低
; b) 将PSP(进程堆栈指针)设置为0,来识别这是第一次运行任务切换
; c) 将MSP(主堆栈指针)初始化为指针OS_CPU_ExceptStkBase的值
; d) 触发PendSV中断
; e) Enable interrupts (tasks will run with interrupts enabled).(开中断)
;********************************************************************************************************
OSStartHighRdy
LDR R0, =NVIC_SYSPRI14 ; Set the PendSV exception priority
LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0] ;*(0xE000ED22) = 0x000000FF
MOVS R0, #0 ; Set the PSP to 0 for initial context switch call
MSR PS