基于STM32结合CubeMX学习Free-RT-OS的源码之深入学习软件定时器实现过程

news2025/1/18 4:37:58

概述

关于在CUBEMX上的配置

 介绍

        软件定时器基于硬件定时器实现。

        软件定时器允许设置一段时间,当设置的时间到达之后就执行指定的功能函数,被定时器
调用的这个功能函数叫做定时器的回调函数。回调函数的两次执行间隔叫做定时器的定时周期,
简而言之,软件定时器的定时周期到了以后就会执行回调函数。

        Free RT OS基于systick中断产生计数值。软件定时其开始计数,当计数值到了所设置的时间时执行回调函数。

        在RT-Thread中 软件定时器的定时回调函数直接放到了systick的中断回调函数里进行处理,但Free-RT-OS并没有这样做,主要原因是在于不知道定时处理回调函数需要花费多少时间,使得systick的节拍受到影响。

那么软件定时器是如何处理回调函数的?

答:Free RT OS创建了一个管理软件定时器的任务,叫做DaemonTask 也被称为守护任务,由它处理软件定时器的定时回调函数。
 

        Free RT OS允许用户自己选择是否使用软件定时器。

当选择使用软件定时器时,Free RTOS内部的具体流程:  

Free RT OS为管理软件定时器创建的 守护任务,定时器链表,超时链表,定时器命令队列。对应下面的代码。

具体过程:

1 :  当使用了软件定时器,则在开启调度器时,创建Daemon Task(守护任务)

 osKernelStart() ->vTaskStartScheduler()(启动调度器)->    xTimerCreateTimerTask(); (创建守护任务)

创建守护任务

2:守护任务内部定时任务的函数入口   prvTimerTask

3: 同时在创建内部调用  prvCheckForValidListAndQueue

  prvCheckForValidListAndQueue  (创建定时器命令队列,创建当前定时器链表和超时定时器链表。

  

4:守护任务的函数入口 prvTimerTask

  • 查询计时器列表,看看它是否包含任何计时器,如果包含,获取下一个计时器将过期的时间。
  • 如果计时器已过期,则处理它。否则,阻塞此任务。直到计时器过期或收到命令为止。
  • 处理软件定时器的命令接受。(开启,关闭,调整周期,复位)

到这,已经能摸清除软件定时器工作流程的大概了。

以上都是RTOS内部为管理软件定时器所做的辅助工作。

再看看创建软件定时器:

软件定时器刚创建处于休眠态。

        调用开启软件定时器的API(xTimerStart)后则把封装一个message对象 ,写入定时器命令队列。(这个定时器命令队列就是在守护函数中所创建的那个)

#define xTimerStart( xTimer, xTicksToWait )      xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )

 //这个开启软件定时器的函数向定时器服务任务发送相应命令

调用宏 xTimerGenericCommand( ( xTimer ) 发message到定时器命令队列

       

     同理,停止某个软件定时器,调整软件定时器的定时周期也是通过写命令队列的方式实现。

        不能在软件定时器的回调函数中调用会阻塞当前任务的API.(如不能调用 vTaskDelay()、vTaskDelayUnti(),还有一些访问队列或者信号量的非零阻塞时间的 API 函数也不能调用。) 
 

为什么?因为软件定时器的回调函数都依赖于守护任务去处理实现其回调函数,在软件定时器的回调函数中阻塞则意味在守护任务中阻塞,守护任务将无法继续管理所有的软件定时器。

 

再剖析守护任务的回调函数。

 

   prvGetNextExpireTime  获取定时器下一次超时时间。

定时器列表(pxCurrentTimerList)按定时器的溢出时间按升序排列,最前面的任务首先超时。

static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty )
{
TickType_t xNextExpireTime;

	/* Timers are listed in expiry time order, with the head of the list
	referencing the task that will expire first.  Obtain the time at which
	the timer with the nearest expiry time will expire.  If there are no
	active timers then just set the next expire time to 0.  That will cause
	this task to unblock when the tick count overflows, at which point the
	timer lists will be switched and the next expiry time can be
	re-assessed.  */
	*pxListWasEmpty = listLIST_IS_EMPTY( pxCurrentTimerList );
	if( *pxListWasEmpty == pdFALSE )
	{
		xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );
	}
	else
	{
		/* Ensure the task unblocks when the tick count rolls over. */
		xNextExpireTime = ( TickType_t ) 0U;
	}

	return xNextExpireTime;
}

prvProcessTimerOrBlock()  //处理软件定时器任务(溢出超时)  |  让软件定时器阻塞(未溢出)

过程:

取出当前systick的计数值,stm32默认以systick作为os的时基,而systick是24位计数器。

把获取到的计数值与上一次进行比较,正常情况是现在的计数值>上一次计数值。若小于则计数值发生了溢出。

static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty )
{
TickType_t xTimeNow;
BaseType_t xTimerListsWereSwitched;

	vTaskSuspendAll();
	{
		/* Obtain the time now to make an assessment as to whether the timer
		has expired or not.  If obtaining the time causes the lists to switch
		then don't process this timer as any timers that remained in the list
		when the lists were switched will have been processed within the
		prvSampleTimeNow() function. */
		xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );
		if( xTimerListsWereSwitched == pdFALSE )
		{
			/* The tick count has not overflowed, has the timer expired? */
			if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) )
			{
				( void ) xTaskResumeAll();
				prvProcessExpiredTimer( xNextExpireTime, xTimeNow );
			}
			else
			{
				/* The tick count has not overflowed, and the next expire
				time has not been reached yet.  This task should therefore
				block to wait for the next expire time or a command to be
				received - whichever comes first.  The following line cannot
				be reached unless xNextExpireTime > xTimeNow, except in the
				case when the current timer list is empty. */
				if( xListWasEmpty != pdFALSE )
				{
					/* The current timer list is empty - is the overflow list
					also empty? */
					xListWasEmpty = listLIST_IS_EMPTY( pxOverflowTimerList );
				}

				vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ), xListWasEmpty );

				if( xTaskResumeAll() == pdFALSE )
				{
					/* Yield to wait for either a command to arrive, or the
					block time to expire.  If a command arrived between the
					critical section being exited and this yield then the yield
					will not cause the task to block. */
					portYIELD_WITHIN_API();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		else
		{
			( void ) xTaskResumeAll();
		}
	}
}

处理软件定时器溢出任务(这个函数是最为关键的函数,更新下一次溢出值,并插入正确的链表中)

static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow )
{
BaseType_t xResult;
Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList );

	/* Remove the timer from the list of active timers.  A check has already
	been performed to ensure the list is not empty. */
	( void ) uxListRemove( &( pxTimer->xTimerListItem ) );
	traceTIMER_EXPIRED( pxTimer );

	/* If the timer is an auto reload timer then calculate the next
	expiry time and re-insert the timer in the list of active timers. */
	if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE )
	{
		/* The timer is inserted into a list using a time relative to anything
		other than the current time.  It will therefore be inserted into the
		correct list relative to the time this task thinks it is now. */
		if( prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xNextExpireTime ) != pdFALSE )
		{
			/* The timer expired before it was added to the active timer
			list.  Reload it now.  */
			xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY );
			configASSERT( xResult );
			( void ) xResult;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	/* Call the timer callback. */
	pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );
}

prvProcessReceiveCommands()   //对定时器命令队列中的数据进行处理。

遍历并处理定时器命令队列的所有message。

static void	prvProcessReceivedCommands( void )
{
DaemonTaskMessage_t xMessage;
Timer_t *pxTimer;
BaseType_t xTimerListsWereSwitched, xResult;
TickType_t xTimeNow;

	while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ) /*lint !e603 xMessage does not have to be initialised as it is passed out, not in, and it is not used unless xQueueReceive() returns pdTRUE. */
	{
		#if ( INCLUDE_xTimerPendFunctionCall == 1 )
		{
			/* Negative commands are pended function calls rather than timer
			commands. */
			if( xMessage.xMessageID < ( BaseType_t ) 0 )
			{
				const CallbackParameters_t * const pxCallback = &( xMessage.u.xCallbackParameters );

				/* The timer uses the xCallbackParameters member to request a
				callback be executed.  Check the callback is not NULL. */
				configASSERT( pxCallback );

				/* Call the function. */
				pxCallback->pxCallbackFunction( pxCallback->pvParameter1, pxCallback->ulParameter2 );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* INCLUDE_xTimerPendFunctionCall */

		/* Commands that are positive are timer commands rather than pended
		function calls. */
		if( xMessage.xMessageID >= ( BaseType_t ) 0 )
		{
			/* The messages uses the xTimerParameters member to work on a
			software timer. */
			pxTimer = xMessage.u.xTimerParameters.pxTimer;

			if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) /*lint !e961. The cast is only redundant when NULL is passed into the macro. */
			{
				/* The timer is in a list, remove it. */
				( void ) uxListRemove( &( pxTimer->xTimerListItem ) );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}

			traceTIMER_COMMAND_RECEIVED( pxTimer, xMessage.xMessageID, xMessage.u.xTimerParameters.xMessageValue );

			/* In this case the xTimerListsWereSwitched parameter is not used, but
			it must be present in the function call.  prvSampleTimeNow() must be
			called after the message is received from xTimerQueue so there is no
			possibility of a higher priority task adding a message to the message
			queue with a time that is ahead of the timer daemon task (because it
			pre-empted the timer daemon task after the xTimeNow value was set). */
			xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );

			switch( xMessage.xMessageID )
			{
				case tmrCOMMAND_START :
			    case tmrCOMMAND_START_FROM_ISR :
			    case tmrCOMMAND_RESET :
			    case tmrCOMMAND_RESET_FROM_ISR :
				case tmrCOMMAND_START_DONT_TRACE :
					/* Start or restart a timer. */
					if( prvInsertTimerInActiveList( pxTimer,  xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) != pdFALSE )
					{
						/* The timer expired before it was added to the active
						timer list.  Process it now. */
						pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );
						traceTIMER_EXPIRED( pxTimer );

						if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE )
						{
							xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, NULL, tmrNO_DELAY );
							configASSERT( xResult );
							( void ) xResult;
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
					break;

				case tmrCOMMAND_STOP :
				case tmrCOMMAND_STOP_FROM_ISR :
					/* The timer has already been removed from the active list.
					There is nothing to do here. */
					break;

				case tmrCOMMAND_CHANGE_PERIOD :
				case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR :
					pxTimer->xTimerPeriodInTicks = xMessage.u.xTimerParameters.xMessageValue;
					configASSERT( ( pxTimer->xTimerPeriodInTicks > 0 ) );

					/* The new period does not really have a reference, and can
					be longer or shorter than the old one.  The command time is
					therefore set to the current time, and as the period cannot
					be zero the next expiry time can only be in the future,
					meaning (unlike for the xTimerStart() case above) there is
					no fail case that needs to be handled here. */
					( void ) prvInsertTimerInActiveList( pxTimer, ( xTimeNow + pxTimer->xTimerPeriodInTicks ), xTimeNow, xTimeNow );
					break;

				case tmrCOMMAND_DELETE :
					/* The timer has already been removed from the active list,
					just free up the memory if the memory was dynamically
					allocated. */
					#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
					{
						/* The timer can only have been allocated dynamically -
						free it again. */
						vPortFree( pxTimer );
					}
					#elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
					{
						/* The timer could have been allocated statically or
						dynamically, so check before attempting to free the
						memory. */
						if( pxTimer->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
						{
							vPortFree( pxTimer );
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
					break;

				default	:
					/* Don't expect to get here. */
					break;
			}
		}
	}
}

参考:

FreeRTOS源码探析之——软件定时器 - 墨天轮 (modb.pro)

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

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

相关文章

什么是需求分析,如何进行需求分析?

目录 前言 需求分析在分析什么 如何看待产品原型设计 技术同学培养产品意识 如何应对需求变更问题 总结 重点&#xff1a;配套学习资料和视频教学 前言 这篇文章&#xff0c;我会将软件工程中关于需求分析相关的知识进行总结梳理&#xff0c;并以自己理解的方式进行阐述…

iNFTnews|风口之上,耐克推出的web3平台.SWOOSH能否引领市场?

本周一&#xff0c;著名的体育运动品牌耐克宣布&#xff0c;公司将进一步进军Web3&#xff0c;并将发布一个旨在通过旗下的数字可穿戴设备吸引用户的新平台——.SWOOSH。 耐克表示&#xff0c;.SWOOSH将成为Web3教育的资源库&#xff0c;以及购买和交易虚拟运动鞋或运动衫等数…

Oracle SQL执行计划操作(3)——物化视图相关操作

3. 物化视图相关操作 该类操作与SQL语句执行计划中访问物化视图数据的路径和方式相关,主要包括物化视图访问类相关操作。根据不同的具体SQL语句及其他相关因素,如下各操作可能会出现于相关SQL语句的执行计划。 1)MAT_VIEW ACCESS FULL 对物化视图(Materialized View)直…

培训学校的教务管理系统存在的问题有哪些?

教务管理在培训学校管理里面占据不可或缺的地位。随着计算机、网络、大数据、人工智能等技术的发展&#xff0c;国家对智慧校园的重视&#xff0c;促进了培训学校教务管理信息化与智慧化建设。然而&#xff0c;随着科技进步速度不断加快&#xff0c;教育改革步伐的不断加大&…

解决:将Ubuntu系统打包成ios镜像并制作U盘系统

将Ubuntu系统打包成ios镜像并制作U盘系统一、安装 Systemback二、将创建的.sblive镜像文件转为.iso格式三、写入U盘进行安装四、制作系统U盘一、安装 Systemback Currently supported Ubuntu releases: - 14.04.X LTS - 15.04 - 15.10 - 16.04.X LTS - 16.10Systemback的作者在…

linux的基本指令(中)

文章目录1.man指令1.安装2.用法3.man数字1. printf函数的查询2.cp指令1.cp 文件1.拷贝到当前目录2.拷贝到 其他目录中3. 拷贝到上一级目录2.cp 目录1.返回上一级目录3. mv指令1.剪切2. 文件的重命名4. cat指令1.显示文件的全部内容并且不可以修改2.cat -n指令3. cat -s 指令5. …

6张思维导图,搞定项目管理!(PMP项目管理可用)

工作中&#xff0c;我们最常遇到的就是大大小小的工作项目。项目要怎么做&#xff0c;才能100%达成目标呢&#xff1f; 小哈总结了6组项目管理思维导图。只要从五大阶段掌握诀窍&#xff0c;用项目管理的思维去管理工作&#xff0c;工作就能有条不紊按预期达到目标&#xff0c…

GitHub Codespaces 安装 .NET 7

本文主要介绍如何在 GitHub Codespaces 这个云上 IDE 环境中安装 .NET 7 背景 GitHub 的 Codespaces 可以让我们随时随地编写代码&#xff0c;一些简单的修改也非常方便快捷。特别是 .NET 7 发布后&#xff0c;一些可以直接升级的小项目只需要更改配置就可以了&#xff0c;我们…

EN 14915实木镶板和包层—CE认证

实木镶板和包层CE认证&#xff08;欧盟强制认证&#xff09;&#xff0d;简介 在欧盟市场“CE”标志属强制性认证标志&#xff0c;以表明产品符合欧盟《技术协调与标准化新方法》指令的基本要求。这是欧盟法律对产品提出的一种强制性要求。 在实木镶板和包层上加贴CE标志不但可…

Active Directory报表计划

ADManager Plus的“计划报表”功能是一个独有模块&#xff0c;可使报表生成实现自动化。IT管理员现在可为任何所需的时间段&#xff08;从一个小时到一个月&#xff09;计划报表。因而&#xff0c;计划功能可提供一致、准确的报表交付&#xff0c;而不会产生任何麻烦和费用。本…

【ESP32_8266_WiFi (十三)】ESP8266自动配网 – WiFiManager库使用说明

文章目录ESP8266自动配网 – WiFiManager库使用说明1 WiFiManager库使用说明1.1 WiFi配置流程1.2 WiFi配置示例程序1.2.1 预备程序 – 清理ESP8266储存的WiFi连接信息1.2.2 WiFiManager网络配置示例程序1.2.3 WiFiManager网络配置测试2 WiFiManager库汉化和定制说明2.1 WiFiMan…

Spring(十)- Spring Bean的基本注解

文章目录一、Spring Bean的基本注解一、Spring Bean的基本注解 Spring除了xml配置文件进行配置之外&#xff0c;还可以使用注解方式进行配置&#xff0c;注解方式慢慢成为xml配置的替代方案。我们有了xml开发的经验&#xff0c;学习注解开发就方便了许多&#xff0c;注解开发更…

调节自噬的小分子化合物

自噬? 生物体需要通过不断合成和降解物质来维持自身的内稳态&#xff08;Homeostasis&#xff09;。细胞内物质的降解主要通过两种途径进行&#xff1a;泛素化蛋白酶体途径&#xff08;Ubiquitin-proteasome system, UPS&#xff09;和自噬溶酶体途径&#xff08;Autophagy-ly…

06 数组

本文仅作为个人笔记 数组 数组的引入 import java.util.Scanner; public class TestArray01{public static void main(String[] args){//功能&#xff1a;键盘录入是个学生的成绩&#xff0c;求和&#xff0c;求平均数&#xff1a;//定义一个求和的变量&#xff1a;int sum …

GoF之代理模式

GoF之代理模式 1、 对代理模式的理解 生活场景1&#xff1a;牛村的牛二看上了隔壁村小花&#xff0c;牛二不好意思直接找小花&#xff0c;于是牛二找来了媒婆王妈妈。这里面就有一个非常典型的代理模式。牛二不能和小花直接对接&#xff0c;只能找一个中间人。其中王妈妈是代…

Linux | 磁盘结构 | 文件系统认识 | inode | 软硬链接

文章目录磁盘物理结构的认识磁盘的分区inode和文件数据之间的映射文件名和inode之间的映射文件知道自己的inode号吗&#xff1f;文件的软硬链接硬链接数磁盘物理结构的认识 &#xff08;图片来自于网络&#xff09;一个磁盘由许多盘片构成&#xff0c;每个盘片上有着许多磁道&a…

SAP Table function 执行报错 code CX_SQL_EXCEPTION feature not supported 该如何分析

我的知识星球里有朋友提问&#xff1a; 我的场景是cds 调用 table function, table function 调用 amdp 然后报错: code:CX_SQL_EXCEPTION message: feature not supported.Contains predicates only supported when table function is unfolded? 请问这个是什么意思&#xf…

使用分贝的波动大小将声音数据降维成字符

🍿*★,*:.☆欢迎您/$:*.★* 🍿 目录 背景 正文 总结 背景描述

【高速数字化仪应用案例系列】虹科数字化仪在大型物理实验领域的应用

大型物理实验应用 了解物质和自然力量的使命推动着在物理学科领域进行更大&#xff0c;更复杂的实验。为了做到这一点&#xff0c;科学家和工程师正在建造比以往更大&#xff0c;更强大&#xff0c;更灵敏的机器和仪器。例如&#xff0c;天文学家正在使用不断膨胀的电磁波谱部…

【序列召回推荐】(task1)NeuralCF(学习匹配函数)

note&#xff1a; 和协同过滤矩阵分解的区别&#xff1a;NeuralCF 用一个多层的神经网络替代掉了原来简单的点积操作&#xff0c;另外user隐向量和item隐向量在进入MLP前需要concat拼接&#xff0c;其实就是两个矩阵在水平位置上拼接而已。这样就可以让用户和物品隐向量之间进…