将 EspMon 移植到 ZephyrOS 并运行在 STM32 板上(或其他板子上,只需稍作修改)





0/5 (0投票)
使用 Zephyr RTOS 和我的 IoT 生态系统的速成课程。
引言
前方可能有危险。
请仔细阅读本文,因为它非常重要。例如,在“子清单”部分,有些内容必须从文章中复制并粘贴到 Zephyr 根目录之外的一个文件中。
如果您从未进行过任何物联网或嵌入式编程,甚至您使用过 Arduino 但对其不熟悉,那么这篇文章不适合您。您将无法使用 fancy IDE,也无法依赖庞大而有用的社区,尽管至少可以通过一些对 .cpp_properties 文件进行修改来使用 VS Code 的 Intellisense。这里没有任何东西是轻松就能获得的。您已被警告。
如果您想继续在 Zephyr 平台上的开发,可以考虑加入 zephyrproject Discord 服务器和 /r/embedded subreddit。这两个平台都有一些乐于助人的人,尽管这方面文档稀少,用户群和社区都相对较小(至少公开的)。
必备组件
获取一些 VS Code 的优势。如果您喜欢其他编辑器,也可以使用,但本文假定您使用 VS Code。
购买一块 STM32 Nucleo H723GZ 板。您可以尝试在其他板子上运行,方法是创建一个新的覆盖文件,其中包含适当的设备引用和总线设置。我尝试过我的 Nucleo H745ZI,但不幸的是,Zephyr 尚未为其提供完整的板级支持,因此目前不支持 SPI - 这是我们的显示屏所必需的。更新:GitHub 仓库还包含了对 Nucleo F767ZI 的支持,我认为这款板子更受欢迎。接线方式与所有 Nucleo 144(较大尺寸)板子相同。
找到一块 ILI9341 显示屏,采用 3 线或 4 线 SPI 格式(MISO 将不被使用)。
您需要从 EspMon Reboot 文章中获取配套应用程序。
作为对您能力的考验,请设置 Zephyr 开发环境和 West 构建系统。如果您做不到,请将其视为本文其余部分的验证码。除非您能跨过这道坎,否则无人能继续。请注意,我在 Windows 11 上通过 Chocolatey 安装 "dtc-msys2
" 组件时遇到了一个错误。它继续安装,并且我在后续完整构建项目时收到一个警告,但我尚未发现它导致任何问题。另外,pip
或其他组件可能会抱怨有新版本,但我建议保持不变。CMake 和您通过 Zephyr 和/或 West 安装的任何其他组件也是如此。请注意,令人不安的是,它似乎会在根目录下的“zephyr”中创建一个看起来像是自身的副本,如果您按照所有说明操作,它会变成 zephyrproject/zephyr。它实际上不是副本,尽管可能只是部分副本。请保留它。您可能需要创建一个批处理文件,在构建环境 shell 中调用 python。我在 zephyrproject
下创建了一个 zephyr.cmd。
为了熟悉,请阅读 EspMon Reboot 文章 这里(上面也有链接),但请注意,我将更贴近 GitHub 代码进行跟进,因为它更新。我没有更新文章和代码中的最新内容,因为它们更复杂。概念和流程是相同的。只是结构略有不同,并且进行了重构以支持更多平台和框架。
也就是说,我们不会直接扩展那个项目,而是在 zephyrproject/zephyr/applications/zepyhr_mon 下创建一个新的 zephyr 项目,并基本上将代码从另一个项目复制过来 - 我已经完成了这项工作,并提供了完整的项目。您可能需要创建 applications 文件夹。我将我的项目放在 zephyr 结构下,因为文档建议这样做,而且我不想混淆本已复杂的构建系统。您可以下载本文顶部的 zip 文件,但我建议从提供的 Github 链接克隆到 zephyrproject/zephyr/applications/zephyr_mon。
理解这个项目
我们不会过多地介绍代码,因为它与 esp_mon2
GitHub 代码基本相同,并且与相关文章的代码密切相关,甚至比之前的 GitHub 代码稍有简化。
设备树 (Devicetree)
任何项目的核心都是 设备树 - 以 .dts 文件表示,一种混合了 JSON 和 C++ 的奇怪的混合语言。在成功构建一次后,您的项目会在 build/zephyr 下有一个名为 zephyr.dts 的主设备树文件。您将主要参考它来获取设备名称和设置。
您可以通过“覆盖”来覆盖、增强甚至删除主文件的部分内容。这是 Zephyr 中初始化和配置所需设备的标准方法。默认情况下,您的主板的大部分功能将被禁用,等待您通过 devicetree
条目启用它,您将在其中配置设备的属性并使其可供您的代码访问。
#include <zephyr/dt-bindings/display/ili9xxx.h>
/ {
aliases {
command-serial = &usart3;
};
chosen {
zephyr,display = &ili9340;
};
zephyr,user {
// backlight
bl_gpios = <&gpioc 6 GPIO_ACTIVE_HIGH>;
};
};
&usart3 {
compatible = "st,stm32-usart", "st,stm32-uart";
status = "okay";
current-speed = <115200>;
};
&arduino_spi {
status = "okay";
cs-gpios = <&gpioa 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
ili9340: ili9340@0 {
compatible = "ilitek,ili9340";
spi-max-frequency = <20000000>;
reg = <0>;
reset-gpios = <&gpiof 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
cmd-data-gpios = <&gpiod 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
width = <320>;
height = <240>;
pixel-format = <ILI9XXX_PIXEL_FORMAT_RGB565>;
rotation = <270>;
frmctr1 = [00 18];
pwctrl1 = [23 00];
vmctrl1 = [3e 28];
vmctrl2 = [86];
pgamctrl = [0f 31 2b 0c 0e 08 4e f1 37 07 10 03 0e 09 00];
ngamctrl = [00 0e 14 03 11 07 31 c1 48 08 0f 0c 31 36 0f];
};
};
第一行是一个 include,它的工作方式与 C++ 中的一样。实际上,它会被复制到从该设备树信息集生成的 C++ 代码中。
之后,我们用一些内容来增强根 /
节点。
首先,我们为我们的串行 UART 声明一个别名 - 我们将其命名为 command-serial
。这使得在代码中引用它变得容易,同时也可以在 devicetree
中为不同的板子进行配置。
第二个块使用 ili9340
设备设置显示子系统,该设备在文件更下方被声明。我们也可以使用控制台子系统来包装 UART,但由于我们使用的是二进制数据,并且我遇到过它们在至少一个区域没有按照规范实现 stdio.h 的问题,所以我直接使用了 UART 本身。
第三个块是用于用户定义属性的部分。由于 GPIO 不是设备,我们无法使用上述任何块来声明它。我们为此创建一个用户属性,并在代码中通过该属性引用它。
在设置显示屏之前,我们必须配置我们的 UART。我们使用 usart3
,因为在此特定板子上,它连接到嵌入式 ST-Link 硬件暴露的虚拟 COM 端口。其他真正重要的部分是启用设备(status = "okay"
)和设置当前波特率。compatible
属性必须与它引用的给定设备匹配,否则该节点将被忽略(我猜是这样 - 我从未实际测试过这个假设)。
现在来看两件事 - 配置我们的 SPI 设备以及 ILI9340 设备驱动程序。您会看到我们引用了 &arduino-spi
,在我们的板子上,它是 Arduino 扩展板接口上暴露的 SPI 总线的别名。主要部分是设置 CS 线,因为在这个板子上它有两个可能的值之一,以及设置通过总线通信的速度。
您会看到 ILI9340 驱动程序嵌套在 SPI 总线声明中。这样您就可以告诉 devicetree
给定的设备是通过某个总线连接的。在 ILI9340 驱动程序内部,有 几个属性,用于配置显示屏的各种方面。它们的作用大多是显而易见的,但超出了本文的范围。很多属性对于使用过这些显示屏的人来说应该很熟悉。
您可以为任何给定的板子创建一个新的覆盖文件,只要它具有必要的硬件,代码应该就能正常工作。代码无需更改,除非您做了类似更改显示控制器硬件的操作。
我通过在线查找示例以及我在上面提到的 Discord 服务器上获得的一些帮助来弄清楚了这一切。目前我对这方面的知识还有很多不足,所以提供上述信息是为了让您知道在哪里可以找到更多信息。直接搜索 Google 可能找不到太多内容,因此 Discord 是无价的。
通过 prj.conf 配置您的项目
除了创建项目时的覆盖文件外,prj.conf 文件也非常重要。它包含大量潜在属性,以 CONFIG_
开头,通常以 =y
结尾。这些属性用于启用项目的各种功能。它的文档不是很好,其中一部分原因是这些属性是由单个模块定义的,而这些模块是任意的,因此属性本身也是任意的,类似于 C 头文件中的 #define
s。我建议将此项目中的文件复制到您开始的任何新项目中,然后删除您不需要的部分。
其中一些很重要但并不明显,例如 CONFIG_NEWLIB_LIBC
。这个属性尤其关键,再加上与 C++ 相关的属性,才能使用我的图形库。默认情况下,使用的 C 库是最小化的,并且不符合 C99 标准,因为其中很多内容都被删除了。上述配置属性启用了符合完整规范的 C 库。此外,我的库要求 C++17。
以下是启用我整个 Zephyr 化生态系统(包括需要其余组件的 UIX)所需的内容
CONFIG_HTCW_BITS=y
CONFIG_HTCW_IO=y
CONFIG_HTCW_ML=y
CONFIG_HTCW_DATA=y
CONFIG_HTCW_GFX=y
CONFIG_HTCW_UIX=y
CONFIG_CPP=y
CONFIG_STD_CPP17=y
CONFIG_PICOLIBC=y
过渡到子清单 (Submanifests)
这里没有合适的位置放置它,因为它属于带外内容,所以将放在文章中。
某些库(例如我的 htcw 物联网和嵌入式生态系统)与 Zephyr 兼容,但它们不在官方模块存储库中。因此,为了使用它们,您必须通过在 zephyrproject/zephyr/submanifests 中创建一个 .yaml 文件来添加它们。您必须创建 submanifests 文件夹。这些 yaml 文件由构建系统用来获取库的本地副本。要使用我的生态系统,您需要以下文件,我将其命名为 htcw.yaml
manifest:
projects:
- name: htcw_bits
url: https://github.com/codewitch-honey-crisis/htcw_bits
revision: master
- name: htcw_io
url: https://github.com/codewitch-honey-crisis/htcw_io
revision: master
- name: htcw_ml
url: https://github.com/codewitch-honey-crisis/htcw_ml
revision: master
- name: htcw_data
url: https://github.com/codewitch-honey-crisis/htcw_data
revision: master
- name: htcw_gfx
url: https://github.com/codewitch-honey-crisis/gfx
revision: master
- name: htcw_uix
url: https://github.com/codewitch-honey-crisis/uix
revision: master
要实际下载它们,您需要调用
west update
在 zephyrproject/zephyr 内部执行此操作。
不幸的是,VS Code 不会自动为这些文件找到包含文件。因此,您必须将每个子模块的包含文件夹路径添加到 .cpp_properties 中,如下所示
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"windowsSdkVersion": "10.0.22000.0",
"compilerPath": "cl.exe",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64"
},
{
"name": "ARM",
"includePath": [
"C:/Users/username/zephyrproject/**",
"C:/Users/username/zephyrproject/zephyr/**",
"${workspaceFolder}/**",
"C:/Users/username/zephyrproject/htcw_bits/include",
"C:/Users/username/zephyrproject/htcw_io/include",
"C:/Users/username/zephyrproject/htcw_ml/include",
"C:/Users/username/zephyrproject/htcw_data/include",
"C:/Users/username/zephyrproject/htcw_gfx/include",
"C:/Users/username/zephyrproject/htcw_uix/include",
"${workspaceFolder}"
],
"defines": [],
"windowsSdkVersion": "10.0.22000.0",
"compilerPath": "C:/mingw64/bin/g++.exe",
"cStandard": "c99",
"cppStandard": "gnu++17",
"intelliSenseMode": "windows-gcc-arm64",
"compileCommands": "C:/Users/username/zephyrproject/zephyr/build/compile_commands.json"
}
],
"version": 4
}
您可以在 includePath
下看到所有路径。如果您有更好的方法,请告知我。
为了实际加载模块并在您的项目中使用它们,在第一次构建之前,在您的虚拟环境和您的项目目录中执行以下命令
west update
CMakeLists.txt
您的项目文件夹中需要一个这样的文件。内容大致如下
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(zephyr_mon)
target_sources(app PRIVATE src/main.cpp src/ui.cpp)
这大部分是样板代码。只需确保在 project()
中设置项目名称,并在 target_sources()
中保持 C 和 C++ 源文件列表的最新。
使用这个烂摊子
按照 wiring.txt 中的说明接线。
从前面链接的 EspMon Reboot 文章中下载项目。您将需要 .NET 配套应用程序来使用此项目。
然后从项目目录中刷写板子
(.venv) C:\Users\username\zephyrproject\zephyr\applications\zephyr_mon\west build -b nucleo_h723zg
(.venv) C:\Users\username\zephyrproject\zephyr\applications\zephyr_mon\west flash
它应该显示一个白色屏幕上的断开连接图标 - 实际上它是一个 SVG 文档,但这无关紧要。
运行 PC 配套应用程序(它会请求提升的权限 - 允许它们),然后检查/选择与板子关联的 ST-Link 虚拟 COM 端口。勾选“started”复选框,您应该会看到设备上的屏幕变为一个监视器屏幕,显示 CPU 和 GPU 的使用率和温度。
Bug
Zephyr 可以使用的 newlib 中存在一些 bug。例如,控制台子系统的 getchar()
函数在没有输入时返回 0 而不是 -1。我已将项目切换到 picolib,尽管我之前已经解决了这个问题。Picolib 由 Zephyr 项目维护。如我所说,前面可能有危险。
结论
Zephyr 有相当陡峭的学习曲线,而且文档还可以改进。话虽如此,它是在不同板子上快速切换的最好选择,这可以极大地帮助解决硬件采购问题。
历史
- 2023年7月25日 - 首次提交