【RT-Thread】将BSP工程从RT-Thread源码中独立出来开发(1)

前提

在项目开发过程中,经常遇到的开发模式是,在RTT源码路径下的bsp下,建立一个bsp文件夹,进行应用的开发,这是官方推荐的做法。

但是在实际的项目开发过程中,往往遇到的情况是,每个项目包含了整个RTT源码,在做版本控制的时候,不同项目都会提交大量重复代码,随着RTT的发展,bsp数量也越来越多,一个RTT源码文件夹可达数G。

每当看到RTT更新了内核,或者添加了一些有意思的功能,看着手里的项目,想要更新内核,需要保留层层路径下的bsp,然后删掉RTT源码,然后下载新的(半天过去了),再删掉很多我不需要的bsp,放回我原来的bsp,尝试编译,编译失败,卒。

遂思考,RTT是否可以像ESP IDF一样,开发的时候独立于SDK,只需关心app开发,源码由构建阶段自动寻找,这样我的每个项目大约只有一个bsp。理论上应该是可行的,记录折腾过程如下:

制作单独的bsp

首先准备一个bsp工程,步骤如下

  1. 从RT-Thread官方clone源码到某个目录,我的目录为E:\Workspace\01.software\Embeded_Project\rt-thread
  2. 从官方源码的bsp中复制一个bsp或者按照官方文档从模板建立一个bsp工程到RTT源码外的路径下
  3. 根据bsp对应的芯片型号进行适配和移植,参考官方文档

在bsp路径下进入env工具,但此时bsp是无法编译的,由于更换了bsp的路径,原本的构建系统可能会找不到某些文件,我们继续分析。

通过使用 scons --target=cmake,我们尝试通过scons生成一个cmake工程,但是出现了以下错误,独立出来的bsp是找不到原来的RTT源码在什么路径的:

$ scons --target=cmake
scons: Reading SConscript files ...
Cannot found RT-Thread root directory, please check RTT_ROOT

分析Scons寻找源码的方式

打开bsp根路径下的 Sonstruct, 在文件的开头有如下代码,代表构建时会从系统环境变量中查找 RTT_ROOT,如果能找到,则作为RTT源码路径,否则,向上找三个父路径作为默认的RTT源码路径。

这也就是bsp不独立出来时,RTT源码工程的结构(例如:rt-thread\bsp\stm32\stm32f103-fire-arbitrary),因此,使用默认的bsp路径时,scons才可以找到源码并构建:

if os.getenv('RTT_ROOT'):
    RTT_ROOT = os.getenv('RTT_ROOT')
else:
    RTT_ROOT = os.path.normpath(os.getcwd() + '/../../..')

因此,我们独立bsp后,最方便的做法是,在系统中添加环境变量,这样在任何路径下建立的bsp均可找到RTT源码,步骤如下:

  1. 打开搜索-高级系统设置

  2. 点击-环境变量,在用户变量下新建

  3. 添加环境变量如下

  4. 在这里插入图片描述

  5. 这一步为bsp提供的路径,用于构建时找到RTT源码

此时,由于我们修改了系统的环境变量,我们需要重启env工具,重新在bsp中进入env工具后,再次执行 scons --target=cmake:

$ scons --target=cmake
scons: Reading SConscript files ...
Newlib version: 4.1.0

scons: *** missing SConscript file 'build\\libraries\\STM32F1xx_HAL\\SConscript'
File "E:\Workspace\01.software\Embeded_Project\stm32f103-fire-arbitrary\SConstruct", line 54, in <module>

RTT源码已经可以找打,现在新的问题出在无法找到STM32的HAL库,因为HAL存在在RTT的源码中,因此我们分析bsp中的SConstruct,看它是如何找依赖的代码的:

SDK_ROOT = os.path.abspath('./')

if os.path.exists(SDK_ROOT + '/libraries'):
    libraries_path_prefix = SDK_ROOT + '/libraries'
else:
    libraries_path_prefix = os.path.dirname(SDK_ROOT) + '/libraries'

SDK_LIB = libraries_path_prefix
Export('SDK_LIB')

# prepare building environment
objs = PrepareBuilding(env, RTT_ROOT, has_libcpu=False)

stm32_library = 'STM32F1xx_HAL'
rtconfig.BSP_LIBRARY_TYPE = stm32_library

# include libraries
objs.extend(SConscript(os.path.join(libraries_path_prefix, stm32_library, 'SConscript'), variant_dir='build/libraries/'+stm32_library, duplicate=0))

通过 os.path.abspath('./') 找到一个路径作为 SDK_ROOT,然后在其后添加 '/libraries',我们可以在这行后面添加一个打印,看下这个路径是什么:

SDK_ROOT = os.path.abspath('./')
print("SDK_ROOT ", SDK_ROOT)
$ scons --target=cmake
scons: Reading SConscript files ...
SDK_ROOT  E:\Workspace\01.software\Embeded_Project\stm32f103-fire-arbitrary
Newlib version: 4.1.0

scons: *** missing SConscript file 'build\\libraries\\STM32F1xx_HAL\\SConscript'
File "E:\Workspace\01.software\Embeded_Project\stm32f103-fire-arbitrary\SConstruct", line 56, in <module>

可知构建时,SDK_ROOT为当前bsp的根路径,在bsp中显然是找不到我们需要的库文件的,因此修改如下:

SDK_ROOT = os.path.join(RTT_ROOT, 'bsp', 'stm32')
print(RTT_ROOT, libraries_path_prefix)

由于我们目前在制作的是stm32的bsp,因此,直接通过已知的RTT_ROOT获取stm32 library的路径,再次尝试构建,打印输出如下:

$ scons --target=cmake
scons: Reading SConscript files ...
E:\Workspace\01.software\Embeded_Project\rt-thread E:\Workspace\01.software\Embeded_Project\rtthread\bsp\stm32\libraries
Newlib version: 4.1.0
Update setting files for CMakeLists.txt...
Done!

可知cmake工程已经正常生成,说明scons已经可以正常找到需要的文件。此时独立编译的bsp for stm32已经基本制作完毕,对于其他芯片的bsp,根据需要的源码分析依赖的文件路径修改即可。

构建和编译测试

尝试编译,这里选择cmake方式,低版本RTT可能不支持,其他方式也可:

$ mkdir build && cd build

$ cmake .. -G "Unix Makefiles"
-- The C compiler identification is GNU 10.3.1
-- The CXX compiler identification is GNU 10.3.1
-- The ASM compiler identification is GNU
-- Found assembler: D:/Tools/env-windows/tools/gnu_gcc/arm_gcc/mingw/bin/arm-none-eabi-gcc.exe
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: D:/Tools/env-windows/tools/gnu_gcc/arm_gcc/mingw/bin/arm-none-eabi-gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: D:/Tools/env-windows/tools/gnu_gcc/arm_gcc/mingw/bin/arm-none-eabi-g++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (5.2s)
-- Generating done (0.1s)
-- Build files have been written to: E:/Workspace/01.software/Embeded_Project/stm32f103-fire-arbitrary/build

$ make
[  1%] Building C object CMakeFiles/rtthread.elf.dir/applications/main.c.obj
[  3%] Building C object CMakeFiles/rtthread.elf.dir/E_/Workspace/01.software/Embeded_Project/rt-thread/components/libc/compilers/common/cctype.c.obj
[  4%] Building C object CMakeFiles/rtthread.elf.dir/E_/Workspace/01.software/Embeded_Project/rt-thread/components/libc/compilers/common/cstdlib.c.obj
[  6%] Building C object CMakeFiles/rtthread.elf.dir/E_/Workspace/01.software/Embeded_Project/rt-thread/components/libc/compilers/common/cstring.c.obj
......
[ 95%] Building C object CMakeFiles/rtthread.elf.dir/E_/Workspace/01.software/Embeded_Project/rt-thread/bsp/stm32/libraries/STM32F1xx_HAL/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_uart.c.obj
[ 96%] Building C object CMakeFiles/rtthread.elf.dir/E_/Workspace/01.software/Embeded_Project/rt-thread/bsp/stm32/libraries/STM32F1xx_HAL/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio_ex.c.obj
[ 98%] Building C object CMakeFiles/rtthread.elf.dir/E_/Workspace/01.software/Embeded_Project/rt-thread/bsp/stm32/libraries/STM32F1xx_HAL/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_usart.c.obj
[100%] Linking C executable rtthread.elf
   text    data     bss     dec     hex filename
  72344    1908    2636   76888   12c58 rtthread.elf
[100%] Built target rtthread.elf

编译构建成功

其他思考

在实际的开发过程中,通过这种方式,我们已经可以最小化工程了,而不是再像之前一样,每个项目都包含着一个RTT源码,而是多个项目都使用同一个RTT源码,项目本身仅包含一个bsp工程,这在代码同步、分享时也极为方便,一般一个小的bsp仅有KB级别的大小,添加 .gitignore 后,需要做版本控制的内容不多。相对于包含整个RTT源码动辄几个G来说,太轻量化了。

但是这会存在一个问题,就像python版本需要conda等工具管理多个环境一样,此时的RTT相当于一个安装在PC上的软件,我们在项目中引用它,但我们的工程也许不是使用同一个版本的RTT内核开发,也许不同的同事使用的RTT版本不一致,也许我们还会使用一些定制化的RTT内核来进行应用开发,我们不可能下载所有版本的RTT内核,然后每次开发的时候重新配置RTT_ROOT。

有个比较好的解决办法是,利用Git的分支管理来辅助我们切换不同的版本,当我们clone下官方的源码并进行应用开发后,我们常常会遇到以下几种情况:

  • 一直在维护的bsp版本比较旧,使用的是v3.x或v4.x的内核,我们可以在RTT源码中切换到对应的版本,可以减少可能出现的编译问题;
  • RTT更新了新的特性,想要更新新的RTT内核尝鲜,我们可以在RTT源码中重新拉取,更新本地的代码,由于bsp是独立的,因此也不会影响bsp工程,然后就可以在bsp中体检新的内核,或者编译不了,那就适配bsp到新的内核吧,或者退回拉取,让内核回到之前的状态;
  • 使用了定制化的RTT内核,一般可以在RTT源码中添加分支,分支上做定制化开发,当某些bsp需要这个定制的内核时,切换到这个分支即可,官方分支的更新和回退也不会影响这个定制化分支;

这种方式的不便之处可能就是ide的静态分析可能不太方便了,跳转到内核相关的源码可能会出现找不到的情况,ide可能需要做特殊配置,后续再谈。

下一篇:【RT-Thread】将BSP工程从RT-Thread源码中独立出来开发(2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值