概述
FreeRTOS和Ucos中不同优先级的任务运行时采用的是抢占式调度,同一优先级下有多个任务需要运行时,则对同优先级的任务进行时间片轮转调度。将抢占式调度和时间片轮转调度联合使用就是混合式调度。
在 RTOS 中,最小的时间单位为一个Tick,即 SysTick 的中断周期, 同优先级轮转调度时Ucos可以指定每个任务运行多少个 Tick,但是FreeRTOS的任务运行时间片是固定的一个Tick,不可调整。在Ucos中若优先级0下有两个任务A和B,任务时间片设置为10ms,SysTick 的中断周期为1ms。则系统运行结果 是A运行10ms后切换B运行10ms然后再切换为任务A运行如此反复。在FreeRTOS中则是A运行1ms切换任务B运行1ms再切换任务A运行。
为了方便在同一优先级下面挂载与删除任务,RTOS的任务控制块当中(Task Control Block TCB)中引入了双向链表结构。FreeRTOS和Ucos任务控制块结构体如下,本例程中模仿FreeRTOS中链表的用法来实现双向循环链表。
/*FreeRTOS*/
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
#endif
ListItem_t xStateListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
ListItem_t xEventListItem; /*< Used to reference a task from an event list. */
UBaseType_t uxPriority; /*< The priority of the task. 0 is the lowest priority. */
StackType_t *pxStack; /*< Points to the start of the stack. */
...
...
...
}
/*Ucos*/
struct os_tcb {
CPU_STK *StkPtr; /* Pointer to current top of stack */
void *ExtPtr; /* Pointer to user definable data for TCB extension */
CPU_STK *StkLimitPtr; /* Pointer used to set stack 'watermark' limit */
OS_TCB *NextPtr; /* Pointer to next TCB in the TCB list */
OS_TCB *PrevPtr; /* Pointer to previous TCB in the TCB list */
OS_TCB *TickNextPtr;
OS_TCB *TickPrevPtr;
...
...
...
}
一、双向链表的实现
网上关于双向循环链表的教程有很多,这里不再叙述链表的实现过程直接贴代码。推荐B站上的两个视频教程,一个是关于C语言指针的《强烈推荐】4小时彻底掌握C指针 - 顶尖程序员图文讲解 - UP主翻译校对 (已完结)》,另一个是关于数据结构的《【强烈推荐】深入浅出数据结构 - 顶尖程序员图文讲解 - UP主翻译校对 (已完结)》,讲师是一个印度小哥,讲解的很不错。
typedef struct STRUCT_NODE
{
struct STRUCT_NODE* next;
struct STRUCT_NODE* prev;
void* pOwner; /* Pointer to a TCB that contains the node */
}Node,*NodePtr;
typedef struct STRUCT_LIST
{
UINT16 nodeCount;
NodePtr pHeadNode;
}List,*ListPtr;
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void NodeInit(NodePtr pNode,void* pOwner)
{
pNode->next = pNode;
pNode->prev = pNode;
pNode->pOwner = pOwner;
}
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void ListInit(ListPtr pList)
{
pList->pHeadNode = NULL;
pList->nodeCount = 0;
}
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void ListInsertAtHead(ListPtr pList,NodePtr pNode)
{
if(pList->pHeadNode == NULL)
{
pList->pHeadNode = pNode;
pList->nodeCount++;
return ;
}
pList->pHeadNode->prev->next = pNode;
pNode->next = pList->pHeadNode;
pNode->prev = pList->pHeadNode->prev;
pList->pHeadNode->prev = pNode;
pList->pHeadNode = pNode;
pList->nodeCount++;
}
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void ListInsertAtTail(ListPtr pList,NodePtr pNode)
{
if(pList->pHeadNode == NULL)
{
pList->pHeadNode = pNode;
pList->nodeCount++;
return ;
}
pList->pHeadNode->prev->next = pNode;
pNode->next = pList->pHeadNode;
pNode->prev = pList->pHeadNode->prev;
pList->pHeadNode->prev = pNode;
pList->nodeCount++;
}
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
NodePtr ListPopHead(ListPtr pList)
{
NodePtr pTmpNode = NULL;
if(pList->pHeadNode == NULL)
{
return NULL;
}
pTmpNode = pList->pHeadNode;
pList->pHeadNode = pTmpNode->next;
pList->pHeadNode->prev = pTmpNode->prev;
pTmpNode->prev->next = pList->pHeadNode;
pList->nodeCount--;
return pTmpNode;
}
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void ListRemoveNode(ListPtr pList, NodePtr pNode)
{
pNode->prev->next = pNode->next;
pNode->next->prev = pNode->prev;
NodeInit(pNode,pNode->pOwner);
pList->nodeCount--;
}
二、同优先级轮转调度设计
任务控制块中增加链表节点变量和时间片设置变量,当设置的任务运行时间片为0时,就采用系统缺省值代替0。
typedef struct STRUCT_TASK_TCB
{
UINT32 *stackPtr; /*Points to the location of the last item placed on the tasks stack*/
Node taskTcbNode;
UINT8 priority; /*The priority of the task. 0 is the highest priority*/
UINT8 timeSliceSet;
UINT8 timeSlice;
}TaskTcb,*TaskTcbPtr;
List taskTcbPtrPrioTbl[OS_TASK_MAX_PRIORITIES + 1];
/**********************************************************************************************
* @brief TaskCreate
*
* @param None
* @retval None
**********************************************************************************************/
void TaskCreate(TaskTcb *pTcb, FunPtr pTaskFun, UINT32* pTaskStack, UINT32 stackSize, UINT8 taskPrio, UINT8 timeSlice)
{
UINT32 *pStack;
UINT32 cnt;
for(cnt = 0; cnt < stackSize*sizeof(UINT32);cnt++)/* Fill the stack with a known value to assist debugging. */
{
*((UINT8*)pTaskStack + cnt) = OS_STACK_FILL_BYTE;
}
pStack = &pTaskStack[stackSize];/* Get top address of stack, because grows from high memory to low*/
pStack = (UINT32 *)((UINT32)(pStack) & 0xFFFFFFF8u);/* AAPCS(ARM application procedure all standard) Align the stack to 8bytes */
/*Registers stacked as if auto-saved on exception*/
*(--pStack) = (UINT32)0x01000000uL; /*xPSR bit24 thumb state bit*/
*(--pStack) = (UINT32)pTaskFun; /*Entry Point(PC)*/
*(--pStack) = (UINT32)0x12121212uL; /*R14 (LR)*/
*(--pStack) = (UINT32)0x12121212uL; /* R12*/
*(--pStack) = (UINT32)0x03030303uL; /* R3*/
*(--pStack) = (UINT32)0x02020202uL; /* R2*/
*(--pStack) = (UINT32)0x01010101uL; /* R1*/
*(--pStack) = (UINT32)0x00000000u; /* R0*/
/*Remaining registers saved on process stack*/
*(--pStack) = (UINT32)0x11111111uL; /* R11*/
*(--pStack) = (UINT32)0x10101010uL; /* R10*/
*(--pStack) = (UINT32)0x09090909uL; /* R9*/
*(--pStack) = (UINT32)0x08080808uL; /* R8*/
*(--pStack) = (UINT32)0x07070707uL; /* R7*/
*(--pStack) = (UINT32)0x06060606uL; /* R6*/
*(--pStack) = (UINT32)0x05050505uL; /* R5*/
*(--pStack) = (UINT32)0x04040404uL; /* R4*/
pTcb->stackPtr = pStack;
pTcb->priority = taskPrio;
if(timeSlice != 0)
pTcb->timeSliceSet = timeSlice;
else
pTcb->timeSliceSet = OS_TASK_DEFAULT_TIME_SLICE;
pTcb->timeSlice = pTcb->timeSliceSet;
NodeInit(&pTcb->taskTcbNode, pTcb);
ListInsertAtTail(&taskTcbPtrPrioTbl[taskPrio], &pTcb->taskTcbNode);
BitMapSetBit(&taskPrioBitMap, taskPrio);
}
将taskTcbPtrPrioTbl由上一章的任务控制块指针数组修改为双向链表数组,并修改相应的函数。在OsTaskStart函数中自动获取最高优先级任务的控制块指针并启动任务运行。
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void TaskScheduleInit(void)
{
UINT8 i = 0;
for(i = 0; i < OS_TASK_MAX_PRIORITIES + 1; i++)
{
ListInit(&taskTcbPtrPrioTbl[i]);
}
BitMapInit(&taskPrioBitMap);
}
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void OsTaskStart(void)
{
UINT8 highestPrio = BitMapFirstGet(&taskPrioBitMap);
pOsHighRdyTcb = (TaskTcbPtr)(taskTcbPtrPrioTbl[highestPrio].pHeadNode->pOwner);
OSStartHighRdy();
}
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void TaskSchedule(void)
{
UINT8 prio = BitMapFirstGet(&taskPrioBitMap);
if((TaskTcbPtr)(taskTcbPtrPrioTbl[prio].pHeadNode->pOwner) != pOsCurTcb)
{
pOsHighRdyTcb = (TaskTcbPtr)(taskTcbPtrPrioTbl[prio].pHeadNode->pOwner);
TaskSwitch();
}
}
每进入一次SysTick中断就将时间片减1,只有同优先级任务数量大于1个时才进行轮转调度,并更新下一任务运行时的时间片长度。时间片运行结束的任务放入队尾,等待下一周期的轮转调用。
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void SysTick_Handler(void)
{
NodePtr pHeadNode;
TaskTcbPtr pTaskTcb;
if(pOsCurTcb->timeSlice)
pOsCurTcb->timeSlice--;
if(taskTcbPtrPrioTbl[pOsCurTcb->priority].nodeCount > 1)
{
if(pOsCurTcb->timeSlice == 0)
{
pHeadNode = ListPopHead(&taskTcbPtrPrioTbl[pOsCurTcb->priority]);
ListInsertAtTail(&taskTcbPtrPrioTbl[pOsCurTcb->priority],pHeadNode);
pTaskTcb = (TaskTcbPtr)(taskTcbPtrPrioTbl[pOsCurTcb->priority].pHeadNode->pOwner);
pTaskTcb->timeSlice = pTaskTcb->timeSliceSet;
}
}
TaskSchedule();
}
三、仿真验证
在main函数当中创建两个任务,优先级设置为0,StartTask任务时间片设置为2,SecondTask任务时间片设置为3。仿真查看两个两个任务是否交替运行。
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
LED_GPIO_Config();
SysTickRateInit();
TaskScheduleInit();
TaskCreate(&StartTaskTcb, StartTask, StartTaskStack, START_TASK_SIZE,0, 2);
TaskCreate(&SecondTaskTcb, SecondTask, SecondTaskStack, SECOND_TASK_SIZE,0,3);
OsTaskStart();
return 0;
}
当前StartTask任务的时间片运行结束即将切换到SecondTask运行。
当前SecondTask任务的时间片运行结束即将切换到StartTask运行。
最后
以上就是有魅力砖头为你收集整理的基于STM32的RTOS简易分析-混合式调度一、双向链表的实现二、同优先级轮转调度设计三、仿真验证的全部内容,希望文章能够帮你解决基于STM32的RTOS简易分析-混合式调度一、双向链表的实现二、同优先级轮转调度设计三、仿真验证所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复