Toriyung

导航

优先级

rtos中,对任务的调度是按最高优先级的顺序进行的,所以需要对每个任务进行优先级的定义,而有了优先级之后,其他的代码:如结构体,创建任务,切换任务,阻塞延时等都需要相应修改

 

1. 任务TCB结构体:TCB结构体添加优先级属性成员

typedef struct tasTaskControlerBlock
{
    volatile StackType_t *pxTopOfStack;                        //栈顶
    ListItem_t xStateListItem;                                        //任务节点(链表项)
    StackType_t *pxStack;                                                    //任务栈起始地址
    char pcTaskName[configMAX_TASK_NAME_LEN];            //任务名称
    TickType_t xTicksToDelay;                                            //任务延时计数值
    UBaseType_t uxPriority;                                                //优先级
}tskTCB;    
typedef tskTCB    TCB_t;

 

2. 创建任务→初始化任务:初始化任务优先级

static void prvInitialiseNewTask(TaskFunction_t pxTaskCode, const char * const pcName, const uint32_t ulStackDepth, void * pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, TCB_t *pxNewTCB)
{
    /*    
        brief:初始化新任务    
        param:
            pxTaskCode:任务函数
            pcName:任务名称
            ulStackDepth:任务栈深
            pvParameters:任务函数传参
            pxCreatedTask:任务句柄,指向TCB
            pxNewTCB:TCB
        return:NULL
    
    */
    
    StackType_t *pxTopOfStack;
    UBaseType_t x;
    
    //计算栈顶
    pxTopOfStack = pxNewTCB->pxStack + (ulStackDepth - (uint32_t)1);
    pxTopOfStack = (StackType_t *)((uint32_t)pxTopOfStack & ~((uint32_t)0x0007));    //向下对齐八个字节
    
    //初始化任务栈(伪造现场)
    pxNewTCB->pxTopOfStack = pxPortInitialiseStack(pxTopOfStack, pxTaskCode, pvParameters);
    
    //初始化任务名称
    for(x = (UBaseType_t)0;x < (UBaseType_t)configMAX_TASK_NAME_LEN;x++)
    {
        pxNewTCB->pcTaskName[x] = pcName[x];
        if(pcName[x] == 0x00)
        {
            break;
        }
    }
    pxNewTCB->pcTaskName[configMAX_TASK_NAME_LEN -1] = '\0';    //最后一个字符为结束符?
    
    //初始化任务节点
    vListInitialiseItem(&(pxNewTCB->xStateListItem));
    
    //设置节点owner
    listSET_LIST_ITEM_OWNER((pxNewTCB->xStateListItem),pxNewTCB);    //关联owner,令该任务节点挂载TCB
    
    //初始化优先级
    if(uxPriority >= (UBaseType_t) configMAX_PRIORITIES)
    {
        uxPriority = (UBaseType_t) configMAX_PRIORITIES - (UBaseType_t) 1U;
    }
    pxNewTCB->uxPriority = uxPriority;
    
    //任务句柄指向TCB
    if((void *) pxCreatedTask != NULL)
    {
        *pxCreatedTask = (TaskHandle_t) pxNewTCB;
    }
}

 

3. 添加任务到就绪链表:需要做三件事:1. 当前任务指向最高任务;2. 更新优先级(保证最高优先级能被记录);3. 插入相应优先级链表尾部

  首先定义添加函数prvAddNewTaskToReadyList。其完成了任务计数加1,当前任务指向

static void prvAddNewTaskToReadyList(TCB_t *pxNewTCB)
{
    taskENTER_CRITICAL();                                    //进入临界区
    
    uxCurrentNumberOfTasks++;                            //添加了新任务,计数加一
    
    if(pxCurrentTCB == NULL)                            //如果当前任务为空,则指向新创建的任务
    {
        pxCurrentTCB = pxNewTCB;
        
        if(uxCurrentNumberOfTasks == (UBaseType_t) 1)        //第一次创建任务则初始化任务列表
        {
            prvInitialiseTaskLists();
        }
    }
    else
    {
        if(pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority)    //保持当前任务始终为最高优先级任务
        {
            pxCurrentTCB = pxNewTCB;
        }
    }
    prvAddTaskToReadyList(pxNewTCB);            //添加到就绪链表

    taskEXIT_CRITICAL();                                    //退出临界区
}

    函数中使用了添加就绪链表的宏定义prvAddTaskToReadyList。其完成了更新优先级,插入相应链表尾部的操作

#define prvAddTaskToReadyList(pxTCB) \
    { /* 更新最高优先级 */ \
        taskRECORD_READY_PRIORITY((pxTCB)->uxPriority);    \
        /* 插入相应优先级尾部 */ \
        vListInsertEnd(&(pxReadyTasksLists[(pxTCB)->uxPriority]),&((pxTCB)->xStateListItem));  \
    }

    最后在创建任务xTaskCreateStatic函数中,完成初始化任务后调用prvAddNewTaskToReadyList函数,由于代码过长不演示

 

4. 任务调度函数:调度函数内核自动创建空闲任务后,再无需将空闲任务手动插入就绪链表和指定当前任务,由3可知,创建任务函数内部已自动将任务加入就绪链表,同时指定当前任务位最高优先级任务,所以把两个相关语句注释

void vTaskStartScheduler(void)
{
    /* 启动任务调度(含有空闲任务创建) */
    TaskHandle_t xIdleTaskHandle;                                            //空闲任务句柄
    TCB_t *pxIdleTaskTCBBuffer = NULL;                                //空闲任务TCB的指针
    StackType_t *pxIdleTaskStackBuffer = NULL;                //空闲任务栈指针
    uint32_t ulIdleTaskStackSize;                                            //空闲任务栈大小
    
    vApplicationGetIdleTaskMemory(&pxIdleTaskTCBBuffer,&pxIdleTaskStackBuffer,&ulIdleTaskStackSize);        //获取空闲任务信息,注意用二重指针
    
    //创建空闲任务
    xIdleTaskHandle = xTaskCreateStatic((TaskFunction_t)prvIdleTask,(char *)"IDLE", (uint32_t)ulIdleTaskStackSize,(void *)NULL, (UBaseType_t)tskIDLE_PRIORITY, (StackType_t *)pxIdleTaskStackBuffer,(TCB_t *)pxIdleTaskTCBBuffer);
    
    //vListInsertEnd(&(pxReadyTasksLists[0]),&(((TCB_t *)pxIdleTaskTCBBuffer)->xStateListItem));  //换成(*xIdleTaskHandle)->xStateListItem呢?

    /* 原本调度器的调度部分 */
    
    //pxCurrentTCB = &Task1TCB;    //指定第一个任务
    
    if(xPortStartScheduler() != pdFALSE)
    {
        //调度器启动失败则进入这里
    }
}

 

5. 阻塞延时:由前面文章可知,在没有优先级的情况下,阻塞延时函数的作用是给任务赋予延时计数值,当有了优先级,就需要将任务从就绪链表移出,放入延时链表(但本章未实现延时链表,所以仅仅是将这一优先级从优先级位图表置零)

void vTaskDelay(const TickType_t xTicksToDelay)
{
    TCB_t *pxTCB = NULL;
    
    //设置当前任务的延时计数
    pxTCB = pxCurrentTCB;
    pxTCB->xTicksToDelay = xTicksToDelay;
    
    // 任务从就绪列表中删除进入延时列表 
    //uxListRemove(&(pxTCB->xStateListItem));
    
    //就绪优先级位置0    
    taskRESET_READY_PRIORITY(pxTCB->uxPriority);

    //任务切换    
    taskYIELD();        
}

 

 

 

6. 切换任务(切换上下文):由前面的文章可知,在没有优先级情况下,仅仅依靠判断任务的延时计数值进行切换,但是,当任务多的时候,其切换顺序或者说判断顺序乱套了(因为是人工决定的),所以,当有优先级概念时,按优先级顺序进行切换

  由下面代码可知,定义一个选择最高优先级任务的宏定义,其完成两件事:获取当前最高优先级,然后按着最高优先级去找到链表的入口链表项(任务),然后将其作为当前任务,这样就实现了任务切换

/* 选择最高优先级的任务(TCB) */
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \
    UBaseType_t uxTopPriority; \
    /* 获得最高优先级 */ \
    portGET_HIGHEST_PRIORITY(uxTopPriority, uxTopReadyPriority); \
    /* 如果是表头则下一项就是第一个链表项,如果是某个已经执行的链表项则是下一个链表项 */ \
    listGET_OWNER_OF_NEXT_ENTRY(pxCurrentTCB, &(pxReadyTasksLists[uxTopPriority])); \
}


void vTaskSwitchContext(void)
{
    /* 上下文切换(任务切换) */
    
    taskSELECT_HIGHEST_PRIORITY_TASK();    //选择优先性最高的任务切换
}

 

7. 计数器递减:由5知道,任务进入阻塞时,将任务从就绪链表移出,放入延时链表;那当任务就绪时要回到就绪链表怎么做?答案就是在计数器递减函数中实现。

  计数器递减函数是对每个优先级任务延时计数值进行减1,同时,如果有任务的延时计数值已经为0,表示延时结束,则将其移出延时链表,加入就绪链表(但这里没有实现延时链表,所以只有实现优先级位图相应优先级置1)

void xTaskIncrementTick(void)
{
    /* 进入计数器递减,并在每一次递减后判断是否有超时任务,进行切换 */
    TCB_t *pxTCB = NULL;
    BaseType_t i = 0;
    
    extern TickType_t xTickCount;
        
    const TickType_t xConstTickCount = xTickCount + 1;
    xTickCount = xConstTickCount;
    
    
    /*    扫描就绪列表中所有优先级链表的第一个任务的xTicksToDelay,如不为0,则减1    */
    for(i=0;i<configMAX_PRIORITIES;i++)
    {
        pxTCB = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY((&pxReadyTasksLists[i]));
        if(pxTCB->xTicksToDelay > 0)
        {
            pxTCB->xTicksToDelay--;
            /* 如果该任务计数值为0,则放入就绪链表 */
            if(pxTCB->xTicksToDelay == 0)
            {
                taskRECORD_READY_PRIORITY(pxTCB->uxPriority);   //暂时先实现优先级位图置1
            }
        }
    }    
    portYIELD();
}

 

  

posted on 2022-11-22 12:51  Toriyung  阅读(120)  评论(0编辑  收藏  举报