FreeRTOS深入教程(软件定时器源码分析)

news2024/11/26 14:48:02

文章目录

  • 前言
  • 一、软件定时器结构体
  • 二、软件定时器的工作机制
  • 三、创建软件定时器
  • 四、启动软件定时器
  • 五、软件定时器如何知道什么时候被调用
  • 总结


前言

除了有硬件定时器,还有软件定时器,那么这篇文章将带大家学习一下软件定时器是如何工作的,以及分析软件定时器的内部源码。

一、软件定时器结构体

软件定时器的本质其实也是一个结构体,在FreeRTOS中会使用一个结构体来管理软件定时器。

软件定时器结构体:

    typedef struct tmrTimerControl                  /* The old naming convention is used to prevent breaking kernel aware debuggers. */
    {
        const char * pcTimerName;                   /*<< Text name.  This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
        ListItem_t xTimerListItem;                  /*<< Standard linked list item as used by all kernel features for event management. */
        TickType_t xTimerPeriodInTicks;             /*<< How quickly and often the timer expires. */
        void * pvTimerID;                           /*<< An ID to identify the timer.  This allows the timer to be identified when the same callback is used for multiple timers. */
        TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */
        #if ( configUSE_TRACE_FACILITY == 1 )
            UBaseType_t uxTimerNumber;              /*<< An ID assigned by trace tools such as FreeRTOS+Trace */
        #endif
        uint8_t ucStatus;                           /*<< Holds bits to say if the timer was statically allocated or not, and if it is active or not. */
    } xTIMER;

参数解释:

pcTimerName (const char):*

用途:文本名称,主要用于调试目的,不被内核使用。
说明:该成员是一个指向常量字符的指针,表示定时器的文本名称。在调试过程中,可以使用这个名称来标识定时器,但它对于内核本身并没有实际的功能。

xTimerListItem (ListItem_t):

用途:链表项,用于事件管理。
说明:这是一个用于链接定时器的标准链表项。它被用于将定时器链接到一个链表中,以进行事件管理。链表通常由RTOS内核用于跟踪定时器。

xTimerPeriodInTicks (TickType_t):

用途:定时器的周期,以时钟节拍为单位。
说明:表示定时器的周期,即定时器多久触发一次,以时钟节拍(Tick)为单位。时钟节拍是RTOS中的基本时间单位。

pvTimerID (void):*

用途:用于标识定时器的ID。
说明:这是一个指向void类型的指针,用于标识定时器。当相同的回调函数用于多个定时器时,通过这个ID可以唯一标识定时器。

pxCallbackFunction (TimerCallbackFunction_t):

用途:定时器到期时调用的回调函数。
说明:这是一个指向定时器到期时将被调用的回调函数的指针。当定时器到期时,RTOS将调用此函数执行相应的操作。

uxTimerNumber (UBaseType_t):

用途:由跟踪工具分配的定时器ID。
说明:在启用了跟踪工具(如FreeRTOS+Trace)的情况下,该成员表示由这些工具分配的定时器ID。

ucStatus (uint8_t):

用途:包含位信息,指示定时器的静态分配状态和活动状态。
说明:这是一个包含位信息的字节,用于表示定时器的状态。其中的位可能包括指示定时器是否静态分配、定时器是否活动等信息。

二、软件定时器的工作机制

使用软件定时器的这些API其实就是在给一个队列发送消息,那么谁来接收这些消息并执行呢?

答案就是守护任务来接收消息并执行。

在xTimerCreateTimerTask函数中会调用prvCheckForValidListAndQueue函数创建出队列和两个链表用于管理软件定时器。
在这里插入图片描述
创建链表和队列代码:
在这里插入图片描述
这里会创建两个链表:pxCurrentTimerList链表和pxOverflowTimerList链表。

当前定时器列表 pxCurrentTimerList:

插入新定时器: 当系统创建并激活新的定时器时,该定时器会以超时时间升序的方式插入到 pxCurrentTimerList 列表中。这样的设计可以使得链表中的定时器按照即将到期的顺序排列。

扫描与处理: 定时器任务在系统运行中会扫描 pxCurrentTimerList 中的第一个定时器,检查是否已经超时。如果已经超时,就会调用与该定时器关联的回调函数进行相应的处理。如果还未超时,可能会将定时器任务挂起等待,直到下一次扫描。

溢出定时器列表 pxOverflowTimerList:

与 pxCurrentTimerList 相似: pxOverflowTimerList 的作用与 pxCurrentTimerList 类似,用于存储那些因为系统计数器溢出而暂时不活动的定时器。

处理溢出情况: 当系统的节拍计数器溢出时,说明经过了很长一段时间,此时原本在 pxCurrentTimerList 中的定时器可能已经到期。这时,这些到期的定时器将会被移动到 pxOverflowTimerList 中。

列表交换:

溢出后的交换: 当系统节拍计数器发生溢出时,两个列表的功能会进行交换。也就是说,pxOverflowTimerList 变为当前列表,而 pxCurrentTimerList 变为溢出列表。这样交换后,之前在 pxCurrentTimerList 中因为溢出而移动到 pxOverflowTimerList 的定时器将成为下一轮的活动定时器,而 pxCurrentTimerList 将被重新用于新的定时器的插入。

这样的设计使得系统在处理定时器时能够更高效地管理和利用定时器,特别是在考虑到系统计数器溢出的情况下。通过不断地在两个列表之间交换,系统能够有效地处理长时间运行和定时器溢出的情况,确保定时器功能的可靠性。

消息队列xTimerQueue,这个队列会存储接收到的消息,守护任务可以读取这个队列中的消息并进行处理。

守护任务在启动调度器时会自动被创建:

 #if ( configUSE_TIMERS == 1 )
     {
         if( xReturn == pdPASS )
         {
             xReturn = xTimerCreateTimerTask();
         }
         else
         {
             mtCOVERAGE_TEST_MARKER();
         }
     }

守护任务的优先级通常需要设置为最高,可以通过配置这个configTIMER_TASK_PRIORITY宏来改变优先级。

在这里插入图片描述
在守护任务内部会处理队列中的消息:

在这里插入图片描述
在这里插入图片描述
软件定时器工作机制:
在这里插入图片描述

三、创建软件定时器

重要代码分析:

首先需要使用pvPortMalloc申请一个软件定时器结构体。

Timer_t * pxNewTimer;

pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) );

初始化创建出来的软件定时器:

prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer );

对软件定时器中的成员进行赋值,并且初始化链表项。

pxNewTimer->pcTimerName = pcTimerName;
pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;
pxNewTimer->pvTimerID = pvTimerID;
pxNewTimer->pxCallbackFunction = pxCallbackFunction;
vListInitialiseItem( &( pxNewTimer->xTimerListItem ) );

四、启动软件定时器

启动软件定时器其实就是调用xTimerGenericCommand函数向队列发送消息。

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

构造消息:

DaemonTaskMessage_t xMessage;
xMessage.xMessageID = xCommandID;
xMessage.u.xTimerParameters.xMessageValue = xOptionalValue;
xMessage.u.xTimerParameters.pxTimer = xTimer;

发送消息:

if( xCommandID < tmrFIRST_FROM_ISR_COMMAND )
{
    if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
    {
        xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait );
    }
    else
    {
        xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY );
    }
}
else
{
    xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken );
}

启动软件定时器后会将软件定时器挂入pxCurrentTimerList链表中。

启动软件定时器其实就是给队列发送了命令,那么守护任务接收到命令后就会进行处理:

在这里插入图片描述
根据超时时间的长短挂入pxCurrentTimerList链表中。

在这里插入图片描述

五、软件定时器如何知道什么时候被调用

在守护任务中有一个无限循环会一直判断是否有软件定时器超时。

在这里插入图片描述

/* 获取最近一次定时器超时时间 */
xNextExpireTime = prvGetNextExpireTime(&xListWasEmpty);

/* 处理超时的定时器或者让队列阻塞 */
prvProcessTimerOrBlockTask(xNextExpireTime, xListWasEmpty);

/* 处理队列接收到的命令 */
prvProcessReceivedCommands();

总结

本篇文章就讲解到这里,下篇文章继续给大家讲解。

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

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

相关文章

金鸣表格文字识别客户端输出项该如何选择?

智能布局&#xff1a;根据提交的图片自动设置输出的打印纸张大小和方向&#xff0c;其中表格识别默认为A4纵向&#xff0c;勾选“合并”可将N张图片批量识别成一个文件、一个表。 表格识别&#xff1a; excel&#xff1a;输出可编辑的excel。 word&#xff1a;输出可编辑的w…

express习惯养成小程序-计算机毕设 附源码 32209

习惯养成小程序的设计与实现 摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;习惯养成小程序被用户普遍使…

虾皮买手号怎么弄的

想要拥有虾皮买手号&#xff0c;可以使用shopee买家通系统进行自动化注册&#xff0c;这款软件目前支持菲律宾、泰国、马来西亚、越南、巴西、印度尼西亚等国家使用。 软件注册流程简单方便&#xff0c;首先我们需要先准备好手机号&#xff0c;因为现在注册虾皮买家号基本上都是…

TechSmith Camtasia2024中文版简单好用的视频处理软件

TechSmith Camtasia 2024中文版是由techsmith公司推出的一款简单好用的视频处理软件&#xff0c;它集视频录制与视频后期处理为一体&#xff0c;用户可以使用软件来进行屏幕录制&#xff0c;其中包括了影像、音效、鼠标移动的轨迹、解说声音等任何模式下的电脑屏幕状态&#xf…

IIP3参数的含义

IIP3参数的含义 三阶交调频率分量 混频器的输入端的总输入信号通常由射频输入&#xff08;载波被调制信号&#xff09;和本振组成。以输入总信号由3个正弦信号为例&#xff0c;输入端的总输入信号为&#xff1a; u u 1 cos ⁡ ω 1 t u 2 cos ⁡ ω 2 t u 3 cos ⁡ ω 3 …

COMP2121 Discrete Mathematics

COMP2121 Discrete Mathematics 需要可WeChat: zh6-86

国产1433D/E/F/H手持式信号发生器,可覆盖到50GHz

1433D/E/F/H信号发生器 1433系列信号发生器是中电科思仪科技股份有限公司专为外场测试设计的一款手持式仪器&#xff0c;具有连续波信号输出、频率/幅度/脉冲多种调制、大动态范围幅度调节、步进/列表扫描等功能&#xff0c;采用8.4寸大屏幕液晶及电容触摸屏一体化设计&#xf…

获取数据库中最占用内存的sql语句

SELECT TOP 20 total_worker_time/1000 AS [总消耗CPU 时间(ms)],execution_count [运行次数], qs.total_worker_time/qs.execution_count/1000 AS [平均消耗CPU 时间(ms)], last_execution_time AS [最后一次执行时间],min_worker_time /1000 AS [最小执行时间(ms…

《opencv实用探索·三》opencv Mat与数组互转

1、利用Mat来存储数据&#xff0c;避免使用数组等操作 //创建一个两行一列的矩阵cv::Mat mean (cv::Mat_<float>(2, 1) << 0.77, 0.33);std::cout() << mean << std::endl;float a mean.at<float>(0, 0); //0.77float b mean.at<float&…

『Linux升级路』基础开发工具——make/Makefile

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;Linux &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、认识make/Makefile &#x1f4d2;1.1make/Makefile的优点 &#x1f4d2;…

windows dockerdesktop 安装sqlserver2022

1.下载windows dockertop软件 下载连接 2.安装完成配置&#xff0c;下载源地址 {"builder": {"gc": {"defaultKeepStorage": "20GB","enabled": true}},"experimental": false,"registry-mirrors": …

flink源码分析之功能组件(三)-rpc组件

简介 本系列是flink源码分析的第二个系列,上一个《flink源码分析之集群与资源》分析集群与资源,本系列分析功能组件,kubeclient,rpc,心跳,高可用,slotpool,rest,metrics,future。 本文解释rpc组件,rpc组件用于个核心组件,包括作业管理器,资源管理器和任务管理器之…

基于C#实现鸡尾酒排序(双向冒泡排序)

通俗易懂点的话&#xff0c;就叫“双向冒泡排序”。 冒泡是一个单向的从小到大或者从大到小的交换排序&#xff0c;而鸡尾酒排序是双向的&#xff0c;从一端进行从小到大排序&#xff0c;从另一端进行从大到小排序。 从图中可以看到&#xff0c;第一次正向比较&#xff0c;我们…

vulfocus apache-cve_2021_41773 漏洞复现

vulfocus apache-cve_2021_41773 漏洞复现 名称: vulfocus/apache-cve_2021_41773 描述: Apache HTTP Server 2.4.49、2.4.50版本对路径规范化所做的更改中存在一个路径穿越漏洞&#xff0c;攻击者可利用该漏洞读取到Web目录外的其他文件&#xff0c;如系统配置文件、网站源码…

SDN之Ubuntn20.04OpenDaylight控制器的安装

目录 1.OpenDaylight简介2.安装JAVA环境3.安装OpenDaylight4.问题总结 1.OpenDaylight简介 OpenDaylight&#xff08;ODL&#xff09;是一个开源的软件定义网络&#xff08;SDN&#xff09;控制器平台&#xff0c;提供了非常美观且功能完善的可视化管理界面&#xff0c;方便用…

邮政快递查询单号查询入口,分析筛选出其中的提前签收件

批量查询邮政快递单号的物流信息&#xff0c;并将其中的提前签收件分析筛选出来。 所需工具&#xff1a; 一个【快递批量查询高手】软件 邮政快递单号若干 操作步骤&#xff1a; 步骤1&#xff1a;运行【快递批量查询高手】软件&#xff0c;第一次使用的朋友记得先注册&…

MySQL 高可用架构

MySQL 是实际生产中最常用的数据库&#xff0c;生产环境数据量极为庞大&#xff0c;对性能和安全要求很高&#xff0c;单机的 MySQL 是远远达不到的&#xff0c;所以必须搭建一个主从复制架构&#xff0c;同时可以基于一些工具实现高可用架构&#xff0c;在此基础上&#xff0c…

制造业工厂如何选择生产管理MES系统?

一、技术架构 虽然绝大多数的用户不会关心MES设计的技术架构&#xff0c;但如果是好的MES系统&#xff0c;需首先必须具备先进的技术支撑&#xff0c;只有先进的开发平台配合上可配置的模块模组&#xff0c;才可快速构建出符合不同用户场景的业务功能。 试想一下&#xff1a;在…

基于C#实现奇偶排序

这篇就从简单一点的一个“奇偶排序”说起吧&#xff0c;不过这个排序还是蛮有意思的&#xff0c;严格来说复杂度是 O(N2)&#xff0c;不过在多核的情况下&#xff0c;可以做到 N2 /(m/2)的效率&#xff0c;这里的 m 就是待排序的个数&#xff0c;当 m100&#xff0c;复杂度为 N…