FreeRTOS 软件定时器的使用

news2025/1/13 11:43:56

        FreeRTOS中加入了软件定时器这个功能组件,是一个可选的不属于freeRTOS内核的功能,由定时器服务任务(其实就是一个定时器任务)来提供。

        软件定时器是当设定一个定时时间,当达到设定的时间之后就会执行指定的功能函数,而这个功能函数就叫做回调函数。也就是说回调函数的两次执行间隔叫做定时器的定时周期。

        回调函数:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。警告:不能在回调函数中调用任何会阻塞任务的API函数 !比如,定时器回调函数中不能调佣vTaskDelay()、vTaskDelayUnti(),还有一些访问队列或者信号量的非零阻塞时间的API函数。

        总之,软件定时器主要是用于定时触发或者是周期性执行的触发功能,从很多特种描述看,感觉是用软件模拟了一个定时中断ISR。软件定时器由Free RTOS内核实现并控制。 它们不需要硬件支持,也与硬件定时器或硬件计数器无关。

       说明:我使用的FreeRTOS版本为(FreeRTOS Kernel V10.4.3 LTS Patch 2)    

一、FreeRTOS启用软件定时器

        FreeRTOS软件定时器是一个可选的不属于freeRTOS内核的功能,因此要在FreeRTOS中使用软件定时器的话,需要在配置文件freeRTOSConfig.h中添加如下所示的4个宏:

#define configUSE_TIMERS				1		//使能软件定时器
#define configTIMER_TASK_PRIORITY		7		//软件定时器的优先级
#define configTIMER_QUEUE_LENGTH		10		//软件定时器的队列长度
#define configTIMER_TASK_STACK_DEPTH	(configMINIMAL_STACK_SIZE * 5)		//软件定时器的堆栈空间大小(单位是字)

二、FreeRTOS软件定时器的种类

        FreeRTOS定时器有两种:单次定时器和周期定时器。

1、单次定时器

        单次定时器定时时间到了,只执行一次回调函数,之后不会再执行。只有再次重新启动它,才会再执行一次。也就是说,定时器启动一次,开始计时,时间到了,执行一次回调函数。要想再次执行,必须重新启动它。

2、周期定时器

        周期定时器根据设定的时间周期的执行的。它一旦启动以后,每执行一次完一次回调函数以后定时器会自动重启,回调函数会周期性的执行。周期定时器相当于单片机硬件定时器中配置为自动重装定时器。

        单次定时器和周期定时器的示意图如下所示:

在这里插入图片描述

三、FreeRTOS软件定时器API函数

        FreeRTOS软件定时器API函数包含:1、创建软件定时器。2、启动软件定时器。3、停止定时器。4、复位定时器。5、查询定时器是否已经开始运行。

1、创建软件定时器

TimerHandle_t xTimerCreate( const char * const pcTimerName,  /* 定时器名字 */
                            const TickType_t xTimerPeriodInTicks,  /* 定时器周期 */
                            const UBaseType_t uxAutoReload,  /* 单次定时器或者周期定时器式 */
                            void * const pvTimerID,          /* 定时器 ID */   
                            TimerCallbackFunction_t pxCallbackFunction )/* 定时器回调函数 */

        API函数 xTimerCreate 用于创建软件定时器。

(1)、pcTimerName:定时器名字,一般用于调试,方便识别不同的定时器。

(2)、xTimerPeriodInTicks:定时器周期,单位是系统时钟节拍。

(3)、uxAutoReload:选择定时器是周期模式还是单次模式。若参数为 pdTRUE,则表示选择周期定时器,若参数为pdFALSE,则表示选择单次定时器。

(4)、pvTimerID:定时器的 ID。当创建多个不同的定时器,但又使用同一个回调函数时,在回调函数中就可以通过不同的ID 号来区分不同的定时器。

(5)、pxCallbackFunction:定时器的回调函数。

返回值:创建成功返回定时器的句柄,失败会返回 NULL。

        例子:创建一个单次触发的软件定时器示例如下:

                软件定时器的名字为singalTIMERS,定时器的ID号=1,定时周期=500毫秒。

    /*创建单次定时器*/
    xTimers = xTimerCreate("singalTIMERS",  	//软件定时器的名字  
                            500,       	        //定时器周期(500毫秒),单位时钟节拍
                            pdFALSE,   //定时器模式,pdTRUE为周期定时器,pdFALSE为单次定时器
                            (void*)1,        	//定时器的ID号=1
                            vTimerCallback); 	//定时器回调函数
								  

2、启动软件定时器

        启动软件定时器分为两种:1、在任务中启动定时器。2、在中断中启动定时器。

2.1、在任务中启动定时器

BaseType_t xTimerStart( TimerHandle_t xTimer, 		/* 定时器句柄 */
    		     		TickType_t xBlockTime );	/* 成功启动定时器前的最大等待时间设置,单位系统时钟节拍 */

        API函数 xTimerStart 用于启动软件定时器。

(1)、xTimer:定时器句柄。

(2)、xBlockTime:成功启动定时器前的最大等待时间设置,单位系统时钟节拍。
返回值:返回 pdFAIL 表示此函数向消息队列发送消息失败,返回 pdPASS 表示此函数向消息队列发送消息成功。

        注意:定时器组的大部分API函数不是调用后就会直接运行的,而是通过消息队列给定时器任务发消息来实现的,此参数设置的等待时间就是当消息队列已经满的情况下,等待消息队列有空间时的最大等待时间。
        注意:定时器任务实际执行消息队列发来的命令依赖于定时器任务的优先级,如果定时器任务是高优先级会及时得到执行,如果是低优先级,就要等待其余高优先级任务释放 CPU 权才可以得到执行。

        例子:在任务中启动软件定时器示例如下:

xTimerStart(xTimers, 200);

       表示刚刚创建的单次周期定时器,定时周期=500毫秒,等待消息队列有空间时的最大等待时间为200毫秒。

2.2、在中断中启动定时器

BaseType_t	xTimerStartFromISR(TimerHandle_t  xTimer
    		                   BaseType_t*    pxHigherPriorityTaskWoken);

(1)、XTimer:软件定时器的句柄
(2)、pxHigherPriorityTaskWoken:退出此函数时是否要进行任务切换

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

        例子:在中断中启动软件定时器示例如下:

void USART2_IRQHandler(void)
{

	BaseType_t xHigherPriorityTaskWoken = pdFALSE;	//不进行任务切换


	if (xTimerStartFromISR(xTimers , &xHigherPriorityTaskWoken) != pdPASS)
	{
		//软件定时器开启失败
	}			
} 

3、停止定时器

        停止软件定时器分为两种:1、在任务中停止定时器。2、在中断中停止定时器。

3.1、在任务中停止定时器

BaseType_t xTimerStop(TimerHandle_t xTime,
    				   TickType_t xTicksToWait)

(1)、xTimer:软件定时器的句柄。
(2)、xTicksToWait:阻塞时间,即停止定时器最大的等待时间。

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

        例子:停止软件定时器示例如下: 

xTimerStop(xTimers, portMAX_DELAY);


3.2、在中断中停止软件定时器

xTimerStopFormISR(TimerHandle_t xTimer,
    				BaseType_t pxHigherPriorityTaskWoken);

(1)、xTimer:软件定时器句柄
(2)、pxHigherPriorityTaskWoken:退出此函数时是否要进行任务切换

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

4、复位定时器

        复位软件定时器分为两种:1、在任务中复位定时器。2、在中断中复位定时器。

        警告:复位软件定时器,会重新计算定时周期到达的时间点,这个新的时间点是相对于复位定时器的那个时刻计算的,并不是第一次启动软件定时器的那个时间点!!!

         假设:定时周期=5秒,调用xTimerStart后,定时器已经运行了3秒,此时调用xTimerReset复位定时器,定时器会从当前时刻(3秒)时刻,重新启动,当5秒后,执行一次回调函数。

4.1、在任务中复位定时器

BaseType_t xTimerReset(TimerHandle_t  xTimer,
    					TickType_t xTicksToWait)

(1)、xTimer:软件定时器的句柄。
(2)、xTicksToWait:阻塞时间,即停止定时器最大的等待时间。

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

4.2、在中断中复位定时器

BaseType_t xTimerResetFromISR(TimerHandle_t xTimer,
BaseType_t *pxHigherPriorityTaskWoken);

(1)、xTimer:软件定时器句柄
(2)、pxHigherPriorityTaskWoken:退出此函数时是否要进行任务切换

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

5、查询定时器是否已经开始运行

BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer )

        查询定时器以查看它是活动的还是休眠的。如果出现以下情况,计时器将处于休眠状态:

  1. 已创建但未启动。
  2. 已过期的计时器尚未重新启动。

返回值:
pdFALSE,没有运行。其他值,运行。

四、实例

        下面我们创建一个单次周期软件定时器,定时器的名字为singalTIMERS,定时器的ID号=1,定时周期=500毫秒。

1、配置文件freeRTOSConfig.h中添加如下所示的4个宏:

#define configUSE_TIMERS				1		//使能软件定时器
#define configTIMER_TASK_PRIORITY		7		//软件定时器的优先级
#define configTIMER_QUEUE_LENGTH		10		//软件定时器的队列长度
#define configTIMER_TASK_STACK_DEPTH	(configMINIMAL_STACK_SIZE * 5)		//软件定时器的堆栈空间大小(单位是字)

        注意:软件定时器的优先级为7,那么configMAX_PRIORITIES的优先级至少为8,我设置的configMAX_PRIORITIES优先级为8,参见下图

2、包含头文件

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "timers.h"

3、定义定时器句柄

TimerHandle_t xTimers = NULL;				//单次定时器

4、定义回调函数

static void vTimerCallback(xTimerHandle pxTimer);	//单次定时器回调函数


/*
**************************************************************************************
*	函 数 名: vTimerCallback
*	功能说明: 定时器回调函数
*	形    参: 无
*	返 回 值: 无
**************************************************************************************
*/
static void vTimerCallback(xTimerHandle pxTimer)
{

	configASSERT(pxTimer);
	
	//添加用户代码
}

5、动态创建定时器

    /*创建单次定时器*/
    xTimers = xTimerCreate("singalTIMERS",  	//软件定时器的名字  
                            200,       	//定时器周期,单位时钟节拍
                            pdFALSE,    //定时器模式,pdTRUE为周期定时器,pdFALSE为单次定时器
                            (void*)1,        	//定时器的ID号
                            vTimerCallback); 	//定时器回调函数

        想要搞清楚【FreeRTOS 的软件定时器消息队列】的内核知识,请参阅以下文章:

(10条消息) FreeRTOS个人笔记-软件定时器_Couvrir洪荒猛兽的博客-CSDN博客_freertos 定时器

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

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

相关文章

【趣学算法】Day2 贪心算法——最优装载问题

14天阅读挑战赛努力是为了不平庸~ 算法学习有些时候是枯燥的,这一次,让我们先人一步,趣学算法! ❤️一名热爱Java的大一学生,希望与各位大佬共同学习进步❤️ 🧑个人主页:周小末天天开心 各位大…

ESP8266/esp32接入阿里云物联网平台点灯控制类案例

ESP8266/esp32接入阿里云物联网平台点灯控制类案例📌阿里云物联网云平台介绍:https://help.aliyun.com/product/30520.html 🎋需要自己在阿里云物联网云平台注册自己的账户,这里不做介绍了。 🌻阿里云物联网云平台创建…

Python基础入门(持续更新中)

一、发展历程 Python的创始人为荷兰人吉多范罗苏姆(Guido van Rossum)。1989年圣诞节期间,在阿姆斯特丹,Guido为了打发圣诞节的无趣,决心开发一个新的脚本解释程序,作为ABC语言的一种继承。之所以选中单词P…

CANoe-以太网软硬件网络自动映射的问题

以太网软硬件网络自动映射的问题 当我们设置CANoe以太网模式为Network-based access模式时 我们需要在CANoe硬件里配置segment,具体内容请参考文章《如何配置CANoe Network-based access模式的以太网网络拓扑》 有时我们图省事,配置的segment默认名称是什么,我们也不管,也…

UVM如何处理out-of-order乱序传输

文章目录前言1、基本思路2、支持乱序传输的sequence3、支持乱序传输的Driver总结前言 乱序传输(out-of-order)是指在协议中,后发出去的req,支持先回resp,通常通过ID来保证req和resp之间的关系。很多协议支持乱序传输,例如AXI4。本…

我们简单的new了一个对象,JVM都做了哪些贡献?

前言:大家好,我是小威,24届毕业生,在一家满意的公司实习。本篇文章是关于CAS的介绍以及在我们new对象时,Java虚拟机会为我们做些什么贡献。 本篇文章记录的基础知识,适合在学Java的小白,也适合复…

【C++笔试强训】第九天

🎇C笔试强训 博客主页:一起去看日落吗分享博主的C刷题日常,大家一起学习博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话:夜色难免微凉,前方必有曙光 🌞。 💦 &…

基于骨骼的动作识别:PoseConv3D

Revisiting Skeleton-based Action Recognition解读摘要1. 简介2. Related Work2.1 基于3D-CNN的rgb视频动作识别2.2 基于GCN的骨骼动作识别2.3 基于CNN的骨骼动作识别3. Framework3.1 Pose Extraction3.2 From 2D Poses to 3D Heatmap Volumes3.3 基于骨骼的动作识别3D-CNN3.3…

智能的逻辑与理性的逻辑是不同的逻辑

1.世界是由事实和价值共同组成的1.1 事实由对象、事态及其联系(如语言)构成1.2 价值是事实在实践中的作用和效果构成1.3 事实中存在着决定论,价值里包含有自由意志1.3.1 事实反映有无,价值反映好坏1.4 每一个事实都具有变价值&…

8.2 从堆中绕过SafeS.E.H

一、实验环境 操作系统&#xff1a;windows XP SP2&#xff08;关闭DEP&#xff09; 软件版本&#xff1a;VS2008&#xff08;release&#xff09;、原版OD&#xff08;实时调试&#xff09; 二、实验代码 #include <stdafx.h> #include <stdlib.h> #include <…

计算机操作系统引论(操作系统)

目录 一、操作系统的目标 二、操作系统的作用 三、操作系统的基本特性 四、操作系统的主要功能 五、操作系统的结构设计 一、操作系统的目标 操作系统&#xff08;Operating System&#xff0c;OS&#xff09;是计算机硬件的第一层软件&#xff0c;是计算机必备配置的最基…

接口自动化测试(二)—— Postman实现接口测试

其他 接口自动化测试 知识参考&#xff1a;接口自动化测试 目录 一、Postman介绍和安装 1、简介 2、安装 3、安装Postman插件newman 二、Postman基本用法 1、案例一 2、案例二 三、Postman高级用法 1、管理测试用例Collections 1.1、创建 collections 1.2、创建 用…

libusb系列-002-Windows下libusb源码编译

libusb系列-002-Windows下libusb源码编译 文章目录libusb系列-002-Windows下libusb源码编译摘要Windows下编译libusb库下载源码进入msvc目录选择对应的项目工程编译查看编译好的库文件关键字&#xff1a; vs、 编译、 Qt、 libusb、 windows内容背景&#xff1a; 最近项目终于切…

从 0 到 1 落地前端工程化

你将获得 初识&#xff1a;总结前端工程化技能图谱 了解&#xff1a;梳理前端工程化落地流程 掌握&#xff1a;搭建前端工程化基建项目 提高&#xff1a;实战前端工程化解决方案 作者介绍 JowayYoung&#xff0c;资深前端工程师&#xff0c;目前就职于网易互动娱乐事业群&…

建立私人知识网站 cpolar轻松做到1 (部署DokuWiki)

维基百科&#xff08;wiki&#xff09;相信大家都有所耳闻&#xff0c;作为全球最有名的百科知识搜索网站&#xff0c;很多不常见的知识点都能在这上面搜索到&#xff0c;而维基百科能够成功的原因&#xff0c;就是可以编辑的和修订的功能&#xff0c;让维基百科能够收到各领域…

vue学习笔记——简单入门总结(一)

Vue学习笔记 文章目录Vue学习笔记1. 学习vue的准备1.1. vue简介:1.2. vue特点:1.2.1. 组件化&#xff1a;1.2.1. 声明式&#xff1a;1.2. vue的引入1.2.1. 直接引入&#xff1a;1.2.2. 脚手架构建vue项目&#xff1a;1.2.3&#xff1a;vue开发插件安装&#xff1a;2. vue的简单…

apache服务web页面执行shell脚本

首先安装apache服务 yum -y install httpd 如下介绍两种执行的方式 方式一、url直接传参的方式 #cat /var/www/cgi-bin/shell #!/bin/sh printf "Content-Type: text/plain\n\n" your_commands_here传参的动作 http://172.16.61.119:8098/cgi-bin/shell?pwd在…

STM32使用寄存器开发底层驱动学习(USART+DMA)

目录学习资料下载任务USART的DMA功能发送数据DMA非中断模式接收USART数据。DMA中断模式接收USART数据总结学习资料下载 在本文学习中会用到如下的文档资料&#xff0c;没有的朋友先下载。 工程模板 Cortex M3权威指南&#xff08;中文&#xff09; &#xff1a;本文简称为《…

1024程序节|你知道老师上课随机点名是怎么实现的吗

个人主页&#xff1a;天寒雨落的博客_CSDN博客-C,CSDN竞赛,python领域博主 目录 前言 随机点名 搭建主体框架 简述 执行代码 添加功能 块级元素随机输出姓名 Math.random() Math.round() 按钮控制开始/结束 onclick() clearInterval() 简述 完整代码 执行结果 片…

I/O复用--浅谈epoll

我们聊了聊select和poll知道&#xff1a; 它们都是采取轮询的方式查找是否有就绪描述符。都有数据结构从用户态拷贝到内核态&#xff0c;内核态拷贝到用户态这个过程。 为了针对许多大量连接&#xff0c;高并发的的场景下大量的资源消耗&#xff0c;效率低的问题&#xff0c;这…