FreeRTOS—软件定时器

一、软件定时器

1.1.简介

定时器,就是一个闹钟,从指定的时刻开始,经过一个指定时间,然后触发一个超时事件,用户可自定义定时器的周期。定时器又分为两种:

  • 硬件定时器:芯片本身自带的定时器模块,硬件定时器的精度一般很高,每次在定时时间到达之后就会自动触发一个中断,用户在中断服务函数中处理信息。硬件定时器属于外设,数量有限
  • 软件定时器:是指具有定时功能的软件,可以设置定时周期,当指定时间到达后要调用回调函数(也称超时函数),用户在回调函数中处理信息。软件定时的数量理论上来说可以有无限个,由用户自定设定软件定时器的数量

1.2.优缺点

  • 优点:硬件定时器数量有限,而软件定时器理论上只需要足够的内存,就可以创建多个;使用简单,成本低
  • 缺点:软件定时器相对硬件定时器来说,精度没有那么高(因为它以系统时钟为基准,系统时钟中断优先级又是最低,容易被打断)。对于需要高精度要求的场合,不建议使用软件定时器

1.3.特点

  • 可裁剪:软件定时器是可裁剪可配置的功能,如果要使能软件定时器,需将configUSE_TIMERS配置成 1
  • 单次和周期:软件定时器支持设置成:单次定时器和周期定时器

需注意,软件定时器的超时回调函数是由软件定时器服务任务调用的,软件定时器的超时回调函数本身不是任务,因此不能在该回调函数中使用可能会导致任务阻塞的 API 函数

1.4.软件定时器服务任务

在调用函数vTaskStartScheduler( )开启任务调度器时,会创建一个用于管理软件定时器的任务,这个任务就叫做软件定时器服务任务。软件定时器服务任务作用:

  • 负责软件定时器超时的逻辑判断
  • 调用超时软件定时器的超时回调函数
  • 处理软件定时器命令队列

软件定时器命令队列:FreeRTOS 提供了许多软件定时器相关的 API 函数,这些 API 函数大多是往定时器的队列中写入消息(发送命令),这个队列叫做软件定时器命令队列,是提供给 FreeRTOS 中的软件定时器使用的,用户是不能直接访问的。

在这里插入图片描述

1.5.相关配置

  • 当 FreeRTOS 的配置项configUSE_TIMERS设置为 1,在启动任务调度器时,会自动创建软件定时器的服务 / 守护任务prvTimerTask( );
  • 软件定时器服务任务的优先级为configTIMER_TASK_PRIORITY = 31;
  • 定时器的命令队列长度为configTIMER_QUEUE_LENGTH = 5;

需注意,软件定时器的超时回调函数是在软件定时器服务任务中被调用的,服务任务不是专为某个定时器服务,它还需处理其他定时器,因此定时器的回调函数不能影响其他定时器:

  • 回调函数要尽快实行,不能进入阻塞状态,既不能调用那些会阻塞任务的 API 函数,如vTaskDelay( );
  • 访问队列或者信号量的非零阻塞时间的 API 函数也不能调用

1.6.状态

软件定时器共有两种状态:

  • 休眠态:软件定时器可以通过其句柄被引用,但因为没有运行,所以其定时超时回调函数不会被执行
  • 运行态:当指定时间到达之后,它的超时回调函数会被调用

新创建的软件定时器处于休眠状态,也就是未运行的,需要发送命令队列使之运行。

1.7.单次定时器和周期定时器

FreeRTOS 提供了两种软件定时器:

  • 单次定时器:单次定时器的一旦定时超时,只会执行一次它的超时回调函数,不会自动重新开始定时,可以被手动开启,下面是单次定时器的状态转换图

在这里插入图片描述

  • 周期定时器:周期定时器的一旦启动之后就会在执行完回调函数以后自动重新启动,从而周期性的执行其软件定时器回调函数,下面是周期定时器的状态转换图:

在这里插入图片描述

下面是单次定时器和周期定时器的对比图:

在这里插入图片描述

二、FreeRTOS 软件定时器相关 API 函数

FreeRTOS 提供了软件定时器的一些相关操作函数,其中常用的软件定时器相关 API 函数还有一些在中断中使用的函数没有写上,如下表所示:

函数描述
xTimerCreate( )动态方式创建软件定时器
xTimerCreateStatic( )静态方式创建软件定时器
xTimerStart( )开启软件定时器定时
xTimerStop( )停止软件定时器定时
xTimerReset( )复位软件定时器定时
xTimerChangePeriod( )更改软件定时器的定时超时时间

2.1.动态方式创建软件定时器

动态方式创建软件定时器API函数的函数原型如下所示:

TimerHandle_t xTimerCreate(const char * const    	pcTimerName, 
						   const TickType_t  	    xTimerPeriodInTicks, 
						   const UBaseType_t 		uxAutoReload, 
						   void * const       		pvTimerID, 
						   TimerCallbackFunction_t  pxCallbackFunction);

下面表格是它的形参和描述:

形参描述
pcTimerName软件定时器名
xTimerPeriodInTicks定时超时时间,单位:系统时钟节拍
uxAutoReload定时器模式,pdTRUE:周期定时器,pdFALSE:单次定时器
pvTimerID软件定时器ID,用于多个软件定时器公用一个超时回调函数
pxCallbackFunction软件定时器超时回调函数

下面表格是它的返回值:

返回值描述
NULL软件定时器创建失败
其他值软件定时器创建成功,返回其句柄

2.2.开启软件定时器定时

在任务中开启软件定时器定时 API 函数的函数原型如下所示:

BaseType_t xTimerStart(TimerHandle_t  	xTimer, 
					   const TickType_t xTicksToWait); 

下面表格是它的形参和描述:

形参描述
xTimer待开启的软件定时器的句柄
xTickToWait发送命令到软件定时器命令队列的最大等待时间

下面表格是它的返回值:

返回值描述
pdPASS软件定时器开启成功
pdFAIL软件定时器开启失败

2.3.停止软件定时器定时

在任务中停止软件定时器定时 API 函数的函数原型如下所示:

BaseType_t xTimerStop(TimerHandle_t    xTimer, 
					  const TickType_t xTicksToWait);

下面表格是它的形参和描述:

形参描述
xTimer待停止的软件定时器的句柄
xTickToWait发送命令到软件定时器命令队列的最大等待时间

下面表格是它的返回值:

返回值描述
pdPASS软件定时器停止成功
pdFAIL软件定时器停止失败

2.4.复位软件定时器定时

在任务中复位软件定时器定时 API 函数的函数原型如下所示:

BaseType_t xTimerReset(TimerHandle_t    xTimer, 
					   const TickType_t xTicksToWait); 

下面表格是它的形参和描述:

形参描述
xTimer待复位的软件定时器的句柄
xTickToWait发送命令到软件定时器命令队列的最大等待时间

下面表格是它的返回值:

返回值描述
pdPASS软件定时器复位成功
pdFAIL软件定时器复位失败

2.5.更改软件定时器的定时超时时间

在任务中更改软件定时器的定时超时时间 API 函数的函数原型如下所示:

BaseType_t xTimerChangePeriod(TimerHandle_t  	xTimer, 
							  const TickType_t  xNewPeriod, 
							  const TickType_t  xTicksToWait); 

下面表格是它的形参和描述:

形参描述
xTimer待更改定时超时时间的软件定时器的句柄
xNewPeriod新的定时超时时间,单位:系统时钟节拍
xTickToWait发送命令到软件定时器命令队列的最大等待时间

下面表格是它的返回值:

返回值描述
pdPASS软件定时器定时超时时间更改成功
pdFAIL软件定时器定时超时时间更改失败

三、实验

3.1.实验设计

本实验将设计两个任务:

  • start_task:用来创建 task1 任务,并创建两个定时器(单次和周期)
  • task1:用于按键扫描,并对软件定时器进行开启、停止操作

3.2.软件设计

在 start_task 函数上面定义定时器 1 和 2 的句柄,然后创建这两个定时器:

TimerHandle_t timer1_handle = 0;
TimerHandle_t timer2_handle = 0;

void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();   

	timer1_handle = xTimerCreate("timer1", 1000, pdFALSE, (void *)1, timer1_callback);	//创建定时器1,单次定时器
	timer2_handle = xTimerCreate("timer1", 1000, pdTRUE, (void *)2, timer2_callback);	//创建定时器2,周期定时器
	
	xTaskCreate((TaskFunction_t)    task1,	
                (char*)             "task1",
                (uint16_t)          TASK1_STACK_SIZE,
                (void*)             NULL,
                (UBaseType_t)       TASK1_PRIO,
                (TaskHandle_t*)     &task1_handler);
				
	vTaskDelete(NULL);	
    taskEXIT_CRITICAL();            
}

下面是 task1 代码实现,按下 KEY0 开启两个定时器,按下 KEY1 停止两个定时器:

void task1(void *pvParameters)
{
	
	uint8_t key = 0;
	while(1)
	{	
		key = key_scan(0);
		if(key == KEY0_PRES)
		{
			xTimerStart(timer1_handle, portMAX_DELAY);
			xTimerStart(timer2_handle, portMAX_DELAY);
		}else if(key == KEY1_PRES)
		{
			xTimerStop(timer1_handle, portMAX_DELAY);
			xTimerStop(timer2_handle, portMAX_DELAY);
		}
		vTaskDelay(10);
	}
}

下面代码是两个定时器的回调函数:

void timer1_callback(TimerHandle_t pxTimer)
{
	static uint32_t timer = 0;
	printf("timer1的运行次数:%d\r\n",++timer);
}

void timer2_callback(TimerHandle_t pxTimer)
{
	static uint32_t timer = 0;
	printf("timer2的运行次数:%d\r\n",++timer);
}

下图是运行结果:

在这里插入图片描述

由上图可知,单次定时器开启之后只会运行一次,再次运行需要手动开启;周期定时器每隔 1s 运行一次,需要手动停止。

内容概要:文章基于4A架构(业务架构、应用架构、数据架构、技术架构),对SAP的成本中心和利润中心进行了详细对比分析。业务架构上,成本中心是成本控制的责任单元,负责成本归集与控制,而利润中心是利润创造的独立实体,负责收入、成本和利润的核算。应用架构方面,两者都依托于SAP的CO模块,但功能有所区分,如成本中心侧重于成本要素归集和预算管理,利润中心则关注内部交易核算和获利能力分析。数据架构中,成本中心与利润中心存在多对一的关系,交易数据通过成本归集、分摊和利润计算流程联动。技术架构依赖SAP S/4HANA的内存计算和ABAP技术,支持实时核算与跨系统集成。总结来看,成本中心和利润中心在4A架构下相互关联,共同为企业提供精细化管理和决策支持。 适合人群:从事企业财务管理、成本控制或利润核算的专业人员,以及对SAP系统有一定了解的企业信息化管理人员。 使用场景及目标:①帮助企业理解成本中心和利润中心在4A架构下的运作机制;②指导企业在实施SAP系统时合理配置成本中心和利润中心,优化业务流程;③提升企业对成本和利润的精细化管理水平,支持业务决策。 其他说明:文章不仅阐述了理论概念,还提供了具体的应用场景和技术实现方式,有助于读者全面理解并应用于实际工作中。
### FreeRTOS 软件定时器的使用方法 FreeRTOS软件定时器是一种轻量级的任务替代方案,主要用于实现一次性或周期性的事件触发功能[^1]。它通过操作系统内核管理时间片分配,因此无需依赖特定硬件资源即可运行。 #### 创建和配置软件定时器 创建一个软件定时器需要调用 `xTimerCreate` 函数。该函数允许指定定时器名称、超时时间、自动重载标志以及回调函数等参数。以下是其定义: ```c TimerHandle_t xTimerCreate( const char *pcTimerName, // 定时器的名字(调试用途) TickType_t xTimerPeriodInTicks, // 定时器的时间间隔(单位:ticks) UBaseType_t uxAutoReload, // 是否自动重新加载 (pdTRUE 或 pdFALSE) void *pvTimerID, // 用户自定义数据传递给回调函数 TimerCallbackFunction_t pxCallback// 定时器到期后的回调函数指针 ); ``` - **`uxAutoReload` 参数**决定了定时器是一次性还是周期性工作模式。如果设置为 `pdTRUE`,则每次到期后会自动重启;如果是 `pdFALSE`,那么只会在第一次到期时触发回调。 #### 启动与停止定时器 一旦创建好定时器实例之后,可以利用下面两个 API 来启动或者暂停它们: - 使用 `xTimerStart(TimerHandle_t xTimer, TickType_t xBlockTime)` 开始计时; - 停止正在运行中的某个定时器可调用 `xTimerStop(TimerHandle_t xTimer, TickType_t xBlockTime)` 方法。 另外还有其他一些辅助操作比如改变现有活动对象属性值之类的接口可供选用。 #### 示例代码展示如何运用这些概念构建简单的应用程序逻辑结构如下所示: ```c #include "FreeRTOS.h" #include "task.h" #include "timers.h" /* Callback function prototype */ void vTimerCallback( TimerHandle_t xTimer ); int main(void){ /* Create a software timer with auto-reload enabled and assign callback function */ TimerHandle_t xMyTimer = xTimerCreate("OneShot", // Name of the timer. pdMS_TO_TICKS(500),// Period/Timespan in ticks. pdFALSE, // One-shot mode. NULL, // No ID used. vTimerCallback); // Function to call when expired. if(xMyTimer != NULL){ /* Start the created one shot timer. Block indefinitely until successful.*/ if( xTimerStart(xMyTimer, portMAX_DELAY ) == pdPASS ){ printf("Successfully started my one-time timer.\n"); } } /* Start scheduler after setting up all tasks & timers...*/ vTaskStartScheduler(); } /* Implementation of user-defined callback routine invoked upon expiration event occurrence within context switching mechanism provided by OS kernel itself! */ void vTimerCallback( TimerHandle_t xTimer ){ static uint8_t counter=0; ++counter; printf("%d times called since initialization.",counter); } ``` 此例子展示了怎样建立单发型软体计时装置及其相应的处理流程设计思路[^1].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值