UCOSIII学习-任务管理
- 1、UCOSIII 任务组成
- 2、UCOSIII 默认系统任务
- 3、UCOSIII 任务状态
- 4、任务堆栈
-
- 5、任务控制块
-
- 6、任务就绪表
-
- 7、任务调度
- 1. 可剥夺型任务调度
- 2. 任务调度点
- 3. 调度器上锁与解锁
- 4. 时间片轮转调度
- 8、任务切换
-
- 9、UCOSIII系统初始化
1、UCOSIII 任务组成
UCOSIII由任务堆栈、任务控制块、任务函数三部分组成。
- 任务堆栈:保存工作环境,如寄存器的值
- 任务控制块:记录任务的各个属性
- 任务函数:任务具体的实现代码
2、UCOSIII 默认系统任务
- 空闲任务:必须任务,由UCOSIII自己创建的第一个任务
- 时钟节拍任务:必须创建
- 统计任务:可选任务。用来统计CPU使用率和各个任务的堆栈使用量
- 定时任务:可选任务。向用户提供定时服务
- 中断服务管理任务:可选任务
3、UCOSIII 任务状态
- 休眠态(INITIAL): 任务已经在空闲任务池中(CPU的flash中),但还未受到UCOSIII管理
- 等待态(IDLE): 任务一旦被创建就进入等待态,或者正在运行的任务需要等待一段时间或者等待某个事件(任务完成或者任务退出了),此时系统把CPU的使用权交给别的任务,当前任务就进入等待态
- 就绪态(READY): 系统为任务分配了任务控制块,并且任务已经在就绪表中登记,具备了被执行的条件,此时任务就在就绪态
- 运行态(RUNNING): 任务获得CPU使用权,正在运行
- 中断服务态(SUSPEND): 当发生中断,当前的任务会被挂起,CPU先去执行中断服务函数,此时当前任务需要等待资源或者等待其他任务结束
4、任务堆栈
1. 任务堆栈的创建
- 遵循 先进先出FIFO 原则的一块连续内存空间
- 存储任务切换时或中断时CPU寄存器的内容
- 每个任务都有自己的堆栈空间
2. 任务堆栈初始化
创建一个任务时,需要把系统启动这个任务时所需的CPU各个寄存器的初始值事先存放到任务堆栈中。当该任务获得CPU使用权时,就把任务堆栈的内容复制到CPU的寄存器中,开始运行任务。
5、任务控制块
1. 任务控制块创建
- 记录与任务相关的信息的 数据结构
- 每个任务都有自己的任务控制块
- 任务控制块需要自己创建
结构体如下:
struct os_tcb {
CPU_STK *StkPtr;
void *ExtPtr;
CPU_STK *StkLimitPtr;
OS_TCB *NextPtr;
OS_TCB *PrevPtr;
OS_TCB *TickNextPtr;
OS_TCB *TickPrevPtr;
OS_TICK_SPOKE *TickSpokePtr;
CPU_CHAR *NamePtr;
CPU_STK *StkBasePtr;
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
OS_TLS TLS_Tbl[OS_CFG_TLS_TBL_SIZE];
#endif
OS_TASK_PTR TaskEntryAddr;
void *TaskEntryArg;
OS_PEND_DATA *PendDataTblPtr;
OS_STATE PendOn;
OS_STATUS PendStatus;
OS_STATE TaskState;
OS_PRIO Prio;
CPU_STK_SIZE StkSize;
OS_OPT Opt;
OS_OBJ_QTY PendDataTblEntries;
CPU_TS TS;
OS_SEM_CTR SemCtr;
OS_TICK TickCtrPrev;
OS_TICK TickCtrMatch;
OS_TICK TickRemain;
OS_TICK TimeQuanta;
OS_TICK TimeQuantaCtr;
#if OS_MSG_EN > 0u
void *MsgPtr;
OS_MSG_SIZE MsgSize;
#endif
#if OS_CFG_TASK_Q_EN > 0u
OS_MSG_Q MsgQ;
#if OS_CFG_TASK_PROFILE_EN > 0u
CPU_TS MsgQPendTime;
CPU_TS MsgQPendTimeMax;
#endif
#endif
#if OS_CFG_TASK_REG_TBL_SIZE > 0u
OS_REG RegTbl[OS_CFG_TASK_REG_TBL_SIZE];
#endif
#if OS_CFG_FLAG_EN > 0u
OS_FLAGS FlagsPend;
OS_FLAGS FlagsRdy;
OS_OPT FlagsOpt;
#endif
#if OS_CFG_TASK_SUSPEND_EN > 0u
OS_NESTING_CTR SuspendCtr;
#endif
#if OS_CFG_TASK_PROFILE_EN > 0u
OS_CPU_USAGE CPUUsage;
OS_CPU_USAGE CPUUsageMax;
OS_CTX_SW_CTR CtxSwCtr;
CPU_TS CyclesDelta;
CPU_TS CyclesStart;
OS_CYCLES CyclesTotal;
OS_CYCLES CyclesTotalPrev;
CPU_TS SemPendTime;
CPU_TS SemPendTimeMax;
#endif
#if OS_CFG_STAT_TASK_STK_CHK_EN > 0u
CPU_STK_SIZE StkUsed;
CPU_STK_SIZE StkFree;
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN
CPU_TS IntDisTimeMax;
#endif
#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
CPU_TS SchedLockTimeMax;
#endif
#if OS_CFG_DBG_EN > 0u
OS_TCB *DbgPrevPtr;
OS_TCB *DbgNextPtr;
CPU_CHAR *DbgNamePtr;
#endif
};
2. 任务控制块初始化
参考正点原子移植代码及PDF:
void OS_TaskInitTCB (OS_TCB *p_tcb)
{
#if OS_CFG_TASK_REG_TBL_SIZE > 0u
OS_REG_ID reg_id;
#endif
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
OS_TLS_ID id;
#endif
#if OS_CFG_TASK_PROFILE_EN > 0u
CPU_TS ts;
#endif
p_tcb->StkPtr = (CPU_STK *)0;
p_tcb->StkLimitPtr = (CPU_STK *)0;
p_tcb->ExtPtr = (void *)0;
p_tcb->NextPtr = (OS_TCB *)0;
p_tcb->PrevPtr = (OS_TCB *)0;
p_tcb->TickNextPtr = (OS_TCB *)0;
p_tcb->TickPrevPtr = (OS_TCB *)0;
p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
p_tcb->NamePtr = (CPU_CHAR *)((void *)"?Task");
p_tcb->StkBasePtr = (CPU_STK *)0;
p_tcb->TaskEntryAddr = (OS_TASK_PTR )0;
p_tcb->TaskEntryArg = (void *)0;
#if (OS_CFG_PEND_MULTI_EN > 0u)
p_tcb->PendDataTblPtr = (OS_PEND_DATA *)0;
p_tcb->PendDataTblEntries = (OS_OBJ_QTY )0u;
#endif
p_tcb->TS = (CPU_TS )0u;
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = (void *)0;
p_tcb->MsgSize = (OS_MSG_SIZE )0u;
#endif
#if OS_CFG_TASK_Q_EN > 0u
OS_MsgQInit(&p_tcb->MsgQ,
(OS_MSG_QTY)0u);
#if OS_CFG_TASK_PROFILE_EN > 0u
p_tcb->MsgQPendTime = (CPU_TS )0u;
p_tcb->MsgQPendTimeMax = (CPU_TS )0u;
#endif
#endif
#if OS_CFG_FLAG_EN > 0u
p_tcb->FlagsPend = (OS_FLAGS )0u;
p_tcb->FlagsOpt = (OS_OPT )0u;
p_tcb->FlagsRdy = (OS_FLAGS )0u;
#endif
#if OS_CFG_TASK_REG_TBL_SIZE > 0u
for (reg_id = 0u; reg_id < OS_CFG_TASK_REG_TBL_SIZE; reg_id++) {
p_tcb->RegTbl[reg_id] = (OS_REG)0u;
}
#endif
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
for (id = 0u; id < OS_CFG_TLS_TBL_SIZE; id++) {
p_tcb->TLS_Tbl[id] = (OS_TLS)0;
}
#endif
p_tcb->SemCtr = (OS_SEM_CTR )0u;
#if OS_CFG_TASK_PROFILE_EN > 0u
p_tcb->SemPendTime = (CPU_TS )0u;
p_tcb->SemPendTimeMax = (CPU_TS )0u;
#endif
p_tcb->StkSize = (CPU_STK_SIZE )0u;
#if OS_CFG_TASK_SUSPEND_EN > 0u
p_tcb->SuspendCtr = (OS_NESTING_CTR )0u;
#endif
#if OS_CFG_STAT_TASK_STK_CHK_EN > 0u
p_tcb->StkFree = (CPU_STK_SIZE )0u;
p_tcb->StkUsed = (CPU_STK_SIZE )0u;
#endif
p_tcb->Opt = (OS_OPT )0u;
p_tcb->TickCtrPrev = (OS_TICK )OS_TICK_TH_INIT;
p_tcb->TickCtrMatch = (OS_TICK )0u;
p_tcb->TickRemain = (OS_TICK )0u;
p_tcb->TimeQuanta = (OS_TICK )0u;
p_tcb->TimeQuantaCtr = (OS_TICK )0u;
#if OS_CFG_TASK_PROFILE_EN > 0u
p_tcb->CPUUsage = (OS_CPU_USAGE )0u;
p_tcb->CPUUsageMax = (OS_CPU_USAGE )0u;
p_tcb->CtxSwCtr = (OS_CTX_SW_CTR )0u;
p_tcb->CyclesDelta = (CPU_TS )0u;
ts = OS_TS_GET();
p_tcb->CyclesStart = ts;
p_tcb->CyclesTotal = (OS_CYCLES )0u;
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN
p_tcb->IntDisTimeMax = (CPU_TS )0u;
#endif
#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
p_tcb->SchedLockTimeMax = (CPU_TS )0u;
#endif
p_tcb->PendOn = (OS_STATE )OS_TASK_PEND_ON_NOTHING;
p_tcb->PendStatus = (OS_STATUS )OS_STATUS_PEND_OK;
p_tcb->TaskState = (OS_STATE )OS_TASK_STATE_RDY;
p_tcb->Prio = (OS_PRIO )OS_PRIO_INIT;
#if OS_CFG_DBG_EN > 0u
p_tcb->DbgPrevPtr = (OS_TCB *)0;
p_tcb->DbgNextPtr = (OS_TCB *)0;
p_tcb->DbgNamePtr = (CPU_CHAR *)((void *)" ");
#endif
}
6、任务就绪表
1. 优先级
- UCOSIII理论上支持无限个任务,优先级无数个,默认64个优先级,同一个优先级使用时间片轮转调度的方法
- UCOSIII中优先级越高,数值越小
2. 就绪表
UCOSIII就绪表由两部分组成:
- 优先级位映射表:用来记录哪个优先级下有任务就绪
- 就绪任务列表:用来记录每一个优先级下所有就绪的任务。
7、任务调度
1. 可剥夺型任务调度
- 任务调度是中止当前正在运行的任务而去执行其他任务
- UCOSIII是可剥夺型内核。即当一个高优先级任务准备就绪,且此时发生任务调度,那么高优先级任务会获得CPU使用权
- 任务调度由任务调度器完成,任务调度器分为两种:任务级调度器,中断级调度器。
2. 任务调度点
以正点原子STM32库函数为例,高亮为STM32重点关注点(常用)
- 释放信号量或者发送消息,也可通过配置相应的参数不发生任务调度。(内核对象时使用)
- 使用延时函数OSTimeDly()或者OSTimeDl yHMSM()
- 任务等待的事情还没发生(等待信号量,消息队列等)。
- 任务取消等待。
- 创建任务
- 删除任务
- 删除一个内核对象。
- 任务改变自身的优先级或者其他任务的优先级。
- 任务通过调用OSTaskSuspend将自身挂起
- 任务解挂某个挂起的任务
- 退出所有的嵌套中断
- 通过OSSchedUnlock给调度器解锁
- 任务调用OSSchedRoundRobinYield(放弃其执行时间片
- 用户调用oSSched()函数
3. 调度器上锁与解锁
有时候我们并不希望发生任务调度,因为始终有一些代码的执行过程是不能被打断的。此时我们就可以使用函数OSSchedLock()对调度器加锁,当我们想要恢复任务调度的时候就可以使用函数OSSchedUnlock()给已经上锁的任务调度器解锁。如:创建任务时
4. 时间片轮转调度
- UCOSIII允许一个优先级下有多个任务,每个任务可以执行指定的时间(时间片),然后轮到下一个任务,这个过程就是时间片轮转调度,当一个任务不想在运行的时候就可以放弃其时间片。
- 时间片轮转调度器为: 0S_SchedRounuRobin()。
8、任务切换
1. 任务切换
- 当UCOSIII需要切换到另外一个任务时,它将保存当前任务的现场到当前任务的堆栈中,主要是CPU寄存器值,然后恢复新的现场并且执行新的任务,这个过程就是任务切换
- 任务切换分为两种: 任务级切换、中断级切换
- 任务级切换函数为:OSCtxSw()
- 中断级切换函数为:OSIntCtxSw()
任务调度会调用任务切换,是包含关系。任务切换是保存与恢复现场的过程,至于任务调度,要比任务切换过程更为复杂。汇编代码如下:
任务级切换源码
;********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From task level) - OSCtxSw()
;
; Note(s) : 1) OSCtxSw() is called when OS wants to perform a task context switch. This function
; triggers the PendSV exception which is where the real work is done.
;********************************************************************************************************
OSCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
中断级切换源码
;********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From interrupt level) - OSIntCtxSw()
;
; Note(s) : 1) OSIntCtxSw() is called by OSIntExit() when it determines a context switch is needed as
; the result of an interrupt. This function simply triggers a PendSV exception which will
; be handled when there are no more interrupts active and interrupts are enabled.
;********************************************************************************************************
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
9、UCOSIII系统初始化
在使用UCOSIII之前我们必须先初始化UCOSIII,函数OSInit()用来完成UCOSIII的初始化,而且OSInit()必须先于其他UCOSIII函数调用,包括oSStart()函数。
main():
int main(void)
{
OS_ERR err;
......
......
OSInit(&err);
......
......
OSStart(&err);
}
OSInit():
void OSInit (OS_ERR *p_err)
{
CPU_STK *p_stk;
CPU_STK_SIZE size;
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
OSInitHook();
OSIntNestingCtr = (OS_NESTING_CTR)0;
OSRunning = OS_STATE_OS_STOPPED;
OSSchedLockNestingCtr = (OS_NESTING_CTR)0;
OSTCBCurPtr = (OS_TCB *)0;
OSTCBHighRdyPtr = (OS_TCB *)0;
OSPrioCur = (OS_PRIO)0;
OSPrioHighRdy = (OS_PRIO)0;
OSPrioSaved = (OS_PRIO)0;
#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
OSSchedLockTimeBegin = (CPU_TS)0;
OSSchedLockTimeMax = (CPU_TS)0;
OSSchedLockTimeMaxCur = (CPU_TS)0;
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
OSSafetyCriticalStartFlag = DEF_FALSE;
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
OSSchedRoundRobinEn = DEF_FALSE;
OSSchedRoundRobinDfltTimeQuanta = OSCfg_TickRate_Hz / 10u;
#endif
if (OSCfg_ISRStkSize > (CPU_STK_SIZE)0) {
p_stk = OSCfg_ISRStkBasePtr;
if (p_stk != (CPU_STK *)0) {
size = OSCfg_ISRStkSize;
while (size > (CPU_STK_SIZE)0) {
size--;
*p_stk = (CPU_STK)0;
p_stk++;
}
}
}
#if OS_CFG_APP_HOOKS_EN > 0u
OS_AppTaskCreateHookPtr = (OS_APP_HOOK_TCB )0;
OS_AppTaskDelHookPtr = (OS_APP_HOOK_TCB )0;
OS_AppTaskReturnHookPtr = (OS_APP_HOOK_TCB )0;
OS_AppIdleTaskHookPtr = (OS_APP_HOOK_VOID)0;
OS_AppStatTaskHookPtr = (OS_APP_HOOK_VOID)0;
OS_AppTaskSwHookPtr = (OS_APP_HOOK_VOID)0;
OS_AppTimeTickHookPtr = (OS_APP_HOOK_VOID)0;
#endif
#if OS_CFG_TASK_REG_TBL_SIZE > 0u
OSTaskRegNextAvailID = (OS_REG_ID)0;
#endif
OS_PrioInit();
OS_RdyListInit();
#if OS_CFG_FLAG_EN > 0u
OS_FlagInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if OS_CFG_MEM_EN > 0u
OS_MemInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if (OS_MSG_EN) > 0u
OS_MsgPoolInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if OS_CFG_MUTEX_EN > 0u
OS_MutexInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if OS_CFG_Q_EN > 0u
OS_QInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if OS_CFG_SEM_EN > 0u
OS_SemInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
OS_TLS_Init(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
OS_TaskInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
OS_IntQTaskInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
OS_IdleTaskInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
OS_TickTaskInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#if OS_CFG_STAT_TASK_EN > 0u
OS_StatTaskInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if OS_CFG_TMR_EN > 0u
OS_TmrInit(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
#if OS_CFG_DBG_EN > 0u
OS_Dbg_Init();
#endif
OSCfg_Init();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)