刘火良FreeRTOS内核实现与应用学习之7——任务延时列表

发布于:2025-04-14 ⋅ 阅读:(21) ⋅ 点赞:(0)

        在《刘火良FreeRTOS内核实现与应用学习之6——多优先级》的基础上:关键是添加了全局变量:xNextTaskUnblockTime  ,与延时列表(xDelayedTaskList1、xDelayedTaskList2)来高效率的实现延时。

        以前需要在扫描就绪列表中所有任务的xTicksToDelay值,进行任务就绪与否的操作;现在修改之后只需根据xNextTaskUnblockTime 值判断延时列表中的任务是否就绪,进行相应的操作;

重要数据结构及数据

0.  全局变量  xNextTaskUnblockTime        

位置:在task.c文件中定义;静态变量;

static volatile TickType_t xNextTaskUnblockTime        = ( TickType_t ) 0U;

1.  全局变量 任务延时列表

位置:在task.c文件中定义;静态变量;

static List_t xDelayedTaskList1;   //xTickCount没有溢出时,使用一个列表
static List_t xDelayedTaskList2;   //xTickCount       溢出时,使用另外一个列表

static List_t * volatile pxDelayedTaskList;   //xTickCount没有溢出时,使用一个列表
static List_t * volatile pxOverflowDelayedTaskList;   //xTickCount       溢出时,使用另外一个列表

a. 列表中的每一个节点代表了正在延时的任务;

b. 且节点按照延时时间大小做升序排列;

c. 当时基中断(SysTick中断)来临时,就用系统时基计数器的值xTickCount 与xNextTaskUnblockTime   进行比较:相等则任务就绪;否则,更新系统时基计数器xTickCount,任务切换;

有关任务操作重要函数

1.  任务延时列表初始化

/* 初始化任务相关的列表 */
void prvInitialiseTaskLists( void )
{
    UBaseType_t uxPriority;
    
    /* 初始化就绪列表 */
    for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
    {
        vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
    }
    
    vListInitialise( &xDelayedTaskList1 );
    vListInitialise( &xDelayedTaskList2 );
    
    pxDelayedTaskList = &xDelayedTaskList1;
    pxOverflowDelayedTaskList = &xDelayedTaskList2;

}

2. 修改延时函数

void vTaskDelay( const TickType_t xTicksToDelay )
{
    TCB_t *pxTCB = NULL;
    
    /* 获取当前任务的TCB */
    pxTCB = pxCurrentTCB;
    
    /* 设置延时时间 */
    //pxTCB->xTicksToDelay = xTicksToDelay;
    
    /* 将任务插入到延时列表 */
    prvAddCurrentTaskToDelayedList( xTicksToDelay );

    
    /* 任务切换 */
    taskYIELD();
}

实现将任务插入延时列表;

在 prvAddCurrentTaskToDelayedList函数中实现
a. 把当前任务从就绪列表中移除;

    /* 将任务从就绪列表中移除 */
    if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
    {
        /* 将任务在优先级位图中对应的位清除 */
        portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
    }

b. 计算xNextTaskUnblockTime  值:

    /* 计算延时到期时,系统时基计数器xTickCount的值是多少 */
    xTimeToWake= xConstTickCount + xTicksToWait;

c. 把延时任务插入延时列表(xDelayedTaskList1 或者 xDelayedTaskList2)中

i. 由于《节点按照延时时间大小做升序排列》,在插入前需要设置节点的xItemValue值:用于帮助节点做顺序排列;

 /* 将延时到期的值设置为节点的排序值 */
    listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );

ii. 根据刚才计算的 xTimeToWake是否溢出,决定把延时任务是放到延时列表(非溢出xDelayedTaskList1 或者 溢出xDelayedTaskList2)中;

    /* 溢出 */
    if( xTimeToWake < xConstTickCount )
    {
        vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
    }
    else /* 没有溢出 */
    {

        vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );

        /* 更新下一个任务解锁时刻变量xNextTaskUnblockTime的值 */
        if( xTimeToWake < xNextTaskUnblockTime )
        {
            xNextTaskUnblockTime = xTimeToWake;
        }
    }

 iii. 更新xNextTaskUnblockTime值:

        /* 更新下一个任务解锁时刻变量xNextTaskUnblockTime的值 */
        if( xTimeToWake < xNextTaskUnblockTime )
        {
            xNextTaskUnblockTime = xTimeToWake;
        }

重要函数 :prvAddCurrentTaskToDelayedList
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait )
{
    TickType_t xTimeToWake;
    
    /* 获取系统时基计数器xTickCount的值 */
    const TickType_t xConstTickCount = xTickCount;

    /* 将任务从就绪列表中移除 */
	if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
	{
		/* 将任务在优先级位图中对应的位清除 */
        portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
	}

    /* 计算延时到期时,系统时基计数器xTickCount的值是多少 */
    xTimeToWake = xConstTickCount + xTicksToWait;

    /* 将延时到期的值设置为节点的排序值 */
    listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );

    /* 溢出 */
    if( xTimeToWake < xConstTickCount )
    {
        vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
    }
    else /* 没有溢出 */
    {

        vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );

        /* 更新下一个任务解锁时刻变量xNextTaskUnblockTime的值 */
        if( xTimeToWake < xNextTaskUnblockTime )
        {
            xNextTaskUnblockTime = xTimeToWake;
        }
    }	
}

3. 修改xTaskIncrementTick函数

实现如下功能:

a. 更新xTickCount 值;为何不直接加一?

    const TickType_t xConstTickCount = xTickCount + 1;
    xTickCount = xConstTickCount;

b. 如果 xConstTickCount溢出,则切换延时列表;

    /* 如果xConstTickCount溢出,则切换延时列表 */
    if( xConstTickCount == ( TickType_t ) 0U )
    {
        taskSWITCH_DELAYED_LISTS();
    }

重要宏代码 taskSWITCH_DELAYED_LISTS

/* 
 * 当系统时基计数器溢出的时候,延时列表pxDelayedTaskList 和
 * pxOverflowDelayedTaskList要互相切换
 */
#define taskSWITCH_DELAYED_LISTS()\
{\
    List_t *pxTemp;\
    pxTemp = pxDelayedTaskList;\
    pxDelayedTaskList = pxOverflowDelayedTaskList;\
    pxOverflowDelayedTaskList = pxTemp;\
    xNumOfOverflows++;\
    prvResetNextTaskUnblockTime();\
}

当前的延时列表指针切换到溢出延时列表中;

 c. 最近的延时任务延时到期:

直接通过比较xConstTickCount与xNextTaskUnblockTime,大于则进去看谁到期了,无需遍历所有的;

/* 最近的延时任务延时到期 */
    if( xConstTickCount >= xNextTaskUnblockTime )
    {
        for( ;; )
        {
            if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
            {
                /* 延时列表为空,设置xNextTaskUnblockTime为可能的最大值 */
                xNextTaskUnblockTime = portMAX_DELAY;
                break;
            }
            else /* 延时列表不为空 */
            {
                pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
                xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );

                /* 直到将延时列表中所有延时到期的任务移除才跳出for循环 */
                if( xConstTickCount < xItemValue )
                {
                    xNextTaskUnblockTime = xItemValue;
                    break;
                }

                /* 将任务从延时列表移除,消除等待状态 */
                ( void ) uxListRemove( &( pxTCB->xStateListItem ) );

                /* 将解除等待的任务添加到就绪列表 */
                prvAddTaskToReadyList( pxTCB );
            }
        }
    }/* xConstTickCount >= xNextTaskUnblockTime */

代码框架为死循环<用于部分遍历延时到期任务>,设置条件跳出;

i.  根据列表中的节点计数器判断,延时列表是否为空,空则跳出;

ii. 编列列表中的任务节点的xItemValue(节点此值是升序的),若xConstTickCount < xItemValue: 表示此任务延时未到,跳出死循环;

若xConstTickCount > xItemValue: 表示此任务延时到,进行如下操作:

  • 从当前延时任务从延时列表中移除;
  • 将当前延时任务加到就绪列表中;

d. 任务切换;

xTaskIncrementTick函数由时基函数调用:

时基函数SysTick中断服务函数:xPortSysTickHandler

位置:在port.c文件中定义;

/*
*************************************************************************
*                             SysTick中断服务函数
*************************************************************************
*/
void xPortSysTickHandler( void )
{
	/* 关中断 */
    vPortRaiseBASEPRI();
    
    /* 更新系统时基 */
    xTaskIncrementTick();

	/* 开中断 */
    vPortClearBASEPRIFromISR();
}


网站公告

今日签到

点亮在社区的每一天
去签到