FreeRTOS/RT-Thread双教程:嵌入式开发者入门到实战(2025版)

一、为什么学FreeRTOS/RT-Thread?嵌入式开发者必看

嵌入式开发从"裸机"到"多任务"的跨越,核心是掌握实时操作系统(RTOS)。FreeRTOS和RT-Thread是目前国内嵌入式领域最主流的两款RTOS:FreeRTOS以"轻量、稳定、资料多"成为入门首选,RT-Thread以"国产、中文生态、组件丰富"适配国内工业场景。本文从零基础讲起,对比两款RTOS的核心用法,帮你快速落地项目。

1.1 两款RTOS核心差异(新手选型必看)

维度 FreeRTOS RT-Thread
开源协议 MIT开源(完全免费,商用无限制) Apache 2.0(国产开源,合规性高)
核心特点 内核精简(核心代码仅数万行),专注实时调度 模块化设计,内置文件系统、网络、GUI等组件
生态支持 全球社区,英文文档为主,STM32适配最佳 中文社区,官方教程完善,适配国产MCU(如华大、复旦微)
适用场景 消费电子、IoT低功耗设备、小型工业控制 国内工业控制、智能家居、车载设备
学习成本 低(入门案例多,易上手) 中(组件多,需掌握模块化使用)

1.2 学习前提(避免走弯路)

  • 具备基础C语言和STM32裸机开发能力(会配置GPIO、串口、定时器);
  • 准备硬件:STM32F103/STM32F407开发板(两款RTOS均完美适配);
  • 开发环境:Keil MDK 5.x 或 STM32CubeIDE(本文以Keil为例)。

二、FreeRTOS入门:从环境搭建到多任务点灯

2.1 FreeRTOS工程搭建(STM32F103为例)

步骤1:下载FreeRTOS源码

从FreeRTOS官网(www.freertos.org/)下载最新版源码,解压...

  • 内核文件:tasks.c(任务调度)、queue.c(消息队列)、semphr.c(信号量/互斥锁);
  • 端口文件:portable/ARM/Cortex-M3/port.c(适配Cortex-M3内核);
  • 内存管理:portable/MemMang/heap_4.c(推荐使用,解决内存碎片)。

步骤2:移植到STM32工程

  1. 新建STM32F103裸机工程(配置时钟、GPIO、串口);
  2. 将FreeRTOS核心文件添加到工程,新建FreeRTOS分组,加入上述文件;
  3. 添加头文件路径:FreeRTOS/Source/includeFreeRTOS/Source/portable/ARM/Cortex-M3
  4. 复制FreeRTOSConfig.h到工程(关键配置文件,文末附模板)。

步骤3:核心配置(FreeRTOSConfig.h关键宏)

c 复制代码
#define configUSE_PREEMPTION        1    // 开启抢占式调度(必开)
#define configUSE_TIME_SLICING      1    // 开启时间片轮转
#define configTICK_RATE_HZ          100  // 系统节拍100Hz(时间片10ms)
#define configMAX_PRIORITIES        16   // 任务最大优先级(0-15,15最高)
#define configMINIMAL_STACK_SIZE    128  // 最小任务栈大小
#define configTOTAL_HEAP_SIZE       ((size_t)10*1024) // 堆大小10KB

2.2 FreeRTOS第一个例程:多任务点灯

核心目标:创建两个任务,LED1每500ms闪烁,LED2每1000ms闪烁,体现多任务并发。

步骤1:硬件初始化(LED引脚配置)

c 复制代码
// LED1接PA0,LED2接PA1
void LED_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    GPIO_SetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_1); // 初始灭灯
}

步骤2:创建任务函数

c 复制代码
// 任务1:LED1闪烁(优先级1)
void LED1_Task(void *pvParameters)
{
    while(1)
    {
        GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 亮灯
        vTaskDelay(500); // 延时500ms(释放CPU)
        GPIO_SetBits(GPIOA, GPIO_Pin_0);   // 灭灯
        vTaskDelay(500);
    }
}

// 任务2:LED2闪烁(优先级1)
void LED2_Task(void *pvParameters)
{
    while(1)
    {
        GPIO_ResetBits(GPIOA, GPIO_Pin_1);
        vTaskDelay(1000);
        GPIO_SetBits(GPIOA, GPIO_Pin_1);
        vTaskDelay(1000);
    }
}

步骤3:在main函数中启动调度器

c 复制代码
int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 中断分组(必配)
    LED_Init();
    USART1_Init(115200); // 串口初始化(可选,用于调试)
    
    // 创建任务
    xTaskCreate(LED1_Task, "LED1_Task", 128, NULL, 1, NULL);
    xTaskCreate(LED2_Task, "LED2_Task", 128, NULL, 1, NULL);
    
    vTaskStartScheduler(); // 启动调度器(从此任务接管CPU)
    
    // 调度器启动后,以下代码不会执行
    while(1);
}

关键说明

  • vTaskDelay()是FreeRTOS延时函数,会释放CPU,让其他任务执行(区别于裸机for循环延时);
  • 任务优先级数值越大,优先级越高;同优先级任务按时间片轮转执行;
  • 任务栈大小设为128(单位:字,STM32中1字=4字节),避免栈溢出。

三、RT-Thread入门:从移植到FinSH控制台

RT-Thread的核心优势是"组件化"和"中文生态",入门重点是掌握其内核用法和FinSH控制台(调试神器)。

3.1 RT-Thread工程搭建(STM32F103为例)

步骤1:下载RT-Thread源码

从RT-Thread官网(www.rt-thread.org/)下载`RT-Thre... Standard`版本,核心目录:

  • kernel:内核文件(任务、调度、IPC通信);
  • components:组件(finsh、dfs文件系统、lwip网络);
  • bsp/stm32/stm32f103-template:STM32F103模板工程(直接复用)。

步骤2:快速移植(复用官方模板)

  1. 复制bsp/stm32/stm32f103-template到本地,用Keil打开工程;
  2. 配置芯片型号为STM32F103C8T6,修改时钟配置为72MHz;
  3. 开启FinSH控制台:在rtconfig.h中开启#define RT_USING_FINSH,并配置串口1为FinSH端口。

3.2 RT-Thread第一个例程:多任务点灯+FinSH调试

步骤1:创建点灯线程(RT-Thread中"任务"称为"线程")

c 复制代码
#include <rtthread.h>
#include "stm32f10x.h"

#define LED1_PIN    GET_PIN(A, 0)
#define LED2_PIN    GET_PIN(A, 1)

// 线程1:LED1闪烁
void led1_thread_entry(void *parameter)
{
    rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);
    while (1)
    {
        rt_pin_write(LED1_PIN, PIN_LOW);
        rt_thread_mdelay(500); // RT-Thread延时函数
        rt_pin_write(LED1_PIN, PIN_HIGH);
        rt_thread_mdelay(500);
    }
}

// 线程2:LED2闪烁
void led2_thread_entry(void *parameter)
{
    rt_pin_mode(LED2_PIN, PIN_MODE_OUTPUT);
    while (1)
    {
        rt_pin_write(LED2_PIN, PIN_LOW);
        rt_thread_mdelay(1000);
        rt_pin_write(LED2_PIN, PIN_HIGH);
        rt_thread_mdelay(1000);
    }
}

步骤2:初始化线程并启动

c 复制代码
int main(void)
{
    // 创建线程1(优先级10,栈大小1024)
    rt_thread_t led1_thread = rt_thread_create("led1",
                                             led1_thread_entry,
                                             RT_NULL,
                                             1024,
                                             10,
                                             10);
    // 创建线程2
    rt_thread_t led2_thread = rt_thread_create("led2",
                                             led2_thread_entry,
                                             RT_NULL,
                                             1024,
                                             10,
                                             10);
    // 启动线程
    if (led1_thread != RT_NULL)
        rt_thread_startup(led1_thread);
    if (led2_thread != RT_NULL)
        rt_thread_startup(led2_thread);
    
    return 0;
}

步骤3:FinSH控制台调试(RT-Thread核心优势)

编译下载程序后,通过串口助手连接开发板(波特率115200),输入list_thread可查看所有线程状态:

arduino 复制代码
msh > list_thread
thread     pri  status      sp     stack size max used left tick  error
---------- ---  ------- ---------- ----------  ------  ---------- ---
led2       10   suspend 0x00000070 0x00000400    23%   999         000
led1       10   suspend 0x00000070 0x00000400    23%   499         000
tshell     20   running 0x00000100 0x00001000    10%   999         000
idle       31   ready   0x00000020 0x00000100    0%    999         000

通过FinSH可实时查看线程、修改参数,大幅降低调试难度。

四、FreeRTOS/RT-Thread核心功能对比(实战必学)

4.1 任务/线程管理(核心用法)

功能 FreeRTOS API RT-Thread API
创建任务 xTaskCreate() rt_thread_create()/rt_thread_init()
启动任务 vTaskStartScheduler() rt_thread_startup()
延时 vTaskDelay() rt_thread_mdelay()
挂起/恢复 vTaskSuspend()/vTaskResume() rt_thread_suspend()/rt_thread_resume()

4.2 任务同步与通信(解决多任务协作)

1. 消息队列(任务间传数据)

  • FreeRTOS示例:
c 复制代码
// 创建队列(长度5,每个元素4字节)
QueueHandle_t xQueue = xQueueCreate(5, sizeof(uint32_t));
// 发送数据
uint32_t data = 100;
xQueueSend(xQueue, &data, portMAX_DELAY);
// 接收数据
uint32_t recv_data;
xQueueReceive(xQueue, &recv_data, portMAX_DELAY);
  • RT-Thread示例:
c 复制代码
// 创建队列
rt_mq_t mq = rt_mq_create("mq", 4, 5, RT_IPC_FLAG_FIFO);
// 发送数据
uint32_t data = 100;
rt_mq_send(mq, &data, sizeof(data));
// 接收数据
uint32_t recv_data;
rt_mq_recv(mq, &recv_data, sizeof(recv_data), RT_WAITING_FOREVER);

2. 信号量(解决资源竞争)

  • FreeRTOS示例:
c 复制代码
// 创建二值信号量
SemaphoreHandle_t xSemaphore = xSemaphoreCreateBinary();
// 获取信号量
xSemaphoreTake(xSemaphore, portMAX_DELAY);
// 释放信号量
xSemaphoreGive(xSemaphore);
  • RT-Thread示例:
c 复制代码
// 创建信号量
rt_sem_t sem = rt_sem_create("sem", 1, RT_IPC_FLAG_FIFO);
// 获取信号量
rt_sem_take(sem, RT_WAITING_FOREVER);
// 释放信号量
rt_sem_release(sem);

五、实战项目:FreeRTOS/RT-Thread实现串口数据采集+处理

5.1 需求说明

  • 任务1(串口接收):接收上位机发送的数据,放入消息队列;
  • 任务2(数据处理):从队列中取数据,解析并打印;
  • 要求:避免串口资源竞争,保证数据不丢失。

5.2 FreeRTOS实现核心代码

c 复制代码
// 定义队列句柄
QueueHandle_t xUartQueue;

// 串口中断服务函数(接收数据)
void USART1_IRQHandler(void)
{
    uint8_t ch;
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        ch = USART_ReceiveData(USART1);
        // 中断中发送数据到队列(用FromISR版本)
        xQueueSendFromISR(xUartQueue, &ch, NULL);
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}

// 任务1:串口初始化(略)
// 任务2:数据处理任务
void DataProcess_Task(void *pvParameters)
{
    uint8_t recv_ch;
    while(1)
    {
        // 从队列取数据
        if(xQueueReceive(xUartQueue, &recv_ch, portMAX_DELAY))
        {
            printf("接收数据:%c\n", recv_ch);
        }
    }
}

// main函数中创建队列和任务
xUartQueue = xQueueCreate(32, sizeof(uint8_t));
xTaskCreate(DataProcess_Task, "DataProcess", 128, NULL, 2, NULL);

5.3 RT-Thread实现核心差异

  • 串口中断中发送数据到消息队列,使用rt_mq_send_from_isr()
  • 线程优先级配置:数据处理线程优先级高于串口接收线程,保证数据及时处理。

六、学习误区与进阶方向

6.1 新手常见误区

  1. 死记API不理解原理:比如分不清vTaskDelay()和裸机延时的区别,核心是前者释放CPU,后者占用CPU;
  2. 任务栈设置过小:FreeRTOS/RT-Thread任务栈建议至少128字(FreeRTOS)/1024字节(RT-Thread);
  3. 滥用全局变量:多任务间传数据必须用队列/信号量,避免全局变量冲突;
  4. 忽略中断优先级:FreeRTOS/RT-Thread中断优先级需低于configMAX_SYSCALL_INTERRUPT_PRIORITY

6.2 进阶学习方向

  1. FreeRTOS:深入内存管理(heap_4/heap_5)、优先级继承、低功耗模式;
  2. RT-Thread:学习设备驱动框架、LWIP网络协议栈、DFS文件系统;
  3. 项目落地:基于FreeRTOS做IoT传感器节点,基于RT-Thread做工业控制终端。

七、附:FreeRTOSConfig.h极简模板(STM32F103)

c 复制代码
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

#define configUSE_PREEMPTION            1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
#define configUSE_TICKLESS_IDLE         0
#define configCPU_CLOCK_HZ              (72000000)
#define configTICK_RATE_HZ              (100)
#define configMAX_PRIORITIES            (16)
#define configMINIMAL_STACK_SIZE        (128)
#define configTOTAL_HEAP_SIZE           ((size_t)(10*1024))
#define configMAX_TASK_NAME_LEN         (16)
#define configUSE_16_BIT_TICKS          0
#define configIDLE_SHOULD_YIELD         1
#define configUSE_TASK_NOTIFICATIONS    1
#define configUSE_MUTEXES               1
#define configUSE_RECURSIVE_MUTEXES     1
#define configUSE_COUNTING_SEMAPHORES   1
#define configUSE_ALTERNATIVE_API       0
#define configQUEUE_REGISTRY_SIZE       8
#define configUSE_QUEUE_SETS            0
#define configUSE_TIME_SLICING          1
#define configUSE_NEWLIB_REENTRANT      0
#define configENABLE_BACKWARD_COMPATIBILITY 1
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5

// 中断优先级配置
#define configPRIO_BITS                 4
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY   15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))

// 钩子函数
#define configUSE_IDLE_HOOK             0
#define configUSE_TICK_HOOK             0
#define configCHECK_FOR_STACK_OVERFLOW  0
#define configUSE_MALLOC_FAILED_HOOK    0

// 调试配置
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }

// 包含头文件
#include "stm32f10x.h"
#endif /* FREERTOS_CONFIG_H */

总结

FreeRTOS和RT-Thread的核心都是"多任务调度",入门先掌握任务创建、队列/信号量用法,再结合实战项目深化。新手建议先学FreeRTOS打基础,再学RT-Thread掌握组件化开发;国内项目优先选RT-Thread(适配国产芯片+中文支持),海外项目优先选FreeRTOS(生态更通用)。学习过程中多动手调试,比如用FinSH查看RT-Thread线程状态,用vTaskList()查看FreeRTOS任务状态,能快速定位问题。

相关推荐
大聪明-PLUS4 小时前
Linux进程间通信(IPC)指南 - 第3部分
linux·嵌入式·arm·smarc
技术小泽6 小时前
MQTT从入门到实战
java·后端·kafka·消息队列·嵌入式
应该会好起来的9 小时前
基于定时器中断的多任务轮询架构
嵌入式
切糕师学AI1 天前
NuttX RTOS是什么?
嵌入式·rtos
大聪明-PLUS1 天前
一个简单高效的 C++ 监控程序,带有一个通用的 Makefile
linux·嵌入式·arm·smarc
华清远见成都中心1 天前
2026新版嵌入式春招面试题
嵌入式·秋招·嵌入式面试
hk11242 天前
【Hardware/Robotics】2026年度多态硬件重构与自主机器人内核基准索引 (Benchmark Index)
开发语言·数据库·机器人·嵌入式·硬件开发
乔碧萝成都分萝2 天前
二十、设备树
linux·驱动开发·嵌入式
大聪明-PLUS2 天前
Unix 工作实战。第二部分:软件
linux·嵌入式·arm·smarc