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

文章目录

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


前言

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

一、软件定时器结构体

软件定时器的本质其实也是一个结构体,在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();

总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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

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

NJU操作系统公开课笔记(2)

上期目录&#xff1a; NJU操作系统公开课笔记&#xff08;1&#xff09;https://blog.csdn.net/jsl123x/article/details/134431343?spm1001.2014.3001.5501 目录 一.处理器与寄存器 二.中断 三.中断系统 四.进程 五.线程与多线程技术概述 六.处理器调度算法 一.处理器…

Couldn‘t agree a key exchange algorithm(available:curve25519-sha256,curve25519-sha256@libssh.org解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

终于等到你!常用的组织架构图模板,高清图片一键导出

组织架构图是一种用来展示一个组织内部人员和职责关系的图表。通过组织架构图&#xff0c;我们可以清晰地了解一个组织的层级架构和各个部门之间的关系。在本文中&#xff0c;我们将向大家推荐8个常用的组织架构图模板&#xff0c;帮助你快速制作出专业的组织架构图。 1. 市场营…

java开发必备的Vue知识点和技能

vue介绍 什么是Vue&#xff1f;vue就是一款用于构建用户界面的渐进式的JavaScript框架。&#xff08;官方&#xff1a;https://cn.vuejs.org/&#xff09; 框架&#xff1a;就是一套完整的项目解决方案&#xff0c;用于快速构建项目。 优点&#xff1a;大大提升前端项目的开…

基于C#实现奇偶排序

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

牛客 算法题 【HJ102 字符统计】 golang实现

题目 HJ102 字符统计 golang代码实现 package mainimport ("bufio""fmt""os""sort" )func main() {// str_arry :make([]string, 0)str_map : make(map[rune]int)result_map : make(map[int][]string)scanner : bufio.NewScanner(os…

k8s中安装consul集群

一、准备知识 headless services一般结合StatefulSet来部署有状态的应用&#xff0c;比如kafka集群&#xff0c;mysql集群&#xff0c;zk集群等&#xff0c;也包括本文要部署的consul集群。 0、consul集群 consul集群的分布式协议算法采用的是raft协议&#xff0c;这意味着必…

2024重庆大学计算机考研分析

24计算机考研|上岸指南 重庆大学 重庆大学计算机考研招生学院是计算机学院和大数据与软件学院。目前均已出拟录取名单。 重庆大学计算机学院是我国高校最早开展计算机研究的基地之一&#xff0c;1978年和1986年获西南地区首个硕士和博士点&#xff0c;1998年成立计算机学院&a…

IIP3参数的含义

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

森林无人机高效解决巡查难题,林区防火掀新篇

山东省某市为了强化森林火灾防范&#xff0c;采用了一项新兴手段——复亚智能无人机森林火情监测系统。这套系统在AI飞行大脑的指挥下&#xff0c;让无人机在空中巡逻&#xff0c;实现了无人机森林防火系统的实施落地。 一、AI大脑如何引领森林无人机高空巡逻&#xff1f; 在山…

Echarts 设备状态 甘特图

在做工厂智能化生产看板时&#xff0c;绝对会有设备状态看板&#xff0c;展示设备当天或者当前状态&#xff0c;设备状态数据一般是有mes 系统设备管理模块对设备信息进行采集&#xff0c;一般包括过站数据&#xff0c;设备当前状态&#xff0c;是否在线是否故障、检修、待生产…

利用 LD_PRELOAD 环境变量

文章目录 原理LD_PRELOAD介绍如何上传.so文件 例题 [虎符CTF 2022]ezphp 原理 LD_PRELOAD介绍 LD_PRELOAD是Linux系统的一个环境变量&#xff0c;它可以影响程序的运行时的链接&#xff08;Runtime linker&#xff09;&#xff0c;它允许你定义在程序运行前优先加载的动态链接…

你好python!——python中的函数与数据容器

一、函数的定义 1.1函数定义语法 1.2函数的参数 1.2.1参数的传入 python中函数的参数和C语言函数的参数其实一样&#xff0c;他们都是形参&#xff0c;是实参的一份临时拷贝。我们来定义一个加法函数来看看函数的参数传入&#xff1a; 参数之间使用逗号进行分隔。 1.2.2函…

【挑战业余一周拿证】一、亚马逊云科技简介 - 第 3 节 - 云计算

第 3 节 - 云计算 在深入了解亚马逊云科技的各个部分之前&#xff0c;让我们先缩小视野&#xff0c;对云进行一个合理的定义。云计算就是通过互联网按需提供 IT 资源并采用按需付费定价模式&#xff0c;下面&#xff0c;我们将进行详细说明。 按需提供表示的是亚马逊云科技会在…

Qt C++中调用python,并将软件打包发布,python含第三方依赖

工作中遇到qt c调用我的python 代码&#xff0c;并且想要一键打包&#xff0c;这里我根据参考的以及个人实践的结果来简单实现一下。 环境&#xff1a;windows系统&#xff0c;QT Creater 4.5&#xff0c; python 3.8&#xff08;anaconda虚拟环境&#xff09; 1. 简单QT调用…

机器学习第13天:模型性能评估指标

☁️主页 Nowl &#x1f525;专栏《机器学习实战》 《机器学习》 &#x1f4d1;君子坐而论道&#xff0c;少年起而行之 文章目录 交叉验证 保留交叉验证 k-折交叉验证 留一交叉验证 混淆矩阵 精度与召回率 介绍 精度 召回率 区别 使用代码 偏差与方差 介绍 区…

FreeRTOS学习之路,以STM32F103C8T6为实验MCU(2-9:任务通知)

学习之路主要为FreeRTOS操作系统在STM32F103&#xff08;STM32F103C8T6&#xff09;上的运用&#xff0c;采用的是标准库编程的方式&#xff0c;使用的IDE为KEIL5。 注意&#xff01;&#xff01;&#xff01;本学习之路可以通过购买STM32最小系统板以及部分配件的方式进行学习…

非参数估计与参数估计的区别,以及详细列举了常用的非参数估计方法和参数估计方法,一网打尽非参数估计与参数估计!!!

文章目录 前言一、非参数估计与参数估计的区别二、常用的非参数估计方法三、常用的参数估计方法总结 前言 非参数估计和参数估计是统计学中的两种不同的估计方法。 一、非参数估计与参数估计的区别 参数估计是指&#xff0c;对于已知分布形式的数据&#xff0c;根据样本数据…

HTML5原生视频播放器组件video的videocontrolslist属性详解

HTML5提供了内置的视频播放控件,其中videocontrolslist是其中一个很有用的属性。videocontrolslist属性可以用于告诉浏览器在视频播放过程中应该显示哪些默认的用户界面控件。下面我们将从几个方面来介绍videocontrolslist的详细使用。 一、启用videocontrolslist videocont…