首页 > 自考资讯 > 自考知识

ucos任务栈,ucos调度算法

头条共创 2024-07-05

如果你用过ucosii,你就知道单片机+嵌入式实时操作系统可以让你充分利用CPU资源。添加实时操作系统可以让您创建更强大的产品和应用程序。

使用过ucosii的朋友是否了解任务调度的原理以及如何实现呢?

我个人会结合ucosii源码和我自己的理解来分享一下ucosii中任务管理和调度的一些实现。

1、ucos-ii 任务创建与任务调度

1.1、任务的创建

调用OSTaskCreate()创建任务会初始化任务堆栈、保存CPU寄存器、创建任务控制块(OS_TCB)等

if (OSTCBPrioTbl[prio]==(OS_TCB *)0) { /* 检查此优先级是否已存在任何任务*/OSTCBPrioTbl[prio]=OS_TCB_RESERVED;/* 没有其他任务可以运行将优先级保留为. *//* .直到任务创建。 */OS_EXIT_CRITICAL(); psp=OSTaskStkInit(task, p_arg, ptos, 0u); /* 初始化任务堆栈*/err=OS_TCBInit(prio, psp, (OS_STK *)0, 0u, 0u, (void *)0 , 0u); if (err==OS_ERR_NONE) { if (OSRunning==OS_TRUE) { /* 如果启动多任务,则查找最高优先级任务*/OS_Sched(); } } else { OSTCBPrioTbl[prio];=(OS_TCB *)0;/* 使该优先级可供其他人使用*/OS_EXIT_CRITICAL() } 注意:ucosii 不支持多个插头。对于任务优先级相同的任务,ucosiii 支持时间片轮换。

ucosii中的任务控制块是一个非常重要的块,它记录了任务优先级、延迟时间、状态等信息。控制块定义如下。

typedef struct os_tcb { OS_STK *OSTCBStkPtr; /* 指向当前堆栈顶部的指针*/#if OS_TASK_CREATE_EXT_EN 0u void *OSTCBExtPtr; /* 指向TCB 扩展的用户可定义数据的指针*/OS_STK *OSTCBStkBottom; */INT32U OSTCBStkSize; /* 任务堆栈的大小(堆栈元素的数量) */INT16U OSTCBOpt; /* 由OSTaskCreateExt() 传递的任务选项*/INT16U OSTCBId /* 任务ID (0. 65535) endif struct os_tcb *OSTCBNext; /* 指向TCB 列表中的下一个TCB */struct os_tcb *OSTCBPrev; /* 指向TCB 列表中的上一个TCB */#if (OS_EVENT_EN) OS_EVENT *OSTCBEventPtr; endif#if (OS_EVENT_EN) (OS_EVENT_MULTI_EN 0u) OS_EVENT **OSTCBEventMultiPtr; /* 指向多个事件控制块的指针*/#endif#if ((OS_Q_EN 0u) (OS_MAX_QS 0u)) || (OS_MBOX_EN 0u ) *OSTCBMSg;从OSMboxPost() 或OSQPost() 接收的消息*/#endif#if (OS_FLAG_EN 0u) (OS_MAX_FLAGS 0u)#if OS_TASK_DEL_EN 0u OS_FLAG_NODE *OSTCBFlagNode /* 指向事件标志节点的指针*/#endif OS_FLAGS OSTCBFlagsRdy; /* 事件标志使任务可运行*/#endif INT32U OSTCBDly; /* 勾选以延迟任务,或在等待事件时超时*/INT8U OSTCBStat; /* 任务的待处理状态*/INT8U OSTCBPrio /* 任务优先级(0)==最高) */INT8U OSTCBX; /* 任务优先级对应的位在组中的位置*/INT8U OSTCBY; /* 到任务优先级对应的就绪表索引*/OS_PRIO OSTCBBitX /* 访问就绪的位掩码表位位置*/OS_PRIO OSTCBBitY; /* 用于访问就绪组位位置的位掩码*/#if OS_TASK_DEL_EN 0u INT8U OSTCBDelReq /* 任务指示是否应删除自身*/#endif; #if OS_TASK_PROFILE_EN 0u INT32U OSTCBCtxSwCtr;任务切换的次数*/INT32U OSTCBCCyclesTot; /* 任务运行的时钟周期总数*/INT32U OSTCBCyclesStart; /* 任务重启开始时的周期计数器快照*/OS_STK *OSTCBStkBase;任务堆栈顶部*/INT32U OSTCBStkused; /* 堆栈使用的字节数*/#endif# if OS_TASK_NAME_EN 0u INT8U *OSTCBTaskName;#endif#if OS_TASK_REG_TBL_SIZE 0u INT32U OSTCBRegTbl [OS_TASK_REG_TBL_SIZE];#endif} OS_TCB;

2、任务调度实现

2.1、将任务优先级进行分组

ucosii 的优先级最大数量为64,因此可以分为8 组,每组有8 个优先级。

任务创建成功后,其组号由优先级高3 位(bit5、bit4、bit3)决定,组内编号由优先级低3 位(bit2、bit1、bit0)决定,如下: Masu.

#if OS_LOWEST_PRIO=63u /* 预先计算X, Y */ptcb-OSTCBY=(INT8U)(prio 3u) //组ptcb-OSTCBX=(INT8U)(prio0x07u); //组号#else;

2.2、任务就绪表

ucosii 通过查询任务就绪表来调度和管理任务优先级。任务就绪表存储所有任务当前的就绪状态:

OSRdyTbl[8] 说明: 1) uint8 数据类型。它的长度为8,每个元素代表一个组。例如,OSRdyTbl[0] 代表组0,OSRdyTbl[1] 代表组1,OSRdyTbl[2] 代表组2。 2)每个元素中的每一位代表组中某个任务的就绪状态(1表示就绪,0表示未就绪)。注意: 1) 当优先级为12 的任务就绪时,OSRdyTbl[1]相应的第四位将在系统范围内绝对为1。当只有优先级12 的任务准备就绪时,所有其他任务都完成。如果没有准备好,OSRdyTbl[1]绝对等于0x10。 2)当优先级为0和1的任务就绪时,对应的OSRdyTbl[0]的第0和1位绝对等于1。在系统范围内,如果只有优先级0 和1 任务准备就绪,而所有其他任务均未准备就绪,则OSRdyTbl[0] 恰好等于0x03。

2.3、任务释放CPU使用权

当在任务内调用OSTimeDly() 时,该任务将进入休眠状态,并将CPU 执行权限移交给其他可以运行的任务。这个过程涉及到任务切换。

简单来说,就是将任务就绪表OSRdyTbl中对应任务的优先级改为组内编号的状态,并将任务本身置于休眠状态。代码将如下所示。

if (ticks 0u) { /* 0 表示不延迟*/OS_ENTER_CRITICAL() /* 延迟当前任务*/OSRdyTbl[y]=(OS_PRIO)~OSTCBCur-OSTCBitX ]==0u) { OSRdyGrp=(OS_PRIO); ~OSTCBCur-OSTCBDly=tinys; /* 将刻度加载到TCB */OS_EXIT_CRITICAL(); /* 查找下一个要执行的任务。我在上面的代码中发现了一些东西:OSRdyGrp。这有什么好处?

OSRdyGrp:OSRdyGrp,管理任务准备组,类型为INT8U。只要该组中有一个任务就绪,相应的位就设置为1,表示当前有一个就绪任务。否则对应位为0。

例如:

1) 如果系统中只有任务0就绪,则OSRdyGrp等于0x01(二进制00000001)。 2) 如果系统上任务0 和任务63 已就绪,则OSRdyGrp 等于0x81(二进制10000001)。

2.4、任务实现调度切换操作

通过OS_Sched() 调度一项任务。这是源代码:

void OS_Sched (void){#if OS_CRITICAL_METHOD==3u /* 为CPU 状态寄存器分配存储*/OS_CPU_SR cpu_sr=0u;#endif OS_ENTER_CRITICAL() if (OSIntNesting==0u) { /* 所有ISR 完成, */if (OSLockNesting==0u) { /* . 调度器未锁定*/OS_SchedNew(); if (OSPrioHighRdy !=OSPrioCur) { /* 当前情况下没有Ctx Sw 任务具有最高的rdy */#if OS_TASK_PROFILE_EN 0u OSTCBHighRdy-OSTCBCtxSwCtr++; /* 上下文切换次数*/#endif OSCtxSwCtr++; /* 增加上下文切换计数器*/OS_TASK_SW();} OS_EXIT_CRITICAL();}

(1)首先通过OS_SchedNew()找到当前处于就绪状态的最高优先级任务,如下:

y=OSUnMapTbl[OSRdyGrp];OSPrioHighRdy=(INT8U)((y 3u) + OSUnMapTbl[OSRdyTbl[y]]); (2) 接下来,使用OS_TASK_SW() 执行任务切换。

1)OS_TASK_SW只是一个宏,实际上OSCtxSw()#define OS_TASK_SW() OSCtxSw()2) OSCtxSw()就是OSCtxSw PUSH {R4, R5}触发LDR R4,=NVIC_INT_CTRL(如果上下文切换) LDR R5,=NVIC_PENDSVSET STR R5,[R4] POP {R4,R5} BX LR 上下文切换完成。

版权声明:本文由今日头条转载,如有侵犯您的版权,请联系本站编辑删除。

猜你喜欢