FreeRTOS实时操作系统学习笔记

发布于:2025-06-03 ⋅ 阅读:(30) ⋅ 点赞:(0)

一 RTOS入门

1.1 裸机与RTOS介绍(了解)

        裸机编程是指在嵌入式系统中,直接在硬件上运行代码,没有操作系统的支持。这种方式下,开发者需要完全掌握硬件资源,包括时钟、中断、外设等。任务调度和资源管理都由开发者手动管理。这就像手动操纵一辆汽车,想开车从城市A到城市B,你需要了解汽车的每个部件,掌握如何驾驶,包括油门、刹车、方向盘等。你需要手动决定何时加速、何时刹车、何时转弯。这就好比裸机编程,开发者需要亲自管理每个硬件资源,编写所有的控制逻辑。

        RTOS 全称是 Real Time Operating System,中文名就是实时操作系统,提供了任务调度、内存管理、中断处理等功能。RTOS能够让开发者更专注于应用层的开发,而不用亲自管理底层硬件资源。想从城市A到城市B,你可以选择坐出租车。在出租车上,你只需要告诉司机目的地,不用亲自操纵汽车的每个部分。司机会负责加速、刹车、转弯等操作。这就好比使用RTOS,开发者只需定义任务、调度和数据通信,RTOS会负责底层管理。

总的来说,裸机编程就像是自己开车,而使用RTOS则像是坐出租车,更专注于目的地而非具体的驾驶操作。

  • 任务调度:裸机编程需要手动调度任务,而RTOS提供自动的任务调度器。
  • 硬件管理:裸机编程需要开发者手动管理硬件资源,RTOS提供了抽象接口,简化了硬件管理。
  • 复杂性:裸机编程相对较复杂,需要深入了解硬件细节。RTOS提供了更高层次的抽象,简化了开发流程。

1.2 FreeRTOS简介(了解) 

        RTOS是指一类系统,如 FreeRTOS,uC/OS,RTX,RT-Thread 等,都是 RTOS 类操作系统。

1.2.1 FreeRTOS优势

        FreeRTOS是一款受欢迎、广泛应用于嵌入式系统的RTOS,其开源、轻量级、可移植的特点使其成为许多嵌入式开发者的首选,主要优势如下:

  • 开源和免费:FreeRTOS是一款开源的RTOS,采用MIT许可证发布,可以免费使用、修改和分发。
  • 轻量级设计:FreeRTOS注重轻量级设计,适用于资源受限的嵌入式系统,不占用过多内存和处理器资源。
  • 广泛应用:FreeRTOS在嵌入式领域得到广泛应用,包括工业自动化、医疗设备、消费电子产品、汽车电子等。
  • 多平台支持:FreeRTOS的设计注重可移植性,可以轻松地移植到不同的硬件平台,支持多种处理器架构。
  • 丰富的功能:提供了多任务调度、任务通信、同步等功能,适用于复杂的嵌入式应用场景。

1.2.2 FreeRTOS介绍 

官网:FreeRTOS™ - FreeRTOS™,并且支持中文。

  • 任务调度:FreeRTOS通过任务调度器管理多个任务,支持不同优先级的任务,实现任务的有序执行。
  • 任务通信和同步:提供了队列、信号量等机制,支持任务之间的通信和同步,确保数据的安全传递。
  • 内存管理:提供简单的内存管理机制,适用于嵌入式环境,有效利用有限的内存资源。
  • 定时器和中断处理:支持定时器功能,能够处理中断,提供了可靠的实时性能。
  • 开发社区:拥有庞大的用户社区,开发者可以在社区中获取支持、解决问题,并分享经验。
  • 可移植性:设计注重可移植性,可以轻松地移植到不同的硬件平台,提高了代码的重用性。

二 FreeRTOS基础介绍 

2.1 任务调度简介(熟悉)

一个处理器核心在某一时刻只能运行一个任务,如果在各个任务之间迅速切换,这样看起来就像多个任务在同时运行。操作系统中任务调度器的责任就是决定在某一时刻要执行哪个任务。

FreeRTOS使用基于优先级的抢占式任务调度策略。

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

但是并不是说高优先级的任务会一直执行,导致低优先级的任务无法得到执行。如果高优先级任务等待某个资源(延时或等待信号量等)而无法执行,调度器会选择执行其他就绪的高优先级的任务。

任务优先级相同的,按时间片调度,每个任务执行一个时间片(一次系统时钟中断)
任务执行不足一个时间片(或阻塞),没有用完的时间片不会再使用。

2.2 任务状态(熟悉)

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

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

只有就绪态可转变成运行态,其他状态的任务想运行,必须先转变成就绪态。转换关系如下:
 

这四种状态中,除了运行态,其他三种任务状态的任务都有其对应的任务状态列表:

  • 就绪列表:pxReadyTasksLists[x],其中x代表任务优先级数值。
  • 阻塞列表:pxDelayedTaskList。
  • 挂起列表:xSuspendedTaskList。

列表类似于链表

以就绪列表为例。如果在32位的硬件中,会保存一个32位的变量,代表0-31的优先级。当某个位,置一时,代表所对应的优先级就绪列表有任务存在。

如果有多个任务优先级相同,会连接在同一个就绪列表上:

调度器总是在所有处于就绪列表的任务中,选择具有最高优先级的任务来执行。

三 FreeRTOS移植

3.1 FreeRTOS源码结构介绍

3.1.1 获取源码

3.1.2 源码结构介绍

3.2 FreeRTOS移植步骤

四 FreeRTOS的任务创建和删除

4.1 任务创建和删除API函数(熟悉)

任务的创建和删除本质就是调用FreeRTOS的API函数,主要如下:

API函数

描述

xTaskCreate()

动态方式创建任务

xTaskCreateStatic()

静态方式创建任务

vTaskDelete()

删除任务

  • 动态创建任务:任务的任务控制块以及任务的栈空间所需的内存,均由 FreeRTOS 从 FreeRTOS 管理的堆中分配。
  • 静态创建任务:任务的任务控制块以及任务的栈空间所需的内存,需用户分配提供。

4.1.1 动态创建任务函数 

1)函数说明

BaseType_t xTaskCreate

(

    TaskFunction_t pxTaskCode,                  /* 指向任务函数的指针 */

    const char * const pcName,                  /* 任务名字,最大长度configMAX_TASK_NAME_LEN */

    const configSTACK_DEPTH_TYPE usStackDepth,  /* 任务堆栈大小,默认单位2字节 */

    void * const pvParameters,                  /* 传递给任务函数的参数 */

    UBaseType_t uxPriority,                     /* 任务优先级,范围:0 ~ configMAX_PRIORITIES - 1 */

    TaskHandle_t * const pxCreatedTask          /* 任务句柄,就是任务的任务控制块 */

)  

返回值说明如下:

  • pdPASS:任务创建成功。
  • errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:任务创建失败。

2)动态创建任务步骤(代码思路)

  1. 将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1。
  2. 定义函数入口参数。
  3. 编写任务函数。

此函数创建的任务会立刻进入就绪态,由任务调度器调度运行。

3)动态创建任务函数内部实现(底层)

  1. 申请堆栈内存&任务控制块内存。
  2. TCB结构体成员赋值。
  3. 添加新任务到就绪列表中。

任务控制块结构体成员介绍。

typedef struct tskTaskControlBlock       
{
    volatile StackType_t * pxTopOfStack; /* 任务栈栈顶,必须为TCB的第一个成员 */
    ListItem_t xStateListItem;                  /* 任务状态列表项 */
    ListItem_t xEventListItem;                  /* 任务事件列表项 */
    UBaseType_t uxPriority;                     /* 任务优先级,数值越大,优先级越大 */
    StackType_t * pxStack;                      /* 任务栈起始地址 */
    char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务名字 */  
    …
    省略很多条件编译的成员
} tskTCB;

 任务栈栈顶,在任务切换时的任务上下文保存、任务恢复息息相关。每个任务都有属于自己的任务控制块,类似身份证。

4.1.2 静态创建任务函数

1)函数说明

TaskHandle_t xTaskCreateStatic
( 
    TaskFunction_t pxTaskCode,          /* 指向任务函数的指针 */
    const char * const pcName,          /* 任务函数名 */
    const uint32_t ulStackDepth,        /* 任务堆栈大小,单位是4字节 */
    void * const pvParameters,          /* 传递的任务函数参数 */
    UBaseType_t uxPriority,             /* 任务优先级 */
    StackType_t * const puxStackBuffer, /* 任务堆栈,一般为数组,由用户分配 */
    StaticTask_t * const pxTaskBuffer   /* 任务控制块指针,由用户分配 */
)

返回值如下:

  • NULL:用户没有提供相应的内存,任务创建失败。
  • 其他值:任务句柄,任务创建成功。

2)静态创建任务步骤 (代码思路)

        (1)将宏configSUPPORT_STATIC_ALLOCATION 配置为 1。

        (2)定义空闲任务&定时器任务的任务堆栈及TCB。

        (3)实现接口函数:

                        vApplicationGetIdleTaskMemory()

                        vApplicationGetTimerTaskMemory()(如果开启软件定时器)

        (4)定义函数入口参数。

        (5)编写任务函数。

此函数创建的任务会立刻进入就绪态,由任务调度器调度运行。

4.1.3 删除函数

1)函数说明

void vTaskDelete( TaskHandle_t xTaskToDelete )

参数说明:xTaskToDelete待删除任务的任务句柄。当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)。

该函数用于删除已被创建的任务,被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除。

需要注意的是,空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存,则需要由用户在任务被删除前提前释放,否则将导致内存泄露。

2)删除任务流程

  1. 使用删除任务函数,需将宏INCLUDE_vTaskDelete 配置为 1
  2. 入口参数输入需要删除的任务句柄(NULL代表删除本身)

3)内部实现过程

(1)获取所要删除任务的控制块

        通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身。

(2)将被删除任务,移除所在列表

        将该任务在所在列表中移除,包括:就绪、阻塞、挂起、事件等列表。

(3)判断所需要删除的任务

        如果删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行;如果删除其他任务,释放内存,任务数量--。

(4)更新下个任务的阻塞时间

        更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务。

 

 


网站公告

今日签到

点亮在社区的每一天
去签到