【FreeRTOS】软件定时器

在介绍本文之前,向大家推荐个非常容易入门的人工智能学习网站,建议点击收藏❤️

1. 前言

软件定时器类似于生活中的闹钟,定时时间到了就会干一些我们指定的事情。
在这里插入图片描述
软件定时器有由FreeRTOSD内核实现,并受内核控制,开启调度的时候会自动创建一个定时器后台任务。软件定时器不需要硬件定时器支持。接下来了解软件定时器的一些基本概念以及如何使用软件定时器。

2. 软件定时器有两种状态

  • 休眠:已创建但未开始运行的定时器
  • 运行:开始运行了的定时器

3. 软件定时器有两种类型

单次和自动重载。

  • 单次定时器一旦启动,时间到了只执行一次回调函数。单次定时器可以手动重新启动,但是不会自动重新启动。
  • 自动重新加载计时器 一旦启动,自动重新加载计时器将在每次到期时重新启动,从而定期执行其回调函数。

4. 定时器服务任务

所有软件计时器回调函数都在同一RTOS守护进程(或“计时器服务”)任务的上下文中执行。守护程序任务,是在启动调度程序时,自动创建的标准FreeRTOS任务。其优先级和堆栈大小分别由configTIMER_TASK_PRIORITY和configTIMER_TASK_STACK_DEPTH编译时间配置常量设置。这两个常量都在FreeRTOSConfig.h中定义。
软件计时器回调函数不得调用会导致调用任务进入阻塞状态的API函数,否则将导致守护程序任务进入阻塞状态。

5. 定时器命令队列

软件计时器API函数将命令从调用任务发送到称为“计时器命令队列”的队列上的守护程序任务。计时器命令队列是在启动调度程序时自动创建的标准FreeRTOS队列。定时器命令队列的长度由FreeRTOSConfig.h中的configTIMER_QUEUE_LENGTH编译时间配置常量设置。
在这里插入图片描述

6. 软件定时器的常用API

6.1 xTimerCreate()

可以在调度器运行之前创建软件计时器,也可以在调度器启动后从任务创建软件计时器。

TimerHandle_t xTimerCreate( const char * const pcTimerName, 
                            TickType_t xTimerPeriodInTicks, 
                            UBaseType_t uxAutoReload, 
                            void * pvTimerID, 
                            TimerCallbackFunction_t pxCallbackFunction );
  • pcTimerName:定时器的描述性名称。
  • xTimerPeriodInTicks:指定的定时器周期。单位是系统节拍。
  • uxAutoReload:设置是否可自动重新计时。
  • pvTimerID:每个定时器都有一个ID,当多个软件计时器使用相同的回调函数时,ID特别有用,因为它可以用来判断是哪个定时器到期产生的回调。
  • pxCallbackFunction:指定的回调函数。

6.2 xTimerStart()和xTimerStop()

xTimerStart()开启定时器,该定时器从休眠态切换到运行态。xTimerStop()停止定时器,定时器切换到休眠态。xTimerStart()可以在调度程序之前调用,但是这样做的话也要到调度程序启动时才会实际运行。

BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );
  • xTimer:正在启动或重置的定时器句柄。
  • xTicksToWait:如果为0且定时器命令队列已满,xTimerStart()会运行失败。

6.3 xTimerChangePeriod()

更改软件定时器的周期。如果用于更改已在运行的定时器周期,则该定时器将使用新的周期值计算到期时间。如果用于更改休眠中的定时器,则该定时器将切换到运行状态。

BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,  
                               TickType_t xNewTimerPeriodInTicks, 
                               TickType_t xTicksToWait );
  • pcTimerName:定时器的描述性名称。
  • xTimerPeriodInTicks:定时器的新周期。
  • xTicksToWait:如果为0且定时器命令队列已满,xTimerChangePeriod()会运行失败。

6.4 vTimerSetTimerID() 和 pvTimerGetTimerID()

每个定时器都有一个ID,开发者可以设置或者获取软件定时器的ID。

void vTimerSetTimerID( const TimerHandle_t xTimer, void *pvNewID );
void *pvTimerGetTimerID( TimerHandle_t xTimer );

在实际应用中,常常将相同的回调函数分配给多个软件定时器,此时这两个API就显得至关重要。

6.5 xTimerReset()

重置软件定时器意味着重启定时器,同时还可以重新设置周期值。

BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );
  • xTimer:需要重置的定时器句柄。
  • xTimerPeriodInTicks:想修改成的周期值时间。
  • xTicksToWait:命令超时时间,如果定时器命令队列满了的话,超时会导致重置失败。

7. 软件定时器生活示例

接下来设计了一个示例,该示例来源于生活。

7.1 需求定义

小爱同学可以提供给我们提醒功能,现在即将实现:让小爱同学凌晨4点叫你起来学习RTOS,然后学习2个小时后叫你去跑步锻炼。

你:小爱同学~
小爱:哎!
你:明天凌晨4点叫我起来学习
小爱:好的
你:小爱同学~
小爱:干嘛~
你:明天6点提醒我去跑步
小爱:好的~

7.2需求分析

因为4点起床实在是太早了,所以也不是每天,所以这里我们用单次定时器,上面定了两个闹钟,所以我们需要创建两个定时器。用RTOS实现以上提醒功能,需要:

  • 先创建个任务(小爱同学)
  • 任务中创建两个定时器(让小爱知道几点提醒你)
  • 两个定时器可以用同一个回调服务函数(通过定时器ID来判断提醒你学习还是锻炼)

7.3 代码实现

7.3.1 创建任务

TaskHandle_t XiaoAi;
void XiaoAiTiXing( void *pvParameters );

int main( void )
{
	//买一个小爱同学回家~
	xTaskCreate(XiaoAiTiXing, "我是小爱同学\r\n", 1000, NULL, 1, &XiaoAi);

	vTaskStartScheduler();	
	for( ;; );
	return 0;
}

void XiaoAiTiXing( void *pvParameters )
{
	/* Print out the name of this task. */
	vPrintString(pcTaskGetName(XiaoAi));
	for( ;; )
	{
		while (1)
		{
			Sleep(pdMS_TO_TICKS(1000));
		}
	}
}

7.3.2 任务中创建定时器并开启

这里为了更好得看到输出效果,我们多创建了一个定时器,用于每秒打印一个输出。

void XiaoAiTiXing( void *pvParameters )
{
	vPrintString(pcTaskGetName(XiaoAi));

	Clock = xTimerCreate("Clock", pdMS_TO_TICKS(1000), pdTRUE, 0, ShiJianDao);
	Learn = xTimerCreate("learn", pdMS_TO_TICKS(4000), pdFALSE, 1, ShiJianDao);
	Run = xTimerCreate("run", pdMS_TO_TICKS(6000), pdFALSE, 2, ShiJianDao);

	xTimerStart(Clock, 0);
	xTimerStart(Learn, 0);
	xTimerStart(Run, 0);

	for( ;; )
	{
		while (1)
		{
			Sleep(pdMS_TO_TICKS(1000));
		}
	}
}

7.3.3 定时器到期回调函数里干相应的事情

static void ShiJianDao(TimerHandle_t timer)
{
	static uint8_t i = 0;
	uint8_t timerId = pvTimerGetTimerID(timer);

	//时间到了在这里提醒
	switch (timerId)
	{
	case 0:
		i++;
		printf("现在是凌晨%d点\r\n", i);
		break;
	case 1:
		vPrintString("现在是凌晨4点,该起来学习啦\r\n");

		break;
	case 2:
		vPrintString("现在是早上6点,该去跑步啦\r\n");

		break;
	default:
		break;
	}
	printf("\r\n");
}

7.3.4 运行结果展示

在这里插入图片描述

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

强人电子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值