首页 > 其他分享 >FreeRTOS 原理 --- 软件定时器

FreeRTOS 原理 --- 软件定时器

时间:2023-10-03 21:47:41浏览次数:32  
标签:链表 定时器 FreeRTOS 队列 void 阻塞 xListWasEmpty ---

 简介

 有一个定时器任务,任务内读队列。启动定时器,会向队列发送消息,定时器任务读到消息后把定时器回调函数等信息作为一个链表项插入链表。当链表有链表项,算出还剩多长时间执行定时器回调函数,这个时间作为定时器任务阻塞时间。所以定时器任务重新运行要么是时间到准备运行定时器回调函数,要么是队列接收到消息。

相关的API

xTimerCreate()

xTimerStart()

xTimerPendFunctionCallFromISR() 

 

流程

创建定时器,调用 xTimerCreate(),如果没有创建队列 xTimerQueue,则创建;

启动定时器,调用 xTimerStart(),把xMessage写入队列 xTimerQueue

        xMessage.xMessageID = tmrCOMMAND_START;
        xMessage.u.xTimerParameters.xMessageValue = xTaskGetTickCount();
        xMessage.u.xTimerParameters.pxTimer = ( Timer_t * ) xTimer; // 定时器描述符

 启动调度器时,如果有使用FreeRTOS提供的定时器功能,则创建一个任务给定时器用

void vTaskStartScheduler( void )
{
...

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

 任务对应的执行函数是 prvTimerTask()

static void prvTimerTask( void *pvParameters )
{
TickType_t xNextExpireTime;
BaseType_t xListWasEmpty;

    /* Just to avoid compiler warnings. */
    ( void ) pvParameters;

    for( ;; )
    {
        /* Query the timers list to see if it contains any timers, and if so,
        obtain the time at which the next timer will expire. */
        xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty ); // 刚开始,定时器链表为空,返回值为0,xListWasEmpty为true

        /* If a timer has expired, process it.  Otherwise, block this task
        until either a timer does expire, or a command is received. */
        prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty ); // 定时器回调函数在这里执行,算出阻塞时间(链表有链表项,即有要处理的定时器回调函数,阻塞到执行这个定时器回调函数;链表无链表项,无限期阻塞,除非队列收到消息),阻塞在这里

        /* Empty the command queue. */
        prvProcessReceivedCommands(); // 读队列消息,没消息不阻塞;启动定时器,会发送一个消息,这个函数内处理消息添加到定时器链表
    }
}

 

 

    static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty )
    {
    TickType_t xTimeNow;
    BaseType_t xTimerListsWereSwitched;

        vTaskSuspendAll();
        {
            xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );
            if( xTimerListsWereSwitched == pdFALSE )
            {
                /* The tick count has not overflowed, has the timer expired? */
                if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) ) // 有要处理的定时器回调函数,并且时间已到
                {
                    ( void ) xTaskResumeAll();
                    prvProcessExpiredTimer( xNextExpireTime, xTimeNow ); // 如果是周期性回调函数,把定时器再加到链表;执行定时器回调函数
                }
                else
                {
                    /* The tick count has not overflowed, and the next expire
                    time has not been reached yet.  This task should therefore
                    block to wait for the next expire time or a command to be
                    received - whichever comes first.  The following line cannot
                    be reached unless xNextExpireTime > xTimeNow, except in the
                    case when the current timer list is empty. */
                    if( xListWasEmpty != pdFALSE )
                    {
                        /* The current timer list is empty - is the overflow list
                        also empty? */
                        xListWasEmpty = listLIST_IS_EMPTY( pxOverflowTimerList );
                    }

                    vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ), xListWasEmpty ); // 没要处理的定时器回到函数,或者时间还没到,阻塞在此

                    if( xTaskResumeAll() == pdFALSE )
                    {
                        /* Yield to wait for either a command to arrive, or the
                        block time to expire.  If a command arrived between the
                        critical section being exited and this yield then the yield
                        will not cause the task to block. */
                        portYIELD_WITHIN_API();
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
            }
            else
            {
                ( void ) xTaskResumeAll();
            }
        }
    }

 

    void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) // xWaitIndefinitely 为true表示链表空
    {
    Queue_t * const pxQueue = ( Queue_t * ) xQueue;
        prvLockQueue( pxQueue );
        if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0U ) // 队列空
        {
            /* There is nothing in the queue, block for the specified period. */
            vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait, xWaitIndefinitely );
        }
        else // 队列非空,不阻塞,在后面会读出队列消息处理
        {
            mtCOVERAGE_TEST_MARKER();
        }
        prvUnlockQueue( pxQueue );
    }

 

 

    void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely )
    {
        configASSERT( pxEventList );

        vListInsertEnd( pxEventList, &( pxCurrentTCB->xEventListItem ) );

        if( xWaitIndefinitely != pdFALSE ) // 链表空,无限期阻塞;否则根据下一个定时器回调函数运行时刻算出阻塞时间
        {
            xTicksToWait = portMAX_DELAY;
        }

        traceTASK_DELAY_UNTIL( ( xTickCount + xTicksToWait ) );
        prvAddCurrentTaskToDelayedList( xTicksToWait, xWaitIndefinitely );
    }

 

标签:链表,定时器,FreeRTOS,队列,void,阻塞,xListWasEmpty,---
From: https://www.cnblogs.com/god-of-death/p/17741686.html

相关文章

  • 3.Maven基础概念-坐标
    1.maven仓库地址https://repo1.maven.org/maven2/https://mvnrepository.com/2.什么是坐标Maven中坐标用户描述仓库中资源的位置3.maven坐标主要组成groupld:定义当前Maven项目隶属组织名称(通常是域名反写,例如:orgmybatis)artifactld:定义当前Maven项目名称(通常是模块名称......
  • 【前端规范全攻略】开启高效开发之旅!ESLint + Prettier + husky + lint-staged+Commit
    本文从两个方向出发:1、git提交规范;2、代码风格统一假如团队中的小伙伴在提交代码时没有遵循规范要求,例如只写了一个"修改"或"更新,这会给团队中其他小伙伴造成困扰呢,不得不花时间查看代码和推测逻辑。不仅会浪费了时间和精力,可能会导致以下问题:可读性差维护困难变更历史不......
  • 04-共阳数码管的动态显示
    共阳数码管的动态显示代码如下:#include<REGX52.H>voidDisplay_Dynamic();unsignedcharmonth=1;voidDelay_ms(unsignedintxms){ unsignedinti,j; for(i=0;i<xms;i++){ for(j=0;j<299;j++); }}voidDelay(unsignedchart){ while(t--){ //......
  • 小林图解网络-基础篇
    2.1TCP\IP有哪几层TCP、IP协议栈主要有应用层、传输层、网络层它们的功能作用、拥有哪些协议?应用层主要为用户提供服务,完成特定的功能。场景的协议有HTTP、FTP、DNS传输层主要提供应用进程之间的通信,以端口标识应用主要协议有TCP、UDP协议UDP提供不可靠、无连接的数据传输......
  • 2017 China Collegiate Programming Contest Final (CCPC-Final 2017)
    Preface今天打学校统一要求的这场CCPC2017Final,直接被打爆了,各种数学题搞得人生活不能自理主要是H徐神开场就秒出了正确的思路,然后一心认准高斯消元然后一直想+写+调到结束都没卡过去比赛最后20min的时候祁神想到了更好写的基于施密特正交化的方法,可以碍于时间有限没调出来不......
  • [题解] CF632F - Swimmers in the Pool
    CF632F-SwimmersinthePool题目传送门题意给定一个大小为\(n\timesn\)的矩阵\(A\)。假设\(A\)满足以下条件,那么称该矩阵为MAGIC,否则为NOTMAGIC,并输出对应的属性(即\(A\)是MAGIC还是NOTMAGIC)。$A_{i,j}=A_{j,i}$;$A_{i,i}=0$;$A_{i,j}\le......
  • Depth Camera-based 3D Modeling
    基于深度相机的3D建模受到夏同学和王希同学的启发,我这几天看了下深度相机这一块,用于三维重建三维重建的pipeline是:深度图采集(主动式和被动式)->深度图预处理(噪音)->场景表示(立体/表面表示)->深度图像融合(相邻帧,涉及到点对匹配和位姿联合优化)->纹理重建。trade-offs有:基于体素的......
  • ElasticSearch系列-索引原理与数据读写流程
    索引原理倒排索引倒排索引(InvertedIndex)也叫反向索引,有反向索引必有正向索引。通俗地来讲,正向索引是通过key找value,反向索引则是通过value找key。ES底层在检索时底层使用的就是倒排索引。索引模型现有索引和映射如下:{"products":{"mappings":{"proper......
  • linux-scp用法
    scp(SecureCopyProtocol)是一个用于在本地系统和远程系统之间安全地传输文件的命令行工具。它基于SSH协议,能够加密传输数据,提供了对文件传输的安全支持。下面详细介绍scp的用法。1.将本地文件复制到远程主机scp[options]<local_file><username@remote_host>:<remote_pat......
  • 超级实用!React-Router v6实现页面级按钮权限
    大家好,我是王天~今天咱们用reac+reactRouter来实现页面级的按钮权限功能。这篇文章分三部分,实现思路、代码实现、踩坑记录。嫌啰嗦的朋友,直接拖到第二章节看代码哦。前言通常情况下,咱们为用户添加权限时,除了页面权限,还会细化到按钮级别,比如、新增、删除、查看等权限。如下效......