杰瑞科技汇

RT-Thread教程该怎么学?

RT-Thread 综合教程:从零开始的嵌入式系统开发

目录

  1. 第一部分:初识 RT-Thread

    RT-Thread教程该怎么学?-图1
    (图片来源网络,侵删)
    • 1 什么是 RT-Thread?
    • 2 RT-Thread 的核心特点与优势
    • 3 RT-Thread 的应用领域
    • 4 为什么选择 RT-Thread?
  2. 第二部分:开发环境搭建

    • 1 硬件准备
    • 2 软件安装
      • 2.1 安装 RT-Thread Studio (推荐新手)
      • 2.2 安装 VS Code + 插件 (推荐进阶)
    • 3 第一个工程:Blinky (LED闪烁)
  3. 第三部分:RT-Thread 核心概念与组件

    • 1 系统架构
    • 2 内核 (Kernel)
      • 2.1 线程
      • 2.2 信号量
      • 2.3 消息队列
      • 2.4 互斥量
      • 2.5 事件标志组
    • 3 设备框架
      • 3.1 I/O 设备模型
      • 3.2 设备驱动框架
    • 4 文件系统
    • 5 网络框架
    • 6 软件包
  4. 第四部分:常用组件详解与实践

    • 1 FinSH 命令行
    • 2 设备虚拟文件系统
    • 3 常用软件包介绍 (如: CJSON, LittlevGL, MQTT)
  5. 第五部分:实战项目

    RT-Thread教程该怎么学?-图2
    (图片来源网络,侵删)
    • 1 项目概述:智能温湿度监测系统
    • 2 硬件连接
    • 3 软件实现
      • 3.1 创建工程
      • 3.2 编写传感器驱动 (基于设备框架)
      • 3.3 创建数据采集线程
      • 3.4 通过 FinSH 命令查看数据
      • 3.5 (可选) 通过 MQTT 上传数据到云平台
  6. 第六部分:资源与进阶

    • 1 官方文档与社区
    • 2 推荐书籍
    • 3 进阶学习方向

第一部分:初识 RT-Thread

1 什么是 RT-Thread?

RT-Thread 是一款由 entirely 中国人主导开发的开源实时操作系统,它主要面向资源受限的嵌入式系统,如微控制器,RT-Thread 提供了一个清晰、稳定、可裁剪的实时内核,并拥有丰富的组件和软件包生态,使得开发者可以轻松构建复杂的物联网应用。

RT-Thread 就是为嵌入式设备装上了一个“小型的、功能强大的电脑操作系统”

2 RT-Thread 的核心特点与优势

  • 高度可裁剪:内核功能可以根据项目需求进行裁剪,最小内核可以小到只有 1-2KB,非常适合资源有限的 MCU。
  • 丰富的组件:除了内核,还集成了文件系统、网络协议栈、设备框架等大量现成的组件,开箱即用。
  • 强大的软件包生态:拥有一个庞大的在线软件包仓库,包含各种协议库、工具库、UI 框架(如 LittlevGL)、AI 框架等,极大加速开发。
  • 友好的开发体验
    • RT-Thread Studio:官方提供的图形化 IDE,集成了开发、编译、调试、模拟器于一体,对新手极其友好。
    • VS Code 插件:为专业开发者提供了轻量、灵活的开发环境。
  • 跨平台:支持 ARM Cortex-M, Cortex-A/R, RISC-V, X86 等多种主流架构。
  • 开源免费:遵循 Apache 2.0 许可证,可以免费用于商业产品。

3 RT-Thread 的应用领域

从简单的按键检测,到复杂的智能家居网关、工业控制器、智能穿戴设备、无人机等,RT-Thread 的应用范围非常广泛。

RT-Thread教程该怎么学?-图3
(图片来源网络,侵删)

4 为什么选择 RT-Thread?

  • 对新手友好:完善的文档、教程和工具链,降低了嵌入式开发的门槛。
  • 功能强大:一站式解决方案,无需自己拼接多个开源库。
  • 社区活跃:国内社区非常活跃,遇到问题容易找到帮助。
  • 国产化趋势:在国家政策支持下,RT-Thread 在国内的应用越来越广泛。

第二部分:开发环境搭建

1 硬件准备

  • 开发板:推荐一块 RT-Thread 官方支持或社区热门的开发板。
    • 初学者推荐
      • 野火 STM32F103 Mini 开发板
      • 正点原子 STM32F103 开发板
      • 安富莱 STM32H743 开发板
    • 这些开发板资料丰富,社区支持好。
  • USB 数据线:用于连接电脑和开发板,供电和下载程序。

2 软件安装

2.1 安装 RT-Thread Studio (推荐新手)

这是最简单、最推荐的方式,尤其适合初学者。

  1. 下载:访问 RT-Thread 官网 下载并安装 RT-Thread Studio。
  2. 启动:安装完成后,启动 Studio。
  3. 新建工程
    • 选择 File -> New -> RT-Thread Project
    • 选择目标板型号(Fire-Apm32F103-V1)。
    • 选择工具链(如 GCC)。
    • 工程模板选择 Hello, RT-Thread
  4. 编译与下载
    • 点击工具栏的“锤子”图标编译工程。
    • 点击“闪电”图标将程序下载到开发板。
    • 按下开发板的复位按钮,你应该能在串口助手上看到 "hello rt-thread!" 的打印信息。

2.2 安装 VS Code + 插件 (推荐进阶)

如果你习惯使用 VS Code,这种方式更灵活。

  1. 安装 VS Code:从 官网 下载并安装。
  2. 安装插件
    • C/C++ (Microsoft)
    • RT-Thread Assistant (官方插件)
  3. 创建工程
    • 使用 RT-Thread 官方提供的 env 工具创建一个工程目录。
    • 在 VS Code 中打开该目录。
    • 使用 RT-Thread Assistant 插件来配置、编译和下载工程。

3 第一个工程:Blinky (LED闪烁)

无论使用哪种 IDE,创建一个 Blinky 工程都非常简单。

  1. 硬件连接:将开发板上的一个 LED 连接到一个 GPIO 引脚(PA5)。

  2. 代码编写

    • main.c 文件中,你会看到类似下面的代码:
    #include <rtthread.h>
    #include <board.h> // 包含板级支持包头文件
    /* 定义 LED 的引脚 */
    #define LED_PIN GET_PIN(A, 5) // 根据你的板子修改引脚
    /* 定义线程入口函数 */
    void led_thread_entry(void *parameter)
    {
        /* 设置 LED 引脚为输出模式 */
        rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);
        while (1)
        {
            /* LED 亮 */
            rt_pin_write(LED_PIN, PIN_HIGH);
            rt_thread_mdelay(500); // 延时 500ms
            /* LED 灭 */
            rt_pin_write(LED_PIN, PIN_LOW);
            rt_thread_mdelay(500); // 延时 500ms
        }
    }
    /* 在 main 函数之前创建线程 */
    int main(void)
    {
        /* 创建一个线程,名字是 led_thread,入口是 led_thread_entry,栈大小 512,优先级 7 */
        rt_thread_t tid = rt_thread_create("led_thread",
                                           led_thread_entry,
                                           RT_NULL,
                                           512,
                                           7, // 优先级
                                           20);
        /* 如果线程创建成功,启动它 */
        if (tid != RT_NULL)
        {
            rt_thread_startup(tid);
        }
        return 0;
    }
  3. 编译与运行:编译并下载程序,你将看到 LED 以 1Hz 的频率闪烁,这个例子展示了 RT-Thread 线程 的基本用法。


第三部分:RT-Thread 核心概念与组件

1 系统架构

RT-Thread 的架构可以分为四层:

  1. 硬件层:CPU、内存、外设等。
  2. 内核层:提供最基础的调度、同步、通信等机制。
  3. 组件层:文件系统、网络框架、设备框架等。
  4. 应用层:用户自己编写的业务逻辑。

2 内核

2.1 线程

线程是 RT-Thread 中调度的基本单位,一个 RT-Thread 系统至少有一个主线程 main,其他任务都由线程来完成。

  • 创建线程rt_thread_create() / rt_thread_init()
  • 启动线程rt_thread_startup()
  • 挂起/恢复线程rt_thread_suspend() / rt_thread_resume()
  • 删除线程rt_thread_delete()
  • 线程延时rt_thread_mdelay() (毫秒) / rt_thread_delay() (秒)

示例:创建两个线程

// 线程1:打印 "A"
void thread1_entry(void *param)
{
    while (1) {
        rt_kprintf("A\n");
        rt_thread_mdelay(1000);
    }
}
// 线程2:打印 "B"
void thread2_entry(void *param)
{
    while (1) {
        rt_kprintf("B\n");
        rt_thread_mdelay(1000);
    }
}
int main(void)
{
    // 创建线程1
    rt_thread_t tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, 6, 20);
    if (tid1) rt_thread_startup(tid1);
    // 创建线程2
    rt_thread_t tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, 6, 20);
    if (tid2) rt_thread_startup(tid2);
    return 0;
}

2.2 信号量

信号量用于线程间的同步,常用于控制资源访问次数或实现任务间的通知。

  • 创建rt_sem_create()
  • 获取 (P/V操作)rt_sem_take() (等待) / rt_sem_trytake() (不等待)
  • 释放rt_sem_release()
  • 删除rt_sem_delete()

示例:生产者-消费者模型

#define BUFFER_SIZE 5
rt_sem_t producer_sem, consumer_sem;
rt_mutex_t buffer_mutex;
int buffer[BUFFER_SIZE];
int count = 0;
// 生产者线程
void producer_thread_entry(void *param)
{
    for (int i = 0; i < 20; i++) {
        // 模拟生产
        rt_thread_mdelay(100);
        // 获取生产信号量
        rt_sem_take(producer_sem, RT_WAITING_FOREVER);
        // 加锁
        rt_mutex_take(buffer_mutex, RT_WAITING_FOREVER);
        buffer[count] = i;
        rt_kprintf("Produced: %d\n", i);
        count++;
        // 解锁
        rt_mutex_release(buffer_mutex);
        // 释放消费信号量
        rt_sem_release(consumer_sem);
    }
}
// 消费者线程
void consumer_thread_entry(void *param)
{
    for (int i = 0; i < 20; i++) {
        // 获取消费信号量
        rt_sem_take(consumer_sem, RT_WAITING_FOREVER);
        // 加锁
        rt_mutex_take(buffer_mutex, RT_WAITING_FOREVER);
        int item = buffer[--count];
        rt_kprintf("Consumed: %d\n", item);
        // 解锁
        rt_mutex_release(buffer_mutex);
        // 模拟消费
        rt_thread_mdelay(200);
    }
}
int main(void)
{
    producer_sem = rt_sem_create("producer", BUFFER_SIZE, RT_IPC_FLAG_FIFO);
    consumer_sem = rt_sem_create("consumer", 0, RT_IPC_FLAG_FIFO);
    buffer_mutex = rt_mutex_create("buffer", RT_IPC_FLAG_FIFO);
    rt_thread_create("producer", producer_thread_entry, RT_NULL, 512, 7, 20);
    rt_thread_create("consumer", consumer_thread_entry, RT_NULL, 512, 7, 20);
    return 0;
}

2.3 消息队列

消息队列用于线程间的异步通信,一个线程可以将一个或多个数据块(消息)发送到队列中,另一个线程可以从队列中读取这些消息。

  • 创建rt_mq_create()
  • 发送rt_mq_send() / rt_mq_urgent() (紧急消息)
  • 接收rt_mq_recv()
  • 删除rt_mq_delete()

2.4 互斥量

互斥量是一种特殊的二值信号量,专门用于解决线程间对共享资源的互斥访问问题,具有优先级继承特性,可以避免优先级反转。

  • 创建rt_mutex_create()
  • 获取rt_mutex_take()
  • 释放rt_mutex_release()

2.5 事件标志组

事件标志组用于线程间的同步,允许一个线程等待多个事件中的任意一个(OR)或所有事件(AND)发生。

  • 创建rt_event_create()
  • 发送/接收rt_event_send() / rt_event_recv()

3 设备框架

RT-Thread 的设备框架统一了所有设备的操作接口,使得上层应用可以以统一的方式访问不同类型的设备(如串口、SPI、I2C、GPIO)。

  • 设备注册rt_device_register()
  • 设备查找rt_device_find()
  • 设备控制rt_device_control()
  • 读写操作rt_device_read() / rt_device_write()

示例:使用设备框架操作串口

#define UART_NAME "uart2" // 根据你的板子修改
int main(void)
{
    rt_device_t uart_dev;
    // 查找设备
    uart_dev = rt_device_find(UART_NAME);
    if (uart_dev == RT_NULL) {
        rt_kprintf("find %s failed.\n", UART_NAME);
        return -1;
    }
    // 打开设备
    rt_device_open(uart_dev, RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_RDWR);
    // 写数据
    rt_device_write(uart_dev, 0, "Hello RT-Thread!\n", 18);
    // 读数据 (需要中断配合)
    // ...
    return 0;
}

4 文件系统

RT-Thread 支持多种文件系统,如 FatFs (SD卡)、LittleFS (SPI Flash) 等,通过统一的虚拟文件系统接口,你可以像操作 PC 上的文件一样操作存储设备。

5 网络框架

RT-Thread 集成了轻量级的 TCP/IP 协议栈(如 LwIP),支持 Socket 编程,可以轻松实现 TCP/UDP 通信、HTTP 客户端/服务器、MQTT 等。

6 软件包

RT-Thread 的软件包管理器 pkgs 是其强大生态的核心,通过 menuconfig 命令行工具,你可以轻松地搜索、选择、配置和下载各种软件包。

  • 进入配置:在工程目录下运行 scons --menuconfig
  • 导航Software Packages -> 选择你需要的包 -> 保存并退出。
  • 重新编译:运行 scons 即可。

第四部分:常用组件详解与实践

1 FinSH 命令行

FinSH 是 RT-Thread 提供的一个命令行接口,用于调试、系统管理和快速功能测试。

  • 启用:在 menuconfig 中开启 FinSH 组件。
  • 使用
    • 通过串口工具连接,默认波特率通常是 115200。
    • 输入 help 查看所有可用命令。
    • 输入 ps 查看系统所有线程及其状态。
    • 输入 list_thread 查看线程详细信息。
    • 你可以自定义自己的命令,非常方便。

2 设备虚拟文件系统

RT-Thread 将所有设备都映射到 /dev 目录下,使得应用可以通过文件 API 来操作设备。

/dev/uart1 代表串口1,/dev/spi1 代表 SPI1 总线。

3 常用软件包介绍

  • CJSON:一个轻量级的 JSON 解析器,用于处理物联网数据。
  • LittlevGL:一个功能强大的嵌入式图形库,用于创建漂亮的 UI 界面。
  • MQTT Client:用于设备与云平台(如阿里云、腾讯云)的通信。
  • LVGL (LittlevGL):同上,用于开发图形界面。

第五部分:实战项目

1 项目概述:智能温湿度监测系统

本项目将实现一个基于 DHT11 传感器的温湿度监测系统,系统将:

  1. 通过 DHT11 传感器实时读取温湿度数据。
  2. 将数据通过串口打印出来。
  3. 通过 FinSH 命令可以随时查询当前温湿度。
  4. (可选) 将数据通过 MQTT 协议上传到云平台。

2 硬件连接

  • DHT11 数据引脚 -> 开发板的 PA0 (或其他 GPIO)。
  • DHT11 VCC -> 3.3V。
  • DHT11 GND -> GND。

3 软件实现

3.1 创建工程

使用 RT-Thread Studio 创建一个新的 B 工程。

3.2 编写传感器驱动 (基于设备框架)

为了代码的复用性,我们将 DHT11 封装成一个 RT-Thread 设备。

  1. 创建 dht11.h

    #ifndef __DHT11_H__
    #define __DHT11_H__
    #include <rtthread.h>
    #include <rtdevice.h>
    #define DHT11_DEVICE_NAME "dht11" // 设备名称
    // 定义温湿度数据结构
    struct dht11_data {
        float temperature;
        float humidity;
    };
    // 注册 DHT11 设备
    int rt_hw_dht11_init(const char *name, rt_uint16_t pin);
    #endif
  2. 创建 dht11.c

    #include "dht11.h"
    #include <board.h>
    // DHT11 驱动实现代码
    // ... (这里省略具体的 DHT11 通信协议实现,包括开始信号、数据读取、校验等)
    // 假设我们已经实现了 _dht11_read_data 函数来读取原始数据
    static rt_device_t dht11_dev = RT_NULL;
    // 设备读取函数
    static rt_size_t _dht11_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
    {
        struct dht11_data *data = (struct dht11_data *)buffer;
        // 调用底层读取函数
        if (_dht11_read_data(data) == RT_EOK) {
            return sizeof(struct dht11_data);
        }
        return 0;
    }
    // 设备初始化
    int rt_hw_dht11_init(const char *name, rt_uint16_t pin)
    {
        static struct rt_device_device dht11_device;
        dht11_device.type = RT_Device_Class_Miscellaneous; // 杂项设备
        dht11_device.read = _dht11_read;
        dht11_dev = rt_device_register(name, &dht11_device, RT_DEVICE_FLAG_RDONLY, RT_NULL);
        if (dht11_dev == RT_NULL) {
            return -RT_ERROR;
        }
        return RT_EOK;
    }
  3. 在 main.c 中初始化

    #include <rtthread.h>
    #include "dht11.h"
    int main(void)
    {
        // 初始化 DHT11 设备,引脚为 PA0
        rt_hw_dht11_init(DHT11_DEVICE_NAME, GET_PIN(A, 0));
        while (1)
        {
            struct dht11_data data;
            rt_size_t read_len = rt_device_read(dht11_dev, 0, &data, sizeof(data));
            if (read_len == sizeof(data)) {
                rt_kprintf("Temperature: %.2f C, Humidity: %.2f %%\n", data.temperature, data.humidity);
            } else {
                rt_kprintf("Read DHT11 failed.\n");
            }
            rt_thread_mdelay(2000); // 每2秒读取一次
        }
        return 0;
    }

3.3 创建数据采集线程

上面的 main 函数已经是一个简单的数据采集线程,为了更规范,我们可以创建一个独立的线程。

// 在 main.c 中
void sensor_thread_entry(void *param)
{
    rt_device_t dht11_dev = rt_device_find(DHT11_DEVICE_NAME);
    if (dht11_dev == RT_NULL) {
        rt_kprintf("Find DHT11 device failed.\n");
        return;
    }
    rt_device_open(dht11_dev, RT_DEVICE_FLAG_RDONLY);
    while (1)
    {
        struct dht11_data data;
        rt_size_t read_len = rt_device_read(dht11_dev, 0, &data, sizeof(data));
        if (read_len == sizeof(data)) {
            rt_kprintf("Temperature: %.2f C, Humidity: %.2f %%\n", data.temperature, data.humidity);
        }
        rt_thread_mdelay(2000);
    }
}
int main(void)
{
    rt_hw_dht11_init(DHT11_DEVICE_NAME, GET_PIN(A, 0));
    rt_thread_create("sensor", sensor_thread_entry, RT_NULL, 512, 6, 20);
    return 0;
}

3.4 通过 FinSH 命令查看数据

我们可以注册一个 FinSH 命令来查询温湿度。

  1. 在 main.c 中添加命令函数

    #include <rtthread.h>
    #include "dht11.h"
    #include <shell.h> // 包含 shell 头文件
    void dht11_cmd(int argc, char **argv)
    {
        rt_device_t dht11_dev = rt_device_find(DHT11_DEVICE_NAME);
        if (dht11_dev == RT_NULL) {
            rt_kprintf("Find DHT11 device failed.\n");
            return;
        }
        struct dht11_data data;
        rt_size_t read_len = rt_device_read(dht11_dev, 0, &data, sizeof(data));
        if (read_len == sizeof(data)) {
            rt_kprintf("Current Temperature: %.2f C, Humidity: %.2f %%\n", data.temperature, data.humidity);
        } else {
            rt_kprintf("Read DHT11 failed.\n");
        }
    }
    // MS_CMD_CMD_EXPORT 是一个宏,用于导出命令
    // 参数: 命令名, 函数, 描述
    MSH_CMD_EXPORT(dht11_cmd, "Get current temperature and humidity from DHT11");
  2. 使用:编译下载后,在 FinSH 中输入 dht11_cmd 即可查询当前温湿度。

3.5 (可选) 通过 MQTT 上传数据到云平台

  1. 启用软件包:在 menuconfig 中开启 mqtt client 软件包。
  2. 编写 MQTT 客户端代码:连接云平台,设置 Topic,然后定时调用 mqtt_publish() 发布数据。
  3. 将温湿度数据打包成 JSON 格式:使用 CJSON 库。

第六部分:资源与进阶

1 官方文档与社区

2 推荐书籍

  • 《RT-Thread 内核实现与应用开发实战指南》 - 官方出品的权威书籍。

3 进阶学习方向

  • 深入内核:学习调度算法、内存管理、中断处理等底层机制。
  • 驱动开发:学习如何为新的硬件编写设备驱动。
  • 低功耗:学习 RT-Thread 的低功耗管理机制,应用于电池供电设备。
  • 安全框架:了解 RT-Thread 的安全组件,如 TrustZone 支持。
  • AIoT:结合 AI 框架(如 TensorFlow Micro)和 RT-Thread,开发智能物联网设备。
分享:
扫描分享到社交APP
上一篇
下一篇