返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >FreeRTOS进阶之系统延时完全解析
  • 815
分享到

FreeRTOS进阶之系统延时完全解析

2024-04-02 19:04:59 815人浏览 八月长安
摘要

目录1. 相对延时函数vTaskDelay()2. 绝对延时函数vTaskDelayUntil()3.小结FreeRTOS提供了两个系统延时函数:相对延时函数vTaskDelay()

FreeRTOS提供了两个系统延时函数:相对延时函数vTaskDelay()和绝对延时函数

vTaskDelayUntil()。相对延时是指每次延时都是从任务执行函数vTaskDelay()开始,延时指定的时间结束;

绝对延时是指每隔指定的时间,执行一次调用vTaskDelayUntil()函数的任务。换句话说:任务以固定的频率执行。

在《FreeRTOS任务控制》一文中,已经介绍了这两个api函数的原型和用法,本文将分析这两个函数的实现原理。

1. 相对延时函数vTaskDelay()

考虑下面的任务,任务A在执行任务主体代码后,调用相对延时函数vTaskDelay()进入阻塞状态。系统中除了任务A外,还有其它任务,但是任务A的优先级最高。

void vTaskA( void * pvParameters )  
 {  
       
     const portTickType xDelay = pdMS_TO_TICKS(500);  
   
     for( ;; )  
     {  
         //  ...
         //  这里为任务主体代码
         //  ...
        
         
         vTaskDelay( xDelay );  
     }  
}  

对于这样一个任务,执行过程如图1-1所示。当任务A获取CPU使用权后,先执行任务A的主体代码,之后调用系统延时函数vTaskDelay()进入阻塞状态。任务A进入阻塞后,其它任务得以执行。

FreeRTOS内核会周期性的检查任务A的阻塞是否达到,如果阻塞时间达到,则将任务A设置为就绪状态。由于任务A的优先级最高,会抢占CPU,再次执行任务主体代码,不断循环。

从图1-1可以看出,任务A每次延时都是从调用延时函数vTaskDelay()开始算起的,延时是相对于这一时刻开始的,所以叫做相对延时函数。

从图1-1还可以看出,如果执行任务A的过程中发生中断,那么任务A执行的周期就会变长,所以使用相对延时函数vTaskDelay(),不能周期性的执行任务A。

图1-1:相对延时函数执行示意图

我们来看一下源码

void vTaskDelay( const TickType_t xTicksToDelay )
{
BaseType_t xAlreadyYielded = pdfALSE;
    
    if( xTicksToDelay > ( TickType_t ) 0U )
    {
        vTaskSuspendAll();
        {
            
            prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
        }
        xAlreadyYielded = xTaskResumeAll();
    }
    
    if( xAlreadyYielded == pdFALSE )
    {
        portYIELD_WITHIN_API();
    }
}

如果延时大于0,则会将当前任务从就绪列表删除,然后加入到延时列表。是调用函数prvAddCurrentTaskToDelayedList()完成这一过程的。我们在前面一系列博文中多次提到,tasks.c中定义了很多局部静态变量,其中有一个变量xTickCount定义如下所示:

static volatile TickType_t xTickCount = ( TickType_t ) 0U;

这个变量用来计数,记录系统节拍中断的次数,它在启动调度器时被清零,在每次系统节拍时钟发生中断后加1。相对延时函数会使用到这个变量,xTickCount表示了当前的系统节拍中断次数,这个值加上参数规定的延时时间(以系统节拍数表示)xTicksToDelay,就是下次唤醒任务的时间,xTickCount+ xTicksToDelay会被记录到任务TCB中,随着任务一起被挂接到延时列表。

我们知道变量xTickCount是TickType_t类型的,它也会溢出。在32位架构中,当xTicksToDelay达到4294967295后再增加,就会溢出变成0。为了解决xTickCount溢出问题,FreeRTOS使用了两个延时列表:xDelayedTaskList1和xDelayedTaskList2,并使用两个列表指针类型变量pxDelayedTaskList和pxOverflowDelayedTaskList分别指向上面的延时列表1和延时列表2(在创建任务时将延时列表指针指向延时列表)。顺便说一下,上面的两个延时列表指针变量和两个延时列表变量都是在tasks.c中定义的静态局部变量。

如果内核判断出xTickCount+ xTicksToDelay溢出,就将当前任务挂接到列表指针pxOverflowDelayedTaskList指向的列表中,否则就挂接到列表指针pxDelayedTaskList指向的列表中。

每次系统节拍时钟中断,中断服务函数都会检查这两个延时列表,查看延时的任务是否到期,如果时间到期,则将任务从延时列表中删除,重新加入就绪列表。如果新加入就绪列表的任务优先级大于当前任务,则会触发一次上下文切换。

2. 绝对延时函数vTaskDelayUntil()

考虑下面的任务,任务B首先调用绝对延时函数vTaskDelayUntil ()进入阻塞状态,阻塞时间到达后,执行任务主体代码。系统中除了任务B外,还有其它任务,但是任务B的优先级最高。

void vTaskB( void * pvParameters )  
{  
    static portTickType xLastWakeTime;  
    const portTickType xFrequency = pdMS_TO_TICKS(500);  
    // 使用当前时间初始化变量xLastWakeTime ,注意这和vTaskDelay()函数不同 
    xLastWakeTime = xTaskGetTickCount();  
    for( ;; )  
    {  
                
        vTaskDelayUntil( &xLastWakeTime,xFrequency );  
         //  ...
         //  这里为任务主体代码,周期性执行.注意这和vTaskDelay()函数也不同
         //  ...
  
    }  
}  

对于这样一个任务,执行过程如图2-1所示。当任务B获取CPU使用权后,先调用系统延时函数vTaskDelayUntil()使任务进入阻塞状态。任务B进入阻塞后,其它任务得以执行。FreeRTOS内核会周期性的检查任务A的阻塞是否达到,如果阻塞时间达到,则将任务A设置为就绪状态。

由于任务B的优先级最高,会抢占CPU,接下来执行任务主体代码。任务主体代码执行完毕后,会继续调用系统延时函数vTaskDelayUntil()使任务进入阻塞状态,周而复始。

从图2-1可以看出,从调用函数vTaskDelayUntil()开始,每隔固定+
-周期,任务B的主体代码就会被执行一次,即使任务B在执行过程中发生中断,也不会影响这个周期性,只是会缩短其它任务的执行时间!所以这个函数被称为绝对延时函数,它可以用于周期性的执行任务A的主体代码。

图2-1:绝对延时函数执行示意图

函数vTaskDelayUntil()是如何做到周期性的呢,我们来看一下源码。

void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )
{
TickType_t xTimeToWake;
BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;
    vTaskSuspendAll();
    {
        
        const TickType_t xConstTickCount = xTickCount;
        
        xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;
        
        if( xConstTickCount < *pxPreviousWakeTime )
        {
            
            if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )
            {
                xShouldDelay = pdTRUE;
            }
        }
        else
        {
            
            if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )
            {
                xShouldDelay = pdTRUE;
            }
        }
        
        *pxPreviousWakeTime = xTimeToWake;
 
        if( xShouldDelay != pdFALSE )
        {
            
            prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );
        }
    }
    xAlreadyYielded = xTaskResumeAll();
 
    
    if( xAlreadyYielded == pdFALSE )
    {
        portYIELD_WITHIN_API();
    }
}

与相对延时函数vTaskDelay不同,本函数增加了一个参数pxPreviousWakeTime用于指向一个变量,变量保存上次任务解除阻塞的时间。这个变量在任务开始时必须被设置成当前系统节拍中断次数(见上文的任务B举例),此后函数vTaskDelayUntil()在内部自动更新这个变量。

由于变量xTickCount可能会溢出,所以程序必须检测各种溢出情况,并且要保证延时周期不得小于任务主体代码执行时间。这很好理解,不可能出现每5毫秒执行一个需要20毫秒才能执行完的任务。
如果我们以横坐标表示变量xTickCount的范围,则横坐标左端为0,右端为变量xTickCount所能表示的最大值。在如图2-2所示的三种情况下,才可以将任务加入延时列表。

图2-2中

*pxPreviousWakeTime和xTimeToWake之间表示任务周期性延时时间,

*pxPreviousWakeTime和xConstTickCount之间表示任务B主体代码执行时间。

图2-2中

第一种情况处理系统节拍中断计数器(xConstTickCount)和唤醒时间计数器(xTimeToWake)溢出情况;

第二种情况处理唤醒时间计数器(xTimeToWake)溢出情况

第三种情况处理常规无溢出的情况。

从图中可以看出,不管是溢出还是无溢出,都要求在下次唤醒任务之前,当前任务主体代码必须被执行完。表现在图2-2中,就是变量xTimeToWake总是大于变量xConstTickCount(每溢出一次的话相当于加上一次最大值Max)。

图2-2:将任务加入延时列表的三种情况

计算的唤醒时间合法后,就将当前任务加入延时列表,同样延时列表也有两个。每次系统节拍中断,中断服务函数都会检查这两个延时列表,查看延时的任务是否到期,如果时间到期,则将任务从延时列表中删除,重新加入就绪列表。如果新加入就绪列表的任务优先级大于当前任务,则会触发一次上下文切换。

3.小结

上面的例子中,调用系统延时的任务都是最高优先级,这是为了便于分析而特意为之的,实际上的任务可不一定能设置为最高优先级。对于相对延时,如果任务不是最高优先级,则任务执行周期更不可测,这个问题不大,我们本来也不会使用它作为精确延时;

对于绝对延时函数,如果任务不是最高优先级,则仍然能周期性的将任务解除阻塞,但是解除阻塞的任务不一定能获得CPU权限,因此任务主体代码也不会总是精确周期性执行。

如果要想精确周期性执行某个任务,可以使用系统节拍钩子函数vApplicationTickHook(),它在系统节拍中断服务函数中被调用,因此这个函数中的代码必须简洁。

以上就是FreeRTOS进阶之系统延时完全解析的详细内容,更多关于FreeRTOS进阶系统延时分析的资料请关注编程网其它相关文章!

--结束END--

本文标题: FreeRTOS进阶之系统延时完全解析

本文链接: https://lsjlt.com/news/145549.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • FreeRTOS进阶之系统延时完全解析
    目录1. 相对延时函数vTaskDelay()2. 绝对延时函数vTaskDelayUntil()3.小结FreeRTOS提供了两个系统延时函数:相对延时函数vTaskDelay()...
    99+
    2024-04-02
  • FreeRTOS进阶系统节拍时钟示例的完全解析
    目录前言1.调度器正常情况2.调度器挂起情况3.自动任务切换前言 操作系统的运行是由系统节拍时钟驱动的。 在FreeRTOS中,我们知道系统延时和阻塞时间都是以系统节拍时钟周期为单位...
    99+
    2024-04-02
  • FreeRTOS进阶之队列示例完全解析
    目录前言1.队列创建函数2.入队2.1 xQueueGenericSend()2.2 xQueueGenericSendFromISR ()3.出队前言 FreeRTOS提供了多种任...
    99+
    2024-04-02
  • FreeRTOS进阶之任务创建完全解析
    目录前言1.创建任务堆栈和任务TCB2.初始化任务TCB必要的字段3.初始化任务堆栈4.进入临界区5.当前任务数量增加16.为第一次运行做必要的初始化7.更新当前正在运行的任务TCB...
    99+
    2024-04-02
  • FreeRTOS进阶之任务切换完全分析
    目录FreeRTOS任务切换过程代码分析运行FreeRTOS过程FreeRTOS任务切换过程 FreeRTOS任务相关的代码大约占总代码的一半左右,这些代码都在为一件事情而努力,即找...
    99+
    2024-04-02
  • FreeRTOS进阶之任务通知示例完全解析
    目录前言1.发送通知1.1 xTaskGenericNotify()1.2 vTaskNotifyGiveFromISR()1.3 xTaskGenericNotifyFromISR...
    99+
    2024-04-02
  • FreeRTOS进阶之空闲任务示例完全解析
    目录1.释放内存2. 处理空闲优先级任务3.执行空闲任务钩子函数4.低功耗tickless模式当RTOS调度器开始工作后,为了保证至少有一个任务在运行,空闲任务被自动创建,占用最低优...
    99+
    2024-04-02
  • FreeRTOS进阶信号量示例的完全解析
    目录前言1.信号量创建1.1.创建二进制信号量1.2.创建计数信号量1.3创建互斥量1.4创建递归互斥量2.释放信号量2.1 xSemaphoreGive()2.2xSemaphor...
    99+
    2024-04-02
  • FreeRTOS进阶内存管理示例完全解析
    目录前言1. heap_1.c1.1内存申请:pvPortMalloc() 函数源码为:1.2获取当前未分配的内存堆大小:xPortGetFreeHeapSize()1.3其它函数2...
    99+
    2024-04-02
  • FreeRTOS系统延时实例分析
    这篇文章主要讲解了“FreeRTOS系统延时实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“FreeRTOS系统延时实例分析”吧!FreeRTOS提供了两个系统延时函数:相对延时函数v...
    99+
    2023-06-29
  • freertos实时操作系统空闲任务阻塞延时示例解析
    目录前言空闲任务阻塞延时SysTick实验现象前言 阻塞态:如果一个任务当前正在等待某个外部事件,则称它处于阻塞态。 rtos中的延时叫阻塞延时,即任务需要延时的时候,会放弃CPU的...
    99+
    2024-04-02
  • FreeRTOS实时操作系统的内核控制示例解析
    目录前言1.强制上下文切换宏2.进入临界区宏3.退出临界区宏4.禁止可屏蔽中断宏5.使能可屏蔽中断宏6.启动调度器6.1函数描述7.停止调度器7.1函数描述8.挂起调度器8.1函数描...
    99+
    2024-04-02
  • 操作系统日志分析的魅力之旅:从新手到专家的进阶之路
    一、何为操作系统日志分析? 操作系统日志分析是指通过对系统日志的收集、存储、分析和展示,来发现系统中存在的问题,并采取措施来解决问题。操作系统日志是系统运行过程中的记录,其中包含了系统事件、错误信息、安全事件等信息。通过对操作系统日志的...
    99+
    2024-02-06
    操作系统日志分析 系统日志 日志管理 安全日志 性能日志
  • 时序数据库 Apache-IoTDB 源码解析之系统架构(二)
    时序数据库 Apache-IoTDB 源码解析之前言(一) 这一章主要想聊一聊: 物联网行业的基本系统架构,及使用数据库遇到的需求与挑战 IoTDB 的功能特点及系统架构 车联网 因为本人是在做车联网行业,所以对这个行业的信息了解更深入...
    99+
    2021-06-08
    时序数据库 Apache-IoTDB 源码解析之系统架构(二)
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作