RT-Thread,全称是 Real Time-Thread,顾名思义,它是一个嵌入式实时多线程操作系统。
RT-Thread 主要采用 C 语言编写,浅显易懂,方便移植。它把面向对象的设计方法应用到实时系统设计中,使得代码风格优雅、架构清晰、系统模块化并且可裁剪性非常好。针对资源受限的微控制器(MCU)系统,可通过方便易用的工具,裁剪出仅需要 3KB Flash、1.2KB RAM 内存资源的 NANO 版本(NANO 是 RT-Thread 官方于 2017 年 7 月份发布的一个极简版内核);而对于资源丰富的物联网设备,RT-Thread 又能使用在线的软件包管理工具,配合系统配置工具实现直观快速的模块化裁剪,无缝地导入丰富的软件功能包,实现类似 Android 的图形界面及触摸滑动效果、智能语音交互效果等复杂功能。
近年来,物联网(Internet Of Things,IoT)概念广为普及,物联网市场发展迅猛,嵌入式设备的联网已是大势所趋。终端联网使得软件复杂性大幅增加,传统的 RTOS 内核已经越来越难满足市场的需求,在这种情况下,物联网操作系统(IoT OS)的概念应运而生。物联网操作系统是指以操作系统内核(可以是 RTOS、Linux 等)为基础,包括如文件系统、图形库等较为完整的中间件组件,具备低功耗、安全、通信协议支持和云端连接能力的软件平台,RT-Thread 就是一个 IoT OS。
RT-Thread 与其他很多 RTOS 如 FreeRTOS、uC/OS 的主要区别之一是,它不仅仅是一个实时内核,还具备丰富的中间层组件,如下图所示。
它具体包括以下部分:
1、内核层:RT-Thread 内核,是 RT-Thread 的核心部分,包括了内核系统中对象的实现,例如多线程及其调度、信号量、邮箱、消息队列、内存管理、定时器等;libcpu/BSP(芯片移植相关文件 / 板级支持包)与硬件密切相关,由外设驱动和 CPU 移植构成。
2、组件与服务层:组件是基于 RT-Thread 内核之上的上层软件,例如虚拟文件系统、FinSH 命令行界面、网络框架、设备框架等。采用模块化设计,做到组件内部高内聚,组件之间低耦合。
3、RT-Thread 软件包:运行于 RT-Thread 物联网操作系统平台上,面向不同应用领域的通用软件组件,由描述信息、源代码或库文件组成。RT-Thread 提供了开放的软件包平台,这里存放了官方提供或开发者提供的软件包,该平台为开发者提供了众多可重用软件包的选择,这也是 RT-Thread 生态的重要组成部分。软件包生态对于一个操作系统的选择至关重要,因为这些软件包具有很强的可重用性,模块化程度很高,极大的方便应用开发者在最短时间内,打造出自己想要的系统。RT-Thread 已经支持的软件包数量已经达到 60+,如下举例:
<1>物联网相关的软件包:Paho MQTT、WebClient、mongoose、WebTerminal 等等。
<2>脚本语言相关的软件包:目前支持 JerryScript、MicroPython。
多媒体相关的软件包:Openmv、mupdf。
<3>工具类软件包:CmBacktrace、EasyFlash、EasyLogger、SystemView。
<4>系统相关的软件包:RTGUI、Persimmon UI、lwext4、partition、SQLite 等等。
<5>外设库与驱动类软件包:RealTek RTL8710BN SDK。
其他。
该版本适用于需要使用RT-Thread的丰富功能,如各类外设、物联网组件、软件包等的场景。
适用于系统资源紧张或是项目功能较为简单,仅需使用RTOS内核,无需丰富功能的场景,且开发时无需额外的辅助工具。
1、applications RT-Thread 应用程序。
2、rt-thread RT-Thread 的源文件。
- components RT-Thread 的各个组件目录。
- include RT-Thread 内核的头文件。
- libcpu 各类芯片的移植代码,此处包含了 STM32 的移植文件。
- src RT-Thread 内核的源文件。
- tools RT-Thread 命令构建工具的脚本文件。
-
3、drivers RT-Thread 的驱动,不同平台的底层驱动具体实现。
4、Libraries STM32 固件库文件。
5、kernel-sample-0.1.0 RT-Thread 的内核例程。
Applications 它用于存放用户应用代码。
Drivers 它用于存放 RT-Thread 底层的驱动代码。
STM32_HAL 它用于存放 STM32 的固件库文件。
kernel-sample 它用于存放 RT-Thread 的内核例程。
Kernel 它用于存放 RT-Thread 内核核心代码。
CORTEX-M3 它用于存放 ARM Cortex-M3 移植代码。
DeviceDrivers 它用于存放 RT-Thread 驱动框架源码。
finsh 它用于存放 RT-Thread 命令行 finsh 命令行组件。
一般了解一份代码大多从启动部分开始,同样这里也采用这种方式,先寻找启动的源头,因为 MDK-ARM 的用户程序入口为 main() 函数,所以先看看 main() 函数在哪个文件中。这里的 main() 函数位于 Startup 组的 startup.c 文件中,它位于 STM32 的启动汇编代码后,与 C 代码的入口跳转前。启动汇编在 STM32_HAL 组的 startup_stm32f103xe.s 中。
下面我们来看看 main() 函数中的这段代码:
//components.c 中定义
/* re-define main function */
int $Sub$$main(void)
{
rt_hw_interrupt_disable();
rtthread_startup();
return 0;
}
在这里 S u b Sub Sub$main 函数仅仅调用了 rtthread_startup() 函数。RT-Thread 支持多种平台和多种编译器,而 rtthread_startup() 函数是 RT-Thread 规定的统一入口点,所以 S u b Sub Sub$main 函数只需调用 rtthread_startup() 函数即可。例如采用 GNU GCC 编译器编译的 RT-Thread,就是直接从汇编启动代码部分跳转到 rtthread_startup() 函数中,并开始第一个 C 代码的执行的。在 components.c 的代码中找到 rtthread_startup() 函数,我们将可以看到 RT-Thread 的启动流程:
int rtthread_startup(void)
{
rt_hw_interrupt_disable();
/* board level initalization
* NOTE: please initialize heap inside board initialization.
*/
rt_hw_board_init();
/* show RT-Thread version */
rt_show_version();
/* timer system initialization */
rt_system_timer_init();
/* scheduler system initialization */
rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
/* signal system initialization */
rt_system_signal_init();
#endif
/* create init_thread */
rt_application_init();
/* timer thread initialization */
rt_system_timer_thread_init();
/* idle thread initialization */
rt_thread_idle_init();
/* start scheduler */
rt_system_scheduler_start();
/* never reach here */
return 0;
}
#endif
这部分启动代码,大致可以分为四个部分 :
1 初始化与系统相关的硬件;
2 初始化系统内核对象,例如定时器,调度器;
3 初始化系统设备,这个主要是为 RT-Thread的设备框架做的初始化;
4 初始化各个应用线程,并启动调度器。
上面的启动代码基本上可以说都是和 RT-Thread 系统相关的,那么用户如何加入自己的应用程序的初始化代码呢?RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。
int main(void)
{
/* user app entry */
return 0;
}
为了在进入 main 程序之前,完成系统功能初始化,可以使用 $sub 和 $super 函数标识符在进入主程序之前调用另外一个例程,这样可以让用户不用去管 main() 之前的系统初始化操作。