FreeRTOS全·详细

1,679 阅读17分钟

FreeROTS实时操作系统

官网地址:www.freertos.org/

一、介绍

1.1操作系统

操作系统是运行在计算机上的一个程序,用于管理硬件资源,并向其他程序提供服务。操作系统提供的服务可以使应用程序的开发变得更加快速、简单和容易维护。操作系统从整体上分为两大类:通用操作系统实时操作系统

1-1.png

实时操作系统(RTOS-Real Time Operating System)中实时(Real Time)指的是任务(Task)或者说实现一个功能的线程(Thread)必须在给定的时间(Deadline)内完成。

一个实时操作系统能尽力保障每个任务的运行时间在规定时间内完成,这包括

(1)对中断和内部异常的处理

(2)对安全相关的事件的处理

(3)任务调度机制等

目前比较流行的实时操作系统包括黑莓QNX,FreeRTOS,uCOS,RT-Thread等

1.2FreeRTOS特点

Ø 任务调度:FreeRTOS通过任务调度器管理多个任务,支持不同优先级的任务,实现任务的有序执行。

Ø 任务通信和同步:提供了队列、信号量等机制,支持任务之间的通信和同步,确保数据的安全传递。

Ø 内存管理:提供简单的内存管理机制,适用于嵌入式环境,有效利用有限的内存资源。

Ø 定时器和中断处理:支持定时器功能,能够处理中断,提供了可靠的实时性能。

Ø 开发社区:拥有庞大的用户社区,开发者可以在社区中获取支持、解决问题,并分享经验。

Ø 可移植性:设计注重可移植性,可以轻松地移植到不同的硬件平台,提高了代码的重用性。

1.3多任务处理

常规单核处理器同一时刻只能执行一个任务,操作系统可以使处理器在多个任务之间快速进行切换,从而令多个任务看起来像是在同时执行。多任务处理能够提高资源利用率。

1.4调度器

操作系统中负责切换任务的组件叫做调度器,其职责就是决定在每个时刻执行哪个任务,一个任务可以在其生命周期内多次暂停和恢复。

FreeROTS中调度器使用优先级调度+时间片轮询的调度策略

调度策略:

  • 时间片轮询:为每个任务分配一个固定的时间片,轮流执行。
  • 优先级调度:为每个任务分配一个优先级,根据任务的优先级进行调度,高优先级任务优先执行。

二、FreeROTS调度原理

2.1任务调度

FreeRTOS 默认使用固定优先级抢占式调度策略,对同等优先级的任务执行时间片轮询调度:

  • 抢占式调度:FreeRTOS采用抢占式调度方式,允许更高优先级的任务在任何时刻抢占正在执行的低优先级任务。这确保了高优先级任务能够及时响应,并提高了系统的实时性。
  • 时间片轮询:在相同优先级的任务之间,FreeRTOS采用时间片轮转策略。每个任务执行一个时间片,如果有其他同优先级的任务等待执行,则切换到下一个任务。这有助于公平地分配CPU时间。

2.2任务状态

FreeRTOS中任务共存在4种状态:

  • 运行态:当任务实际执行时,它处于运行状态。如果运行 RTOS 的处理器只有一个内核, 那么在任何给定时间内都只能有一个任务处于运行状态。
  • 就绪态:准备就绪任务指那些能够执行(它们不处于阻塞或挂起状态), 但目前没有执行的任务, 因为同等或更高优先级的不同任务已经处于运行状态。
  • 阻塞态:如果任务当前正在等待延时或外部事件,则该任务被认为处于阻塞状态。
  • 挂起态:类似暂停,调用函数 vTaskSuspend() 进入挂起态,需要调用解挂函数vTaskResume()才可以进入就绪态。

1-3.png

2.3任务切换

任务切换时机

SysTick给系统提供时钟,每次sysTick中断,FreeRTOS都会检查是否解除阻塞或唤醒任务。FreeRTOS会判断时间片用完是否需要切换任务,如果有,则会进行任务的上下文切换(Context Swtich)

上下文切换

上下文切换,就是将当前任务的CPU状态保存下来,再将新任务的CPU状态恢复回去。

  • 保存当前任务的寄存器值,包括程序计数器、堆栈指针等,保存在当前任务的任务控制块(TCB)中。
  • 加载新任务的寄存器值,恢复程序计数器和堆栈指针,准备执行新任务。

2.4空闲任务

RTOS调度器启动时,自动创建最低优先级的空闲任务。空闲任务负责释放被删除任务的内存。

三、FreeRTOS移植

3.1.复制必要文件

FreeRTOS下载地址:FreeRTOS™ - FreeRTOS™

1-4.png

  • Source文件夹中:

1-5.png

  • portable文件夹:

1-6.png

  • MemMang文件夹:

1-7.png

  • RVDS文件夹:

1-8.png

  • Demo文件夹:

    找到对应的芯片

1-9.png

> 需要在FreeRTOSConfig.h中额外添加一些配置:
>
> ```
>  #define xPortPendSVHandler PendSV_Handler
>  #define vPortSVCHandler SVC_Handler
>  #define INCLUDE_xTaskGetSchedulerState 1
> ```

所有需要的文件:

1-10.png

3.2.启动FreeRTOS

先配置3个中断

在port.中三个中断函数已经写好了,分别是vPortSVCHandler()xPortPendSVHandler()xPortSysTickHandler(),所以只需要替换或调用就可以。

  1. 在中断处理文件中删除PendSV_Handler()SVC_Handler()方法(FreeRTOSConfig.h中的配置已经替换好了)

    PendSV(Pendable Service Call)和 SVC(Supervisor Call)是 ARM Cortex-M 系列处理器中两种异常(中断)类型

    • SVC是系统调用,用于从用户态切换到内核态。允许用户程序调用内核功能
    • PendSV是由操作系统内核触发,用于任务上下文切换。
     // void SVC_Handler(void)
     // {
     //   /* USER CODE BEGIN SVCall_IRQn 0 */
     ​
     //   /* USER CODE END SVCall_IRQn 0 */
     //   /* USER CODE BEGIN SVCall_IRQn 1 */
     ​
     //   /* USER CODE END SVCall_IRQn 1 */
     // }
     ​
     // void PendSV_Handler(void)
     // {
     //   /* USER CODE BEGIN PendSV_IRQn 0 */
     //   xPortPendSVHandler();
     //   /* USER CODE END PendSV_IRQn 0 */
     //   /* USER CODE BEGIN PendSV_IRQn 1 */
     ​
     //   /* USER CODE END PendSV_IRQn 1 */
     // }
    
  2. SysTick_Handler

 void SysTick_Handler(void)
 {
   /* USER CODE BEGIN SysTick_IRQn 0 */
   if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)//判断调度器状态(挂起、不运行、运行)
   {
     xPortSysTickHandler();//调用写好的中断函数
   }
   /* USER CODE END SysTick_IRQn 0 */
 ​
   /* USER CODE BEGIN SysTick_IRQn 1 */
 ​
   /* USER CODE END SysTick_IRQn 1 */
 }
启动FreeRTOS

FreeRTOS_demo.h

 #ifndef __FREERTOS_DEMO__
 #define __FREERTOS_DEMO__
 ​
 #include "FreeRTOS.h"
 #include "task.h"
 ​
 void FreeRTOS_Start(void);
 ​
 ​
 #endif // __FREERTOS_DEMO__
 ​

FreeRTOS_demo.c

 #include "FreeRTOS_demo.h"
 ​
 void FreeRTOS_Start(void)
 {
     vTaskStartScheduler();
 }

main.c

 #include "main.h"
 #include "FreeRTOS_demo.h"
 ​
 int main(void)
 {
   .......
   FreeRTOS_Start();
   ......
   while (1)
   {
   }
 ​
 }

四、任务状态API

4.1创建任务

  • xTaskCreate动态创建任务。需要configSUPPORT_DYNAMIC_ALLOCATION配置为1

      BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
                              const char * const pcName,
                              const configSTACK_DEPTH_TYPE uxStackDepth,
                              void *pvParameters,
                              UBaseType_t uxPriority,
                              TaskHandle_t *pxCreatedTask
                            );
    

    参数:

    • pvTaskCode:任务函数指针
    • pcName:任务名称
    • uxStackDepth:栈空间大小
    • pvParameters:任务函数的参数
    • uxPriority:任务优先级
    • pxCreatedTask:任务句柄(任务标识)

    返回值:

    • 创建成功为pdPass
    • 否则返回 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY

    示例:

     #include "FreeRTOS_demo.h"
     ​
     /* 任务1 */
     void task1(void *pvParameters);
     #define task1_STACK_SIZE 128 //128 *4 =512字节
     #define task1_PRIORITY 4
     TaskHandle_t task1_handler;
     ​
     void FreeRTOS_Start(void)
     {
         xTaskCreate(task1, "task1", task1_STACK_SIZE, NULL, task1_PRIORITY, &task1_handler);
     ​
         vTaskStartScheduler();
     }
     ​
     void task1(void *pvParameters)
     {
         while (1)
         {
             HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
             printf("1-toggle\n");
             vTaskDelay(1000);
         }
     }
    
  • xTaskCreateStatic静态创建任务。需要configSUPPORT_STATIC_ALLOCATION为1

     TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
                                      const char * const pcName,
                                      const uint32_t ulStackDepth,
                                      void * const pvParameters,
                                      UBaseType_t uxPriority,
                                      StackType_t * const puxStackBuffer,
                                      StaticTask_t * const pxTaskBuffer );
     ​
    

    参数:

    • pvTaskCode:任务函数指针
    • pcName:任务名称
    • uxStackDepth:栈空间大小
    • pvParameters:任务函数的参数
    • uxPriority:任务优先级
    • pxCreatedTask:任务句柄(任务标识)
    • puxStackBuffer:任务堆栈
    • pxTaskBuffer:保存任务TCB的存储位置(TCB用于管理和保存任务所有的信息)

    返回值:

    • 任务句柄

    静态创建任务时,空闲任务也需要提前指定任务堆栈和TCB存储位置

4.2删除任务

  • vTaskDelete。需要INCLUDE_vTaskDelete配置为1

     void vTaskDelete( TaskHandle_t xTask );
    

    参数:

    • xTask:任务句柄(写NULL则表示删除正在执行的任务自己)

4.3任务挂起和恢复

  • vTaskSuspend。需要INCLUDE_vTaskSuspend配置为1

     void vTaskSuspend( TaskHandle_t xTaskToSuspend );
    

    参数:

    • xTaskToSuspend:任务句柄(NULL表示挂起自己)
  • vTaskResume。需要INCLUDE_vTaskSuspend配置为1

     void vTaskResume( TaskHandle_t xTaskToResume );
    

    参数:

    • xTaskToResume:要恢复的任务句柄

4.4任务延时

  • vTaskDelay延时指定的时间。需要INCLUDE_vTaskDelay配置为1

     void vTaskDelay( const TickType_t xTicksToDelay );
    

    参数:

    • xTicksToDelay:延时的时间

    configTICK_RATE_HZ通常配置为1000hz,所以systick中断一次为1ms,延时的单位也就是1ms

  • vTaskDelayUntil延时指定的时间。需要INCLUDE_vTaskDelayUntil为1

     void vTaskDelayUntil( TickType_t *pxPreviousWakeTime,
                           const TickType_t xTimeIncrement );
    

    参数:

    • pxPreviousWakeTime:初始时间
    • xTimeIncrement:增加指定的时间

    示例:

     void task1(void *pvParameters)
     {
         while (1)
         {
             TickType_t tick =  xTaskGetTickCount();
             Int_LED_Toggle(LED2_GPIO_Port, LED2_Pin);
             vTaskDelayUntil(&tick,500);
         }
     }
    

4.5挂起/恢复调度器

  • vTaskSuspendAll

     void vTaskSuspendAll( void );
    

    挂起调度器会阻止上下文切换, 但会让中断处于启用状态。

  • xTaskResumeAll

     BaseType_t xTaskResumeAll( void );
    

    返回值:

    • 如果恢复调度器导致了上下文切换,则返回 pdTRUE,否则返回 pdFALSE。

4.6进/出临界区

  • taskENTER_CRITICAL

     void taskENTER_CRITICAL( void );
    

    进入临界区会屏蔽中断。也就是将systick中断屏蔽,进而无法调度其他任务。

    configMAX_SYSCALL_INTERRUPT_PRIORITY配置屏蔽中断优先级

    示例:

     #define configMAX_SYSCALL_INTERRUPT_PRIORITY 191//屏蔽优先级小于11的
    

    191的二进制是1011 1111,其中1011就是优先级11

  • taskEXIT_CRITICAL

     void taskEXIT_CRITICAL( void );
    

五、其他实用API

uxTaskGetSystemState

  • uxTaskGetSystemState获取所有任务信息。需要configUSE_TRACE_FACILITY为1

     UBaseType_t uxTaskGetSystemState(
                            TaskStatus_t * const pxTaskStatusArray,
                            const UBaseType_t uxArraySize,
                            unsigned long * const pulTotalRunTime );
    

    参数:

    • pxTaskStatusArray:TaskStatus_t类型的数组(TaskStatus_t结构体保存一个任务的各种信息)
    • uxArraySize:数组大小
    • pulTotalRunTime:总运行时间

    返回值:

    • 实际任务的数量,如果在 uxArraySize参数中传递的值太小,则该数量将为零。

    示例:

     /* 获取系统状态(所有任务信息) */
     TaskStatus_t pxTaskStatusArray[6];
     uint32_t ulTotalRunTime;
     ​
     uxTaskGetSystemState(pxTaskStatusArray, 6, &ulTotalRunTime);
     ​
     printf("total run time: %d\n", ulTotalRunTime);
     for (uint8_t i = 0; i < uxTaskGetNumberOfTasks(); i++)
     {
         printf("task name: %s\n", pxTaskStatusArray[i].pcTaskName);
         printf("task priority: %d\n", pxTaskStatusArray[i].uxCurrentPriority);
         printf("task state: %d\n", pxTaskStatusArray[i].eCurrentState);
         printf("task stack size: %d\n", pxTaskStatusArray[i].usStackHighWaterMark);
         printf("task run time: %d\n\n", pxTaskStatusArray[i].ulRunTimeCounter);
     }
    

    配置时间统计的定时器

    要成功获取任务运行时间ulRunTimeCounter,需要先配置一个定时器。

    • 配置一个10us一次中断的定时器

    • 配置中断处理函数

       uint32_t tim6_tick;
       void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
       {
         if (htim->Instance == TIM6)
         {
           tim6_tick++;
         }
       }
      
    • FreeRTOSConfig.h添加配置

       extern uint32_t tim6_tick;
       #define configGENERATE_RUN_TIME_STATS 1
       #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() HAL_TIM_Base_Start_IT(&htim6)
       #define portGET_RUN_TIME_COUNTER_VALUE() tim6_tick
      

vTaskList

  • vTaskList获取任务所有信息(字符串)。需要configUSE_TRACE_FACILITYconfigUSE_STATS_FORMATTING_FUNCTIONS为1

     void vTaskList( char *pcWriteBuffer );
    

    参数:

    • pcWriteBuffer:接收的字符串

    1-11.png

    分别是:任务名称、运行状态、最小栈空间、编号(没啥用)

    • 'B' - 已阻塞
    • 'R' - 准备就绪
    • 'D' - 已删除(等待清理)
    • 'S' - 已挂起或已阻塞,没有超时
    • 'X' - 运行中

vTaskGetInfo

  • vTaskGetInfo获取单个任务信息。需要configUSE_TRACE_FACILITY为1

     void vTaskGetInfo( TaskHandle_t xTask,
                        TaskStatus_t *pxTaskStatus,
                        BaseType_t xGetFreeStackSpace,
                        eTaskState eState );
    

    参数:

    • xTask:任务句柄
    • pxTaskStatus:接收的任务信息结构体
    • xGetFreeStackSpace:写pdTrue表示接收最小栈空间、pdFalse表示不
    • eState:写eInvalid表示获取任务状体信息

vTaskGetRunTimeStats

  • vTaskGetRunTimeStats统计所有任务运行时间。↑配置时间统计的定时器

     void vTaskGetRunTimeStats( char *pcWriteBuffer );
    

    参数:

    • pcWriteBuffer:接收字符串

uxTaskGetStackHighWaterMark

  • uxTaskGetStackHighWaterMark获取最小剩余栈空间。需要INCLUDE_uxTaskGetStackHighWaterMark为1

     UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
    

    参数:

    • xTask:任务句柄

    返回值:

    • 剩余最小栈空间(32位上1表示4个字节)

eTaskGetState

  • eTaskGetState获取任务所处状态。需要INCLUDE_eTaskGetState为1

     eTaskState eTaskGetState( TaskHandle_t xTask );
    

    参数:

    • xTask:任务句柄

    返回值:

    状态返回值
    准备就绪eReady
    运行eRunning(调用任务正在查询自己的优先级)
    已阻塞eBlocked
    已挂起eSuspended
    已删除eDeleted(任务 TCB 正在等待清理)

xTaskGetHandle

  • xTaskGetHandle根据名称获取句柄。需要INCLUDE_xTaskGetHandle为1

     TaskHandle_t xTaskGetHandle( const char *pcNameToQuery );
    

    参数:

    • pcNameToQuery:任务名称

    返回值:

    • 任务句柄

xTaskGetCurrentTaskHandle

  • xTaskGetCurrentTaskHandle获取当前任务句柄。需要INCLUDE_xTaskGetCurrentTaskHandle为1

     TaskHandle_t xTaskGetCurrentTaskHandle( void );
    

    返回值:

    • 任务句柄

uxTaskGetNumberOfTasks

  • uxTaskGetNumberOfTasks获取所有任务的数量。

     UBaseType_t uxTaskGetNumberOfTasks( void );
    

    返回值:

    • 任务数量

uxTaskPriorityGet

  • uxTaskPriorityGet获取优先级。需要INCLUDE_uxTaskPriorityGet为1

     UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );
    

    参数:

    • xTask:任务句柄

    返回值:

    • 任务的优先级

vTaskPrioritySet

  • vTaskPrioritySet设置任务优先级。需要INCLUDE_vTaskPrioritySet为1

     void vTaskPrioritySet( TaskHandle_t xTask,
                            UBaseType_t uxNewPriority );
    

    参数:

    • xTask:任务句柄
    • uxNewPriority:新优先级

六、队列和队列集

队列

  • xQueueCreate创建队列。

      QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
                                  UBaseType_t uxItemSize );
    

    参数:

    • uxQueueLength:队列长度
    • uxItemSize:队列元素大小

    返回值:

    • 队列句柄
  • xQueueSend加入队列。

      BaseType_t xQueueSend(
                             QueueHandle_t xQueue,
                             const void * pvItemToQueue,
                             TickType_t xTicksToWait
                           );
    

    参数:

    • xQueue:队列句柄
    • pvItemToQueue:加入的元素指针
    • xTicksToWait:超时时间

    返回值:

    • 成功为pdTRUE
    • 失败为errQUEUE_FULL
  • xQueueReceive接收。

     BaseType_t xQueueReceive(
                               QueueHandle_t xQueue,
                               void *pvBuffer,
                               TickType_t xTicksToWait
     );
    

    参数:

    • xQueue:队列句柄
    • pvBuffer:接收缓冲
    • xTicksToWait:超时时间

    返回值:

    • 接收成功为pdTRUE
    • 接收失败为pdFALSE

队列集

  • xQueueCreateSet创建队列集。

     QueueSetHandle_t xQueueCreateSet(const UBaseType_t uxEventQueueLength);
    

    参数:

    • uxEventQueueLength:队列长度

    返回值:

    • 队列及句柄
  • xQueueAddToSet添加队列到队列集。

      BaseType_t xQueueAddToSet
                           (
                               QueueSetMemberHandle_t xQueueOrSemaphore,
                               QueueSetHandle_t xQueueSet
                           );
    

    参数:

    • xQueueOrSemaphore:一个队列句柄或信号量句柄
    • xQueueSet:队列集句柄

    返回值:

    • 成功添加返回:pdPASS
    • 失败返回:pdFAIL
  • xQueueSelectFromSet从队列集中选择可用的队列。需要configUSE_QUEUE_SETS 为1

      QueueSetMemberHandle_t xQueueSelectFromSet
                            (
                                  QueueSetHandle_t xQueueSet,
                                  const TickType_t xTicksToWait
                             );
    

    参数:

    • xQueueSet:队列集
    • xTicksToWait:超时时间

    返回值:

    • 返回可用的队列句柄

七、信号量

  • xSemaphoreCreateBinary创建二值信号量。

     SemaphoreHandle_t xSemaphoreCreateBinary( void );
    

    返回值:

    • 信号量句柄
  • xSemaphoreTake获取信号量。

      xSemaphoreTake( SemaphoreHandle_t xSemaphore,
                      TickType_t xTicksToWait );
    

    参数:

    • xSemaphore:信号量句柄
    • xTicksToWait:超时时间
  • xSemaphoreGive释放信号量。

     xSemaphoreGive( SemaphoreHandle_t xSemaphore );
    

    参数:

    • xSemaphore信号量句柄

八、事件标志组

  • xEventGroupCreate

      EventGroupHandle_t xEventGroupCreate( void );
    

    返回值:

    • 事件标志组句柄
  • xEventGroupSetBits设置位。

     EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
                                     const EventBits_t uxBitsToSet );
    

    参数:

    • xEventGroup:句柄
    • uxBitsToSet:设置的位。例如,设置为 0x09,则只设置第 3 位和 0 位。

    返回值:

    • 返回事件组的值。
  • xEventGroupWaitBits等待设置单个位或一组位。

     EventBits_t xEventGroupWaitBits(
                           const EventGroupHandle_t xEventGroup,
                           const EventBits_t uxBitsToWaitFor,
                           const BaseType_t xClearOnExit,
                           const BaseType_t xWaitForAllBits,
                           TickType_t xTicksToWait );
    

    参数:

    • xEventGroup:事件组句柄
    • uxBitsToWaitFor:等待的位值。例如,等待第 3 位和 0 位就设置为 0x09。不能设置为0
    • xClearOnExit:pdTRUE表示函数返回时,uxBitsToWaitFor参数的位值清除。pdFALSE表示不清除。
    • xWaitForAllBits:pdTRUE表示uxBitsToWaitFor参数的所有位都为1才解除阻塞。pdFALSE表示任一位为1就解除阻塞。
    • xTicksToWait:超时时间
  • xEventGroupClearBits清除位

     EventBits_t xEventGroupClearBits(
                                       EventGroupHandle_t xEventGroup,
                                       const EventBits_t uxBitsToClear );
    

    参数:

    • xEventGroup:句柄
    • uxBitsToClear:要清除的位

九、任务通知

任务通知是FreeRTOS中一种用于任务间通信的机制,可以向任务发送通知或信号,以实现任务间的同步。任务通知用于替代信号量和事件标志组,提供了更轻量级的任务间通信方式。

每个任务在创建时都会带有一个通知数组,数组中的每个元素可用于存储一个通知信息,一个元素占用5个字节,其中1个字节用来存储通知状态,其余4个字节(32 bit)用于存储通知值。数组长度可通过configTASK_NOTIFICATION_ARRAY_ENTRIES参数进行设置,默认值为1。

  • xTaskNotify通知

      BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,
                              uint32_t ulValue,
                              eNotifyAction eAction );
    

    参数:

    • xTaskToNotify:要通知的任务(句柄)

    • ulValue:通知值

    • eAction

      • eNoAction

        目标通知值不会更新。在这种情况下ulValue没用。

      • eSetBits

        目标任务的通知值将与 ulValue 进行按位“或”操作。例如,如果 ulValue 设置为 0x01,则目标任务通知值中的第 0 位将被设置1。

      • eIncrement

        目标任务的通知值将增加 1,当于调用xTaskNotifyGive()。在这种情况下ulValue没用。

      • eSetValueWithOverwrite

        目标任务的通知值直接设置为 ulValue。

      • eSetValueWithoutOrwrite

        如果目标任务当前没有通知,则其通知值将设置为 ulValue。

        如果目标任务有通知,则不覆盖,并返回一个pdFALSE。

    返回值:

    • eAction 设置为 eSetValueWithoutOverwrite 才会返回pdFALSE
    • 其他情况都返回pdTRUE
  • xTaskNotifyWait等待通知

      BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,
                                  uint32_t ulBitsToClearOnExit,
                                  uint32_t *pulNotificationValue,
                                  TickType_t xTicksToWait );
    

    参数:

    • ulBitsToClearOnEntry:进入函数前清除对应位。例如,设为 0x01, 则任务通知值中的第 0 位将在进入函数时被清除。
    • ulBitsToClearOnExit:退出函数时清除对应位。
    • pulNotificationValue:接收的通知值
    • xTicksToWait:超时时间

    返回值:

    • 只有超时会返回pdFALSE
  • xTaskNotifyGive

     BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
    

    等价于: xTaskNotify(xTaskToNotify,0,eIncrement);通知值+1

  • ulTaskNotifyTake

     uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,
                                TickType_t xTicksToWait );
    

    参数:

    • xClearCountOnExit:pdFLASE表示函数退出时,通知值-1。pdTRUE表示函数退出时,通知值清0
    • xTicksToWait:超时时间

    返回值:

    • 被递减或清除之前的任务通知值
  • xTaskNotifyStateClear清除通知状态

     BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask );
    
  • ulTaskNotifyValueClear清除通知值

     uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, 
                                      uint32_t ulBitsToClear );
    

    参数:

    • xTask:任务句柄
    • ulBitsToClear:清除的位

    返回值:

    • ulBitsToClear指定位清零前目标任务的通知值。

十、软件定时器

10.1简介

软件定时器是一个轻量级时间管理工具,基于FreeRTOS内核提供的时间(本质sysTick),能够在指定时间或固定周期执行某个函数。软件定时器由软件定时器服务任务管理,configUSE_TIMERS 设置为1,在开启任务调度器的时候,会自动创建软件定时器服务的任务。

定时器回调函数中不能有导致阻塞的API函数,例如使用信号量、队列、调用vTaskDelay。

10.2常用API

需要的配置:

 // 使用软件定时器
 #define configUSE_TIMERS 1
 //定时器服务任务优先级
 #define configTIMER_TASK_PRIORITY 4
 //定时器队列长度
 #define configTIMER_QUEUE_LENGTH 10
  • xTimerCreate

      TimerHandle_t xTimerCreate
                      ( const char * const pcTimerName,
                        const TickType_t xTimerPeriod,
                        const UBaseType_t uxAutoReload,
                        void * const pvTimerID,
                        TimerCallbackFunction_t pxCallbackFunction );
    

    参数:

    • pcTimerName:定时器名称
    • xTimerPeriod:定时器周期(以滴答为单位)
    • uxAutoReload:pdTRUE表示重复周期执行回调,pdFALSE表示一次性定时器
    • pvTimerID:定时器的标识符
    • pxCallbackFunction:回调函数

    返回值:

    • 定时器的句柄,创建不成功返回NULL
  • xTimerStart

      BaseType_t xTimerStart( TimerHandle_t xTimer,
                                 TickType_t xBlockTime );
    

    参数:

    • xTimer:定时器句柄
    • xBlockTime:调用任务处于阻塞状态以等待启动命令成功的时间

    返回值:

    • 如果能将此命令成功发送到定时器命令队列,则返回pdPASS
    • 超时返回pdFAIL
  • xTimerStop

      BaseType_t xTimerStop( TimerHandle_t xTimer,
                             TickType_t xBlockTime );
    

    参数:

    • xTimer:定时器句柄
    • xBlockTime:调用任务处于阻塞状态以等待停止命令成功的时间

    返回值:

    • 如果能将此命令成功发送到定时器命令队列,则返回pdPASS
    • 超时返回pdFAIL
  • xTimerChangePeriod更改周期,并启动定时器

      BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,
                                     TickType_t xNewPeriod,
                                     TickType_t xBlockTime );
    

    参数:

    • xTimer:定时器句柄
    • xNewPeriod:新周期时间
    • xBlockTime:超时时间

    返回值:

    • 成功位pdPASS,失败位pdFAIL

许多公共 FreeRTOS 定时器 API 函数通过定时器命令队列向定时器服务任务发送命令。

十一、低功耗模式

11.1简介

FreeRTOS 的 Tickless 模式是一种特殊的运行模式,用于最小化系统的时钟中断频率,以降低功耗。在 Tickless 模式下,系统只在有需要时才会启动时钟中断,而在无任务要运行时则完全进入休眠状态,从而降低功耗。在滴答中断重启时,会对 RTOS 滴答计数值进行校正调整。

Tickless低功耗模式通过调用指令 __WFI 实现睡眠模式,由于滴答定时器频繁中断则会影响低功耗,所以FreeRTOS的Tickless低功耗模式会自动把滴答定时器的中断周期修改为低功耗运行时间,退出低功耗后再补上系统时钟节拍数。

11.2相关参数

  • configUSE_TICKLESS_IDLE使能Tickless模式,默认0
  • configEXPECTED_IDLE_TIME_BEFORE_SLEEP进入低功耗最短时间,默认2
  • configPRE_SLEEP_PROCESSING(x)进入低功耗之前执行
  • configPOST_SLEEP_PROCESSING(x)退出低功耗后执行
 #define configUSE_TICKLESS_IDLE 1
 #include "FreeRTOS_demo.h"
 #define configPRE_SLEEP_PROCESSING(x) PRE_SLEEP_PROCESSING()
 #define configPOST_SLEEP_PROCESSING(x) POST_SLEEP_PROCESSING()