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

在《刘火良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();
}
相关推荐
安小牛2 小时前
Kotlin 学习-方法和参数类型
android·学习·kotlin·android studio
螺旋小蜗7 小时前
Maven工具学习使用(十一)——部署项目到仓库
学习·maven·部署项目
大雄野比7 小时前
【scikit-learn基础】--『监督学习』之 岭回归
学习·回归·scikit-learn
FAREWELL000757 小时前
C#进阶学习(一)简单数据结构类之ArrayList、Stack、Queue、Hashtable
数据结构·学习·c#·queue·arraylist·stack·hash table
sealaugh329 小时前
AI(学习笔记第一课) 在vscode中配置continue
人工智能·笔记·学习
每次的天空10 小时前
Android学习总结之OKHttp拦截器和缓存
android·学习·okhttp
不知道叫什么呀10 小时前
【5G-A学习】ISAC通信感知一体化学习小记
学习·5g·aigc·我的ai老师
爱编程的鱼10 小时前
深入学习任何技术的实用指南
学习
肖恩想要年薪百万11 小时前
自用:在使用SpringBoot做学生信息管理系统时遇到的问题
java·spring boot·后端·学习·spring·intellij-idea
虾球xz12 小时前
游戏引擎学习第223天
c++·学习·游戏引擎