RT-Thread 综合教程:从零开始的嵌入式系统开发
目录
-
第一部分:初识 RT-Thread
(图片来源网络,侵删)- 1 什么是 RT-Thread?
- 2 RT-Thread 的核心特点与优势
- 3 RT-Thread 的应用领域
- 4 为什么选择 RT-Thread?
-
第二部分:开发环境搭建
- 1 硬件准备
- 2 软件安装
- 2.1 安装 RT-Thread Studio (推荐新手)
- 2.2 安装 VS Code + 插件 (推荐进阶)
- 3 第一个工程:Blinky (LED闪烁)
-
第三部分: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 软件包
-
第四部分:常用组件详解与实践
- 1 FinSH 命令行
- 2 设备虚拟文件系统
- 3 常用软件包介绍 (如: CJSON, LittlevGL, MQTT)
-
第五部分:实战项目
(图片来源网络,侵删)- 1 项目概述:智能温湿度监测系统
- 2 硬件连接
- 3 软件实现
- 3.1 创建工程
- 3.2 编写传感器驱动 (基于设备框架)
- 3.3 创建数据采集线程
- 3.4 通过 FinSH 命令查看数据
- 3.5 (可选) 通过 MQTT 上传数据到云平台
-
第六部分:资源与进阶
- 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 的应用范围非常广泛。

4 为什么选择 RT-Thread?
- 对新手友好:完善的文档、教程和工具链,降低了嵌入式开发的门槛。
- 功能强大:一站式解决方案,无需自己拼接多个开源库。
- 社区活跃:国内社区非常活跃,遇到问题容易找到帮助。
- 国产化趋势:在国家政策支持下,RT-Thread 在国内的应用越来越广泛。
第二部分:开发环境搭建
1 硬件准备
- 开发板:推荐一块 RT-Thread 官方支持或社区热门的开发板。
- 初学者推荐:
- 野火 STM32F103 Mini 开发板
- 正点原子 STM32F103 开发板
- 安富莱 STM32H743 开发板
- 这些开发板资料丰富,社区支持好。
- 初学者推荐:
- USB 数据线:用于连接电脑和开发板,供电和下载程序。
2 软件安装
2.1 安装 RT-Thread Studio (推荐新手)
这是最简单、最推荐的方式,尤其适合初学者。
- 下载:访问 RT-Thread 官网 下载并安装 RT-Thread Studio。
- 启动:安装完成后,启动 Studio。
- 新建工程:
- 选择
File -> New -> RT-Thread Project。 - 选择目标板型号(
Fire-Apm32F103-V1)。 - 选择工具链(如
GCC)。 - 工程模板选择
Hello, RT-Thread。
- 选择
- 编译与下载:
- 点击工具栏的“锤子”图标编译工程。
- 点击“闪电”图标将程序下载到开发板。
- 按下开发板的复位按钮,你应该能在串口助手上看到 "hello rt-thread!" 的打印信息。
2.2 安装 VS Code + 插件 (推荐进阶)
如果你习惯使用 VS Code,这种方式更灵活。
- 安装 VS Code:从 官网 下载并安装。
- 安装插件:
C/C++(Microsoft)RT-Thread Assistant(官方插件)
- 创建工程:
- 使用 RT-Thread 官方提供的
env工具创建一个工程目录。 - 在 VS Code 中打开该目录。
- 使用
RT-Thread Assistant插件来配置、编译和下载工程。
- 使用 RT-Thread 官方提供的
3 第一个工程:Blinky (LED闪烁)
无论使用哪种 IDE,创建一个 Blinky 工程都非常简单。
-
硬件连接:将开发板上的一个 LED 连接到一个 GPIO 引脚(PA5)。
-
代码编写:
- 在
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; } - 在
-
编译与运行:编译并下载程序,你将看到 LED 以 1Hz 的频率闪烁,这个例子展示了 RT-Thread 线程 的基本用法。
第三部分:RT-Thread 核心概念与组件
1 系统架构
RT-Thread 的架构可以分为四层:
- 硬件层:CPU、内存、外设等。
- 内核层:提供最基础的调度、同步、通信等机制。
- 组件层:文件系统、网络框架、设备框架等。
- 应用层:用户自己编写的业务逻辑。
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 传感器的温湿度监测系统,系统将:
- 通过 DHT11 传感器实时读取温湿度数据。
- 将数据通过串口打印出来。
- 通过 FinSH 命令可以随时查询当前温湿度。
- (可选) 将数据通过 MQTT 协议上传到云平台。
2 硬件连接
- DHT11 数据引脚 -> 开发板的 PA0 (或其他 GPIO)。
- DHT11 VCC -> 3.3V。
- DHT11 GND -> GND。
3 软件实现
3.1 创建工程
使用 RT-Thread Studio 创建一个新的 B 工程。
3.2 编写传感器驱动 (基于设备框架)
为了代码的复用性,我们将 DHT11 封装成一个 RT-Thread 设备。
-
创建 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 -
创建 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; } -
在 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 命令来查询温湿度。
-
在 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"); -
使用:编译下载后,在 FinSH 中输入
dht11_cmd即可查询当前温湿度。
3.5 (可选) 通过 MQTT 上传数据到云平台
- 启用软件包:在
menuconfig中开启mqtt client软件包。 - 编写 MQTT 客户端代码:连接云平台,设置 Topic,然后定时调用
mqtt_publish()发布数据。 - 将温湿度数据打包成 JSON 格式:使用 CJSON 库。
第六部分:资源与进阶
1 官方文档与社区
- 官网:https://www.rt-thread.org
- 文档中心:https://www.rt-thread.org/document (最权威、最全面的资料)
- 社区论坛:https://club.rt-thread.org (遇到问题,首选求助地)
- GitHub:https://github.com/RT-Thread/rt-thread (源码)
2 推荐书籍
- 《RT-Thread 内核实现与应用开发实战指南》 - 官方出品的权威书籍。
3 进阶学习方向
- 深入内核:学习调度算法、内存管理、中断处理等底层机制。
- 驱动开发:学习如何为新的硬件编写设备驱动。
- 低功耗:学习 RT-Thread 的低功耗管理机制,应用于电池供电设备。
- 安全框架:了解 RT-Thread 的安全组件,如 TrustZone 支持。
- AIoT:结合 AI 框架(如 TensorFlow Micro)和 RT-Thread,开发智能物联网设备。
