我是靠谱客的博主 有魅力砖头,最近开发中收集的这篇文章主要介绍基于STM32的RTOS简易分析-混合式调度一、双向链表的实现二、同优先级轮转调度设计三、仿真验证,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

        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简易分析-混合式调度一、双向链表的实现二、同优先级轮转调度设计三、仿真验证所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部