uCOS里面触发任务切换是通过触发PendSv异常,然后在PendSv异常服务函数里面实现的。
在运行中实现任务切换的函数有两个:1、任务级任务切换 2、中断级任务切换
任务级任务切换:
OSCtxSw
LDR R0, =NVIC_INT_CTRL ;把 NVIC_INT_CTRL寄存器值加载到R0 ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET ;触发PendSv信号NVIC_PENDSVSET加载到R1
STR R1, [R0] ;触发PendSv中断
BX LR
中断级任务切换:
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL ;把 NVIC_INT_CTRL寄存器值加载到R0 ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET ;触发PendSv信号NVIC_PENDSVSET加载到R1
STR R1, [R0] ;触发PendSv中断
BX LR
OSCtxSw 和 OSIntCtxSw 这两个是用来做任务切换的,这两个代码是一样的,它们都只是触发一个 PendSV 异常,然后在 PendSV异常服务函数里面进行任务切换。
这两个函数虽然看起来是一样的,但是他们的意义是不同的,
- OSCtxSw 是任务级切换,比如从任务 A 切换到任务B;
- OSIntCtxSw 是中断级切换,是从中断退出时切换到一个任务中,从中断切换到任务时,CPU 的寄存器入栈工作已经完成,无需再做第二次。
比如,在OSSched (void)函数里面调用的是OSCtxSw 进行任务级的任务切换;
在OSIntExit (void)中断退出函数调用的是OSIntCtxSw 进行了中断级任务切换。
在这里先了解一下UCOS处理中断的两种方式:
uC/OS-III 有两种方法处理来自于中断的事件,直接发布(或者称为释放)和延迟发布。
通过 os_cfg.h 中的 OS_CFG_ISR_POST_DEFERRED_EN 来选择。
当设置为 0 时,uCOS 使用中断直接发布;
当设置为 1 时,使用中断延迟发布。
一、任务级的任务切换:
关于OSCtxSw的宏定义:
#define OS_TASK_SW() OSCtxSw()
在void OSSched (void)函数里面调用了OS_TASK_SW()函数来触发PendSv异常。
经常用于任务级任务切换的5个函数:
-
OSTimeDly()函数
-
OSTimeDlyHMSM()函数
-
OSSchedRoundRobinYield()函数
该函数只用于在系统使用时间片轮转调度的时候,当一个任务想放弃本次时间片而把 CPU 的使用权让给同优先级下的另外一个任务的任务切换。 -
OS_IntQTask()函数
OS_IntQTask()函数是uCOS自己创建的,优先级为0的中断服务管理任务;该任务函数用于中断延迟发布的方式,可以将中断级发布转换成任务级发布。如果要使用中断延迟发布记得把 OS_CFG_ISR_POST_DEFERRED_EN 定义为 1。 -
OSStartHighRdy()函数
OSStartHighRdy()函数(汇编函数,没有调用OSCtxSw函数)触发了PendSv异常(OSStart函数调用),发生了任务切换。(OSStart函数用于开启uCOS,在定义第一个任务的结尾用到了)
二、中断级任务切换:
void OSIntExit (void)函数里面调用了OSIntCtxSw()函数来触发中断级PendSv异常。
所以在我们所有的中断服务函数的后面我们都会调用OSIntExit()函数来进行任务切换。
在uCOS中断服务函数的写法:
//**********uCOS中中断服务函数的写法**************//
void XXX_Handler(void)
{
if(delay_osrunning==1) //OS开始跑了,才执行正常的调度处理
{
OSIntEnter(); //进入中断
//***********中断函数的内容***********//
OSIntExit(); //触发任务切换软中断
}
}
uCOS里的中断服务函数只要在裸机程序的基础上再在开头和结尾加上这3条语句即可。
注意:
时间片轮转调度通过调用 OS_SchedRoundRobin()函数来完成,这个函数由OSTimeTick()或者 OS_IntQTask()调用。时间片轮转调度的调度方式取决于uCOS处理中断的方式。
1、在使用中断延时发布时,最终是在OS_IntQTask()中断服务管理函数里面发生任务级调度;
2、在使用中断直接发布时,采用中断级任务调度,在SysTick中断服务函数里面调用OSTimeTick函数(在OSTimeTick函数里面调用 OS_SchedRoundRobin函数里面对参数TimeQuantaCtr的修改)。