uCOS-II学习-PART 2
若有不对请斧正,谢谢
触发中断
OSStartHighRdy
OSStartHighRdy()
函数在OSStart()
中被调用,即第一次任务调度。
- 将PendSV中断优先级设置为最低0xFF,0xE000ED22寄存器写入0xFF
- 初始化PSP和MSP指针
- 将OS运行标志设置为1
- 将ICSR寄存器第28位写1,即触发PendSV中断
OSCtxSw
OS_TASK_SW()
函数在OS_Sched()
任务调度函数中被调用。
OS_Sched()
任务调度函数,在任务创建、消息队列、信号量、消息邮箱、时钟等函数中执行。
该任务调度一般是在任务中主动释放CPU,主动切换任务。较为常用的方式比如:OSTimeDly();
、OSSemPost();
、QSQPost();
、等。
OSIntCtxSw
OSIntCtxSw()
函数在OSIntExit()
中被调用
至于OSIntExit()
该函数,在移植uC/OS时,会在系统定时器中断处理函数SysTick_Handler
中调用。
每次滴答时钟中断,都会判断是否执行任务切换,即实现了高优先级任务抢占低优先级任务CPU控制权。
以下是原子的SysTick_Handler
函数
中断执行,上下文切换
中断入口可以在中断向量表中找到。
中断向量表
PendSV_Handler
PendSV_Handler
CPSID I ; Prevent interruption during context switch
MRS R0, PSP ; PSP is process stack pointer
CBZ R0, PendSV_Handler_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, =OSTCBCur ; OSTCBCur->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
PendSV_Handler_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, =OSTCBCur ; OSTCBCur = OSTCBHighRdy;
LDR R1, =OSTCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
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, #0xF4 ; Ensure exception return uses process stack
CPSIE I
BX LR ; Exception return will restore remaining context
END
代码流程
a)获取进程SP,如果为0,则跳过(转到d-PendSV_Handler_nosave)保存部分(第一个上下文切换);
b)将剩余的寄存器r4-r11保存在进程堆栈中;
c)将进程SP保存在其TCB中,OSTCBCur-> OSTCBStkPtr = SP;
d)调用OSTaskSwHook();
e)获得当前的高优先级,OSPrioCur = OSPrioHighRdy;
f)获取当前就绪线程TCB,OSTCBCur = OSTCBHighRdy;
g)从TCB获取新进程SP,SP = OSTCBHighRdy-> OSTCBStkPtr;
h)从新进程堆栈中还原R4-R11;
i)执行异常返回,它将恢复剩余的上下文。
堆栈
参考链接
https://blog.csdn.net/yangkuiwu/article/details/78302659
该链接中使用的uC/OS的版本应该和我的不同,PendSV_Handler代码有些差异,
下面自己梳理总结一下。
-
在中断触发后,进入PendSV_Handler中断服务函数之前,硬件自动将xPSR, PC, LR, R12以及R3‐R0压入堆栈。
-
进入中断后,关闭中断
CPSID I
,再将PSP的值送给R0,判断该值不为0,则开始保存r4-r11寄存器。
-
保存r4-r11寄存器
先将R0减去32(0x20)
将R4-R11写入R0所指向的位置,每存一个寄存器R0会自动加4,所以存储完成后,R0与PSP的值仍相等。
-
保存SP的值放入栈顶
汇编指令
将当前任务 任务控制块 的地址写入R1,(OS_TCB结构体的第一个成员为OS_STK *OSTCBStkPtr;
),[R1]的值为OS_STK类型的指针(栈顶指针),将其再写入R1。
将R0中的值(即SP的值)写入R1(OS_STK类型的指针)为地址指向的存储器。即SP写入栈顶。 -
进入Hook函数后,再回来。
这好像是给我们DIY的,不用管
-
将当前任务的优先级和控制块修改为最高就绪任务的优先级和控制块,
-
修改SP指针
在上一步中 R2最终为最高就绪任务的栈顶指针。
-
将栈顶写入R0
根据上面的流程可以看到我们保存栈顶的指向的地方,所以TASK2(最高就绪任务)的栈顶指向如图
-
弹出R0的值到寄存器R4-R11
LDM R0, {R4, R11}
ADDS R0, R0, #0x20
这里就很惆怅,推测来看是每次传送前地址减4,传送完再加0x20,又回到了原点
-
把栈顶的值送给PSP
-
将LR寄存器的第二位写1,四到七为也写1
-
打开中断,返回LR。此时硬件自动将xPSR, PC, LR, R12以及R3‐R0弹出堆栈。