跟着野火学FreeRTOS:第一段(任务延时列表)

     既然是延时列表,那肯定要先定义相应的链表,延时列表的定义如下。这里定义了两条延时列表(其实就是前面小节里面提到的链表的根节点),一条是准备当记录 S y s t i c k Systick Systick周期个数的变量 x T i c k C o u n t xTickCount xTickCount溢出的时候使用的。这里还定义了指向两条链表的指针。

/*< Delayed tasks. */
List_t xDelayedTaskList1;   
/*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */                      
List_t xDelayedTaskList2;  
/*< Points to the delayed task list currently being used. */                        
List_t * volatile pxDelayedTaskList; 
/*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */             
List_t * volatile pxOverflowDelayedTaskList;      

     延时列表实现延时的基本思想是,当任务需要延时的时候就将任务插入到延时列表,不让任务继续执行且将任务从就绪列表里面删除,同时记录延时结束的时刻,延时结束的时刻为当前的记录 S y s t i c k Systick Systick周期个数的变量 x T i c k C o u n t xTickCount xTickCount值加上当前任务需要延时的 S y s t i c k Systick Systick周期个数的值。当 x N e x t T a s k U n b l o c k T i m e xNextTaskUnblockTime xNextTaskUnblockTime的值等于对应任务记录的延时结束时刻的时候,对应任务延时结束重新加入就绪列表并退出延时列表。变量 x N e x t T a s k U n b l o c k T i m e xNextTaskUnblockTime xNextTaskUnblockTime是一个在源文件 t a s k s . c tasks.c tasks.c中定义的全局变量,它表示下一个即将结束延时的任务的时刻的值,它在接口 v T a s k S t a r t S c h e d u l e r vTaskStartScheduler vTaskStartScheduler里面初始化为值 0 x F F F F F F F F 0xFFFFFFFF 0xFFFFFFFF(这里需要注意的是假设现在有两个任务几乎在同一时刻0,还是有先后顺序,进行了延时操作,任务1延时5个 S y s t i c k Systick Systick周期,任务2延时10个 S y s t i c k Systick Systick周期,任务1先调用延时操作,任务2后调用延时操作,那么此时任务1的结束时刻为5,任务2的结束时刻为10,但是这里 x N e x t T a s k U n b l o c k T i m e xNextTaskUnblockTime xNextTaskUnblockTime的值并不等于10,也就是说 x N e x t T a s k U n b l o c k T i m e xNextTaskUnblockTime xNextTaskUnblockTime的值永远指向最先结束延时的时刻)。

     延时列表定义之后肯定要初始化,前面的小节中的实现只有就绪列表,因此接口 p r v I n i t i a l i s e T a s k L i s t s prvInitialiseTaskLists prvInitialiseTaskLists中只对就绪列表进行了初始化,但是现在延时列表的初始化也在这个接口里面。有了延时列表之后,前面在任务控制块里面添加的元素 x T i c k s T o D e l a y xTicksToDelay xTicksToDelay可以删除,同时延时接口 v T a s k D e l a y vTaskDelay vTaskDelay里面的操作 p x T C B − > x T i c k s T o D e l a y = x T i c k s T o D e l a y pxTCB->xTicksToDelay = xTicksToDelay pxTCB>xTicksToDelay=xTicksToDelay改为调用接口 p r v A d d C u r r e n t T a s k T o D e l a y e d L i s t prvAddCurrentTaskToDelayedList prvAddCurrentTaskToDelayedList将相应的任务插入到延时列表。接口 p r v A d d C u r r e n t T a s k T o D e l a y e d L i s t prvAddCurrentTaskToDelayedList prvAddCurrentTaskToDelayedList(在 F r e e R T O S FreeRTOS FreeRTOS源码里面的 t a s k s . c tasks.c tasks.c文件里面定义,相对于源码中的定义这里做了大量的精简)的定义如下。该接口首先记录此时的 S y s t i c k Systick Systick周期个数值,然后将任务从就绪列表里面删除,如果删除该任务之后该任务之前所在的链表里面已经没有了任务就将该任务的优先级数在变量 u x T o p R e a d y P r i o r i t y uxTopReadyPriority uxTopReadyPriority对应的比特位清0。根据前面保存的 S y s t i c k Systick Systick周期个数值加上任务将要延时的 S y s t i c k Systick Systick周期个数就是结束延时的时刻,这个时刻的值会保存到任务控制块里面的元素 x S t a t e L i s t I t e m xStateListItem xStateListItem的元素 x I t e m V a l u e xItemValue xItemValue里面,延时列表是根据元素 x I t e m V a l u e xItemValue xItemValue的值做从小到大排序的。然后看 x T i c k C o u n t xTickCount xTickCount是否溢出,如果溢出就将当前任务插入到用于溢出的延时列表,如果没有溢出就当前任务插入到另一个延时列表,如果当前任务结束延时的时刻小于变量 x N e x t T a s k U n b l o c k T i m e xNextTaskUnblockTime xNextTaskUnblockTime的值,那么更新 x N e x t T a s k U n b l o c k T i m e xNextTaskUnblockTime xNextTaskUnblockTime的值为当前任务结束延时的时刻,因为 x N e x t T a s k U n b l o c k T i m e xNextTaskUnblockTime xNextTaskUnblockTime的值总是所有任务中延时结束时刻最早的任务的结束延时的时刻值。

/** The currently executing task is entering the Blocked state.  Add the task to* either the current or the overflow delayed task list.*/static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait)
{TickType_t xTimeToWake;const TickType_t xConstTickCount = xTickCount;/* Remove the task from the ready list before adding it to the blocked list* as the same list item is used for both lists. */if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){/* The current task must be in a ready list, so there is no need to* check, and the port reset macro can be called directly. */portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); /*lint !e931 pxCurrentTCB cannot change as it is the calling task.  pxCurrentTCB->uxPriority and uxTopReadyPriority cannot change as called with scheduler suspended or in a critical section. */}/* Calculate the time at which the task should be woken if the event* does not occur.  This may overflow but this doesn't matter, the kernel* will manage it correctly. */xTimeToWake = xConstTickCount + xTicksToWait;/* The list item will be inserted in wake time order. */listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );if( xTimeToWake < xConstTickCount ){/* Wake time has overflowed.  Place this item in the overflow list. */vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );}else{/* The wake time has not overflowed, so the current block list is used. */vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );/* If the task entering the blocked state was placed at the head of the* list of blocked tasks then xNextTaskUnblockTime needs to be updated* too. */if( xTimeToWake < xNextTaskUnblockTime ){xNextTaskUnblockTime = xTimeToWake;}}
}

     相应的接口 x T a s k I n c r e m e n t T i c k xTaskIncrementTick xTaskIncrementTick也需要大的改动,但是常规操作还是每次都会将变量 x T i c k C o u n t xTickCount xTickCount的值加1。如果 x T i c k C o u n t xTickCount xTickCount发生溢出的话需要调用宏接口 t a s k S W I T C H _ D E L A Y E D _ L I S T S taskSWITCH\_DELAYED\_LISTS taskSWITCH_DELAYED_LISTS切换当前使用的延时列表为另一个延时列表。

BaseType_t xTaskIncrementTick( void )
{TCB_t * pxTCB;TickType_t xItemValue;/* Minor optimisation.  The tick count cannot change in this* block. */const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;/* Increment the RTOS tick, switching the delayed and overflowed* delayed lists if it wraps to 0. */xTickCount = xConstTickCount;if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */{taskSWITCH_DELAYED_LISTS();}/* See if this tick has made a timeout expire.  Tasks are stored in* the  queue in the order of their wake time - meaning once one task* has been found whose block time has not expired there is no need to* look any further down the list. */if( xConstTickCount >= xNextTaskUnblockTime ){for( ; ; ){if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ){/* The delayed list is empty.  Set xNextTaskUnblockTime* to the maximum possible value so it is extremely* unlikely that the* if( xTickCount >= xNextTaskUnblockTime ) test will pass* next time through. */xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */break;}else{/* The delayed list is not empty, get the value of the* item at the head of the delayed list.  This is the time* at which the task at the head of the delayed list must* be removed from the Blocked state. */pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );if( xConstTickCount < xItemValue ){/* It is not time to unblock this item yet, but the* item value is the time at which the task at the head* of the blocked list must be removed from the Blocked* state -  so record the item value in* xNextTaskUnblockTime. */xNextTaskUnblockTime = xItemValue;break; /*lint !e9011 Code structure here is deemed easier to understand with multiple breaks. */}/* It is time to remove the item from the Blocked state. */uxListRemove( &( pxTCB->xStateListItem ) );/* Place the unblocked task into the appropriate ready* list. */prvAddTaskToReadyList( pxTCB );}}}return pdTRUE;
}

     宏接口 t a s k S W I T C H _ D E L A Y E D _ L I S T S taskSWITCH\_DELAYED\_LISTS taskSWITCH_DELAYED_LISTS(在 F r e e R T O S FreeRTOS FreeRTOS源码里面的 t a s k s . c tasks.c tasks.c文件里面定义)的定义如下。它在切换当前使用的延时列表之后还会调用接口 p r v R e s e t N e x t T a s k U n b l o c k T i m e prvResetNextTaskUnblockTime prvResetNextTaskUnblockTime(在 F r e e R T O S FreeRTOS FreeRTOS源码里面的 t a s k s . c tasks.c tasks.c文件里面定义)重新设置变量 x N e x t T a s k U n b l o c k T i m e xNextTaskUnblockTime xNextTaskUnblockTime的值,如果切换之后的延迟列表为空则将 x N e x t T a s k U n b l o c k T i m e xNextTaskUnblockTime xNextTaskUnblockTime初始化为值 0 x F F F F F F F F 0xFFFFFFFF 0xFFFFFFFF,否则将 x N e x t T a s k U n b l o c k T i m e xNextTaskUnblockTime xNextTaskUnblockTime初始化为最近的一个即将延迟结束的任务的延迟结束时刻。

/* pxDelayedTaskList and pxOverflowDelayedTaskList are switched when the tick* count overflows. */
#define taskSWITCH_DELAYED_LISTS()                                                \{                                                                             \List_t * pxTemp;                                                          \\/* The delayed tasks list should be empty when the lists are switched. */ \configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) );               \\pxTemp = pxDelayedTaskList;                                               \pxDelayedTaskList = pxOverflowDelayedTaskList;                            \pxOverflowDelayedTaskList = pxTemp;                                       \xNumOfOverflows++;                                                        \prvResetNextTaskUnblockTime();                                            \}
 static void prvResetNextTaskUnblockTime( void )
{if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ){/* The new current delayed list is empty.  Set xNextTaskUnblockTime to* the maximum possible value so it is  extremely unlikely that the* if( xTickCount >= xNextTaskUnblockTime ) test will pass until* there is an item in the delayed list. */xNextTaskUnblockTime = portMAX_DELAY;}else{/* The new current delayed list is not empty, get the value of* the item at the head of the delayed list.  This is the time at* which the task at the head of the delayed list should be removed* from the Blocked state. */xNextTaskUnblockTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxDelayedTaskList );}
}

     接口 x T a s k I n c r e m e n t T i c k xTaskIncrementTick xTaskIncrementTick接着判断是否有任务的延时时间到期了如果有则判断当前的延时列表是否为空,如果为空则只是简单的更新 x N e x t T a s k U n b l o c k T i m e xNextTaskUnblockTime xNextTaskUnblockTime的值为 0 x F F F F F F F F 0xFFFFFFFF 0xFFFFFFFF并跳出循环,如果不为空则判断延时列表中最早延时结束的任务的延时时刻是否大于等于当前 S y s t i c k Systick Systick周期个数的变量 x T i c k C o u n t xTickCount xTickCount的值,如果是则将 x N e x t T a s k U n b l o c k T i m e xNextTaskUnblockTime xNextTaskUnblockTime的值更新为这个延时列表中最早延时结束的任务的延时时刻并跳出循环,否则将当前延时列表中最早延时结束的任务从延时列表删除并加入到就绪列表并继续循环。 如果觉得接口 x T a s k I n c r e m e n t T i c k xTaskIncrementTick xTaskIncrementTick这一部分代码不好理解的话主要是因为 S y s t i c k Systick Systick周期个数的变量 x T i c k C o u n t xTickCount xTickCount的值会溢出,如果仔细想一想 x T i c k C o u n t xTickCount xTickCount的溢出的情况应该可以较好的理解接口 x T a s k I n c r e m e n t T i c k xTaskIncrementTick xTaskIncrementTick这一部分代码。这里的实验现象应该和前一小节一样,工程代码在这里。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/582383.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

idea Spring Boot项目使用JPA创建与数据库链接

1.pom.xml文件中添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>…

创建和配置Spring MVC框架构建Web应用

1 认识Spring MVC Spring Web MVC是构建在Servlet API之上的Web框架&#xff0c;自诞生之时就被纳入了Spring框架中。其正式/官方名称为“Spring Web MVC”&#xff0c;源自其所属的模块&#xff08;spring-webmvc&#xff09;&#xff0c;但通常被称为“Spring MVC”。 1.1…

最短路径(数据结构实训)(难度系数100)

最短路径 描述&#xff1a; 已知一个城市的交通路线&#xff0c;经常要求从某一点出发到各地方的最短路径。例如有如下交通图&#xff1a; 则从A出发到各点的最短路径分别为&#xff1a; B&#xff1a;0 C&#xff1a;10 D&#xff1a;50 E&#xff1a;30 F&#xff1a;60 输…

“暂存”校验逻辑探讨

1、背景 在业务中可能会遇到这种场景&#xff0c;前端页面元素多且复杂&#xff0c;一次性填完提交耗时很长&#xff0c;中间中断面临着丢失数据的风险。针对这个问题&#xff0c;“暂存”应运而生。 那“暂存”的时候&#xff0c;是否需要对数据校验&#xff0c;如何进行校验…

【全局光照GI系统剖析_Enlighten和Progressive Lightmapper_案例分享(附带场景下载链接)_场景】

烘焙预计算 前文:续_直接和间接光照这一篇小结:Unity烘焙预计算烘焙前的场景设置1.2.Contribute GI如下图:物体的Static和面板上的Contribute GILightmap的UV模型自带Lightmap的UVUnity 自动展Lightmap的UV1.3.Meta PassMeta Pass代码如下:1.4.Light Mode模式

k8s部署 CNI 网络组件与k8s集群搭建(二)

目录 部署 CNI 网络组件 部署 flannel K8S 中 Pod 网络通信 Flannel UDP 模式的工作原理 ETCD 之 Flannel 提供说明 Flannel VXLAN 模式跨主机的工作原理 在 node01 节点上操作 在 master01 节点上操作 部署 Calico k8s 组网方案对比 Calico 主要由三个部分组成 Ca…

go 源码解读 - sync.Mutex

sync.Mutex mutex简介mutex 方法源码标志位获取锁LocklockSlowUnlock怎么 调度 goroutineruntime 方法 mutex简介 mutex 是 一种实现互斥的同步原语。&#xff08;go-version 1.21&#xff09; &#xff08;还涉及到Go运行时的内部机制&#xff09;mutex 方法 Lock() 方法用于…

网盘项目话术(0.5w字精选)

功能结构图 数据库设计总结 该项目主要就是对文件的操作&#xff0c;file表&#xff0c;file_share表。 file表主要字段&#xff1a;id&#xff0c;用户id&#xff0c;父级目录id&#xff0c;文件的地址&#xff0c;文件的封面图片地址&#xff0c;创建和修改时间。 file_sha…

Java命令行最实用的命令

Java 命令行提供了许多重要且实用的命令&#xff0c;以下是其中一些常用的命令&#xff1a; java&#xff1a;用于运行 Java 程序的命令。使用 java 命令后面跟着要执行的 Java 类名。 java MyClassjavac&#xff1a;用于编译 Java 源代码文件的命令。使用 javac 命令后面跟着要…

vue中的事件绑定的过程

Vue中事件的绑定分为两种情况&#xff1a;原生DOM事件和组件事件。 原生DOM事件 是指在模板中使用v-on或指令来给元素绑定的事件&#xff0c;如 <button click"handler">click me</button>这种事件的绑定是通过以下步骤实现的&#xff1a; 模板编译阶…

国际物流公司科普_集装箱种类区分和介绍_箱讯科技

集装箱运输的不断发展&#xff0c;为适应装载不同种类货物的需要&#xff0c;因而出现了不同种类的集装箱。今天和大家一起来总结一下。 按使用材料分类 根据箱子主体部件&#xff08;侧壁、端壁、箱顶等&#xff09;采用什么材料&#xff0c;就叫做什么材料制造的集装箱&…

TPRI-DMP平台介绍

TPRI-DMP平台介绍 TPRI-DMP平台概述 TPRI-DMP为华能集团西安热工院自主产权的工业云PaaS平台&#xff0c;已经过13年的发展和迭代&#xff0c;其具备大规模能源电力行业生产应用软件开发和运行能力。提供TPRI-DMP平台主数据管理、业务系统开发与运行、应用资源管理与运维监控…

【数据结构】C语言实现单链表的基本操作

单链表基本操作的实现 导言一、查找操作1.1 按位查找1.1.1 按位查找的C语言实现1.1.2 按位查找的时间复杂度 1.2 按值查找1.2.1 按值查找的C语言实现1.2.2 按值查找的时间复杂度 二、插入操作2.1 后插操作2.2 前插操作 三、删除操作结语 导言 大家好&#xff0c;很高兴又和大家…

C++ 之LeetCode刷题记录(三)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 开始cpp刷题之旅&#xff0c;多学多练&#xff0c;尽力而为。 先易后难&#xff0c;先刷简单的。 13、罗马数字转整数 罗马数字包含以下七种字符: I&#xff0c…

vue3中新增的组合式API:ref、reactive、toRefs、computed、watch、provide/inject、$ref

在 Vue3 中&#xff0c;组合式 API 是一种新的编程模式&#xff0c;它允许你更灵活地组织和重用代码。组合式 API 主要包括以下几个部分&#xff1a; ref&#xff1a;用于创建响应式数据。reactive&#xff1a;用于创建一个响应式对象。toRefs&#xff1a;将一个响应式对象转换…

ClickHouse基础知识(二):ClickHouse 安装教程

1. 准备工作 1.1 确定防火墙处于关闭状态 1.2 CentOS 取消打开文件数限制 &#xff08;1&#xff09;在 hadoop101 的 /etc/security/limits.conf 文件的末尾加入以下内容 sudo vim /etc/security/limits.conf&#xff08;2&#xff09;在 hadoop101 的/etc/security/limits.…

面试问题整理若干

一.数据库测试时关注的要点 1.数据库连接测试&#xff1a;确保数据库可以正常连接&#xff0c;并且连接稳定。 2.数据库结构测试&#xff1a;验证数据库的结构设计是否符合需求&#xff0c;包括表、字段、索引等的设计。 3.数据库数据测试&#xff1a;验证数据库中的数据是否…

Rocky9 1.28安装kubernetes

1.环境准备 二进制安装比较复杂&#xff0c;但是也比较稳定&#xff0c;适用于线上环境使用。   本笔记参考自&#xff1a;https://github.com/cby-chen/Kubernetes &#xff0c;针对文中内容&#xff0c;有部分镜像无法拉取等&#xff0c;还有一部分有点小问题&#xff0c;…

SQL小技巧5:数据去重的N种方法,总有一种你想不到!

在平时工作中&#xff0c;使用SQL语句进行数据去重的场景非常多。 今天主要分享几种数据去重的SQL写法。 假如有一张student表&#xff0c;结构如下&#xff1a; create table student( id int, name varchar(50), age int, address varchar(100)); 表中的数据…

【解决方案】智能语音模块,东胜物联远场语音解决方案让控制更简单,应用于智能家居等场景

现在的天气真是冷得不想多动一下&#xff0c;又想打开取暖器&#xff1f;有了它&#xff0c;用声音就能遥控&#xff0c;今天我们就来聊聊智能语音模块。 技术概述 远场语音技术&#xff0c;采用了麦克风阵列、信号处理技术以及先进的语音识别引擎&#xff0c;使得设备能够在距…