找回密码
 立即注册
首页 业界区 业界 04-FreeRTOS的概述及编程规范

04-FreeRTOS的概述及编程规范

姊囝 2025-10-6 10:44:06
概述

本文对FreeRTOS源码进行概述,包括其核心文件作用,及其编程规范,有助于阅读rtos的内核源码,更好的帮助理解。
一、FreeRTOS 源码核心结构概述

FreeRTOS 是轻量级实时操作系统,核心功能围绕 “任务调度” 和 “任务间通信” 展开,源码结构清晰,可分为内核核心文件可选组件
类别核心文件主要功能任务管理task.c任务创建(xTaskCreate())、删除、挂起 / 恢复,以及核心的调度器实现(vTaskStartScheduler())。调度器核心同上(调度逻辑嵌入task.c)基于优先级的抢占式调度(可配置为时间片轮转),上下文切换(portYIELD())。队列 / 通信queue.c/list.c实现队列(任务间数据传递)、信号量(SemaphoreHandle_t)、互斥锁(MutexHandle_t)等。时间管理timers.c软件定时器(xTimerCreate()),基于系统时基触发回调函数。配置文件FreeRTOSConfig.h内核裁剪配置(如任务最大优先级、栈大小、钩子函数使能等),是适配硬件的关键。硬件接口port.c/portmacro.h与 CPU 架构相关的底层实现(如 ARM Cortex-M 的上下文切换、中断处理),由芯片厂商适配。二、STM32CubeMX 生成的 RTOS 中间件文件及作用

CubeMX 会自动集成 FreeRTOS 源码,并生成适配 STM32 的配置文件初始化代码,核心文件如下(以 STM32 工程为例):

  • FreeRTOSConfig.h

    • 作用:FreeRTOS 的 “开关面板”,由 CubeMX 根据用户配置(如任务优先级数量、栈大小、是否启用互斥锁等)自动生成。
    • 关键配置项:

      • configMAX_PRIORITIES:最大任务优先级(如5,数值越大优先级越高);
      • configTOTAL_HEAP_SIZE:内核堆大小(任务、队列等动态内存从这里分配);
      • configUSE_PREEMPTION:是否启用抢占式调度(1启用,实时性核心);
      • configUSE_IDLE_HOOK:是否启用空闲任务钩子函数(自定义空闲任务行为)。


  • MX_FreeRTOS_Init.c/MX_FreeRTOS_Init.h

    • 作用:用户任务的初始化入口,CubeMX 会在此文件中生成任务创建代码(基于用户在 CubeMX 中配置的任务)。
    • 典型内容:
      1. void MX_FreeRTOS_Init(void) {
      2.   // 创建任务(参数:任务函数、名称、栈大小、参数、优先级、任务句柄)
      3.   xTaskCreate(StartDefaultTask, "DefaultTask", 128, NULL, 1, &DefaultTaskHandle);
      4.   // 若配置了其他任务(如Task1、Task2),会在此处继续创建
      5. }
      复制代码

  • stm32f1xx_it.c(中断服务函数文件)

    • 作用:集成 FreeRTOS 的中断适配,主要是系统时基中断(通常用 SysTick 定时器)。
    • 关键代码:
      1. void SysTick_Handler(void) {
      2.   HAL_IncTick();  // HAL库时基
      3.   if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
      4.     xPortSysTickHandler();  // 调用FreeRTOS的SysTick处理函数,用于任务调度计时
      5.   }
      6. }
      复制代码

  • FreeRTOS 官方源码文件
    CubeMX 会将task.c、queue.c、timers.c等核心文件复制到工程的Middlewares/Third_Party/FreeRTOS/Source目录下,与硬件无关的逻辑保持不变。
三、FreeRTOS 源码的 “入口函数”

FreeRTOS 的运行始于调度器启动,整体流程如下(结合 CubeMX 生成的代码):

  • 用户程序入口:main()函数
    CubeMX 生成的main.c中,main()先初始化硬件(HAL_Init()、时钟配置等),再调用MX_FreeRTOS_Init()创建任务,最后启动调度器:
    1. /* Init scheduler */
    2. osKernelInitialize();  /* 初始化FreeRTOS运行环境 */
    3. MX_FREERTOS_Init();    /* 创建任务 */
    4. /* Start scheduler */
    5. osKernelStart();       /* 启动调度器 里面调用vTaskStartScheduler()函数*/
    复制代码
  • 调度器启动:vTaskStartScheduler()
    位于task.c,是 FreeRTOS 内核真正开始工作的入口,主要做三件事:

    • 初始化内核数据结构(任务链表、就绪列表、堆内存等);
    • 创建空闲任务(prvIdleTask,优先级最低,用于释放删除的任务内存);
    • 触发第一次上下文切换(portRESTORE_CONTEXT()),切换到最高优先级的就绪任务执行。

  • 第一个执行的任务
    调度器启动后,会从 “就绪列表” 中选择优先级最高的任务执行。若用户在MX_FreeRTOS_Init()中创建了StartDefaultTask(默认任务),且它是最高优先级,则第一个执行的就是该任务。
四、FreeRTOS 源码的 “编程规则”

1、基础数据类型的前缀规则

FreeRTOS 不直接使用 C 语言原生类型(如int、long,因不同平台长度可能不同),而是通过自定义类型实现跨平台,这些类型的前缀有明确含义:
前缀含义对应原生类型(举例)用途场景uUnsigned(无符号)unsigned char、unsigned int无符号整数(如计数、长度)sSigned(有符号)signed char、signed int有符号整数(如差值、偏移量)x结构体 / 枚举 / 大型类型(自定义)无固定原生类型(如任务控制块、队列)复杂结构(任务、队列、事件组)pPointer(指针)任意类型的指针指向变量、数组、结构体的指针ulUnsigned Long(无符号长整数)unsigned long(通常 32 位)较大的无符号数值(如系统时间)lLong(有符号长整数)long(通常 32 位)较大的有符号数值2、典型组合示例(前缀 + 类型)

这些组合在源码中高频出现,掌握后能快速识别变量用途:
变量 / 类型示例前缀解析含义说明uint8_tu(无符号)+ int88 位无符号整数(等价于unsigned char)int32_ts(隐含)+ int3232 位有符号整数(等价于int32_t)pucBufferp(指针)+ u(无符号)+ c(字符)指向无符号字符数组的指针(缓冲区指针)pxTaskTCBp(指针)+ x(结构体)指向任务控制块(TCB,结构体)的指针ulTickCountul(无符号长整数)以系统时基为单位的计数器(如uint32_t)xQueueHandlex(自定义类型)队列句柄(本质是指向队列结构体的指针)s16Errors(有符号)+ 16(16 位)16 位有符号错误码3、FreeRTOS 核心自定义类型(带固定前缀)

除了基础类型,FreeRTOS 定义了大量用于特定功能的类型,其命名也遵循规则:
自定义类型前缀 / 后缀含义与用途BaseType_tBase(基础)平台默认的 “基础整数类型”(通常是 32 位有符号),作为多数 API 的返回值类型(如成功返回pdPASS)。TickType_tTick(时基)用于表示系统时基( ticks )的类型(通常是uint32_t),如vTaskDelay()的参数类型。TaskHandle_tHandle_t(句柄)任务句柄(本质是指向TCB_t结构体的指针),用于操作任务(如vTaskSuspend(TaskHandle_t))。QueueHandle_tHandle_t队列句柄(指向队列结构体的指针),用于队列操作(如xQueueSend(QueueHandle_t))。SemaphoreHandle_tHandle_t信号量句柄(与队列句柄通用,因信号量基于队列实现)。TCB_tTCB(任务控制块)任务控制块结构体(x类型),存储任务的所有信息(栈、优先级、状态等)。4、特殊标识:宏定义与常量的前缀

FreeRTOS 的宏和常量也有前缀规则,用于区分功能:
前缀含义示例pdPortable Define(可移植定义)pdTRUE(真)、pdFALSE(假)、pdPASS(成功)、pdFAIL(失败)。config配置项(来自FreeRTOSConfig.h)configMAX_PRIORITIES(最大优先级)、configTICK_RATE_HZ(时基频率)。err错误码errQUEUE_FULL(队列满)、errTIMEOUT(超时)。5、通过前缀快速理解源码的技巧


  • 看到p开头的变量:立即识别为指针(如pxCurrentTCB是当前任务 TCB 的指针)。
  • 看到x开头的类型 / 变量:通常是结构体或复杂类型(如xTaskCreate()返回的是BaseType_t,但xQueue是队列结构体实例)。
  • 看到ul开头的变量:多为 32 位无符号整数(如ulTaskNumber表示任务编号)。
  • 看到Handle_t结尾的类型:均为 “句柄”,本质是指针,用于操作内核对象(任务、队列等)。
五、阅读源码的建议路径


  • 从main()开始,跟踪到freertos.c文件,看到vTaskStartScheduler(),理解调度器启动流程;
  • 重点看task.c中的xTaskCreate()(任务创建)和vTaskSwitchContext()(任务切换);
  • 结合FreeRTOSConfig.h的配置项,理解 “可裁剪” 特性(如关闭某个功能后,对应源码不参与编译);
  • 先忽略硬件相关的port.c,聚焦通用逻辑(任务、队列),再深入架构适配细节。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册