pico-sdk(三)-程序架构之构建系统

pico-sdk(三)-程序架构之构建系统

RP系列微控制器在同类产品中算是功能强大的芯片,尤其在系统RAM方面,拥有大容量内存空间。但它毕竟是一个嵌入式环境,所以在 RAM、CPU 周期和程序空间上仍然很宝贵。因此,性能和其他因素(例如,边界情况错误处理、运行时与编译时配置)之间的权衡对于开发者来说比在其他更高层级的平台上更加明显。

SDK 的设计意图是让开发接口能够开箱即用,使用默认配置而不需要额外设置,但同时也给予开发者尽可能多的控制权(如果他们需要的话),以便他们对正在构建的应用程序和所使用的库进行微调。

pico-sdk 这一系列文章除了介绍 SDK 的使用方法,更多的是想展示 pico-sdk 背后的一些设计决策,不光介绍它设计了什么,也对其背后的设计原因作了一定的解释。

构建系统

SDK 使用 CMake 构建系统,绝大多数 IDE 都支持使用 CMake ,IDE 通过 CMakeLists.txt 文件来解析项目包含的头文件并生成代码自动补全建议。CMakeLists.txt 文件中包含应用程序的构建方式配置,CMake 解析 CMakeLists.txt 文件,生成 make、ninja 等构建工具支持的构建系统。开发人员通过设置 CMake 变量来生成特定平台(例如,Windows 或 Linux 发行版)的构建系统。

以下展示了 blink_simple 的主程序 CMake 构建源码

add_executable(blink_simple
        blink_simple.c
)

# pull in common dependencies
target_link_libraries(blink_simple pico_stdlib)

# create map/bin/hex/uf2 file etc.
pico_add_extra_outputs(blink_simple)

# call pico_set_program_url to set path to example on github, so users can find the source for an example via picotool
example_auto_set_url(blink_simple)

blink_simple 示例中,定义了一个名为 blink_simple 的可执行文件目标,这个可执行程序仅依赖 pico_stdlib 这个 SDK 库。示例中还使用了 SDK 提供的 pico_add_extra_outputs 命令来生成其他格式的二进制文件(.uf2、.hex、.bin、.map、.dis 等)。

SDK 构建的是一个 bare metal1 可执行文件,即它包含了在设备上运行所需的全部代码(除了 RP 系列微控制器 bootrom2 中包含的启动引导代码)。

pico_stdlib 是一个接口库,它提供了编译和链接 blink_simple 应用程序所需的所有其余代码和配置。编译器在编译构建 blink_simple 应用程序的过程中,除了会编译单个 blink_simple.c 文件外,还会编译包含在 pico_stdlib 库中的数十个其他源文件,以使 blink_simple 应用程序能够在 RP 系列微控制器上运行。

每个库都是一个 CMake INTERFACE

SDK 中的所有库都是 CMake INTERFACE 库。(注意,不包括操作系统提供的 C/C++ 标准库)。从概念上讲,CMake 接口库是以下内容的集合:

  • 源文件
  • 包含路径
  • 编译器定义(在代码中以 #define 形式出现)
  • 编译和链接选项
  • 依赖关系(对其他库的依赖)

CMake 根据在 CMakeLists.txt 文件中列出的库,并递归收集这些库所依赖的库,形成了一个接口库依赖关系树,每个库都为构建系统提供源文件、包含路径、编译器定义和编译 / 链接选项。为了构建应用程序,编译器使用解析出来的包含路径、编译器定义和选项进行编译,并根据提供的链接选项将编译输出文件链接成一个可执行文件。

当使用 SDK 构建可执行文件时,编译器会把该可执行程序的源代码及依赖的SDK库的源代码重新编译,输出二进制文件。这种构建方式,能够在每个应用程序中都单独指定 SDK 的编译配置(例如,启用 / 禁用断言,设置静态缓冲区的大小)。不仅能使生成的二进制文件更小、运行速度更快,还能完全移除可执行文件中不需要的功能。

在示例程序的 CMakeLists.txt 文件中,声明了可执行程序对(INTERFACE)库 pico_stdlib 的依赖。这个库同时还依赖于其他库(pico_runtimehardware_gpiohardware_uart 等)。pico_stdlib 库提供了简单应用程序运行所需的所有基本功能,及 GPIO 端口切换功能(切换到向 UART 输出数据),而且链接器会对未调用的函数进行垃圾回收,所以不会使你的二进制文件膨胀。可以快速查看一下 hardware_gpio 库的目录结构,我们的 blink_simple 示例就是用它来控制发光二极管(LED)的亮灭。

hardware_gpio
├── CMakeLists.txt
├── gpio.c
└── include
  └── hardware
  └── gpio.h

该应用程序依赖 hardware_gpio INTERFACE 接口库,以至于 gpio.c 源文件会被编译并链接到可执行文件中,同时会将上面显示的目录路径添加到编译器的依赖文件搜索路径中,这样一来,代码中使用 #include "hardware/gpio.h" 就能引入正确的头文件。

接口库还便于将相关的功能模块聚合一个库(例如 pico_stdlib),这些模块本身不直接包含代码,但依赖一些较低层的库来实现功能。就像 metapackage3 一样,这样能够很方便的引入一组与特定目标相关的库,而无需逐个列出它们的名称。

SDK 的功能被分组到不同的接口(INTERFACE)库中,每个接口库都提供该库所需的代码和包含路径。使用时需直接(或通过另一个接口库间接)声明对所需接口库的依赖关系,这样在编译源文件时(或在集成开发环境(IDE)中进行代码补全时)才能找到头文件。

链接


  1. 可翻译为“裸金属”可执行文件,个人感觉可以对比云计算中的“裸金属”环境,即直接运行在硬件上的程序,程序和硬件中间没有任何系统。 ↩︎

  2. 只读引导存储器,存储设备启动时需要的一些基本的、固定的引导程序。这些程序在设备开机时被首先执行,用于初始化硬件设备。 ↩︎

  3. A metapackage 是一种软件包,它本身通常不包含实际可运行的程序代码(或者只包含极少量用于管理的代码),而是依赖于其他多个软件包。它主要用于将一组相关的软件包作为一个整体来进行管理和安装。 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值