FreeRTOS 软定时器实现

2023-05-16

@(嵌入式)

    • 简述
    • 使用定时器
      • 配置定时器服务任务
      • 创建 启动 停止定时器
      • 修改定时器
      • 获取定时器状态
    • 定时器实现
      • 数据结构
        • 定时器控制块
        • 定时器管理链表
        • 命令队列
      • 定时器服务任务
        • 回调定时器
        • 处理节拍计数器溢出
        • 命令处理
    • 参考

Freertos
FreeRtos

简述

考虑平台硬件定时器个数限制的, FreeRTOS 通过一个 Daemon 任务(启动调度器时自动创建)管理软定时器, 满足用户定时需求. Daemon 任务会在其执行期间检查用户启动的时间周期溢出的定时器,并调用其回调函数。

对于硬件定时器的中断服务程序, 我们知道不应该在里面执行复杂,可能导致阻塞的工作,相应的, 虽然软定时器实际是在定时Daemon 任务中执行,但是阻塞的话会导致其他定时器调用被延时, 所以实际使用也应该避免。

软定时器是通过一个任务来辅助实现,该功能时刻裁剪的 , 只有设置 FreeRTOSConfig.hconfigUSE_TIMERS == 1 将相关代码编译进来, 才能正常使用相关功能。

分析的源码版本是 v9.0.0

使用定时器

开始先介绍下如何在自己的工程中使用 FreeRTOS 的软件定时器。

配置定时器服务任务

程序中需要使用到软件定时器, 需要先在 FreeRTOSConfig.h 中正确配置如下宏 :
* configUSE_TIMERS
是否编译定时器相关代码, 如需要使用定时器, 设置为 1
* configTIMER_TASK_PRIORITY
设置定时器Daemon 任务优先级, 如果优先级太低, 可能导致定时器无法及时执行
* configTIMER_QUEUE_LENGTH
设置定时器Daemon 任务的命令队列深度, 设置定时器都是通过发送消息到该队列实现的。
* configTIMER_TASK_STACK_DEPTH
设置定时器Daemon 任务的栈大小

创建 启动 停止定时器

如下示例代码所示

TimerHandle_t xTimerUser; // 定义句柄

// 定时器回调函数格式
void vTimerCallback( TimerHandle_t xTimer )
{
    // do something no block
    // 获取溢出次数
    static uin32_t ulCount = ( uint32_t ) pvTimerGetTimerID( xTimer );
    // 累积溢出次数
    ++ulCount; 
    // 更新溢出次数
    vTimerSetTimerID( xTimer, ( void * ) ulCount );

    if (ulCount == 10) {
        // 停止定时器
        xTimerStop( xTimer, 0 );
    }
}

void fun()
{
    // 申请定时器, 配置
    xTimerUser = xTimerCreate
                   /*调试用, 系统不用*/
                   ("Timer's name",
                   /*定时溢出周期, 单位是任务节拍数*/
                   100,   
                   /*是否自动重载, 此处设置周期性执行*/
                   pdTRUE,
                   /*记录定时器溢出次数, 初始化零, 用户自己设置*/
                  ( void * ) 0,
                   /*回调函数*/
                  vTimerCallback);

     if( xTimerUser != NULL ) {
        // 启动定时器, 0 表示不阻塞
        xTimerStart( xTimerUser, 0 );
    }
}

如上所示, 调用函数 xTimerCreate申请,配置定时器, 通过 xTimerStart 启动定时器, 当定时器计数溢出时, 系统回调注册的函数。

定时器可以设置为一次性 One-shot 或者自动重载 Auto-reload 两种, 第一种溢出后停止定时器, 第二种溢出后会再次启动定时器。

定时器重复

修改定时器

在申请定时器的时候设置的定时器周期, 可以通过函数 xTimerChangePeriod 修改, 如下示例 :

 void vAFunction_2( TimerHandle_t xTimer )
 {
     // 判断定时器是否处于运行状态
     if( xTimerIsTimerActive( xTimer ) != pdFALSE )
     {
         /* xTimer is active, do something. */
     }
     else
     {
         // 处于这个状态的定时器, 可能由于 : 
         // 1 定时器 create 后没有start
         // 2 一次性定时器执行溢出后

         // 修改定时器周期
         if( xTimerChangePeriod( xTimer, 
                /*修改定时周期*/
                500 / portTICK_PERIOD_MS, 
            /*允许阻塞最大时间 100 ticks*/
            100 ) == pdPASS )
         {
             // update fail
             // 阻塞 100 tick 仍然无法发送命令

             // 删除定时器 释放对应内存!
             xTimerDelete( xTimer );
         }
         else 
         {
             // 定时器配置更新成功, 并已经启动 !!
         }
    } 
 }

如上, 该函数会修改定时器并使定时器 开始运行!!!

另外, 可以通过函数 xTimerReset 重启定时器, 如果已经启动计数, 重新开始计数; 如果没有启动,启动定时器。

定时器使用系统提供 API,涉及 Queue 操作, 如果是在中断程序中调用,需要调用对应带 FromISR的接口。

获取定时器状态

其他获取定时器信息的函数

// 获取名称 , 申请是设置的字符串
pcTimerGetName()
// 定时器溢出周期
xTimerGetPeriod()
// 返回定时器溢出的时间点 (--> xTaskGetTickCount())
xTimerGetExpiryTime()

定时器实现

FreeRTOS 软定时器的实现在源码目录 Source/include/timers.h, 涉及 链表 和 消息队列(后续文章分析)。

数据结构

使用定时器前,需要先申请定时器, 见 配置定时器服务任务 中, 通过函数 xTimerCreate获取一个定时器, 实际上是向系统申请了一块内存存储定时器控制块的数据结构, 并将参数填写到该结构体中。

定时器控制块

xTIMER

typedef struct tmrTimerControl
{
    // 定时器名 方便调试
    const char *pcTimerName;
    // 链表项 用于插入定时链表
    ListItem_t xTimerListItem;
    // 定时器中断周期
    TickType_t xTimerPeriodInTicks;
    // 是否自动重置, 如果 =pdFalse 为一次性
    UBaseType_t uxAutoReload;
    // 溢出计数 需自己设置
    void *pvTimerID;
    // 定时器溢出回调函数
    TimerCallbackFunction_t pxCallbackFunction;
    #if( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t uxTimerNumber;
    #endif
    #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && 
        ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
        // 标记定时器使用的内存, 删除时判断是否需要释放内存
        uint8_t ucStaticallyAllocated;  #endif
} xTIMER;

成功申请定时器后, 定时器并没有开始工作, 需要调用函数将该定时器中的 xTimerListItem 插入到定时器管理链表中, Daemon 任务才能在该定时器设定的溢出时刻调用其回调函数。

定时器管理链表

timers.c 中定义了如下几个链表变量用于管理定时器, 定时器根据其溢出时刻从小到大插入链表进行管理。
使用两个链表是为了应对系统 TickCount 溢出的问题,在 FreeRTOS 任务调度 系统节拍 介绍过。

PRIVILEGED_DATA static List_t xActiveTimerList1;
PRIVILEGED_DATA static List_t xActiveTimerList2;
// 当前节拍计数器对应的定时器管理链表指针
PRIVILEGED_DATA static List_t *pxCurrentTimerList;
// 溢出时间到了下一个节拍计数阶段(当前节拍计数器溢出后)的定时器管理链表指针 
PRIVILEGED_DATA static List_t *pxOverflowTimerList;

命令队列

文章开头提到的使用定时器的函数, 大部分都带有一个参数,用于设置调用后允许阻塞的最大时间, 原因是, 这些函数并没有直接操作定时器管理链表, 而是向定时器Daemon 任务的消息队列 xTimerQueue 发送消息命令。 之后, 定时器Daemon 任务会从消息队列取出消息并响应操作。

  • 命令格式
  • 命令类型

定时器服务任务

此处,从系统启动的定时器Daemon 任务展开分析 FreeRTOS 的软定时器的实现 。
该任务主体的执行流程如下所示 :

TimeLoop

永久循环部分的代码 :

for( ;; )
{
    // 读取定时器队列第一个链表项的值 -> 即将溢出的定时器时间(ticks)
    // 如果链表空, 返回的是 0
    xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty );

    // 处理溢出的定时器
    // 阻塞直到下一个定时器溢出 或 消息队列有新命令
    prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );

    // 读取消息队列,执行命令
    prvProcessReceivedCommands();
}

回调定时器

定时器任务中, 取出下一个定时器溢出的时间,并把它传递给函数prvProcessTimerOrBlockTask, 该函数负责处理溢出定时器, 应对节拍计数器溢出问题等, 并设置合适的时间阻塞 Daemon 任务, 让出 CPU 使用权直到下一个定时器溢出或者接收到新的命令。

static void prvProcessTimerOrBlockTask(
    const TickType_t xNextExpireTime,
    BaseType_t xListWasEmpty )
{
    TickType_t xTimeNow;
    BaseType_t xTimerListsWereSwitched;
    // 挂起调度器 避免任务切换
    vTaskSuspendAll();
    {
        // 判断系统节拍计数是否溢出
        // 如果是,处理溢出定时器, 并切换定时器链表
        xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );

        // 系统节拍计数器没有溢出
        if( xTimerListsWereSwitched == pdFALSE )
        {
            // 判断是否有定时器溢出
            if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) )
            {
                // 恢复调度
                ( void ) xTaskResumeAll();
                //执行相应定时器的回调函数
                // 对于需要自动重载的定时器, 更新下一次溢出时间, 插回链表
                prvProcessExpiredTimer( xNextExpireTime, xTimeNow );
            }
            else
            {
                // 当前链表没有定时器
                if( xListWasEmpty != pdFALSE )
                {
                    // 判断溢出链表上是否有定时器
                    xListWasEmpty = listLIST_IS_EMPTY( pxOverflowTimerList );
                }
                // 阻塞挂起直到 : 下一个定时器溢出 或 新命令消息
                // 下面这个queue函数是内核专用, 调用后不会直接阻塞,但是会把任务加入到阻塞链表中
                vQueueWaitForMessageRestricted( xTimerQueue, 
                        ( xNextExpireTime - xTimeNow ), /*转换阻塞时间*/
                        xListWasEmpty );

                if( xTaskResumeAll() == pdFALSE )
                {
                    // 触发任务切换
                    portYIELD_WITHIN_API();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
        else
        {
            // 恢复调度
            ( void ) xTaskResumeAll();
        }
    }
}

处理节拍计数器溢出

上面提到, 通过函数 prvSampleTimeNow判断节拍计数器是否发发生溢出, 并执行相应处理, 此处看看该函数内容 :

static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched )
{
    TickType_t xTimeNow;
    // 静态变量 记录上一次调用时系统节拍值
    PRIVILEGED_DATA static TickType_t xLastTime = ( TickType_t ) 0U;
    // 获取本次调用节拍结束器值
    xTimeNow = xTaskGetTickCount();

    // 判断节拍计数器是否溢出过
    // 比如 8bit : 0xFF+1 -> 0
    if( xTimeNow < xLastTime )
    {
        // 发生溢出, 处理当前链表上所有定时器并切换管理链表
        prvSwitchTimerLists();
        *pxTimerListsWereSwitched = pdTRUE;
    }
    else
    {
        *pxTimerListsWereSwitched = pdFALSE;
    }
    // 更新记录
    xLastTime = xTimeNow;
    return xTimeNow;
}

可以看到, 该函数每次调用都会记录节拍值, 下一次调用,通过比较相邻两次调用的值判断节拍计数器是否溢出过。
当节拍计数器溢出, 需要处理掉当前链表上的定时器(应为这条链表上的定时器都已经溢出了), 然后切换链表。

对于处理这部分任务的函数, 主要要注意其对于需要重载的定时器的处理 :

类比一下 , 一个自动重载的定时器, 每月需要执行一次, 上次调用是2016 年6月, 之后由于优先级问题,导致下一次调用时间等到第二年2017年 1月了,也就是跨年了(节拍计数器溢出了), 切换日历(链表)前, 需要把旧的先处理掉, 那么实际该定时器在2016年 7~ 12月每月都需要执行一次,所以要补偿回来,直到第二年1月, 才发送消息,插到新日历里面(链表)。

即使时间延迟了,但是该调用几次,是保证的!!

static void prvSwitchTimerLists( void )
{
    TickType_t xNextExpireTime, xReloadTime;
    List_t *pxTemp;
    Timer_t *pxTimer;
    BaseType_t xResult;

    // 切换链表前, 需要先处理当前链表上的所有执行定时器
    while( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE )
    {
        // 获取第一个定时器溢出时间
        xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );
        // 取出定时器并从链表移除
        pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList );
        ( void ) uxListRemove( &( pxTimer->xTimerListItem ) );
        traceTIMER_EXPIRED( pxTimer );
        // 执行定时器回调函数
        pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );

        // 对于自动重载的定时器 计算下一次溢出时间
        if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE )
        {
            // 如果重载后定时器的时间没有溢出, 还在当前链表范围内, 继续插回到当前链表
            // 保证执行的次数
            xReloadTime = ( xNextExpireTime + pxTimer->xTimerPeriodInTicks );
            if( xReloadTime > xNextExpireTime )
            {
                // 设置下一次溢出时间
                listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xReloadTime );
                listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer );
                vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) );
            }
            else
            {
                // 重载后定时器的时间同节拍计数器一样溢出了
                // 需要插入到新的链表中, 通过消息发送
                // 等到处理消息时,链表已经切换了
                xResult = xTimerGenericCommand( 
                    pxTimer, 
                    tmrCOMMAND_START_DONT_TRACE, 
                    xNextExpireTime, 
                    NULL, 
                    tmrNO_DELAY );

                configASSERT( xResult );
                ( void ) xResult;
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }

    // 切换链表
    pxTemp = pxCurrentTimerList;
    pxCurrentTimerList = pxOverflowTimerList;
    pxOverflowTimerList = pxTemp;
}

函数 prvProcessTimerOrBlockTask 中, 当节拍计数器没有溢出, 判断当前管理链表上溢出定时器并进行处理的函数 prvProcessExpiredTimer 整体和上面介绍差别不大, 执行函数回调, 判断是否需要重载等。

命令处理

用户将需要处理的定时器命令发送到定时器的消息队列, Daemon 任务每次执行期间回去读取并执行, 这部分工作有任务主体中的函数 prvProcessReceivedCommands完成, 下面看看这个函数如何实现, 对应平时使用定时器控制函数更加有底。
以下代码做了简化

static void prvProcessReceivedCommands( void )
{
    DaemonTaskMessage_t xMessage;
    Timer_t *pxTimer;
    BaseType_t xTimerListsWereSwitched, xResult;
    TickType_t xTimeNow;

    while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL )
    {
        #if ( INCLUDE_xTimerPendFunctionCall == 1 )
        // 延期执行函数命令
        // 执行注册的函数  
        #endif

        // 定时器命令消息
        if( xMessage.xMessageID >= ( BaseType_t ) 0 )
        {
            // 命令处理的定时器
            pxTimer = xMessage.u.xTimerParameters.pxTimer;

            if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE )
            {
                // 如果定时器已经在链表中, 不管37 21, 移除
                ( void ) uxListRemove( &( pxTimer->xTimerListItem ) );
            }

            // 判断节拍计数器是否溢出过 处理 切换
            // 因为下面可能有新项插入 确保链表对应 
            xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );

            switch( xMessage.xMessageID )
            {
                case tmrCOMMAND_START :
                case tmrCOMMAND_START_FROM_ISR :
                case tmrCOMMAND_RESET :
                case tmrCOMMAND_RESET_FROM_ISR :
                case tmrCOMMAND_START_DONT_TRACE :
                    // 以上 ,都是让定时器跑起来
                    // 设置定时器溢出时间并插到链表中
                    if( prvInsertTimerInActiveList( pxTimer,
                        xMessage.u.xTimerParameters.xMessageValue +
                        pxTimer->xTimerPeriodInTicks, xTimeNow,
                         xMessage.u.xTimerParameters.xMessageValue ) 
                         != pdFALSE )
                    {
                        // 处理定时器慢了, 该定时器已经溢出
                        // 赶紧执行其回调就看看函数
                        pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );
                        // 重载定时器 重新启动
                        if( pxTimer->uxAutoReload 
                            == ( UBaseType_t ) pdTRUE )
                        {
                            xResult = xTimerGenericCommand( pxTimer,
                                 tmrCOMMAND_START_DONT_TRACE,
                                  xMessage.u.xTimerParameters.xMessageValue+pxTimer->xTimerPeriodInTicks,
                                  NULL, tmrNO_DELAY );
                            configASSERT( xResult );
                            ( void ) xResult;
                        }
                    }
                    break;

                case tmrCOMMAND_STOP :
                case tmrCOMMAND_STOP_FROM_ISR :
                    // 停止定时器 开头已经从链表移除
                    // 不需要做其他
                    break;

                case tmrCOMMAND_CHANGE_PERIOD :
                case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR :
                    // 更新定时器配置
                    pxTimer->xTimerPeriodInTicks =
                         xMessage.u.xTimerParameters.xMessageValue;
                    // 插入到管理链表 也就启动了定时器
                    ( void ) prvInsertTimerInActiveList( pxTimer,
                         ( xTimeNow + pxTimer->xTimerPeriodInTicks ),
                         xTimeNow, xTimeNow );
                    break;
                case tmrCOMMAND_DELETE :
                    // 删除定时器
                    // 判断定时器内存是否需要释放(动态的释放)
                    break;
                default :
                    /* Don't expect to get here. */
                    break;
            }
        }
    }
}

函数处理定时器,开头不管后面命令是什么,如果定时器原本在运行, 直接移除。


参考

  • FreeRtos timer
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS 软定时器实现 的相关文章

  • FreeRTOS系列

    1 多任务系统 1 1 前后台系统 单片机裸机开发时 一般都是在main函数里面用while 1 做一个大循环来完成所有的处理 循环中调用相应的函数完成所需的处理 有时也需要在中断中完成一些处理 相对于多任务系统而言 这就是单人单任务系统也
  • FreeRTOS快速上手

    FreeRTOS使用 一 源码下载和移植文件提取 1 1 源码下载 在网站https sourceforge net projects freertos 可以找到freertos最新的源码 1 2 移植文件提取 根据第一步 我们会得到一个f
  • 【FreeRTOS 信号量】互斥信号量

    互斥信号量与二值信号量类似 但是互斥信号量可以解决二值信号量出现的优先级翻转问题 解决办法就是优先级继承 普通互斥信号量创建及运行 参阅安富莱电子demo 互斥信号量句柄 static SemaphoreHandle t xMutex NU
  • FreeRTOS软件定时器创建、复位、开始和停止(备忘)

    目录 一 简介 1 1 开发环境 1 2 摘要 二 STM32CubeIDE配置 三 创建定时器 3 1 头文件声明 3 2 工程文件定义 3 3 创建定时器 3 4 开启 复位 和关闭定时器 四 定时器回调函数 一 简介 1 1 开发环境
  • 解决错误“ #error “include FreeRTOS.h“ must appear in source files before “include event_groups.““例子分享

    今天来给大家分享一下 关于之前自己在学习FreeRTOS过程中遇到的一个错误提示 话不多说 我们直接来看 错误分析 首先 我们看一下错误的提示 error 35 error directive include FreeRTOS h must
  • FreeRTOS学习笔记(3、信号量、互斥量的使用)

    FreeRTOS学习笔记 3 信号量 互斥量的使用 前言 往期学习笔记链接 学习工程 信号量 semaphore 两种信号量的对比 信号量的使用 1 创建信号量 2 give 3 take 4 删除信号量 使用计数型信号量实现同步功能 使用
  • Freertos中vTaskDelay()是怎么用的

    1 常见的使用场景 void vLED Task void pvParameters while 1 Heartbeat LED vTaskDelay 1000 portTICK RATE MS 说明 上面这段代码的意思是 led翻转后经过
  • freeRTOS手册 第六章 . 中断管理

    如果我对本翻译内容享有所有权 允许任何人复制使用本文章 不会收取任何费用 如有平台向你收取费用与本人无任何关系 第六章 中断管理 章节介绍和范围 事件 嵌入式实时系统必需对环境中的事件做出响应 比如 外部网络设备收到一个发送给TCP IP栈
  • FreeRTOS+CubeMX系列第一篇——初识FreeRTOS

    文章目录 一 关于FreeRTOS 二 FreeRTOS的特点 三 如何在CubeMX上配置FreeRTOS 四 FreeRTOS文档资料 五 同系列博客 一 关于FreeRTOS 1 什么是FreeRTOS FreeRTOS是一个迷你的实
  • FreeRTOS临界区

    FreeRTOS临界区是指那些必须完整运行 不能被打断的代码段 比如有的外设的初始化需要严格的时序 初始化过程中不能被打断 FreeRTOS 在进入临界区代码的时候需要关闭中断 当处理完临界区代码以后再打开中断 FreeRTOS 系统本身就
  • FreeRTOS临界段和开关中断

    http blog sina com cn s blog 98ee3a930102wg5u html 本章教程为大家讲解两个重要的概念 FreeRTOS的临界段和开关中断 本章教程配套的例子含Cortex M3内核的STM32F103和Co
  • FreeRTOS,串口中断接收中使用xQueueOverwriteFromISR()函数,程序卡死在configASSERT

    原因 UART的中断优先级设置的太高 高于了configMAX SYSCALL INTERRUPT PRIORITY宏定义的安全中断等级 UART的中断等级小于等于宏定义的优先等级即可
  • 基于STM32的FreeRTOS学习之中断测试实验(五)

    记录一下 方便以后翻阅 本章内容是接着上一章节进行的实际演练 1 实验目的 FreeRTOS可以屏蔽优先级低于configMAX SYSCALL INTERRUPT PRIORITY的中断 不会屏蔽高于其的中断 本次实验就是验证这个说法 本
  • FreeRTOS死机原因

    1 中断回调函数中没有使用中断级API xxFromISR 函数 xSemaphoreGiveFromISR uart busy HighterTask 正确 xSemaphoreGive uart busy 错误 2 比configMAX
  • freeRTOS出现任务卡死的情况。

    最近在做一个产品二代升级的项目 代码是上一任工程师留下的 很多BUG 而且融合了HAL库和LL库 以及github上下载的GSM源码 很不好用 我这边是将2G模块换成了4G 且添加了单独的BLE模块 因此只在源码的基础上 去除2G和BLE代
  • 再论FreeRTOS中的configTOTAL_HEAP_SIZE

    关于任务栈和系统栈的基础知识 可以参考之前的随笔 FreeRTOS 任务栈大小确定及其溢出检测 这里再次说明 define configTOTAL HEAP SIZE size t 17 1024 这个宏 官方文档解释 configTOTA
  • 当一个任务写入变量而其他任务读取该变量时,我们是否需要信号量?

    我正在研究 freeRtos 并且我有一个名为 x 的变量 现在 每秒只有一个任务正在写入该变量 而其他任务正在读取该变量值 我需要用互斥锁来保护变量吗 如果变量为 32 位或更小 并且其值是独立的并且不与任何其他变量一起解释 则不需要互斥
  • 如何更改 FreeRTOS 中任务的最大可用堆大小?

    我通过以下方式在任务中创建元素列表 l dllist pvPortMalloc sizeof dllist dlllist 有 32 字节大 我的嵌入式系统有 60kB SRAM 所以我希望系统可以轻松处理我的 200 个元素列表 我发现在
  • 有可用的 FreeRTOS 解释语言库吗?

    我在一家公司工作 该公司使用 FreeRTOS 为多个设备创建固件 最近 我们对新功能的要求已经超出了我们固件工程师的工作能力 但我们现在也无力雇用任何新人 即使进行微小的更改 也需要固件人员在非常低的级别上进行修改 我一直在为 FreeR
  • 小型 ARM 微控制器的 RTOS 内核之间的可量化差异 [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 有许多不同的 RTOS 可用于微控制器 我专门寻找支持 ARM Cortex M 处理器的 RTOS 另外 我对闭源解决方案不感兴趣 试图从网站

随机推荐

  • JS 闭包 详细说明(教程)

    首先 xff0c 闭包是一个高阶函数 xff0c 高阶函数除了可以接受函数作为参数外 xff0c 还可以把函数作为结果值返回 xff08 不知道高阶函数是啥 xff0c 点击链接 xff09 先通过一个例子来了解一下如何把函数作为结果值返回
  • STVP烧录程序

    STVP介绍 STVD xff1a ST Visual Develop xff0c 可视化开发工具STVP xff1a ST Visual Programmer xff0c 可视化编程工具 xff0c stvp主要打开一些固件 hex s1
  • 传感器标定系列——Kalibr工具

    文章目录 Kalibr介绍安装资源 Kalibr介绍 Kalibr is a toolbox that solves the following calibration problems 多相机标定 Multi Camera Calibra
  • TCP传输图片及多线程

    最终效果图 xff1a 注意 xff1a 图片发送大体流程同 Qt网络编程 TCP IP 一 只是在发送时这里采用 的 数据流QDataStream形式 因为使用摄像进行监控时也是对一帧一帧的图片进 行处理 xff0c 因此掌握了如何用Qt
  • GCC编译过程及基本命令总结

    一 GCC简介 GCC即GNU Compiler Collection xff0c 原本只是针对C语言的编译工具 xff0c 现在已经变成了一个工具集 xff0c 包含了C C 43 43 JAVA等语言的集合体 管理和维护 xff1a 由
  • 第一章 PX4-Pixhawk-程序编译过程解析

    第一章 PX4程序编译过程解析 PX4 是一款软硬件开源的项目 xff0c 目的在于学习和研究 其中也有比较好的编程习惯 xff0c 大家不妨可以学习一下国外牛人的编程习惯 这个项目是苏黎世联邦理工大学的一个实验室搞出来的 该方案是基于 N
  • 第二章 PX4-Pixhawk-RCS启动文件解析

    第二章 PX4 RCS 启动文件解析 RCS 的启动类似于 linux 的 shell 文件 xff0c 如果不知道 shell 文件是什么东西可以理解成是为程序的流程框 xff0c 它是告诉处理器应该怎么样去运行 xff0c 前一章介绍的
  • 第三章 PX4-Pixhawk-SPI底层驱动解析

    第三章 PX4 SPI 底层驱动解析 这一章节我们会对 PX4 的底层驱动进行解析 xff0c 我们这里主要解析的是 SPI 协议 xff0c 因为这个协议是所有传感器的一个协议 xff0c 至于 IIC 和串口就可以类似的读写一下 xff
  • 第四章 PX4-Pixhawk-MPU6000传感器驱动解析

    第四章MPU6000传感器驱动解析 Mpu6000 是一个 3 轴加速度和 3 轴陀螺仪传感器 xff0c 这一章节我们将对 MPU6000 这个传感器进行解析 xff0c 依照这个解析步骤同样可以对 L3GD20 xff08 3 轴陀螺仪
  • 第五章 PX4-Pixhawk-GPS解析

    第五章 PX4 GPS解析 在上一章节我们对传感器MPU6000做了一个解析 xff0c MPU6000所支持的协议是SPI 这一章节我们来解析GPS xff0c GPS使用的是串口通信 这里我们着重讲解UBLOX的解析过程 xff0c 并
  • 第六章 PX4-Pixhawk-Sensors解析

    xfeff xfeff 第六章 PX4 Sensors 解析 这一章节并不难 xff0c 也很容易理解 xff0c 但是这一章节有几个函数需要我们去理解一下 xff0c 所以这里我们这里写一章来说明一下 Sensors 是所有传感器进行数据
  • 第七章 PX4-Pixhawk-Mavlink解析

    xfeff xfeff 第七章 PX4 Mavlink 解析 首先我们是还是来说一说 mavlink 吧 Mavlink 协议是无人机的一种开源通信协议 可以理解就是按照一定的格式来发送数据 这一章节涉及到了消息的打包发送和接收解析 首先我
  • STM8S 低功耗模式

    功耗控制的概念调整 功耗控制之硬件调整 在硬件体系中可以有以下优化手段 xff1a 优化电路设计优化电源供电效率 xff08 减少用于发热的能源 xff09 调节单片机时钟频率 xff08 频率越高功耗越高 xff09 选择单片机或者外围的
  • 第八章 PX4-Pixhawk-SDlog解析

    xfeff xfeff 第八章 PX4 SDlog 解析 这一章节我们对 SD 存储卡来进行解析 SD 卡涉及到 log 日志 xff0c 在很多飞行中 log 文件非常重要的 xff0c 尤其是新手在炸机过程中有了这些数据我们基本上就能分
  • 第九章 PX4-pixhawk-姿态估计解析

    第九章 PX4 pixhawk 姿态估计解析 这一章节我们对姿态估计进行解析 xff0c 这一章节涉及到算法 xff0c 主要涉及到的还是DCM 方向余弦 算法 首先我们还从启动文件开始进行讲解 我们找到rc mc apps中 xff0c
  • 第十章 PX4-Pixhawk-姿态控制

    第十章 PX4 Pixhawk 姿态控制 这一章节我们对姿态控制进行解析 xff0c 姿态解算我们还是从启动文件开始 xff0c 找到姿态解算的启动文件rc mc app 这里面找到姿态解算的启动项为mc att control start
  • 第十一章 PX4-Pixhawk-LPE位置估计

    xfeff xfeff 位置估计的解析我们还是遵循源代码的规则 xff0c 至于组合惯导和扩展卡尔曼我们以后慢慢分析 xff0c 这里主要还是对源代码来进行解析 在前一章节我们找到SYS MC EST GROUP参数设置的是1 xff0c
  • Prometheus + Grafana 接入实践

    文章目录 Prometheus 系统监控数据模型jobs安装 Grafana 可视化应用监控接入exporter 接入应用中集成 prometheus client 告警 Alertmanager参考 Prometheus 系统监控 Pro
  • docker 使用实践

    文章目录 准备环境安装运行配置环境使用镜像加速器修改 docker 目录翻墙设置代理限制容器 log 大小 操作命令基本命令容器网络网络模式容器连接外部外部连接容器容器互联 数据管理数据卷数据卷容器 挂载本机目录 镜像构建使用 docker
  • FreeRTOS 软定时器实现

    64 嵌入式 简述使用定时器 配置定时器服务任务创建 启动 停止定时器修改定时器获取定时器状态 定时器实现 数据结构 定时器控制块定时器管理链表命令队列 定时器服务任务 回调定时器处理节拍计数器溢出命令处理 参考 FreeRtos 简述 考