官方文档
RT-Thread 标准版本。
系统架构
可以看到除了作为一个RTOS的内核部分之外,上层还添加了很多的组件和服务,这也是RT-Thread相比于其他RTOS的优点之一。
源码文件结构
结合RT-Thread的源码文件结构再来看看。
libcpu
首先libcpu是针对不同的MCU所做的底层适配。对应上图系统架构中的最底层,可以看到有着很多不同的芯片架构,例如最常见的ARM、RISC-V、MIPS等。不同的板子可能使用不同的芯片,所以需要通过这一层整合、屏蔽掉不同的芯片所带来的不同的指令集、寄存器等方面的影响。 具体来说的话,举个例子:
假如我现在想关闭系统的总中断,也就是想让系统暂时不响应任何可屏蔽中断,如果在ARM架构下,可以使用CPSID I指令来关闭所有可屏蔽的中断。而在RISC-V架构下,则使用csrrci a0, mstatus, 8。那么现在RT-Thread用一个对外API来封装这个功能,叫做rt_hw_interrupt_disable(),上层就可以统一使用这个函数来实现全局中断关闭了,而不需要关心底层是用的什么芯片架构。这个函数的实现就是在libcpu这个目录下的各个架构所对应的文件夹里。每个芯片都实现了自己对应的功能,名字都叫rt_hw_interrupt_disable()。
目前支持的所有架构如下图。
src
src文件夹中的内容也就是上图系统架构中内核层的RT-Thread内核。 也就是说src中是作为一个RTOS的内核源码。这其中就包括了多线程及其调度、信号量、邮箱、消息队列、内存管理、定时器等。 在这些源码中,就使用到了上一层libcpu中所适配的API来对线程进行创建、对临界区进行保护等。 如果只是想使用一个RTOS的内容,即线程创建、调度、线程间通信、线程间同步等,那么到这里为止RT-Thread的内容就基本结束了,这也就是RT-Thread nano版本的内容:仅仅是一个极小的RTOS内核。
components
components文件夹中对应的就是系统架构中的组件与服务层的内容。 包括文件系统、设备驱动框架、finsh组件、网络框架等。当使用nano版本,也就是仅仅只存在内核的版本时,这一层是用不到的。 components中的组件和服务框架,通过更进一步的封装,能够实现对上层即应用层提供统一API,能够让应用层实现对底层硬件的完全屏蔽。一旦底层适配完毕,那么应用层的代码就可以完全不用修改的进行不同芯片下的移植工作,复用性极高。
BSP
BSP全称Board Support Package,在RT-Thread的架构中,BSP的概念更多的是通过下层components即组件与服务层所提供的API来移植板载的一些硬件外设,如最基本的GPIO和UART。 BSP与具体的硬件开发板密切相关。不同的厂商所生产的板子可能在硬件外设的搭载上有所不同,所以需要对具体的开发板做硬件相关适配。 举个例子:
在STM32的STM32F103-atk-nano这个开发板的底层驱动库中,要想写GPIO,需要调用 HAL_GPIO_WritePin() 函数;而在同样是 Cortex-M3 芯片架构的GD32F107C-evel开发板中,同样的功能则需要调用 gpio_bit_write() 函数,那么这样在进行不同开发板的切换时就需要做很多的非必要工作。 那么在RT-Thread中的 BSP 中,通过下层组件和服务层所提供的驱动框架中的 rt_device_write() 函数适配,应用层就可以不需要关心硬件外设驱动叫什么,只需要调用 rt_device_write() 即可对GPIO进行写操作。 这就是RT-Thread中 BSP 的作用。
可以看到 BSP 中存在大量的对不同板子的适配。官方也一直在更新BSP支持。如果你有能力编写一个存在市场、功能完备的BSP,也可以通过pull-request来提交给官方,通过之后就会被纳入这个目录下了。
工程目录
这是一个典型的RT-Thread工程目录(MDK)。
文件夹名 | 文件来源 | 文件夹作用 |
---|
Applications | bsp/Applications | 应用层代码,使用BSP所适配的API来调用底层硬件外设和组件 | CPU | libcpu/arm | 对开发板所使用的芯片的适配,对上层提供API来操作全局中断与线程创建等 | DeviceDrivers | components/drivers | 组件与服务层内容,实现外设驱动框架,对上层提供API来对硬件外设操作 | Drivers | bsp/drivers | 使用开发板厂商所提供的硬件驱动库对DeviceDrivers中所提供的API进行具体实现 | Finsh | components/finsh | 组件与服务层内容,使用硬件外设的UART功能来模拟控制台的调试与打印等功能 | Kernel | src/ | RT-Thread内核源码,提供最基本的RTOS内容 | Libraries | bsp/Libraries | 开发板所对应的硬件驱动库,用于驱动硬件外设 |
一个RT-Thread还可能包含更多的文件组,但是基本都会包含上图中最基础的文件组。也就是说一个标准的RT-Thread工程至少需要这些功能来实现整体架构。
小结
通过对整体架构的了解,可以总结出要让开发板移植RT-Thread,一般需要:
- 如果开发板的芯片架构是常见的如 ARM 、RISC-V 等,或是在 libcpu 中已存在的,那么直接使用即可。可以放在项目工程中的 CPU 文件组中;如果开发板的芯片架构在 libcpu 文件夹中并没有具体实现,就需要根据芯片架构来自行实现RT-Thread所规定必须的API。
- 将开发板的硬件外设驱动库放入项目工程中。一般由厂商提供,不需要修改。
- 将RT-Thread的内核源码放入项目工程中,这些都是RT-Thread所提供的,不需要修改。
- 将 components/drivers 中需要使用的硬件外设驱动框架和 components/finsh 内容放入项目工程中。这些都是RT-Thread所提供的,不需要修改。
- 尽量了解开发板的硬件驱动库中的基础函数的功能及使用方式,如GPIO的初始化、读、写,UART的初始化、参数配置、读、写,了解RT-Thread的设备驱动框架。然后在设备驱动框架下对具体的硬件外设做适配。可以存放在项目工程中的Drivers文件组中。
- 在已完成的设备驱动下,在Applicaitons文件组中通过统一的设备驱动API来操作硬件外设,编写应用程序。
当然中间还有很多的小细节需要注意,例如系统时钟的配置、内存堆的配置、中断系统的配置等,不过一个原始的标准RT-Thread工程移植总体的流程大概就是这样。
|