01
为什么要进行系统裁剪
在设计嵌入式RTOS系统逻辑时,我们往往希望系统简洁并且代码可控,这样我们做方案时心里才有底。下面我们来从原理层面和实现层面上讲一下rt-thread裁剪相关的知识。
嵌入式的本质特点如下:
1.以应用为中心
2.功耗低,体积小,成本价低
3.软件或硬件可以裁剪
在设计嵌入式产品的过程中,可以充分的考虑这些问题,所以在实现时,我们不仅仅考虑的是系统层面的问题,而更应该关注于业务逻辑上的需求。
1.应用领域
2.研发周期
3.系统复杂度
4.芯片资源情况(ROM and RAM)
5.固件大小要求
6.启动速度要求
从嵌入式系统架构的角度,一定需要符合高内聚,低耦合的思想。
对于裸机开发来说,系统裁剪意味着一个轮训操作中缩减或者优化业务逻辑,多用标志或者返回状态来进行程序设计。
对于Linux来说,系统裁剪就是将不必要的组件或者外设和Linux中的工具去掉,减少系统功能,在uboot中也需要裁剪,合理设计必须初始化的外设,合理算出Linux kernel大小,然后进行跳转。
对于RTOS来说,系统裁剪可以通过对功能的缩减,减少不必要的组件初始化或者ipc初始化,例如,只需要用到关键的ipc比如信号,邮箱,那就不用初始化消息队列和事件,这样就能缩减代码体积,减少不必要的初始化过程。另外就是业务逻辑的设计了,裁剪的目的一方是缩减固件体积,另外一方面也是加快系统的启动速度,所以要充分考虑驱动初始化的流程,在RTOS设计中,多采用设备并行初始化。
02
rt-thread具有高度的可裁剪性
RT-Thread 主要采用 C 语言编写,浅显易懂,方便移植。它把面向对象的设计方法应用到实时系统设计中,使得代码风格优雅、架构清晰、系统模块化并且可裁剪性非常好。完整版的 RT-Thread 与其他很多RTOS 如 FreeRTOS、uC/OS 的主要区别之一是,它不仅仅是一个实时内核,还具备丰富的中间层组件,如下图所示:
完整版功能虽然十分完善,不过相应的,随着功能的增加,资源占用也是在增加的,这对小资源的平台 来说不是十分友好。得益于 RT-Thread 的高度可裁剪性,通过对完整版的裁剪,可以十分便捷的小资源 平台上使用上 RT-Thread。可以通过 env 工具十分方便地进行裁剪,env 工具使用方法如下图所示:
03
资源规划
这里的资源指的是硬件资源和软件资源两个方面,硬件资源从设计的时候就基本已经确定,在与软件进行合理的规划和设计之后,硬件方案就可以确定下来,相关引脚的分配,上电的时序,操作的合理性,以及考虑到功耗的要求,外设的情况,前期软件开发调试,后期工厂测试,这时,整个硬件方案就可以确定下来了,软件开发者利用这个确定的硬件资源,进行软件层面的规划,所以在设计软件的时候,是完全知道硬件的资源的,包括引脚的使用,上电的逻辑,CPU的情况,可以利用的RAM或者ROM资源。这一部分是架构的工作,一个好的硬件设计方案,将使得成型的产品更加稳定可靠。所以硬件的设计也是可裁剪的,因为前期的开发调试与后期的产品成型后的工厂测试板子的设计上是不能变动的,只可以通过裁剪将其区分出来。这个不仅仅是硬件工程师需要了解,软件工程师也需要有产品意识。
对于RT-Thread中,比较关心的是ROM资源以及RAM资源,所以在设计之初,首先评估资源是否合理去跑一个rtos。
比如,rt-thread nano版本对 RAM 与 ROM 的开销非常小,在支持 semaphore 和 mailbox 特性,并运行两个线程 (main 线程 + idle 线程) 情况下,ROM 和 RAM 依然保持着极小的尺寸,RAM 占用约 1K 左右,ROM 占用 4K 左右。
对于RAM 与 ROM资源比较大的场合,合理的利用每个资源也是比较好的习惯,这里首先分析一下rt-thread内存的使用与裁剪。
1.堆空间
一般在具体的bsp的board.h中,都会有堆空间的描述
#define RT_HW_HEAP_BEGIN (void*)&__bss_end
#define RT_HW_HEAP_END (void*)(RT_HW_HEAP_BEGIN + 64 * 1024 * 1024)
在系统设计的时候,评估系统所需要的堆空间。
堆空间的使用,一般是动态申请内存或者是动态创建线程,创建IPC的时候会使用到。当然,如果我们不需要内存管理功能,那么就可以去掉堆空间的使用,所有的线程通过静态创建的方式进行,比较典型的就是rt-thread nano的使用。
2.线程栈空间
我们在使用的时候,一般都会给定一个栈空间去运行线程,所以创建线程的时候,携带了给定的最大运行栈
rt_thread_t rt_thread_create(const char *name,
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
其中栈的尺寸stack_size一般都是固件函数调用深度和可能需要的最大资源来给定,一般刚开始开发的时候,给定的都是最大值,如果要进行裁剪,有三种方法:
1.让系统运行一段时间,通过命令行list_thread来调整栈空间。
2.在MDK中,可以查看Static Call Graph for image文件来查看栈的使用情况
通过函数调用关系,计算最大栈的深度。
3.手动计算
评估局部变量和每个函数中的调用关系,这个需要手动去计算。
04
内核裁剪
rt-thread的组成就是组件+内核,我们先说一下内核裁剪部分,通过env工具可以看到如下功能:
1.线程间通信机制
根据我们系统中常用的一下功能或者组件进行裁剪
2.内存管理
根据具体的情况选择不同的内存管理策略。
3.内核设备
有关console设置。
05
组件裁剪
rt-thread是内核+组件的方式,使用一些组件可以帮助我们更高效的设计出具体的业务逻辑。
这部分裁剪可以根据需要进行
1.设置main线程的栈空间及优先级
2.是否使用C++特性
3.shell相关的操作的配置
4.设备虚拟文件系统
5.设备驱动框架
6.POSIX接口
7.网络部分
8.工具部分
这些裁剪是在设计方案的时候选择是否需要。
06
业务逻辑裁剪
在使用rt-thread操作系统时,往往都是利用rt-thread实现自己的业务逻辑,所以我们在编写自己的代码的时候,也需要充分的理解rt-thread的设计思想。以及我们在做设计的时候,可能需要配置一些可以调整的策略方案进行多角度,多方位的尝试,这时可以利用Kconfig与SConscript相关的脚本进行操作,本质上来说就是在代码中添加一些宏定义,这些配置可以通过图形化的方式选择或者去掉。scons脚本的使用以及env相关的操作可以通过官网文档查阅。
需要注意的是,在设计时,我们考虑的不仅仅是这些宏定义的依赖关系,还需要从使用的角度来考虑,比如代码是否编译进去,选择后,其依赖关系是怎样的?
举个例子,比如摄像头可以输出YUV格式的图像,也可以输出RGB的图像,通过不同的寄存器配置进行,这里我们可以将这个宏利用Kconfig变成图形可选择的对象,然后依赖这些宏后,在进行图像处理的时候,利用rtconfig.h所定义的宏,来选择不同的处理图像的模式代码。这样代码的健壮性更强,也不会去干扰其他的业务逻辑了,并且这些配置也更加的直观。
免责声明:本文系网络转载,版权归原作者所有。如涉及作品版权问题,请与我们联系,我们将根据您提供的版权证明材料确认版权并支付稿酬或者删除内容。