目录

1. 任务延时列表工作原理

1.1 任务延时列表组件

1.2 任务记录要被唤醒的绝对时间

1.3 避免遍历任务延时列表

2. 任务延时列表组件定义

2.1 定义任务延时列表

2.2  定义下一任务解锁时间

3. 修改代码支持任务延时列表

3.1 修改vTaskDelay函数

3.2 修改xTaskIncrementTick函数

3.3 修改taskRESET_READY_PRIORITY函数

4. 实验现象


1. 任务延时列表工作原理

1.1 任务延时列表组件

在系统中设置任务延时列表 + 下一任务解锁时间变量,当有任务需要延时时,先将任务从就绪列表删除,然后插入任务延时列表,同时更新下一任务解锁时间

1.2 任务记录要被唤醒的绝对时间

在调用vTaskDelay函数时指定的是任务要延时的tick值,将系统当前tick值xTickCount与xTicksToDelay相加,就是延时任务要被唤醒的绝对时间

在将任务插入任务延时列表时,会将该绝对时间记录在TCB中xStateListItem的xItemValue字段,并且以升序插入延时列表

1.3 避免遍历任务延时列表

下一任务解锁时间就是任务延时队列中最早要唤醒任务的绝对时间,在每次向延时列表中加入新任务时,会维护该变量

在引入下一任务解锁时间后,每当更新系统时基,无需遍历延时列表,只要比较系统当前tick值与下一任务解锁时间即可,当xTickCount大于等于下一任务解锁时间时,说明有任务需要唤醒

虽然该方案需要在将任务插入延时列表时遍历列表,但是避免了在SysTick中断中遍历列表,代价还是值得的

2. 任务延时列表组件定义

2.1 定义任务延时列表

// file: tasks.c
/* 任务延时列表 */
static List_t xDelayedTaskList1;
static List_t xDelayedTaskList2;
static List_t *volatile pxDelayedTaskList;
static List_t *volatile pxOverflowDelayedTaskList;

说明1:为何要定义2个任务延时列表

记录系统tick值的变量xTickCount的类型为TickType_t(uint32_t),该变量每个tick加1,最终会溢出。由于该变量为无符号整型,溢出后会发生绕回,即重新回到0

为解决溢出问题,设置了两个任务延时列表,当系统时基计数器xTickCount没有溢出时用一个列表;当xTickCount溢出时,使用另一个列表(使用方式详见下文)

说明2:对任务延时列表的初始化在prvInialiseTaskLists函数中进行

void prvInitialiseTaskLists(void)
{// 其他操作vListInitializeList(&xDelayedTaskList1);vListInitializeList(&xDelayedTaskList2);pxDelayedTaskList = &xDelayedTaskList1;pxOverflowDelayedTaskList = &xDelayedTaskList2;
}

2.2  定义下一任务解锁时间

// file: tasks.c
/* 下一任务解锁时间 */
static volatile TickType_t xNestTaskUnblockTime = (TickType_t)0U;/* 溢出次数计数器 */
static volatile BaseType_t xNumOfOverflows = (BaseType_t)0U;

说明1:对xNextTaskUnblockTime的初始化在vTaskStartScheduler函数中进行,默认设置为portMAX_DELAY,该默认值可以理解为没有任务在延时

说明2:由于使用TCB中的xStateListItem的xItemValue记录任务唤醒绝对时间,原先TCB中用于记录任务延时时间的xTicksToDelay字段可以删除

3. 修改代码支持任务延时列表

3.1 修改vTaskDelay函数

// file: tasks.c
/* 阻塞延时函数 */
void vTaskDelay(const TickType_t xTicksToDelay)
{TCB_t *pxTCB;/* 获取当前任务TCB */pxTCB = pxCurrentTCB;/* 将任务插入延时列表 */prvAddCurrentTaskToDelayedList(xTicksToDelay);/* 主动放弃CPU */taskYIELD();
}static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait)
{TickType_t xTimeToWake;const TickType_t xConstTickCount = xTickCount;/* 将任务从就绪列表移除 */if (uxListRemove(&(pxCurrentTCB->xStateListItem)) ==(UBaseType_t)0){/* * 当该优先级就绪列表为空时,注销该优先级* 该特点用于支持同优先级多任务*/portRESET_READY_PRIORITY(pxCurrentTCB->uxPriority,uxTopReadyPriority);}/* 计算任务到期的绝对时间并设置列表项 */xTimeToWake = xConstTickCount + xTicksToWait;listSET_LIST_ITEM_VALUE(&(pxCurrentTCB->xStateListItem),xTimeToWake);/* 处理延时溢出 */if (xTimeToWake < xConstTickCount){/* 如果延时溢出,将任务按升序加入溢出延时列表 */vListInsert(pxOverflowDelayedTaskList,&(pxCurrentTCB->xStateListItem));}else{/* 如果延时未溢出,将任务按升序加入延时列表 */vListInsert(pxDelayedTaskList,&(pxCurrentTCB->xStateListItem));/* 更新下一任务解锁时间 */if (xTimeToWake < xNestTaskUnblockTime){xNestTaskUnblockTime = xTimeToWake;}}
}

说明1:延时溢出的判断

因为xTimeToWake是在当前系统tick值xTickCount上累加得到,所以当xTimeToWake反而小于xTickCount时,说明系统计时发生溢出

说明2:当发生溢出时,只是将任务加入溢出任务延时队列,并不更新下一任务解锁时间,延时溢出的任务要到系统计时溢出时才处理(处理方式详见下文)

说明3:关于延时列表的互斥操作

当任务需要进入阻塞延时状态时,会从就绪列表移出并加入延时列表,在上面的实现中并没有进行互斥处理。在FreeRTOS源码中,此处会将调度器挂起

3.2 修改xTaskIncrementTick函数

// file: tasks.c
/* 更新时基函数 */
void xTaskIncrementTick(void)
{TCB_t *pxTCB = NULL;TickType_t xItemValue;/* 更新时基 */const TickType_t xConstTickCount = xTickCount + 1;xTickCount = xConstTickCount;/* 处理系统计时溢出,交换延时列表指针 */if (xConstTickCount == (TickType_t)0U){taskSWITCH_DELAYED_LISTS();}/* 处理到期的任务 */if (xConstTickCount >= xNestTaskUnblockTime){for (;;){if (listLIST_IS_EMPTY(pxDelayedTaskList) != pdFALSE){/* * 延时列表为空,将xNesTaskUnblockTime置为最大计时值* 说明此时没有任务处于延时等待状态(溢出延时列表除外)*/xNestTaskUnblockTime = portMAX_DELAY;break;}else{/* 延时列表不为空,唤醒所有到期任务 */pxTCB = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY(pxDelayedTaskList);xItemValue = listGET_LIST_ITEM_VALUE(&(pxTCB->xStateListItem));/** 遍历到无需唤醒的任务* 根据该任务的唤醒绝对时间更新下一任务解锁时间*/if (xConstTickCount < xItemValue){xNestTaskUnblockTime = xItemValue;break;}/* 将任务从延时列表移除 */uxListRemove(&(pxTCB->xStateListItem));/* 将任务加入就绪列表 */prvAddTaskToReadyList(pxTCB);}}}/* 触发任务切换 */portYIELD();
}/* 计时溢出时,交换任务延时列表指针 */
#define taskSWITCH_DELAYED_LISTS() \
{ \List_t *pxTemp; \pxTemp = pxDelayedTaskList; \pxDelayedTaskList = pxOverflowDelayedTaskList; \pxOverflowDelayedTaskList = pxTemp; \xNumOfOverflows++; \prvResetNextTaskUnblockTime(); \
}/* 交换任务延时列表后,根据交换后的列表更新下一任务解锁时间 */
static void prvResetNextTaskUnblockTime(void)
{TCB_t *pxTCB;if (listLIST_IS_EMPTY(pxDelayedTaskList) != pdFALSE){/* 当前延时列表为空,将xNextTaskUnblockTime设置为最大计时值 */xNestTaskUnblockTime = portMAX_DELAY;}else{/* 当前延时列表不为空,使用第一个延时任务设置NextTaskUnblockTime */pxTCB = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY(pxDelayedTaskList);xNestTaskUnblockTime = listGET_LIST_ITEM_VALUE(&(pxTCB->xStateListItem));}
}

3.3 修改taskRESET_READY_PRIORITY函数

/* 在系统中注销任务优先级 */
#define taskRESET_READY_PRIORITY(uxPriority) \
{ \if (listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[(uxPriority)])) == \(UBaseType_t)0) \{ \portRESET_READY_PRIORITY(uxPriority, uxTopReadyPriority); \} \
}

说明:此处根据优先级列表中是否有任务来决定是否注销相应的优先级,就可以实现对同优先级多任务的支持

4. 实验现象

此处的实验现象和之前是相同的,但是实现延时的方法已经改变

FreeRTOS内核实现06:任务延时列表相关推荐

  1. FreeRTOS内核实现01:列表与列表项实现

    目录 1. FreeRTOS编程风格 1.1 数据类型 1.2 变量名 1.3 函数名 1.4 宏 2. FreeRTOS列表实现分析 2.1 列表项数据类型 2.2 迷你列表项数据类型 2.3 列表 ...

  2. freertos空闲任务、阻塞延时

    freertos空闲任务.阻塞延时 空闲任务 阻塞延时 SysTick 实验现象 阻塞态:如果一个任务当前正在等待某个外部事件,则称它处于阻塞态. rtos中的延时叫阻塞延时,即任务需要延时的时候,会 ...

  3. freertos内核--任务调度剖析

    前言 在使用freertos的时候,我们都知道在创建了一系列任务之后,启用调度器,系统就可以帮我们管理任务,分配资源.本文主要对调度器的原理进行剖析,从vTaskStartScheduler()函数开 ...

  4. freertos内核 任务定义与切换 原理分析

    freertos内核 任务定义与切换 原理分析 主程序 任务控制块 任务创建函数 任务栈初始化 就绪列表 调度器 总结任务切换 主程序 这个程序目的就是,使用freertos让两个任务不断切换.看两个 ...

  5. FreeRTOS内核实现05:支持多优先级

    目录 1. 支持多优先级的方法 1.1 任务优先级 1.2 基于优先级的就绪列表 1.3 实现基于优先级的调度 2. 查找最高优先级就绪任务的方法 2.1 通用方法 2.2 体系结构优化方法 3. 修 ...

  6. FreeRTOS内核实现07(完):支持时间片

    目录 1. 时间片概念与测试 1.1 时间片概念 1.2 时间片实现原理 1.2.1 taskSELECT_HIGHEST_PRIORITY_TASK宏 1.2.2 taskRESET_READY_P ...

  7. 一种Cortex-M内核中的精确延时方法

    本文介绍一种Cortex-M内核中的精确延时方法 前言 为什么要学习这种延时的方法? 很多时候我们跑操作系统,就一般会占用一个硬件定时器--SysTick,而我们一般操作系统的时钟节拍一般是设置100 ...

  8. FreeRTOS内核笔记(一):基本知识和命名规则

    FreeRTOS内核笔记(一):基本知识和命名规则 FreeRTOS内核笔记 命名规则: 常用宏定义 Thread运行状态: RTOS Tick Context切换: 实时调度器Scheduler F ...

  9. FreeRTOS内核实现04:空闲任务与阻塞延时

    目录 1. 引入原因 2. 实现空闲任务 2.1 定义空闲任务组件 2.2 创建空闲任务 3. 实现阻塞延时 3.1 vTaskDelay函数实现 3.2 修改vTaskSwitchContext函数 ...

最新文章

  1. Perl输出复杂数据结构:Data::Dumper,Data::Dump,Data::Printer
  2. wxWidgets:wxScrollEvent类用法
  3. 2006年软件500强
  4. wordpress.org手动安装主题
  5. 微信可以远程控制电脑吗_用微信就能远程控制电脑,这款神器有些厉害
  6. visual studio 2013 触发挂起事件
  7. unity平行光太亮?物体发白?可能你使用了2个或多个平行光
  8. java 正则 pattern 线程安全_(一)Java Pattern类----java正则
  9. Redis系列四 Redis常见配置
  10. php 不通过表单post,php – 简单表单不通过_POST发送数据
  11. 群晖ds3617xs_23739虚拟机安装与半洗白教程
  12. 电子测量——用C语言设计测量数据误差处理的通用程序
  13. matlab与螺旋桨,基于Matlab的船用螺旋桨计算机实时仿真及GUI设计
  14. 在word中做复选框打对勾钩
  15. Rust中的所有权和借用的关系图
  16. 轨道交通通信施工学习总结(四)漏泄同轴电缆安装
  17. 粒子群算法python实现: 带活化因子
  18. 如何复制那些无法复制的网站
  19. 机器学习实战第8章预测数值型数据:回归2
  20. Chrome网页翻译失效的解决方案

热门文章

  1. java期末考试编程题 界面_救急啊!!!期末考试!怎样用JAVA的GUI(图形用户界面)来设计一个小程序!...
  2. php oracle 8.1.7,Oracle 8.1.7在redhat 7.3的安装方法
  3. oracle ogg 12c mysql_Oracle GoldenGate 下载
  4. android 开机打开串口失败,请大神帮我看看这是怎么回事,一用就说串口打开失败,再电脑上试...
  5. html设置设置字母间的距离,css如何设置字母间距?字母间距的设置方法
  6. rn webview加载本地静态html,React Native - Webview 加载本地文件
  7. 西工大学计算机专业考研容易吗,2022年西北工业大学计算机专业考研成功经验...
  8. linux订阅软件包,安装 Manjaro Linux 后必做的 6 件事 | Linux 中国
  9. python网页提交表单_使用Python中的POST请求通过网站表单上传基本文件
  10. textarea统计剩余字数(动态提示)