刘火良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();
}
相关推荐
牛奶咖啡1318 分钟前
学习设计模式《十七》——状态模式
学习·设计模式·状态模式·认知状态模式·状态模式的优缺点·何时使用状态模式·状态模式的使用示例
郑板桥3027 分钟前
ts学习1
学习·typescript
居然是阿宋2 小时前
【学习笔记】OkHttp源码架构解析:从设计模式到核心实现
笔记·学习·okhttp
想成为大佬的每一天3 小时前
Linux驱动学习day20(pinctrl子系统驱动大全)
学习
不太可爱的叶某人11 小时前
【学习笔记】MySQL技术内幕InnoDB存储引擎——第5章 索引与算法
笔记·学习·mysql
岁岁岁平安11 小时前
Redis基础学习(五大值数据类型的常用操作命令)
数据库·redis·学习·redis list·redis hash·redis set·redis string
知识分享小能手13 小时前
Vue3 学习教程,从入门到精通,使用 VSCode 开发 Vue3 的详细指南(3)
前端·javascript·vue.js·学习·前端框架·vue·vue3
pay4fun14 小时前
2048-控制台版本
c++·学习
知识分享小能手15 小时前
Bootstrap 5学习教程,从入门到精通,Bootstrap 5 表单验证语法知识点及案例代码(34)
前端·javascript·学习·typescript·bootstrap·html·css3
weixin_4188138716 小时前
Python-可视化学习笔记
笔记·python·学习