zephyr 安装与入门

 

如何安装zehpyr和使用zephyr

如何安装zehpyr和使用zephyr

在 ubuntu20.04 环境下的安装

安装依赖项

  1. 更新系统

     sudo apt update
     sudo apt upgrade
    
  2. 主要依赖环境的最低版本要求

    工具 最低版本
    camek 3.20.0
    python 3.6
    devicetree compiler 1.4.6
    • 安装 cmake
      • 下载cmake最新版本
      • 可以选择二进制发行版本: cmake
      • 下载后

        chmod +x cmake-3.23.2-linux-x86_64.sh
        ./cmake-3.23.2-linux-x86_64.sh
        
        • 按照提示先y,安装到对应的目录中,再将此目录的bin/导出到环境PATH
        • 或者ln -s 源文件 /usr/bin/cmake
    • 安装其他依赖项

         wget https://apt.kitware.com/kitware-archive.sh
         sudo bash kitware-archive.sh
      
         sudo apt install --no-install-recommends git cmake ninja-build \
         gperf ccache dfu-util device-tree-compiler wget \
         python3-dev python3-pip python3-setuptools python3-tk python3-wheel\ 
         xz-utils file make gcc gcc-multilib g++-multilib libsdl2-dev
      
  3. 检查版本

     cmake --version
     python3 --version
     dtc --version
    

获取 zephyr

  1. 安装 west 工程管理器

     pip3 install --user -U west
    

    或者

     # 创建一个新的虚拟环境
     python3 -m venv ~/zephyrproject/.venv
    
     # 激活虚拟环境
     source ~/zephyrproject/.venv/bin/activate
    
     # 安装west
     pip install west
    

    也可以通过 pyenv + virtualenv 来实现

     # 安装需要的python版本
     pyenv install -v 3.9.0
    
     # 创建虚拟环境
     pyenv virtualenv 3.9.0 zephyr
    
     # 激活虚拟环境
     pyenv activate zephyr
    
     # 退出虚拟环境
     pyenv deactivate
    
     # 删除虚拟环境
     pyenv uninstall zephyr
    
     # 查看现在安装的python版本
     pyenv versions
    
     # 查看安装的虚拟环境
     pyenv virtualenvs
    
  2. 获取 zephyr 源码

     # 从 gitee 仓库中下载源码
     west init -m https://gitee.com/zephyr-rots/zephyr.git ~/zephyr-rtos
    
    • 为了从gitee上下载,修改下载路径
      • 将目录中所有文件中的的github.com/zephyrproject-rtos 替换成 gitee.com/zephyr-rtos
     cd ~/zephyr-rtos
     west update
    

    或者

     west init ~/zephyrproject
     cd ~/zephyrproject
     west update
    
  3. 导出 cmake 包,cmake包允许cmake自动加载构建zephyr应用程序所需的样板代码

     west zephyr-export
    
  4. 安装python依赖项

     pip install -r zephyr/scripts/requirements.txt
    
  5. 安装工具链

    • 安装 zephyr 工具链

        cd ~
        wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.14.2/zephyr-sdk-0.14.2_linux-x86_64.tar.gz
        wget -O - https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.14.2/sha256.sum | shasum --check --ignore-missing
      
        # 抽取
        tar xvf zephyr-sdk-0.14.2_linux-x86_64.tar.gz
        cd zephyr-sdk-0.14.2
        # 为 zephyr 设置工具链路径
        ./setup.sh
      
      • 推荐安装路径

        • $HOME
        • $HOME/.local
        • $HOME/.local/opt
        • $HOME/bin
        • /opt
        • /usr/local
    • 或者安装GNU GCC工具链

        export ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb
        export GNUARMEMB_TOOLCHAIN_PATH=/home/you/Downloads/gnu_arm_embedded
      
  6. [可选] 安装 udev 规则,它允许你作为一个普通用户烧录大多数 Zephyr 板:

     sudo cp ~/zephyr-sdk-0.14.2/sysroots/x86_64-pokysdk-linux/usr/share/openocd/contrib/60-openocd.rules /etc/udev/rules.d
     sudo udevadm control --reload
    
  7. 编译版本

     west build -p auto -b gd32f450i_eval zephyr/samples/basic/blinky
    
     # 在构建时指定用Jlink下载版本
     west build -p auto -b gd32f450i_eval zephyr/samples/basic/blinky -DBOARD_FLASH_RUNNER=jlink
    
  8. 下载版本

    # 默认用openocd下载版本
    west flash
    
    # 指定用jlink的烧录
    west flash --runner jlink
    
  9. 调试

    west debug -r jlink
    

Application Development 应用程序开发

Note 注意

In this document, we’ll assume your application directory is <home>/app, and that its build directory is <home>/app/build. (These terms are defined in the following Overview.) On Linux/macOS, is equivalent to `~`, whereas on Windows it’s `%userprofile%`.

在这个文档中,我们假设您的应用程序目录是 <home>/app,它的构建目录是 <home>/app/build。(这些术语在以下概述中定义。)在 Linux/macOS 上,<home> 等价于 ~ ,而在 Windows 上,它是%userprofile%

Overview 概览

Zephyr’s build system is based on CMake.

Zephyr 的构建系统是基于 CMake 的。

The build system is application-centric, and requires Zephyr-based applications to initiate building the kernel source tree. The application build controls the configuration and build process of both the application and Zephyr itself, compiling them into a single binary.

构建系统以应用程序为中心,需要基于 zephyr 的应用程序启动构建核心源代码树。应用程序构建控制应用程序和 Zephyr 本身的配置和构建过程,并将它们编译成一个二进制文件。

Zephyr’s base directory hosts Zephyr’s own source code, its kernel configuration options, and its build definitions.

Zephyr的基础目录存放着Zephyr自己的源代码,它的内核配置选项,以及它的构建定义

The files in the application directory link Zephyr with the application. This directory contains all application-specific files, such as configuration options and source code.

应用程序目录中的文件将 Zephyr 与应用程序链接起来。此目录包含所有特定于应用程序的文件,如配置选项和源代码。

An application in its simplest form has the following contents:

最简单的应用程序包括以下内容:

<home>/app
├── CMakeLists.txt
├── prj.conf
└── src
    └── main.c

These contents are:这些内容是:

  • CMakeLists.txt: This file tells the build system where to find the other application files, and links the application directory with Zephyr’s CMake build system. This link provides features supported by Zephyr’s build system, such as board-specific kernel configuration files, the ability to run and debug compiled binaries on real or emulated hardware, and more.

  • CMakeLists.txt: 该文件告诉构建系统在哪里可以找到其他应用程序文件,并将应用程序目录与 Zephyr 的 CMake 构建系统链接起来。这个链接提供了 Zephyr 的构建系统支持的特性,比如特定于板卡的内核配置文件,在实际硬件或仿真硬件上运行和调试已编译的二进制文件的能力,等等。

  • Kernel configuration files: An application typically provides a Kconfig configuration file (usually called prj.conf) that specifies application-specific values for one or more kernel configuration options. These application settings are merged with board-specific settings to produce a kernel configuration.

  • 内核配置文件: 应用程序通常提供一个 Kconfig 配置文件(通常称为 prj.conf) ,该文件为一个或多个内核配置选项指定特定于应用程序的值。这些应用程序设置与特定于板面的设置合并以生成内核配置。

    See Kconfig Configuration below for more information.

    更多信息请参见下面的Kconfig 配置

  • Application source code files: An application typically provides one or more application-specific files, written in C or assembly language. These files are usually located in a sub-directory called src.

  • 应用程序源代码文件: 应用程序通常提供一个或多个用 c 或汇编语言编写的特定于应用程序的文件。这些文件通常位于一个名为 src 的子目录中。

Once an application has been defined, you can use CMake to create project files for building it from a directory where you want to host these files. This is known as the build directory. Application build artifacts are always generated in a build directory; Zephyr does not support “in-tree” builds.

一旦定义了应用程序,就可以使用 CMake 创建项目文件,以便从希望托管这些文件的目录中构建它。这就是所谓的构建目录。应用程序构建制品总是在构建目录中生成; Zephyr 不支持“ in-tree”构建。

The following sections describe how to create, build, and run Zephyr applications, followed by more detailed reference material.

下面的部分将描述如何创建、构建和运行 Zephyr 应用程序,随后将提供更详细的参考资料。

Application types 应用程序类别

Based on where the source code of the application is located we can distinguish between three basic application types.

根据应用程序的源代码所在的位置,我们可以区分三种基本的应用程序类型。

  • Zephyr repository application: Zephyr 库应用程序
  • Zephyr workspace application: Zephyr 工作空间应用
  • Zephyr freestanding application: Zephry 独立式应用

You can find out more about how the build system supports all the application types described in this section in the Zephyr CMake Package section.

您可以在 Zephyr CMake Package 部分了解构建系统如何支持本节中描述的所有应用程序类型

Zephyr repository application

An application located within the zephyr folder in a Zephyr west workspace is referred to as a Zephyr repository application. In the following example, the hello_world sample is a Zephyr repository application:

位于 Zephyr west 工作空间中 Zephyr 文件夹中的应用程序称为 Zephyr 存储库应用程序。在下面的示例中,hello world 示例是一个 Zephyr 库应用程序:

zephyrproject/
├─── .west/
│    └─── config
└─── zephyr/
     ├── arch/
     ├── boards/
     ├── cmake/
     ├── samples/
     │    ├── hello_world/
     │    └── ...
     ├── tests/
     └── ...

Zephyr workspace application 工作空间应用

An application located within a workspace, but outside the Zephyr repository (and thus folder) itself, is referred to as a Zephyr workspace application. In the following example, app is a Zephyr workspace application:

一个位于工作区内、但在 Zephyr 存储库(因而也是文件夹)之外的应用程序被称为 Zephyr 工作区应用程序。在下面的例子中,app 是一个 Zephyr 工作区应用程序:

zephyrproject/
├─── .west/
│    └─── config
├─── zephyr/
├─── bootloader/
├─── modules/
├─── tools/
├─── <vendor/private-repositories>/
└─── applications/
     └── app/

Zephyr freestanding application Zephyr独立式应用

A Zephyr application located outside of a Zephyr workspace is referred to as a Zephyr freestanding application. In the following example, app is a Zephyr freestanding application:

一个Zephyr应用程序位于Zephyr工作空间之外被称为一个独立的 west 应用程序。在下面的例子中,app 是一个 Zephyr 独立应用程序:

<home>/
├─── zephyrproject/
│     ├─── .west/
│     │    └─── config
│     ├── zephyr/
│     ├── bootloader/
│     ├── modules/
│     └── ...
│
└─── app/
     ├── CMakeLists.txt
     ├── prj.conf
     └── src/
         └── main.c

Example workspace application 工作区应用程序示例

A reference workspace application contained in its own Git repository can be found in the Example Application repository. It can be used as a reference on how to structure out-of-tree, Zephyr-based workspace applications using the T2 star topology. It also demonstrates the out-of-tree use of features commonly used in applications such as:

一个包含在自己的 Git 仓库中的参考工作区应用程序可以在示例应用程序仓库中找到。它可以作为一个参考,说明如何使用T2星形拓扑结构构建树外的、基于Zephyr的工作区应用程序。它还演示了树外应用中常用的功能,如。

  • Custom boards 定制板
  • Custom devicetree bindings 自定义设备树
  • Custom drivers 定制驱动程序
  • Continuous Integration (CI) setup 持续集成(CI)设置

Creating an Application 创建应用程序

Follow these steps to create a new application directory. (Refer to the Example Application repository for a reference standalone application in its own Git repository or to Samples and Demos for existing applications provided as part of Zephyr.)

按照这些步骤来创建一个新的应用程序目录。(请参考示例应用程序仓库,以了解在其自身的Git仓库中的参考独立应用程序,或者参考Samples and Demos,了解作为Zephyr一部分提供的现有应用程序)。

  1. Create an application directory on your workstation computer, outside of the Zephyr base directory. Usually you’ll want to create it somewhere under your user’s home directory.

    在工作站计算机上,在Zephyr基本目录之外创建应用程序目录。通常您希望在用户的主目录下创建它。

    For example, in a Unix shell or Windows cmd.exe prompt, navigate to where you want to create your application, then enter:

    例如,在 Unix shell 或 Windows cmd.exe 提示符下,导航到要创建应用程序的位置,然后输入:

    mkdir app
    

    Warning警告

    Building Zephyr or creating an application in a directory with spaces anywhere on the path is not supported. So the Windows path C:\Users\YourName\app will work, but C:\Users\Your Name\app will not.

    不支持在路径上任何地方都有空格的目录中构建 Zephyr 或创建应用程序。因此,Windows 路径 c:\Users \YourName 应用程序将工作,但 c:\Users Your Name 应用程序将不工作。

  2. It’s recommended to place all application source code in a subdirectory named src. This makes it easier to distinguish between project files and sources.

    建议将所有应用程序源代码放在名为 src 的子目录中。这使得区分项目文件和源代码变得更加容易。

    Continuing the previous example, enter:

    继续前面的示例,输入:

    cd app
    mkdir src
    
  3. Place your application source code in the src sub-directory. For this example, we’ll assume you created a file named src/main.c.

    将应用程序源代码放在 src 子目录中。

  4. Create a file named CMakeLists.txt in the app directory with the following contents:

    在 app 目录中创建一个名为 CMakeLists.txt 的文件,其内容如下:

    cmake_minimum_required(VERSION 3.20.0)
       
    find_package(Zephyr)
    project(my_zephyr_app)
       
    target_sources(app PRIVATE src/main.c)
    

    cmake_minimum_required() is required to be in your CMakeLists.txt by CMake. It is also invoked by the Zephyr package. The most recent of the two versions will be enforced by CMake.

    cmake_minimum_required()需要在 CMakeLists.txt 中。它也可以由 Zephyr 包调用。两个版本中的最新版本将由 CMake 强制执行。

    find_package(Zephyr) pulls in the Zephyr build system, which creates a CMake target named app (see Zephyr CMake Package). Adding sources to this target is how you include them in the build. The Zephyr package will define Zephyr-Kernel as a CMake project and enable support for the C, CXX, ASM languages.

    find_package(Zephyr) 拉入Zephyr构建系统,它创建了一个名为app的CMake目标(参见Zephyr CMake Package)。将源码添加到这个目标中是在构建中包含它们的方式。Zephyr包将把Zephyr-Kernel定义为一个CMake项目,并启用对C、CXX、ASM语言的支持。

    project(my_zephyr_app) is required for defining your application project. This must be called after find_package(Zephyr) to avoid interference with Zephyr’s project(Zephyr-Kernel).

    project(my_zephyr_app)是定义应用程序项目所必需的,它必须在 find_package(Zephyr)之后调用,以避免干扰 project(Zephyr-Kernel)

    target_sources(app PRIVATE src/main.c) is to add your source file to the app target. This must come after find_package(Zephyr) which defines the target.

    target_sources(app PRIVATE src/main.c)是将源文件添加到 app 目标。这必须在定义目标的 find_package(Zephyr)之后执行。

  5. Set Kconfig configuration options. See Kconfig Configuration.

    设置 Kconfig 配置选项。请参阅 Kconfig 配置。

  6. Configure any devicetree overlays needed by your application. See Set devicetree overlays.

    配置应用程序所需的任何设备树覆盖图。

Note注意

include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) is still supported for backward compatibility with older applications. Including boilerplate.cmake directly in the sample still requires using Zephyr Environment Scripts before building the application.

include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)在旧的应用程序中仍然支持向下兼容。在示例中直接包含 boilerplate.cmake 仍然需要在构建应用程序之前使用 Zephyr 环境脚本。

Important Build System Variables 重要的构建系统变量

You can control the Zephyr build system using many variables. This section describes the most important ones that every Zephyr developer should know about.

你可以使用许多变量来控制Zephyr构建系统。这一节描述了每一个Zephyr开发者应该知道的内容

Note注意

The variables BOARD, CONF_FILE, and DTC_OVERLAY_FILE can be supplied to the build system in 3 ways (in order of precedence):

变量 BOARD, CONF_FILE, 和 DTC_OVERLAY_FILE 可以通过3种方式提供给构建系统(优先顺序):

  • As a parameter to the west build or cmake invocation via the -D command-line switch. If you have multiple overlay files, you should use quotations, "file1.overlay;file2.overlay"

    作为 west build 或 cmake 调用的参数,通过 -D 命令行开关进行调用。如果有多个覆盖文件,应该使用引号:”file1.overlay; file2.overlay”

  • As Environment Variables.作为环境变量。

  • As a set(<VARIABLE> <VALUE>) statement in your CMakeLists.txt

    作为 CMakeLists.txt 中的 set(<VARIABLE> <VALUE>)语句

  • ZEPHYR_BASE: Zephyr base variable used by the build system. find_package(Zephyr) will automatically set this as a cached CMake variable. But ZEPHYR_BASE can also be set as an environment variable in order to force CMake to use a specific Zephyr installation.

    ZEPHYR_BASE: 用于构建系统的 Zephyr 基本变量。find_package(Zephyr)将自动将其设置为缓存的 CMake 变量。但是 ZEPHYR_BASE 也可以被设置为环境变量,以强制 CMake 使用特定的 Zephyr 安装。

  • BOARD: Selects the board that the application’s build will use for the default configuration. See Supported Boards for built-in boards, and Board Porting Guide for information on adding board support.

    BOARD: 选择应用程序的构建将用于默认配置的板。见支持板的内置板,和主板移植指南的信息,增加板的支持。

  • CONF_FILE: Indicates the name of one or more Kconfig configuration fragment files. Multiple filenames can be separated with either spaces or semicolons. Each file includes Kconfig configuration values that override the default configuration values.

    CONF_FILE: 表示一个或多个 Kconfig 配置片段文件的名称。多个文件名可以用空格或分号分隔。每个文件都包含重写默认配置值的 Kconfig 配置值。

    See The Initial Configuration for more information.

    有关更多信息,请参见初始配置

  • OVERLAY_CONFIG: Additional Kconfig configuration fragment files. Multiple filenames can be separated with either spaces or semicolons. This can be useful in order to leave CONF_FILE at its default value, but “mix in” some additional configuration options.

    OVERLAY_CONFIG: 附加的 Kconfig 配置片段文件。多个文件名可以用空格或分号分隔。这对于保持 CONF_FILE 的默认值很有用,但是要“混合”一些额外的配置选项。

  • DTC_OVERLAY_FILE: One or more devicetree overlay files to use. Multiple files can be separated with semicolons. See Set devicetree overlays for examples and Introduction to devicetree for information about devicetree and Zephyr.

    DTC_OVERLAY_FILE: 一个或多个设备树覆盖文件。多个文件可以用分号分隔。有关设备和 Zephyr 的信息,请参阅 Set 设备树覆盖图和设备简介。

  • SHIELD: see Shields

  • ZEPHYR_MODULES: A CMake list containing absolute paths of additional directories with source code, Kconfig, etc. that should be used in the application build. See Modules (External projects) for details. If you set this variable, it must be a complete list of all modules to use, as the build system will not automatically pick up any modules from west.

    ZEPHYR_MODULES: 一个 CMake 列表,包含应该在应用程序构建中使用的附加目录的绝对路径,以及源代码、 Kconfig 等。详细信息请参阅模块(外部项目)。如果你设置了这个变量,它必须是一个所有模块使用的完整列表,因为构建系统不会自动从 west 接收任何模块。

  • ZEPHYR_EXTRA_MODULES: Like ZEPHYR_MODULES, except these will be added to the list of modules found via west, instead of replacing it.

    ZEPHYR_EXTRA_MODULES: 和 west 模块一样,只不过这些模块将被添加到通过 west 找到的模块列表中,而不是替换它。

Note注意

You can use a Zephyr Build Configuration CMake package to share common settings for these variables.

您可以使用 Zephyr Build Configuration CMake 包来共享这些变量的公共设置。

Application CMakeLists.txt 应用程序 CMakeLists.txt

Every application must have a CMakeLists.txt file. This file is the entry point, or top level, of the build system. The final zephyr.elf image contains both the application and the kernel libraries.

每个应用程序必须有一个 CMakeLists.txt 文件。此文件是生成系统的入口点或顶级。最终的 zephyr.elf 映像包含应用程序和内核库。

This section describes some of what you can do in your CMakeLists.txt. Make sure to follow these steps in order.

本节描述了在 CMakeLists.txt 中可以做的一些事情。

  1. If you only want to build for one board, add the name of the board configuration for your application on a new line. For example:

    如果您只想为一块板构建,请在新行上添加您的应用程序的板配置的名称。例如:

    set(BOARD qemu_x86)
    

    Refer to Supported Boards for more information on available boards.

    请参阅支持的主板的更多信息,关于可用的主板。

    The Zephyr build system determines a value for BOARD by checking the following, in order (when a BOARD value is found, CMake stops looking further down the list):

    Zephyr 构建系统通过按顺序检查以下内容来确定 BOARD 的值(当找到一个 BOARD 值时,CMake 将停止查看列表的下方) :

    • Any previously used value as determined by the CMake cache takes highest precedence. This ensures you don’t try to run a build with a different BOARD value than you set during the build configuration step.

      由 CMake 缓存确定的任何以前使用的值优先级最高。这可以确保您不会尝试使用与在构建配置步骤中设置的不同的 BOARD 值来运行构建。

    • Any value given on the CMake command line (directly or indirectly via west build) using -DBOARD=YOUR_BOARD will be checked for and used next.

      使用 -DBOARD=YOUR_BOARD 在 CMake 命令行(直接或间接地通过 west build)上给出的任何值都将被检查并在下一步使用。

    • If an environment variable BOARD is set, its value will then be used.

      如果设置了一个环境变量,那么它的值就会被使用。

    • Finally, if you set BOARD in your application CMakeLists.txt as described in this step, this value will be used.

      最后,如果您在应用程序 CMakeLists.txt 中按照本步骤所述设置了 BOARD,则将使用此值。

  2. If your application uses a configuration file or files other than the usual prj.conf (or prj_YOUR_BOARD.conf, where YOUR_BOARD is a board name), add lines setting the CONF_FILE variable to these files appropriately. If multiple filenames are given, separate them by a single space or semicolon. CMake lists can be used to build up configuration fragment files in a modular way when you want to avoid setting CONF_FILE in a single place. For example:

    如果您的应用程序使用一个或多个配置文件,而不是通常的 prj.conf (或 prj_YOUR_BOARD.conf),其中 YOUR_BOARD 是一个板名) ,为这些文件添加多行以设置 CONF_FILE 变量。如果给出了多个文件名,则用一个空格或分号将它们分隔开。当您想要避免在单个位置设置 CONF_FILE 时,可以使用 CMake 列表以模块化的方式构建配置片段文件。例如:

    set(CONF_FILE "fragment_file1.conf")
    list(APPEND CONF_FILE "fragment_file2.conf")
    

    See The Initial Configuration for more information.

    有关更多信息,请参见初始配置。

  3. If your application uses devicetree overlays, you may need to set DTC_OVERLAY_FILE. See Set devicetree overlays.

    如果您的应用程序使用设备树层,您可能需要设置 DTC_OVERLAY_FILE。

  4. If your application has its own kernel configuration options, create a Kconfig file in the same directory as your application’s CMakeLists.txt.

    如果应用程序有自己的内核配置选项,请在与应用程序的 CMakeLists.txt 相同的目录中创建一个 Kconfig 文件。

    See the Kconfig section of the manual for detailed Kconfig documentation.

    有关详细的 Kconfig 文档,请参阅手册中的 Kconfig 部分。

    An (unlikely) advanced use case would be if your application has its own unique configuration options that are set differently depending on the build configuration.

    如果应用程序有自己独特的配置选项,并且根据构建配置的不同设置这些选项,那么这将是一种(不太可能的)高级用例。

    If you just want to set application specific values for existing Zephyr configuration options, refer to the CONF_FILE description above.

    如果您只想为现有的 Zephyr 配置选项设置特定于应用程序的值,请参考上面的 CONF_FILE 描述。

    Structure your Kconfig file like this:

    像这样构造你的 Kconfig 文件:

    # SPDX-License-Identifier: Apache-2.0
       
    mainmenu "Your Application Name"
       
    # Your application configuration options go here
       
    # Sources Kconfig.zephyr in the Zephyr root directory.
    #
    # Note: All 'source' statements work relative to the Zephyr root directory (due
    # to the $srctree environment variable being set to $ZEPHYR_BASE). If you want
    # to 'source' relative to the current Kconfig file instead, use 'rsource' (or a
    # path relative to the Zephyr root).
    # 所有的'source'语句都是相对于Zephyr根目录工作的(由于$srctree环境变量被设置为$ZEPHYR_BASE)。
    # 如果你想要'source'相对于当前Kconfig文件,使用'rsource'(或相对于Zephyr根目录的路径)。
    source "Kconfig.zephyr"
    

    Note注意

    Environment variables in source statements are expanded directly, so you do not need to define an option env="ZEPHYR_BASE" Kconfig “bounce” symbol. If you use such a symbol, it must have the same name as the environment variable.

    Source 语句中的环境变量是直接展开的,因此不需要定义一个option env="ZEPHYR_BASE"符号。如果你使用这样一个符号,它必须有相同的名称作为环境变量。

    See Kconfig extensions for more information.

    有关更多信息,请参见 Kconfig 扩展。

    The Kconfig file is automatically detected when placed in the application directory, but it is also possible for it to be found elsewhere if the CMake variable KCONFIG_ROOT is set with an absolute path.

    当放置在应用程序目录中时,会自动检测到 KCONFIG 文件,但是如果使用绝对路径设置 CMake 变量 KCONFIG_ROOT,也可以在其他地方找到它。

  5. Specify that the application requires Zephyr on a new line, after any lines added from the steps above:

    指定应用程序需要 Zephyr,在从上面的步骤之后添加:

    find_package(Zephyr)
    project(my_zephyr_app)
    

    Note注意

    find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) can be used if enforcing a specific Zephyr installation by explicitly setting the ZEPHYR_BASE environment variable should be supported. All samples in Zephyr supports the ZEPHYR_BASE environment variable.

    如果通过显式设置 ZEPHYR_BASE 环境变量来强制执行一个特定的 ZEPHYR 安装,则可以使用 find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})。在Zephyr的所有样本都支持 ZEPHYR_BASE 环境变量。

  6. Now add any application source files to the ‘app’ target library, each on their own line, like so:

    现在将任何应用程序源文件添加到’app’目标库中,每个应用程序都有自己的行,如下所示:

    target_sources(app PRIVATE src/main.c)
    

Below is a simple example CMakeList.txt:

下面是一个简单的例子 CMakeList.txt:

set(BOARD qemu_x86)

find_package(Zephyr)
project(my_zephyr_app)

target_sources(app PRIVATE src/main.c)

The Cmake property HEX_FILES_TO_MERGE leverages the application configuration provided by Kconfig and CMake to let you merge externally built hex files with the hex file generated when building the Zephyr application. For example:

CMake 属性 HEX_FILES_TO_MERGE 利用 Kconfig 和 CMake 提供的应用程序配置,允许您将外部构建的十六进制文件与构建 Zephyr 应用程序时生成的十六进制文件合并。例如:

set_property(GLOBAL APPEND PROPERTY HEX_FILES_TO_MERGE
    ${app_bootloader_hex}
    ${PROJECT_BINARY_DIR}/${KERNEL_HEX_NAME}
    ${app_provision_hex})

CMakeCache.txt

CMake uses a CMakeCache.txt file as persistent key/value string storage used to cache values between runs, including compile and build options and paths to library dependencies. This cache file is created when CMake is run in an empty build folder.

CMake 使用一个 CMakeCache.txt 文件作为持久键/值字符串存储,用于在运行之间缓存值,包括编译和生成选项以及到库依赖项的路径。此缓存文件是在 CMake 在空的生成文件夹中运行时创建的。

For more details about the CMakeCache.txt file see the official CMake documentation runningcmake .

有关 CMakeCache.txt 文件的详细信息,请参阅官方 CMake 文档 runningcmake。

Application Configuration 应用程序配置

Application Configuration Directory 应用程序配置目录

Zephyr will use configuration files from the application’s configuration directory except for files with an absolute path provided by the arguments described earlier, for example CONF_FILE, OVERLAY_CONFIG, and DTC_OVERLAY_FILE.

Zephyr 将使用应用程序的配置目录中的配置文件,除了前面描述的参数提供了绝对路径的文件,例如 CONF_FILE, OVERLAY_CONFIG, 和 DTC_OVERLAY_FILE.

The application configuration directory is defined by the APPLICATION_CONFIG_DIR variable.

应用程序配置目录由 APPLICATION_CONFIG_DIR 变量定义。

APPLICATION_CONFIG_DIR will be set by one of the sources below with the highest priority listed first.

APPLICATION_CONFIG_DIR 将由下面列出的优先级最高的源之一设置。

  1. If APPLICATION_CONFIG_DIR is specified by the user with -DAPPLICATION_CONFIG_DIR=<path> or in a CMake file before find_package(Zephyr) then this folder is used a the application’s configuration directory.

    如果用户使用-DAPPLICATION_CONFIG_DIR=<path> 或者在 find_package(Zephyr)之前在 CMake 文件中指定了 application_config_dir,那么该文件夹将被用作应用程序的配置目录。

  2. The application’s source directory.

    应用程序的源代码目录。

Kconfig Configuration 配置

Application configuration options are usually set in prj.conf in the application directory. For example, C++ support could be enabled with this assignment:

应用程序配置选项通常在应用程序目录中的 prj.conf 中设置。例如,c++ 支持可以通过以下任务启用:

CONFIG_CPLUSPLUS=y

Looking at existing samples is a good way to get started.

查看现有的样本是一个很好的开始方式。

See Setting Kconfig configuration values for detailed documentation on setting Kconfig configuration values. The The Initial Configuration section on the same page explains how the initial configuration is derived. See Kconfig Search for a complete list of configuration options. See Hardening Tool for security information related with Kconfig options.

有关设置 Kconfig 配置值的详细文档,请参阅设置 Kconfig 配置值。同一页上的 Initial Configuration 部分解释了如何派生初始配置。有关配置选项的完整列表,请参见 Kconfig Search。有关 Kconfig 选项的安全信息,请参阅加固工具。

The other pages in the Kconfig section of the manual are also worth going through, especially if you planning to add new configuration options.

手册中 Kconfig 部分的其他页面也值得一看,尤其是如果您计划添加新的配置选项。

Experimental features 实验特性

Zephyr is a project under constant development and thus there are features that are still in early stages of their development cycle. Such features will be marked [EXPERIMENTAL] in their Kconfig title.

Zephyr是一个不断发展的项目,因此仍有一些特征处于其发展周期的早期阶段。这些特性将在 Kconfig 标题中标记为[ EXPERIMENTAL ]。

The CONFIG_WARN_EXPERIMENTAL setting can be used to enable warnings at CMake configure time if any experimental feature is enabled.

如果启用了任何实验特性,则可以使用 CONFIG_WARN_EXPERIMENTAL 设置在 CMake 配置时启用警告。

CONFIG_WARN_EXPERIMENTAL=y

For example, if option CONFIG_FOO is experimental, then enabling it and CONFIG_WARN_EXPERIMENTAL will print the following warning at CMake configure time when you build an application:

例如,如果 CONFIG_FOO 选项是实验性的,那么启用它和 config_warn_experimental 将在构建应用程序时在 CMake configure 时打印以下警告:

warning: Experimental symbol FOO is enabled.

Devicetree Overlays 工具树覆盖

See Set devicetree overlays.

请参阅设置设备树层。

Application-Specific Code 特定应用程序代码

Application-specific source code files are normally added to the application’s src directory. If the application adds a large number of files the developer can group them into sub-directories under src, to whatever depth is needed.

特定于应用程序的源代码文件通常被添加到应用程序的 src 目录中。如果应用程序添加了大量的文件,那么开发人员可以将它们分组到 src 下的子目录中,以任何所需的深度。

Application-specific source code should not use symbol name prefixes that have been reserved by the kernel for its own use. For more information, see Naming Conventions.

特定于应用程序的源代码不应使用由内核保留供自己使用的符号名前缀。有关更多信息,请参见命名约定。

Third-party Library Code

It is possible to build library code outside the application’s src directory but it is important that both application and library code targets the same Application Binary Interface (ABI). On most architectures there are compiler flags that control the ABI targeted, making it important that both libraries and applications have certain compiler flags in common. It may also be useful for glue code to have access to Zephyr kernel header files.

可以在应用程序的 src 目录之外构建库代码,但重要的是应用程序和库代码都要面向同一个应用二进制接口文件(ABI)。在大多数体系结构中,都有控制 ABI 目标的编译器标志,这使得库和应用程序具有某些共同的编译器标志变得非常重要。对于粘合代码来说,访问 Zephyr 内核头文件可能也很有用。

To make it easier to integrate third-party components, the Zephyr build system has defined CMake functions that give application build scripts access to the zephyr compiler options. The functions are documented and defined in cmake/extensions.cmake and follow the naming convention zephyr_get_<type>_<format>.

为了更容易集成第三方组件,Zephyr 构建系统定义了 CMake 函数,使应用程序构建脚本能够访问 Zephyr 编译器选项。函数在 cmake/extensions.cmake 中记录和定义,并遵循变数命名原则 zephyr_get_<type>_<format>

The following variables will often need to be exported to the third-party build system.

下列变量通常需要导出到第三方构建系统。

  • CMAKE_C_COMPILER, CMAKE_AR.

  • ARCH and BOARD, together with several variables that identify the Zephyr kernel version.

    ARCH 和 BOARD,连同几个变量,确定Zephyr核心版本。

samples/application_development/external_lib is a sample project that demonstrates some of these features.

Samples/application_development/external_lib 是一个示例项目,演示了其中的一些特性。

Building an Application 构建应用程序

The Zephyr build system compiles and links all components of an application into a single application image that can be run on simulated hardware or real hardware.

Zephyr 构建系统将应用程序的所有组件编译并链接成单个应用程序映像,该映像可以在模拟硬件或真实硬件上运行。

Like any other CMake-based system, the build process takes place in two stages. First, build files (also known as a buildsystem) are generated using the cmake command-line tool while specifying a generator. This generator determines the native build tool the buildsystem will use in the second stage. The second stage runs the native build tool to actually build the source files and generate an image. To learn more about these concepts refer to the CMake introduction in the official CMake documentation.

与任何其他基于 cmake 的系统一样,构建过程分为两个阶段。首先,在指定生成器时,使用 cmake 命令行工具生成构建文件(也称为构建系统)。此生成器确定构建系统将在第二阶段使用的本机构建工具。第二阶段运行本机构建工具,实际构建源文件并生成映像。要了解关于这些概念的更多信息,请参阅官方 CMake 文档中的 CMake 介绍。

Although the default build tool in Zephyr is west, Zephyr’s meta-tool, which invokes cmake and the underlying build tool (ninja or make) behind the scenes, you can also choose to invoke cmake directly if you prefer. On Linux and macOS you can choose between the make and ninja generators (i.e. build tools), whereas on Windows you need to use ninja, since make is not supported on this platform. For simplicity we will use ninja throughout this guide, and if you choose to use west build to build your application know that it will default to ninja under the hood.

尽管Zephyr的默认构建工具是 west,Zephyr 的元工具,它在幕后调用 cmake 和底层构建工具(ninja 或 make) ,但如果愿意,也可以选择直接调用 cmake。在 Linux 和 macOS 上,你可以在 make 和 ninja 生成器(即构建工具)之间进行选择,而在 Windows 上,你需要使用 ninja,因为 make 在这个平台上不支持。为了简单起见,我们将在本指南中使用 ninja,如果您选择使用 west build 来构建您的应用程序,那么您应该知道它将默认为引擎盖下的 ninja。

As an example, let’s build the Hello World sample for the reel_board:

作为一个例子,让我们为reel_board构建 Hello World 示例:

Using west: 使用west:

west build -b reel_board samples/hello_world

Using CMake and ninja: 使用 CMake 和 ninja:

# Use cmake to configure a Ninja-based buildsystem:
cmake -Bbuild -GNinja -DBOARD=reel_board samples/hello_world

# Now run ninja on the generated build system:
ninja -Cbuild

On Linux and macOS, you can also build with make instead of ninja:

在 Linux 和 macOS 上,你也可以用 make 来代替 ninja:

Using west: 使用west:

  • to use make just once, add -- -G"Unix Makefiles" to the west build command line; see the west build documentation for an example.

    要使用 make 只需一次,向 west build 命令行添加 -- -G"Unix Makefiles";查看 west build 文档中的示例

  • to use make by default from now on, run west config build.generator "Unix Makefiles".

    从现在开始默认使用 make,运行 west config build.generator "Unix Makefiles"

Using CMake directly: 直接使用 CMake:

# Use cmake to configure a Make-based buildsystem:
cmake -Bbuild -DBOARD=reel_board samples/hello_world

# Now run ninja on the generated build system:
make -Cbuild

Basics 基本知识

Note注意

In the below example, west is used outside of a west workspace. For this to work, you must set the ZEPHYR_BASE environment variable to the path of your zephyr git repository, using one of the methods on the Environment Variables page.

在下面的例子中,west 被用在 west 工作区之外。为此,你必须使用环境变量页面上的方法将 ZEPHYR_BASE 环境变量设置为 zephyr git 存储库的路径。

  1. Navigate to the application directory <home>/app.

    导航到应用程序目录 <home>/app

  2. Enter the following commands to build the application’s zephyr.elf image for the board specified in the command-line parameters:

    输入以下命令为命令行参数中指定的板构建应用程序的 zephyr.elf 镜像:

    Using west:使用 west:

    west build -b <board>
    

    Using CMake and ninja:

    使用 CMake 和 ninja:

    mkdir build && cd build
       
    # Use cmake to configure a Ninja-based buildsystem:
    cmake -GNinja -DBOARD=<board> ..
       
    # Now run ninja on the generated build system:
    ninja
    

    If desired, you can build the application using the configuration settings specified in an alternate .conf file using the CONF_FILE parameter. These settings will override the settings in the application’s .config file or its default .conf file. For example:

    如果需要,可以使用备用项中指定的配置设置生成应用程序。使用 CONF_FILE 参数创建 CONF 文件。这些设置将覆盖应用程序的。配置文件或其默认值。配置文件。例如:

    Using west:使用 west :

    west build -b <board> -- -DCONF_FILE=prj.alternate.conf
    

    Using CMake and ninja: 使用 CMake 和 ninja:

    mkdir build && cd build
    cmake -GNinja -DBOARD=<board> -DCONF_FILE=prj.alternate.conf ..
    ninja
    

    As described in the previous section, you can instead choose to permanently set the board and configuration settings by either exporting BOARD and CONF_FILE environment variables or by setting their values in your CMakeLists.txt using set() statements. Additionally, west allows you to set a default board.

    如上一节所述,您可以选择通过导出 BOARD 和 CONF_FILE 环境变量或使用 set() 语句在 CMakeLists.txt 中设置它们的值来永久设置板和配置设置。此外,west允许你设置一个默认的主板。

Build Directory Contents 构建目录内容

When using the Ninja generator a build directory looks like this:

当使用 Ninja 生成器时,构建目录如下所示:

<home>/app/build
├── build.ninja
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
├── rules.ninja
└── zephyr

The most notable files in the build directory are:

构建目录中最值得注意的文件是:

  • build.ninja, which can be invoked to build the application.

    可以调用它来构建应用程序。

  • A zephyr directory, which is the working directory of the generated build system, and where most generated files are created and stored.

    一个 zephyr 目录,是生成的构建系统的工作目录文件夹,大多数生成的文件都是在这个目录下创建和存储的。

After running ninja, the following build output files will be written to the zephyr sub-directory of the build directory. (This is not the Zephyr base directory, which contains the Zephyr source code etc. and is described above.)

在运行 ninja 之后,下面的构建输出文件将被写入构建目录的 zephyr 子目录。(这不是 Zephyr 基本目录,其中包含 Zephyr 源代码等,并在上面进行了描述。)

  • .config, which contains the configuration settings used to build the application.

    .config,它包含用于构建应用程序的配置设置。

    Note注意

    The previous version of .config is saved to .config.old whenever the configuration is updated. This is for convenience, as comparing the old and new versions can be handy.

    当配置更新时,之前版本的.config保存到.config.old。这是为了方便,因为比较新旧版本可能很方便。

  • Various object files (.o files and .a files) containing compiled kernel and application code.

    包含已编译内核和应用程序代码的各种目标文件(. o 文件和. a 文件)。

  • zephyr.elf, which contains the final combined application and kernel binary. Other binary output formats, such as .hex and .bin, are also supported.

    Zephyr.elf 包含了最终的组合应用和内核二进制。其他二进制输出格式,如.hex.bin也支持。

Rebuilding an Application 重建应用程序

Application development is usually fastest when changes are continually tested. Frequently rebuilding your application makes debugging less painful as the application becomes more complex. It’s usually a good idea to rebuild and test after any major changes to the application’s source files, CMakeLists.txt files, or configuration settings.

在不断测试变更时,应用程序开发通常是最快的。随着应用程序变得更加复杂,频繁地重新构建应用程序可以减少调试的痛苦。在对应用程序的源文件、 CMakeLists.txt 文件或配置设置进行任何重大更改之后重新构建和测试通常是一个好主意

Important重要事项

The Zephyr build system rebuilds only the parts of the application image potentially affected by the changes. Consequently, rebuilding an application is often significantly faster than building it the first time.

Zephyr 构建系统只重新构建可能受到更改影响的应用程序映像的部分。因此,重新构建应用程序通常比第一次构建应用程序要快得多。

Sometimes the build system doesn’t rebuild the application correctly because it fails to recompile one or more necessary files. You can force the build system to rebuild the entire application from scratch with the following procedure:

有时生成系统不能正确地重新生成应用程序,因为它无法重新编译一个或多个必要的文件。您可以通过以下步骤强制构建系统从头开始重新构建整个应用程序:

  1. Open a terminal console on your host computer, and navigate to the build directory <home>/app/build.

    在主机上打开终端控制台,导航到构建目录 <home>/app/build

  2. Enter one of the following commands, depending on whether you want to use west or cmake directly to delete the application’s generated files, except for the .config file that contains the application’s current configuration information.

    输入以下命令之一,具体取决于是否要使用 west 或 cmake 直接删除应用程序生成的文件,包含应用程序当前配置信息的配置文件。

    west build -t clean
    

    or或

    ninja clean
    

    Alternatively, enter one of the following commands to delete all generated files, including the .config files that contain the application’s current configuration information for those board types.

    或者,输入以下命令之一以删除所有生成的文件,包括.config配置文件,其中包含应用程序的当前配置信息:

    west build -t pristine
    

    or

    ninja pristine
    

    If you use west, you can take advantage of its capability to automatically make the build folder pristine whenever it is required.

    如果使用 west,则可以利用它的功能,在需要时自动使构建文件夹处于原始状态。

  3. Rebuild the application normally following the steps specified in Building an Application above.

    通常按照上面构建应用程序中指定的步骤重新构建应用程序。

Building for a board revision 用于主板修订的构建系统

The Zephyr build system has support for specifying multiple hardware revisions of a single board with small variations. Using revisions allows the board support files to make minor adjustments to a board configuration without duplicating all the files described in Create your board directory for each revision.

在Zephyr构建系统支持指定多个硬件修订的一个小变化的单一板。使用修订允许主板的支持文件作出轻微的调整,一个主板的配置没有重复所有文件描述在创建您的主板目录为每个修订。 Zephyr 构建系统支持为一个主板指定多个硬件版本与小的变化。使用修订revision允许主板支持文件对板配置做小的调整,而不重复创建您的板目录中描述的所有文件

To build for a particular revision, use <board>@<revision> instead of plain <board>. For example:

若要构建某个特定的修订版本,请使用 <board>@<revision> 而不是简单的 <board> 。例如:

Using west: 使用 west:

west build -b <board>@<revision>

Using CMake and ninja: 使用 CMake 和 ninja:

mkdir build && cd build
cmake -GNinja -DBOARD=<board>@<revision> ..
ninja

Check your board’s documentation for details on whether it has multiple revisions, and what revisions are supported.

查看您的主板的文档,了解是否有多个修订,以及支持哪些修订。

When targeting a board revision, the active revision will be printed at CMake configure time, like this:

当设定一个版本时,活动版本会在 CMake 配置时打印出来,如下所示:

-- Board: plank, Revision: 1.5.0

Run an Application 运行应用程序

An application image can be run on a real board or emulated hardware.

应用程序映像可以在实际的板或仿真硬件上运行

Running on a Board

Most boards supported by Zephyr let you flash a compiled binary using the flash target to copy the binary to the board and run it. Follow these instructions to flash and run an application on real hardware:

Zephyr 支持的大多数板卡都允许您使用 flash 目标烧录一个编译好的二进制文件,将其复制到板卡并运行它。按照以下说明在实际硬件上运行 flash 和应用程序:

  1. Build your application, as described in Building an Application.

    构建应用程序,如构建应用程序中所述

  2. Make sure your board is attached to your host computer. Usually, you’ll do this via USB.

    确保你的主板连接到你的主机上。通常,你会通过 USB 接口这样做。

  3. Run one of these console commands from the build directory, <home>/app/build, to flash the compiled Zephyr image and run it on your board:

    从构建目录 < home >/app/build 中运行这些控制台命令之一,闪存编译后的 Zephyr 映像并在您的主板上运行它:

    west flash
    

    or或

    ninja flash
    

The Zephyr build system integrates with the board support files to use hardware-specific tools to flash the Zephyr binary to your hardware, then run it.

Zephyr 构建系统与板支持文件集成,使用硬件专用工具将 Zephyr 二进制文件闪存到您的硬件,然后运行它。

Each time you run the flash command, your application is rebuilt and flashed again.

每次运行 flash 命令时,应用程序都会重新生成并再次显示

In cases where board support is incomplete, flashing via the Zephyr build system may not be supported. If you receive an error message about flash support being unavailable, consult your board’s documentation for additional information on how to flash your board.

在主板支持不完整的情况下,可能不支持通过 Zephyr 构建系统烧录。如果您收到一个关于 flash 支持不可用的错误消息,请参考您的主板的文档以获得关于如何 flash 您的主板的其他信息

Note注意

When developing on Linux, it’s common to need to install board-specific udev rules to enable USB device access to your board as a non-root user. If flashing fails, consult your board’s documentation to see if this is necessary.

在 Linux 上开发时,通常需要安装特定于主板的 udev 规则,以使您的主板能够以非根用户的身份访问 USB 设备。如果烧录失败,请参考您的主板的文件,看看这是否是必要的。

Running in an Emulator 在模拟器中运行

The kernel has built-in emulator support for QEMU (on Linux/macOS only, this is not yet supported on Windows). It allows you to run and test an application virtually, before (or in lieu of) loading and running it on actual target hardware. Follow these instructions to run an application via QEMU:

内核有内置的 QEMU 模拟器支持(仅在 Linux/macOS 上,Windows 还不支持)。它允许您在加载应用程序并在实际目标硬件上运行之前(或代替)虚拟地运行和测试应用程序。按照以下说明通过 QEMU 运行应用程序:

  1. Build your application for one of the QEMU boards, as described in Building an Application.

    为其中一块 QEMU 板构建应用程序,如构建应用程序中所述

    For example, you could set BOARD to:

    例如,您可以将 BOARD 设置为:

    • qemu_x86 to emulate running on an x86-based board

      qemu_x86来模拟在基于 x86的板上运行

    • qemu_cortex_m3 to emulate running on an ARM Cortex M3-based board

      qemu_cortex_m3 模拟在基于 ARM Cortex m3的板上运行

  2. Run one of these console commands from the build directory, <home>/app/build, to run the Zephyr binary in QEMU:

    在 build 目录 <home>/app/build 中运行这些控制台命令之一,以便在 QEMU 中运行 Zephyr 二进制文件:

    west build -t run
    

    or或

    ninja run
    
  3. Press Ctrl A, X to stop the application from running in QEMU.

    按 Ctrl a,x 停止应用程序在 QEMU 中运行。

    The application stops running and the terminal console prompt redisplays.

    应用程序停止运行,终端控制台提示符重新显示。

Each time you execute the run command, your application is rebuilt and run again.

每次执行 run 命令时,都会重新生成应用程序并再次运行。

Note注意

If the (Linux only) Zephyr SDK is installed, the run target will use the SDK’s QEMU binary by default. To use another version of QEMU, set the environment variable QEMU_BIN_PATH to the path of the QEMU binary you want to use instead.

如果安装了(仅 Linux) zephyrsdk,运行目标将默认使用 SDK 的 QEMU 二进制文件。要使用其他版本的 QEMU,请将环境变量 QEMU bin path 设置为您想要使用的 QEMU 二进制文件的路径。

Note注意

You can choose a specific emulator by appending _<emulator> to your target name, for example west build -t run_qemu or ninja run_qemu for QEMU.

你可以通过在你的目标名字后面添加 _<emulator> 来选择一个特定的模拟器,例如 west build-t run_qemuninja run_qemu

Application Debugging 应用程序调试

This section is a quick hands-on reference to start debugging your application with QEMU. Most content in this section is already covered in QEMU and GNU_Debugger reference manuals.

本节是一个快速的实践参考,用于开始使用 QEMU 调试应用程序。本节中的大多数内容已经在 QEMU 和 GNU_Debugger 参考手册中介绍过了。

In this quick reference, you’ll find shortcuts, specific environmental variables, and parameters that can help you to quickly set up your debugging environment.

在这个快速参考中,您将找到可以帮助您快速设置调试环境的快捷方式、特定的环境变量和参数。

The simplest way to debug an application running in QEMU is using the GNU Debugger and setting a local GDB server in your development system through QEMU.

调试运行在 QEMU 中的应用程序的最简单方法是使用 GNU 调试器,并通过 QEMU 在开发系统中设置本地 GDB 服务器。

You will need an Executable and Linkable Format (ELF) binary image for debugging purposes. The build system generates the image in the build directory. By default, the kernel binary name is zephyr.elf. The name can be changed using a Kconfig option.

为了调试,你需要一个可执行与可链接格式二进制映像。生成系统在生成目录中生成映像。默认情况下,内核二进制名称是 zephyr.elf。可以使用 Kconfig 选项更改名称。

We will use the standard 1234 TCP port to open a GDB server instance. This port number can be changed for a port that best suits the development environment.

我们将使用标准的1234 TCP 端口来打开 GDB 服务器实例。这个端口号可以更改为最适合开发环境的端口。

You can run QEMU to listen for a “gdb connection” before it starts executing any code to debug it.

您可以运行 QEMU 来侦听“ gdb 连接”,然后再执行任何代码来调试它。

qemu -s -S <image>

will setup Qemu to listen on port 1234 and wait for a GDB connection to it.

将设置 Qemu 在端口1234上侦听,并等待与它的 GDB 连接。

The options used above have the following meaning:

上述选项的含义如下:

  • -S Do not start CPU at startup; rather, you must type ‘c’ in the monitor.

    -S 启动时不要启动 CPU,而是必须在监视器中键入“c”。

  • -s Shorthand for -gdb tcp::1234: open a GDB server on TCP port 1234.

    -s -gdb tcp::1234的快捷方式: 在 TCP 端口1234上打开 GDB 服务器。

To debug with QEMU and to start a GDB server and wait for a remote connect, run either of the following inside the build directory of an application:

要使用 QEMU 进行调试并启动 GDB 服务器并等待远程连接,请在应用程序的构建目录中运行以下任一操作:

ninja debugserver

The build system will start a QEMU instance with the CPU halted at startup and with a GDB server instance listening at the TCP port 1234.

构建系统将启动一个 QEMU 实例,其中 CPU 在启动时停止,并且 GDB 服务器实例在 TCP 端口1234处监听。

Using a local GDB configuration .gdbinit can help initialize your GDB instance on every run. In this example, the initialization file points to the GDB server instance. It configures a connection to a remote target at the local host on the TCP port 1234. The initialization sets the kernel’s root directory as a reference.

使用本地 GDB 配置 .gdbinit 可以在每次运行时帮助初始化 GDB 实例。在本例中,初始化文件指向 GDB 服务器实例。它在 TCP 端口1234上配置到本地主机上的远程目标的连接。初始化将内核的根目录设置为引用。

The .gdbinit file contains the following lines:

.gdbinit 文件包含以下行:

target remote localhost:1234
dir ZEPHYR_BASE

Note注意

Substitute the correct ZEPHYR_BASE for your system.

用正确的 ZEPHYR_BASE 替换你的系统。

Execute the application to debug from the same directory that you chose for the gdbinit file. The command can include the --tui option to enable the use of a terminal user interface. The following commands connects to the GDB server using gdb. The command loads the symbol table from the elf binary file. In this example, the elf binary file name corresponds to zephyr.elf file:

从 gdbinit 文件选择的同一目录执行要调试的应用程序。该命令可以包含 --tui 选项,以启用终端用户界面。下面的命令使用 GDB 连接到 GDB 服务器。该命令从精灵二进制文件加载符号表。在这个例子中,精灵二进制文件名对应于 zephyr.elf 文件:

..../path/to/gdb --tui zephyr.elf

Note注意

The GDB version on the development system might not support the –tui option. Please make sure you use the GDB binary from the SDK which corresponds to the toolchain that has been used to build the binary.

开发系统上的 GDB 版本可能不支持 -tui 选项。请确保您使用的是 SDK 中的 GDB 二进制文件,它对应于用于构建二进制文件的工具链。

If you are not using a .gdbinit file, issue the following command inside GDB to connect to the remote GDB server on port 1234:

如果你没有使用。Gdbinit 文件,在 GDB 内发出以下命令连接到端口1234上的远程 GDB 服务器:

(gdb) target remote localhost:1234

Finally, the command below connects to the GDB server using the Data Displayer Debugger (ddd). The command loads the symbol table from the elf binary file, in this instance, the zephyr.elf file.

最后,下面的命令使用 Data Displayer Debugger (ddd)连接到 GDB 服务器。该命令从精灵二进制文件(在本例中为 zephyr.elf 文件)加载符号表。

The DDD may not be installed in your development system by default. Follow your system instructions to install it. For example, use sudo apt-get install ddd on an Ubuntu system.

默认情况下,DDD 可能不会安装在您的开发系统中。按照系统说明进行安装。例如,在 Ubuntu 系统上使用 sudo apt-get install ddd。

ddd --gdb --debugger "gdb zephyr.elf"

Both commands execute the gdb. The command name might change depending on the toolchain you are using and your cross-development tools.

两个命令都执行 gdb。命令名可能会根据您正在使用的工具链和您的交叉开发工具而改变。

Custom Board, Devicetree and SOC Definitions

自定义板,设备树和 SOC 定义

In cases where the board or platform you are developing for is not yet supported by Zephyr, you can add board, Devicetree and SOC definitions to your application without having to add them to the Zephyr tree.

如果您正在开发的板或平台还不支持 Zephyr,您可以添加板、设备树和 SOC 定义到您的应用程序中,而不必将它们添加到 Zephyr 树中

The structure needed to support out-of-tree board and SOC development is similar to how boards and SOCs are maintained in the Zephyr tree. By using this structure, it will be much easier to upstream your platform related work into the Zephyr tree after your initial development is done.

支持树外板和 SOC 开发所需的结构类似于板和 SOC 在 Zephyr 树中的维护方式。通过使用这种结构,在完成初始开发之后,将平台相关工作上游到 Zephyr 树中将会容易得多。

Add the custom board to your application or a dedicated repository using the following structure:

使用以下结构将自定义板添加到应用程序或专用存储库中:

boards/
soc/
CMakeLists.txt
prj.conf
README.rst
src/

where the boards directory hosts the board you are building for:

其中的主板目录托管你正在为之建立的主板:

.
├── boards
│   └── x86
│       └── my_custom_board
│           ├── doc
│           │   └── img
│           └── support
└── src

and the soc directory hosts any SOC code. You can also have boards that are supported by a SOC that is available in the Zephyr tree.

SOC 目录承载任何 SOC 代码。在Zephyr树中,你也可以有板支持的 SOC 是可用的。

Boards 主板

Use the proper architecture folder name (e.g., x86, arm, etc.) under boards for my_custom_board. (See Supported Boards for a list of board architectures.)

my_custom_board使用正确的架构文件夹名称(例如,x86,arm,等等)在boards目录下(参见支持的主板结构的列表。)

Documentation (under doc/) and support files (under support/) are optional, but will be needed when submitting to Zephyr.

文档(在 doc/之下)和支持文件(在 support/之下)是可选的,但在提交到 Zephyr 时将需要这些文档。

The contents of my_custom_board should follow the same guidelines for any Zephyr board, and provide the following files:

我的自定义板的内容应该遵循任何 Zephyr 板相同的指导方针,并提供以下文件:

my_custom_board_defconfig
my_custom_board.dts
my_custom_board.yaml
board.cmake
board.h
CMakeLists.txt
doc/
dts_fixup.h
Kconfig.board
Kconfig.defconfig
pinmux.c
support/

Once the board structure is in place, you can build your application targeting this board by specifying the location of your custom board information with the -DBOARD_ROOT parameter to the CMake build system:

一旦板结构就位,您可以通过使用-DBOARD_ROOT 参数指定您的自定义板信息的位置来构建针对该板的应用程序,从而构建 CMake 构建系统:

Using west:使用 west :

west build -b <board name> -- -DBOARD_ROOT=<path to boards>

Using CMake and ninja: 使用 CMake 和 ninja:

cmake -Bbuild -GNinja -DBOARD=<board name> -DBOARD_ROOT=<path to boards> .
ninja -Cbuild

This will use your custom board configuration and will generate the Zephyr binary into your application directory.

这将使用您的自定义板配置,并将生成 Zephyr 二进制文件到您的应用程序目录。

You can also define the BOARD_ROOT variable in the application CMakeLists.txt file. Make sure to do so before pulling in the Zephyr boilerplate with find_package(Zephyr ...).

您还可以在应用程序 CMakeLists.txt 文件中定义 BOARD_ROOT 变量。在使用find_package(Zephyr…)拉入Zephyr样板之前,请确保这样做。

Note注意

When specifying BOARD_ROOT in a CMakeLists.txt, then an absolute path must be provided, for example list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/<extra-board-root>. When using -DBOARD_ROOT=<board-root> both absolute and relative paths can be used. Relative paths are treated relatively to the application directory.

在 CMakeLists.txt 中指定 BOARD_ROOT 时,必须提供一个绝对路径,例如list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/<extra-BOARD-root> 。当使用-DBOARD_ROOT=<board-root> 时,可以使用绝对路径和相对路径。相对于应用程序目录处理相对路径。

SOC Definitions SOC 定义

Similar to board support, the structure is similar to how SOCs are maintained in the Zephyr tree, for example:

与板支持类似,结构类似于 soc 在 Zephyr 树中的维护方式,例如:

soc
└── arm
    └── st_stm32
            ├── common
            └── stm32l0

The file soc/Kconfig will create the top-level SoC/CPU/Configuration Selection menu in Kconfig.

文件 SoC/Kconfig 将在 Kconfig 创建顶级 SoC/CPU/Configuration Selection 菜单。

Out of tree SoC definitions can be added to this menu using the SOC_ROOT CMake variable. This variable contains a semicolon-separated list of directories which contain SoC support files.

树外的SoC定义可以通过SOC_ROOTCMake变量添加到这个菜单中。这个变量包含一个分号分隔的包含SoC支持文件的目录列表。

Following the structure above, the following files can be added to load more SoCs into the menu.

按照上面的结构,可以添加以下文件以将更多的 soc 加载到菜单中。

soc
└── arm
    └── st_stm32
            ├── Kconfig
            ├── Kconfig.soc
            └── Kconfig.defconfig

The Kconfig files above may describe the SoC or load additional SoC Kconfig files.

上面的 Kconfig 文件可以描述 SoC 或者加载额外的 SoC Kconfig 文件。

An example of loading stm31l0 specific Kconfig files in this structure:

在这个结构中加载 stm31l0特定的 Kconfig 文件的一个例子:

soc
└── arm
    └── st_stm32
            ├── Kconfig.soc
            └── stm32l0
                └── Kconfig.series

can be done with the following content in st_stm32/Kconfig.soc:

可以通过以下内容在 st_stm32/Kconfig.soc 中完成:

rsource "*/Kconfig.series"

Once the SOC structure is in place, you can build your application targeting this platform by specifying the location of your custom platform information with the -DSOC_ROOT parameter to the CMake build system:

一旦 SOC 结构就位,您可以通过使用-DSOC_ROOT 参数向 CMake 构建系统指定定制平台信息的位置来构建针对该平台的应用程序:

Using west: 使用 west :

west build -b <board name> -- -DSOC_ROOT=<path to soc> -DBOARD_ROOT=<path to boards>

Using CMake and ninja: 使用 CMake 和 ninja:

cmake -Bbuild -GNinja -DBOARD=<board name> -DSOC_ROOT=<path to soc> -DBOARD_ROOT=<path to boards> .
ninja -Cbuild

This will use your custom platform configurations and will generate the Zephyr binary into your application directory.

这将使用您自定义的平台配置,并将生成 Zephyr 二进制文件到您的应用程序目录中。

See Build settings for information on setting SOC_ROOT in a module’s zephyr/module.yml file.

有关在模块的 zephyr/module.yml 文件中设置 SoC_ROOT 的信息,请参见 Build 设置。

Or you can define the SOC_ROOT variable in the application CMakeLists.txt file. Make sure to do so before pulling in the Zephyr boilerplate with find_package(Zephyr ...).

或者您可以在应用程序 CMakeLists.txt 文件中定义 SoC_ROOT 变量。确保这样做,然后拉在 west 样板与发现 _ 包( west …)。

Note注意

When specifying SOC_ROOT in a CMakeLists.txt, then an absolute path must be provided, for example list(APPEND SOC_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/<extra-soc-root>. When using -DSOC_ROOT=<soc-root> both absolute and relative paths can be used. Relative paths are treated relatively to the application directory.

在 CMakeLists.txt 中指定 SoC_ROOT 时,必须提供一个绝对路径,例如list(APPEND SoC_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/<extra-SOC-root> 。当使用-DSOC_ROOT=<soc-root> 时,可以使用绝对路径和相对路径。相对于应用程序目录处理相对路径。

Devicetree Definitions 设备定义

Devicetree directory trees are found in APPLICATION_SOURCE_DIR, BOARD_DIR, and ZEPHYR_BASE, but additional trees, or DTS_ROOTs, can be added by creating this directory tree:

目录树可以在 APPLICATION_SOURCE_DIR, BOARD_DIR 和 ZEPHYR_BASE 中找到,但是可以通过创建这个目录树来添加其他树,或者 DTS_ROOT:

include/
dts/common/
dts/arm/
dts/
dts/bindings/

Where ‘arm’ is changed to the appropriate architecture. Each directory is optional. The binding directory contains bindings and the other directories contain files that can be included from DT sources.

这里的“arm”被改变为适当的架构。每个目录都是可选的。绑定目录包含绑定,其他目录包含可以从 DT 源包含的文件。

Once the directory structure is in place, you can use it by specifying its location through the DTS_ROOT CMake Cache variable:

一旦目录结构就位,您可以通过 DTS_ROOT CMake Cache 变量指定它的位置来使用它:

Using west: 使用 west :

west build -b <board name> -- -DDTS_ROOT=<path to dts root>

Using CMake and ninja: 使用 CMake 和 ninja:

cmake -Bbuild -GNinja -DBOARD=<board name> -DDTS_ROOT=<path to dts root> .
ninja -Cbuild

You can also define the variable in the application CMakeLists.txt file. Make sure to do so before pulling in the Zephyr boilerplate with find_package(Zephyr ...).

您还可以在应用程序“CMakeLists.txt”中定义变量。在使用find_package(Zephyr…)拉入Zephyr样板之前,请确保这样做

Note注意

When specifying DTS_ROOT in a CMakeLists.txt, then an absolute path must be provided, for example list(APPEND DTS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/<extra-dts-root>. When using -DDTS_ROOT=<dts-root> both absolute and relative paths can be used. Relative paths are treated relatively to the application directory.

在 CMakeLists.txt 中指定 DTS_ROOT 时,必须提供一个绝对路径,例如列表(APPEND DTS_ROOT ${ CMAKE_CURRENT_SOURCE_DIR }/< extra-DTS-root > 。当使用-ddts_root = < dts-root > 时,可以使用绝对路径和相对路径。相对于应用程序目录处理相对路径。

Devicetree source are passed through the C preprocessor, so you can include files that can be located in a DTS_ROOT directory. By convention devicetree include files have a .dtsi extension.

Devicetree 源是通过 c 预处理器传递的,因此可以包含可以位于 DTS_ROOT 目录中的文件。按照惯例,设备树包含.dtsi后缀的文件

You can also use the preprocessor to control the content of a devicetree file, by specifying directives through the DTS_EXTRA_CPPFLAGS CMake Cache variable:

您还可以使用预处理器来控制设备树文件的内容,方法是通过 DTS_EXTRA_CPPFLAGS CMake Cache 变量指定指令:

Using west: 使用 west :

west build -b <board name> -- -DDTS_EXTRA_CPPFLAGS=-DTEST_ENABLE_FEATURE

Using CMake and ninja: 使用 CMake 和 ninja:

cmake -Bbuild -GNinja -DBOARD=<board name> -DDTS_EXTRA_CPPFLAGS=-DTEST_ENABLE_FEATURE .
ninja -Cbuild

west 工具

West (Zephyr’s meta-tool)¶

The Zephyr project includes a swiss-army knife command line tool named west1. West is developed in its own repository.

Zephyr 项目包括一个名为 west 的瑞士军刀命令行工具。 West 是在自己的存储库中开发的。

West’s built-in commands provide a multiple repository management system with features inspired by Google’s Repo tool and Git submodules. West is also “pluggable”: you can write your own west extension commands which add additional features to west. Zephyr uses this to provide conveniences for building applications, flashing and debugging them, and more.

West 的内置命令提供了一个多存储库管理系统,其特性受到了 Google 的 Repo 工具和 Git 子模块的启发。West 也是“可插入的”: 您可以编写自己的 West 扩展命令,为 West 添加额外的特性。Zephyr 利用这一点为构建应用程序、烧录和调试应用程序等提供便利。

Like git and docker, the top-level west command takes some common options, a sub-command to run, and then options and arguments for that sub-command:

与 git 和 docker 一样,顶级 west 命令采用一些常见的选项,一个要运行的子命令,然后是该子命令的选项和参数:

west [common-opts] <command> [opts] <args>

Since west v0.8, you can also run west like this:

从 west v0.8 开始,你也可以像这样运行:

python3 -m west [common-opts] <command> [opts] <args>

You can run west –help (or west -h for short) to get top-level help for available west commands, and west -h for detailed help on each command.

您可以运行 west --help (简称为 west -h)获得可用的 west 命令的顶级帮助,而 west <command> -h 获得每个命令的详细帮助。

The following pages document west’s v0.13.x releases, and provide additional context about the tool.

west 基本知识

This page introduces west’s basic concepts and provides references to further reading.

这一页介绍了 west 的基本概念,为进一步阅读提供了参考。

West’s built-in commands allow you to work with projects (Git repositories) under a common workspace directory.

West 的内置命令允许您使用公共工作区目录下的项目(Git repositories)。

Example workspace 示例工作区

If you’ve followed the upstream Zephyr getting started guide, your workspace looks like this:

如果你遵循了上游的 Zephyr 入门指南,你的工作空间看起来是这样的:

zephyrproject/                 # west topdir
├── .west/                     # marks the location of the topdir
│   └── config                 # per-workspace local configuration file
│
│   # The manifest repository, never modified by west after creation:
├── zephyr/                    # .git/ repo
│   ├── west.yml               # manifest file
│   └── [... other files ...]
│
│   # Projects managed by west:
├── modules/
│   └── lib/
│       └── tinycbor/          # .git/ project
├── net-tools/                 # .git/ project
└── [ ... other projects ...]

Workspace concepts 工作空间概念

Here are the basic concepts you should understand about this structure. Additional details are in Workspaces.

以下是您应该了解的关于这个结构的基本概念。

  • topdir 顶部

    Above, zephyrproject is the name of the workspace’s top level directory, or topdir. (The name zephyrproject is just an example – it could be anything, like z, my-zephyr-workspace, etc.) You’ll typically create the topdir and a few other files and directories using west init.

    上面,zephyrproject 是工作区顶级目录的名称,或者 topdir。(名称 zephyrproject 只是一个例子——它可以是任何东西,比如 z、 my-zephyr-workspace 等等。) 通常,您将使用 west init 创建 topdir 以及其他一些文件和目录。

  • .west directory

    The topdir contains the .west directory. When west needs to find the topdir, it searches for .west, and uses its parent directory. The search starts from the current working directory (and starts again from the location in the ZEPHYR_BASE environment variable as a fallback if that fails).

    topdir包含.west目录。当west需要找到topdir时,它搜索.west,并使用其父目录。搜索从当前工作目录开始(如果搜索失败,则从ZEPHYR_BASE环境变量的位置再次开始)。

  • configuration file 配置文件

    The file .west/config is the workspace’s local configuration file.

    .West/config 文件是工作区的本地配置文件。

  • manifest repository 清单存储库

    Every west workspace contains exactly one manifest repository, which is a Git repository containing a manifest file. The location of the manifest repository is given by the manifest.path configuration option in the local configuration file.

    每个 west 工作区恰好包含一个 manifest 存储库,这是一个包含一个 manifest 文件的 Git 存储库。清单存储库的位置由本地配置文件中的 manifest.path 配置选项提供。

    For upstream Zephyr, zephyr is the manifest repository, but you can configure west to use any Git repository in the workspace as the manifest repository. The only requirement is that it contains a valid manifest file. See Topologies supported for information on other options, and West Manifests for details on the manifest file format.

    对于上游的 Zephyr,Zephyr 是清单存储库,但是您可以配置 west 使用工作区中的任何 Git 存储库作为清单存储库。唯一的要求是它包含一个有效的清单文件。有关其他选项的信息,请参见所支持的拓扑,有关清单文件格式的详细信息,请参见 west 清单。

  • manifest file 舱单文件

    The manifest file is a YAML file that defines projects, which are the additional Git repositories in the workspace managed by west. The manifest file is named west.yml by default; this can be overridden using the manifest.file local configuration option.

    清单文件是一个定义项目的 YAML 文件,这些项目是 west 管理的工作区中的附加 Git 存储库。清单文件默认命名为 west.yml; 可以使用 manifest.file 本地配置选项重写该文件。

    You use the west update command to update the workspace’s projects based on the contents of the manifest file.

    您可以使用 west update 命令根据清单文件的内容更新工作区的项目

  • projects 项目

    Projects are Git repositories managed by west. Projects are defined in the manifest file and can be located anywhere inside the workspace. In the above example workspace, tinycbor and net-tools are projects.

    项目是由 west 管理的 Git 仓库。项目是在清单文件中定义的,可以位于工作区中的任何位置。在上面的示例工作区中,tinycbor 和 net-tools 都是项目。

    By default, the Zephyr build system uses west to get the locations of all the projects in the workspace, so any code they contain can be used as Modules (External projects).

    默认情况下,Zephyr 构建系统使用 west 获取工作区中所有项目的位置,因此它们包含的任何代码都可以用作 Modules (外部项目)。

  • extensions 扩展

    Any repository known to west (either the manifest repository or any project repository) can define Extensions. Extensions are extra west commands you can run when using that workspace.

    West 已知的任何存储库(manifest 存储库或任何项目存储库)都可以定义 Extensions。扩展是一些额外的 west 命令,您可以在使用该工作区时运行它们。

    The zephyr repository uses this feature to provide Zephyr-specific commands like west build. Defining these as extensions keeps west’s core agnostic to the specifics of any workspace’s Zephyr version, etc.

    Zephyr 存储库使用这个特性来提供 zephyr 特定的命令,比如 west build。将这些定义为扩展使 west 的核心对于任何工作区的 Zephyr 版本的细节不可知,等等。

  • ignored files 忽略文件

    A workspace can contain additional Git repositories or other files and directories not managed by west. West basically ignores anything in the workspace except .west, the manifest repository, and the projects specified in the manifest file.

    工作区可以包含其他 Git 存储库或其他不由 west 管理的文件和目录。基本上忽略工作空间中的任何东西,除了。清单存储库和清单文件中指定的项目。

west init and west update

The two most important workspace-related commands are west init and west update.

两个最重要的与工作区相关的命令是 west init 和 west update。

west init basics

This command creates a west workspace. 这个命令创建一个 west 工作区。

Important 重要事项

West doesn’t change your manifest repository contents after west init is run. Use ordinary Git commands to pull new versions, etc.

在 West init 运行之后,West 不会更改您的 manifest 存储库内容。使用普通的 Git 命令来获取新的版本,等等。

You will typically run it once, like this: 你通常会运行一次,像这样:

west init -m https://github.com/zephyrproject-rtos/zephyr --mr v2.5.0 zephyrproject

This will:

  1. Create the topdir, zephyrproject, along with .west and .west/config inside it

    创建 topdir、 zephyrproject 以及其中的 .west.west/config

  2. Clone the manifest repository from https://github.com/zephyrproject-rtos/zephyr, placing it into zephyrproject/zephyr

    从 https://github.com/zephyrproject-rtos/zephyr 中克隆清单仓库,将其放入 zephyrproject/zephyr

  3. Check out the v2.5.0 git tag in your local zephyr clone

    请查看本地 zephyr 克隆中的 v2.5.0 git 标记

  4. Set manifest.path to zephyr in .west/config

    .west/config 中将 manifest.path 设置为 zephyr

  5. Set manifest.file to west.yml

    将 manifest.file 设置为 west.yml

Your workspace is now almost ready to use; you just need to run west update to clone the rest of the projects into the workspace to finish.

您的工作区现在几乎可以使用了; 您只需要运行 west update 来将其余的项目克隆到工作区中以完成。

For more details, see west init.

有关更多细节,请参见 west init。

west update basics

This command makes sure your workspace contains Git repositories matching the projects in the manifest file.

此命令确保您的工作区包含与清单文件中的项目匹配的 Git 存储库。

Important 重要事项

Whenever you check out a different revision in your manifest repository, you should run west update to make sure your workspace contains the project repositories the new revision expects.

每当您在您的清单存储库中签出一个不同的修订时,您应该运行 west update 以确保您的工作区包含新修订所期望的项目存储库。

The west update command reads the manifest file’s contents by:

West update 命令通过以下方式读取清单文件的内容:

  1. Finding the topdir. In the west init example above, that means finding zephyrproject.

    在上面的 west init 例子中,这意味着找到 zephyrproject。

  2. Loading .west/config in the topdir to read the manifest.path (e.g. zephyr) and manifest.file (e.g. west.yml) options.

    在 topdir 中加载. west/config 以读取 manifest.path (例如 zephyr)和 manifest.file (例如 west.yml)选项。

  3. Loading the manifest file given by these options (e.g. zephyrproject/zephyr/west.yml).

    加载这些选项(例如 zephyrproject/zephyr/west.yml)提供的清单文件。

It then uses the manifest file to decide where missing projects should be placed within the workspace, what URLs to clone them from, and what Git revisions should be checked out locally. Project repositories which already exist are updated in place by fetching and checking out their respective Git revisions in the manifest file.

然后它使用清单文件来决定丢失的项目应该放在工作区的哪个位置,从哪些 url 中克隆它们,以及哪些 Git 修订应该在本地签出。已经存在的项目存储库通过在清单文件中获取和签出它们各自的 Git 修订来进行更新。

For more details, see west update.

更多详细信息,请参阅 west update。

Other built-in commands 其他内置命令

See Built-in commands.

请参阅内置命令。

Zephyr Extensions Zephyr 扩展

See the following pages for information on Zephyr’s extension commands:

有关 Zephyr 的扩展命令的信息,请参阅以下页面:

west Built-in commands 内置命令

This page describes west’s built-in commands, some of which were introduced in Basics, in more detail.

本页面详细介绍了 west 的内置命令,其中一些命令是在 Basics 中介绍的。

Some commands are related to Git commands with the same name, but operate on the entire workspace. For example, west diff shows local changes in multiple Git repositories in the workspace.

有些命令与同名的 Git 命令相关,但是对整个工作区进行操作。例如,west diff 在工作区中的多个 Git 存储库中显示本地更改。

Some commands take projects as arguments. These arguments can be project names as specified in the manifest file, or (as a fallback) paths to them on the local file system. Omitting project arguments to commands which accept them (such as west list, west forall, etc.) usually defaults to using all projects in the manifest file plus the manifest repository itself.

有些命令将项目作为参数。这些参数可以是清单文件中指定的项目名称,或者(作为回退)在本地文件系统上指向它们的路径。将项目参数省略到接受它们的命令(比如 west list、 west forall 等等)通常默认使用清单文件中的所有项目以及清单存储库本身。

For additional help, run west <command> -h (e.g. west init -h).

如果需要额外的帮助,运行 west <command> -h (例如 west init -h)。

west init

This command creates a west workspace. It can be used in two ways:

这个命令创建一个 west 工作区,可以通过两种方式使用:

  1. Cloning a new manifest repository from a remote URL

    从远程 URL 克隆新的清单存储库

  2. Creating a workspace around an existing local manifest repository

    围绕现有的本地清单存储库创建工作区

Option 1: to clone a new manifest repository from a remote URL, use:

选项1: 从远程 URL 克隆一个新的清单存储库,使用:

west init [-m URL] [--mr REVISION] [--mf FILE] [directory]

The new workspace is created in the given directory, creating a new .west inside this directory. You can give the manifest URL using the -m switch, the initial revision to check out using --mr, and the location of the manifest file within the repository using --mf.

在给定的目录中创建新的工作空间,在该目录中创建一个新的.west。您可以使用-m开关给出清单的URL,使用-mr给出要检出的初始修订版,使用-mf给出清单文件在版本库中的位置

For example, running:

west init -m https://github.com/zephyrproject-rtos/zephyr --mr v1.14.0 zp

would clone the upstream official zephyr repository into zp/zephyr, and check out the v1.14.0 release. This command creates zp/.west, and set the manifest.path configuration option to zephyr to record the location of the manifest repository in the workspace. The default manifest file location is used.

将克隆上游官方 zephyr 储存库到 zp/zephyr,并检查 v1.14.0版本。这个命令创建 zp/。然后将 manifest.path 配置选项设置为 zephyr,以记录 manifest 存储库在工作区中的位置。使用默认的清单文件位置。

The -m option defaults to https://github.com/zephyrproject-rtos/zephyr. The --mf option defaults to west.yml. Since west v0.10.1, west will use the default branch in the manifest repository unless the --mr option is used to override it. (In prior versions, --mr defaulted to master.)

-m 选项默认为 https://github.com/zephyrproject-rtos/zephyr。--mf 选项默认为 west.yml。由于 west v0.10.1,west 将使用 manifest 存储库中的缺省分支,除非使用 --mr 选项来覆盖它。(在以前的版本中,--mr默认master)

If no directory is given, the current working directory is used.

如果没有给出目录,则使用当前的工作目录。

Option 2: to create a workspace around an existing local manifest repository, use:

选项2: 要围绕现有的本地清单存储库创建一个工作区,使用:

west init -l [--mf FILE] directory

This creates .west next to directory in the file system, and sets manifest.path to directory.

这将在文件系统中的目录旁创建 .west,并将 manifest.path 设置为 directory。

As above, --mf defaults to west.yml.

如上所述,--mf 默认为 west.yml。

Reconfiguring the workspace:

重新配置工作空间:

If you change your mind later, you are free to change manifest.path and manifest.file using west config after running west init. Just be sure to run west update afterwards to update your workspace to match the new manifest file.

如果以后改变了主意,在运行 west init 之后,可以使用 west config 自由地更改 manifest.path 和 manifest.file。只要确保在之后运行 west update 来更新您的工作区以匹配新的清单文件。

west update West 更新

west update [-f {always,smart}] [-k] [-r]
            [--group-filter FILTER] [--stats] [PROJECT ...]

Which projects are updated:

哪些项目需要更新:

By default, this command parses the manifest file, usually west.yml, and updates each project specified there. If your manifest uses project groups, then only the active projects are updated.

默认情况下,此命令解析清单文件(通常是 west.yml) ,并更新其中指定的每个项目。如果您的清单使用项目组,则只更新活动项目。

To operate on a subset of projects only, give PROJECT argument(s). Each PROJECT is either a project name as given in the manifest file, or a path that points to the project within the workspace. If you specify projects explicitly, they are updated regardless of whether they are active.

若要仅对项目的子集进行操作,请给出 PROJECT 参数。每个 PROJECT 要么是清单文件中给出的项目名,要么是指向工作区中的项目的路径。如果显式指定项目,则不管项目是否处于活动状态,都会更新它们。

Project update procedure:

项目更新流程:

For each project that is updated, this command:

对于每个更新的项目,这个命令:

  1. Initializes a local Git repository for the project in the workspace, if it does not already exist

    初始化工作区中项目的本地 Git 存储库(如果它不存在)

  2. Inspects the project’s revision field in the manifest, and fetches it from the remote if it is not already available locally

    在清单中检查项目的修订字段,如果本地不可用,则从远程获取该字段

  3. Sets the project’s manifest-rev branch to the commit specified by the revision in the previous step

    将项目的 manifest-rev 分支设置为上一步中的修订所指定的提交

  4. Checks out manifest-rev in the local working copy as a detached HEAD

    检查本地工作副本中的载货清单-rev 为分离的 HEAD

  5. If the manifest file specifies a submodules key for the project, recursively updates the project’s submodules as described below.

    如果 manifest 文件指定了项目的子模块键,则递归更新项目的子模块,如下所述。

To avoid unnecessary fetches, west update will not fetch project revision values which are Git SHAs or tags that are already available locally. This is the behavior when the -f (--fetch) option has its default value, smart. To force this command to fetch from project remotes even if the revisions appear to be available locally, either use -f always or set the update.fetch configuration option to always. SHAs may be given as unique prefixes as long as they are acceptable to Git 1.

为了避免不必要的获取,west update 不会获取本地已有的 Git SHAs 或标记的项目修订值。当-f (--fetch)选项具有其缺省值 smart 时,就会出现这种行为。要强制此命令从项目远程取回,即使修订看起来在本地可用,可以使用-f always 或将 update.fetch configuration 选项设置为 always。只要 Git 1可以接受,可以将 sh 作为唯一的前缀。

If the project revision is a Git ref that is neither a tag nor a SHA (i.e. if the project is tracking a branch), west update always fetches, regardless of -f and update.fetch.

如果项目版本是一个既不是标记也不是 SHA 的 Git ref (例如,如果项目正在跟踪一个分支) ,west update 总是获取,而不管 -f 和 update.fetch。

Some branch names might look like short SHAs, like deadbeef. West treats these like SHAs. You can disambiguate by prefixing the revision value with refs/heads/, e.g. revision: refs/heads/deadbeef.

一些分支名称可能看起来像短的 SHAs,像 deadbeef。韦斯特对待这些问题就像对待疑难杂症一样。可以通过在修订值前加上 refs/heads/来消除歧义,例如 refs/heads/deadbeef。

For safety, west update uses git checkout --detach to check out a detached HEAD at the manifest revision for each updated project, leaving behind any branches which were already checked out. This is typically a safe operation that will not modify any of your local branches.

为了安全起见,west update 使用 git checkout– detach 在每个更新项目的 manifest 版本中检出一个分离的 HEAD,留下任何已经检出的分支。这通常是一个不会修改任何本地分支的安全操作。

However, if you had added some local commits onto a previously detached HEAD checked out by west, then git will warn you that you’ve left behind some commits which are no longer referred to by any branch. These may be garbage-collected and lost at some point in the future. To avoid this if you have local commits in the project, make sure you have a local branch checked out before running west update.

然而,如果你添加了一些本地提交到先前被 west 签出的分离的 HEAD 上,那么 git 会警告你你留下了一些不再被任何分支引用的提交。这些可能会被垃圾收集,并在将来的某个时候丢失。为了避免这种情况,如果在项目中有本地提交,请确保在运行 west update 之前签出了本地分支。

If you would rather rebase any locally checked out branches instead, use the -r (--rebase) option.

如果您宁愿重新对任何本地签出的分支进行基础设置,请使用-r (—— rebase)选项。

If you would like west update to keep local branches checked out as long as they point to commits that are descendants of the new manifest-rev, use the -k (--keep-descendants) option.

如果希望 west update 保持签出本地分支,只要它们指向新的 manifest-rev 的后代 commit,那么可以使用-k (—— keep-descendants)选项。

Note 注意

west update --rebase will fail in projects that have git conflicts between your branch and new commits brought in by the manifest. You should immediately resolve these conflicts as you usually do with git, or you can use git -C <project_path> rebase --abort to ignore incoming changes for the moment.

West update --rebase 将会失败,因为这些项目中存在您的分支和 manifest 带来的新提交之间的 git 冲突。您应该立即解决这些冲突,就像您通常对 git 所做的那样,或者您可以使用 git -C <project_path> rebase --abort来忽略传入的更改。

With a clean working tree, a plain west update never fails because it does not try to hold on to your commits and simply leaves them aside.

对于一个干净的工作树,一个普通的 west 更新永远不会失败,因为它不会试图保留你的提交,而只是把它们放在一边。

west update --keep-descendants offers an intermediate option that never fails either but does not treat all projects the same:

West update —— keep-descendants 提供了一个中间选项,这个选项从未失败过,但并不对所有项目一视同仁:

  • in projects where your branch diverged from the incoming commits, it does not even try to rebase and leaves your branches behind just like a plain west update does;

    在一些项目中,如果你的分支与传入的提交发生偏离,它甚至不会尝试重新建立基础,而是像普通的 west update 那样将你的分支留在后面;

  • in all other projects where no rebase or merge is needed it keeps your branches in place.

    在所有其他不需要重建或合并的项目中,它保持您的分支在适当的位置。

One-time project group manipulation:

一次性的项目组操作:

The --group-filter option can be used to change which project groups are enabled or disabled for the duration of a single west update command. See Project Groups and Active Projects for details on the project group feature.

--group-filter 选项可用于更改在单个 west update 命令执行期间启用或禁用哪些项目组。有关项目组特性的详细信息,请参阅项目组和活动项目。

The west update command behaves as if the --group-filter option’s value were appended to the manifest.group-filter configuration option.

West update 命令的行为就好像—— group-filter 选项的值附加到 manifest.group-filter 配置选项后面。

For example, running west update --group-filter=+foo,-bar would behave the same way as if you had temporarily appended the string "+foo,-bar" to the value of manifest.group-filter, run west update, then restored manifest.group-filter to its original value.

例如,运行 west update —— group-filter = + foo,-bar 的行为方式与将字符串“ + foo,-bar”临时附加到 manifest.group-filter 的值、运行 west update、然后将 manifest.group-filter 恢复到其原始值的行为方式相同。

Note that using the syntax --group-filter=VALUE instead of --group-filter VALUE avoids issues parsing command line options if you just want to disable a single group, e.g. --group-filter=-bar.

注意,如果只想禁用单个组,使用语法—— group-filter = VALUE 而不是—— group-filter VALUE 可以避免解析命令行选项的问题,例如—— group-filter =-bar。

Submodule update procedure:

子模块更新过程:

If a project in the manifest has a submodules key, the submodules are updated as follows, depending on the value of the submodules key.

如果清单中的项目具有子模块键,则根据子模块键的值更新子模块,如下所示。

If the project has submodules: true, west first synchronizes the project’s submodules with:

如果项目有子模块: true,west first 将项目的子模块同步为:

git submodule sync --recursive

West then runs one of the following in the project repository, depending on whether you run west update with the --rebase option or without it:

然后在项目存储库中运行以下命令之一,具体取决于是否使用 --rebase 选项运行 West update:

# without --rebase, e.g. "west update":
git submodule update --init --checkout --recursive

# with --rebase, e.g. "west update --rebase":
git submodule update --init --rebase --recursive

Otherwise, the project has submodules: <list-of-submodules>. In this case, west synchronizes the project’s submodules with:

否则,该项目有子模块: < list-of-submodules > :

git submodule sync --recursive -- <submodule-path>

Then it updates each submodule in the list as follows, depending on whether you run west update with the --rebase option or without it:

然后根据是否使用-rebase 选项运行 west update,更新列表中的每个子模块如下:

# without --rebase, e.g. "west update":
git submodule update --init --checkout --recursive <submodule-path>

# with --rebase, e.g. "west update --rebase":
git submodule update --init --rebase --recursive <submodule-path>

The git submodule sync commands are skipped if the update.sync-submodules Configuration option is false.

如果 update.sync-submodules Configuration 选项为 false,git 子模块同步命令将被跳过。

Other project commands 其他项目命令

West has a few more commands for managing the projects in the workspace, which are summarized here. Run west <command> -h for detailed help.

West 还有一些用于管理工作空间中的项目的命令,这里对它们进行了总结。运行 west <command> -h 获取详细帮助。

  • west list: print a line of information about each project in the manifest, according to a format string

    West list: 根据格式字符串打印清单中每个项目的一行信息

  • west manifest: manage the manifest file. See Manifest Command.

    west manifest: 管理清单文件。请参阅清单命令。

  • west diff: run git diff in local project repositories

    West diff: 在本地项目仓库中运行 git diff

  • west status: run git status in local project repositories

    West status: 在本地项目仓库中运行 git status

  • west forall: run an arbitrary command in local project repositories

    West forall: 在本地项目仓库中运行任意命令

Other built-in commands 其他内置命令

Finally, here is a summary of other built-in commands.

最后,这里是其他内置命令的总结。

  • west config: get or set configuration options

    West config: 获取或设置配置选项

  • west topdir: print the top level directory of the west workspace

    West topdir: 打印 west workspace 的顶层目录

  • west help: get help about a command, or print information about all commands in the workspace, including Extensions

    West help: 获取有关命令的帮助,或者打印有关工作区中所有命令(包括 Extensions)的信息

Configuration 配置

This page documents west’s configuration file system, the west config command, and configuration options used by built-in commands. For API documentation on the west.configuration module, see west.configuration.

此页面记录了 west 的配置文件系统、 west config 命令以及内置命令使用的配置选项。有关 west.configuration 模块的 API 文档,请参见 west.configuration。

West Configuration Files 西部配置文件

West’s configuration file syntax is INI-like; here is an example file:

West 的配置文件语法类似于 ini; 下面是一个示例文件:

[manifest]
path = zephyr

[zephyr]
base = zephyr

Above, the manifest section has option path set to zephyr. Another way to say the same thing is that manifest.path is zephyr in this file.

上面,清单部分有选项路径设置为 zephyr。另一种说法是 manifest.path 在这个文件中是 zephyr。

There are three types of configuration file:

有三种类型的配置文件:

  1. System: Settings in this file affect west’s behavior for every user logged in to the computer. Its location depends on the platform:

    系统: 此文件中的设置会影响 west 对每个登录到计算机的用户的行为。它的位置取决于平台:

    • Linux: /etc/westconfig

      Linux:/etc/westconfig

    • macOS: /usr/local/etc/westconfig

      macOS:/usr/local/etc/westconfig

    • Windows: %PROGRAMDATA%\west\config

      Windows:% PROGRAMDATA% west config

  2. Global (per user): Settings in this file affect how west behaves when run by a particular user on the computer.

    全局(每个用户) : 此文件中的设置会影响 west 在计算机上由特定用户运行时的行为。

    • All platforms: the default is .westconfig in the user’s home directory.

      所有平台: 默认是 .westconfig 在用户的home目录。

    • Linux note: if the environment variable XDG_CONFIG_HOME is set, then $XDG_CONFIG_HOME/west/config is used.

      Linux 注意: 如果环境变量 XDG_CONFIG_HOME 被设置,那么使用 $XDG_CONFIG_HOME/west/config

    • Windows note: the following environment variables are tested to find the home directory: %HOME%, then %USERPROFILE%, then a combination of %HOMEDRIVE% and %HOMEPATH%.

      Windows 注意: 下面的环境变量被测试来找到主目录:% HOME% ,然后是% USERPROFILE% ,然后是% HOMEDRIVE% 和% HOMEPATH% 的组合。

  3. Local: Settings in this file affect west’s behavior for the current west workspace. The file is .west/config, relative to the workspace’s root directory.

    Local: 此文件中的设置会影响 west 对当前 west 工作区的行为。这个文件是.West/config,相对于工作区的根目录。

A setting in a file which appears lower down on this list overrides an earlier setting. For example, if color.ui is true in the system’s configuration file, but false in the workspace’s, then the final value is false. Similarly, settings in the user configuration file override system settings, and so on.

文件中出现在此列表下方的设置将覆盖先前的设置。例如,如果 color.ui 在系统的配置文件中为 true,但在工作区中为 false,那么最终值为 false。类似地,用户配置文件中的设置覆盖系统设置,等等。

west config

The built-in config command can be used to get and set configuration values. You can pass west config the options --system, --global, or --local to specify which configuration file to use. Only one of these can be used at a time. If none is given, then writes default to --local, and reads show the final value after applying overrides.

内置的 config 命令可用于获取和设置配置值。您可以通过 west config 选项 --system--global--local 来指定使用哪个配置文件。一次只能使用其中的一个。如果没有给出,那么将默认写入 --local,并在应用覆盖之后读取显示最终值。

Some examples for common uses follow; run west config -h for detailed help, and see Built-in Configuration Options for more details on built-in options.

下面是一些常见用法的示例; 运行 west config -h 以获得详细帮助,并参阅内置配置选项以获得有关内置选项的更多详细信息。

To set manifest.path to some-other-manifest:

将 manifest.path 设置为其他清单:

west config manifest.path some-other-manifest

Doing the above means that commands like west update will look for the west manifest inside the some-other-manifest directory (relative to the workspace root directory) instead of the directory given to west init, so be careful!

执行上面的操作意味着像 west update 这样的命令将在 some-other-manifest 目录(相对于工作区根目录)中查找 west manifest,而不是给 west init 的目录,所以要小心!

To read zephyr.base, the value which will be used as ZEPHYR_BASE if it is unset in the calling environment (also relative to the workspace root):

要读取 ZEPHYR.base,如果在调用环境中未设置该值(也相对于工作区根) ,则该值将用作 ZEPHYR _ base:

west config zephyr.base

You can switch to another zephyr repository without changing manifest.path – and thus the behavior of commands like west update – using:

您可以切换到另一个 zephyr 存储库,而不需要更改 manifest.path-以及 west update-using 之类命令的行为:

west config zephyr.base some-other-zephyr

This can be useful if you use commands like git worktree to create your own zephyr directories, and want commands like west build to use them instead of the zephyr repository specified in the manifest. (You can go back to using the directory in the upstream manifest by running west config zephyr.base zephyr.)

如果您使用像 git worktree 这样的命令创建自己的 zephyr 目录,并希望像 west build 这样的命令使用它们,而不是清单中指定的 zephyr 存储库,那么这将非常有用。(你可以通过运行 west config zephyr.base zephyr. 回到上游清单中的目录)

To set color.ui to false in the global (user-wide) configuration file, so that west will no longer print colored output for that user when run in any workspace:

在全局(用户范围)配置文件中将 color.ui 设置为 false,以便 west 在任何工作区中运行时都不再为该用户打印有色输出:

west config --global color.ui false

To undo the above change: 要撤消上述更改:

west config --global color.ui true

Built-in Configuration Options 内置配置选项

The following table documents configuration options supported by west’s built-in commands. Configuration options supported by Zephyr’s extension commands are documented in the pages for those commands.

下表记录了 west 的内置命令所支持的配置选项。Zephyr 的扩展命令所支持的配置选项在这些命令的页面中有记录。

Option选择 Description描述
color.ui Boolean. If true (the default), then west output is colorized when stdout is a terminal.
如果为 true (默认值) ,那么当 stdout 是终端时,west 输出是彩色的。
commands.allow_extensions Boolean, default true, disables Extensions if false
布尔值,默认值为 true,如果为 false 则禁用 Extensions
manifest.file String, default west.yml. Relative path from the manifest repository root directory to the manifest file used by west init and other commands which parse the manifest.默认值 west.yml。
从清单存储库根目录到 west init 和解析清单的其他命令所使用的清单文件的相对路径。
manifest.group-filter String, default empty. A comma-separated list of project groups to enable and disable within the workspace. Prefix enabled groups with + and disabled groups with -. For example, the value "+foo,-bar" enables group foo and disables bar. See Project Groups and Active Projects.
字符串,默认为空。要在工作区中启用和禁用的项目组的逗号分隔列表。前缀启用了带 + 的组和带-的禁用组。例如,值“ + foo,-bar”启用组 foo 并禁用 bar。请参阅项目组和活动项目。
manifest.path String, relative path from the west workspace root directory to the manifest repository used by west update and other commands which parse the manifest. Set locally by west init.
字符串,从 west workspace 根目录到 west update 和其他解析 manifest 的命令使用的 manifest 存储库的相对路径。由 west init 在本地设置。
update.fetch String, one of "smart" (the default behavior starting in v0.6.1) or "always" (the previous behavior). If set to "smart", the west update command will skip fetching from project remotes when those projects’ revisions in the manifest file are SHAs or tags which are already available locally. The "always" behavior is to unconditionally fetch from the remote.
字符串,“智能”(从 v0.6.1开始的默认行为)或“总是”(以前的行为)之一。如果设置为“ smart”,west update 命令将跳过从项目远程处获取,当这些项目在清单文件中的修订是本地已有的 SHAs 或标记时。“始终”行为是无条件地从远程获取数据。
update.name-cache更新,名字缓存 String. If non-empty, west update will use its value as the --name-cache option’s value if not given on the command line.
字符串。如果非空,west update 将使用其值作为—— name-cache 选项的值(如果不是在命令行中给出的话)。
update.narrow Boolean. If true, west update behaves as if --narrow was given on the command line. The default is false.
如果为真,west update 的行为就像 –narrow 是在命令行上给出的。
update.path-cache String. If non-empty, west update will use its value as the --path-cache option’s value if not given on the command line.
字符串。如果非空,west update 将使用其值作为 –path-cache 选项的值(如果没有在命令行中给出)。
update.sync-submodules Boolean. If true (the default), west update will synchronize Git submodules before updating them.
如果为真(默认值) ,west update 将在更新 Git 子模块之前同步它们。
zephyr.base String, default value to set for the ZEPHYR_BASE environment variable while the west command is running. By default, this is set to the path to the manifest project with path zephyr (if there is one) during west init. If the variable is already set, then this setting is ignored unless zephyr.base-prefer is "configfile".
字符串,在 west 命令运行时为 ZEPHYR_BASE 环境变量设置的默认值。默认情况下,这被设置为在 west init 期间使用 path zephyr (如果有的话)到 manifest 项目的路径。如果变量已经设置,那么这个设置将被忽略,除非 zephyr.base-prefer 是“ configfile”。
zephyr.base-prefer String, one the values "env" and "configfile". If set to "env" (the default), setting ZEPHYR_BASE in the calling environment overrides the value of the zephyr.base configuration option. If set to "configfile", the configuration option wins instead.
字符串,一个值“ env”和“ configfile”。如果设置为“ env”(默认值) ,则在调用环境中设置 ZEPHYR _ base 将覆盖 ZEPHYR.base 配置选项的值。如果设置为“ configfile”,则配置选项胜出。

printk分析

printk(main.c)
-> vprintk(char_out, &ctx, fmt, ap)(zephyr/lib/os/printk.c)
-> OUTC(c) = (*out)(c, ctx) // c是经过处理的fmt,ap的部分
-> static int char_out(int c, void *ctx_p) {
        struct out_context *ctx = ctx_p;
        ctx->count++;
        return _char_out(c); // 是这个_char_out输出了字符
    }

_char_out 有两处赋值 (zephyr/lib/os/printk.c)

int (*_char_out)(int) = arch_printk_char_out;

void __printk_hook_install(int (*fn)(int)) {
	_char_out = fn;
}

第1段为空,从第2段找:

zephyr/drivers/console/uart_console.c
  1. 首先在build/zephyr/zephyr_final.map中找到了

     .text.__printk_hook_install
       0x00000000080004a8    0xc zephyr/libzephyr.a(printk.c.obj)
       0x00000000080004a8    __printk_hook_install
    

    说明有这个函数

  2. 再继续搜索__printk_hook_install,在zephyr/drivers/console/uart_console.c这个文件中找到,其他文件也有,但是调用它的函数像uart_console_hook_install却没有其他调用者,说明那个文件中的__printk_hook_install函数没有入口

     static void uart_console_hook_install(void) {
     #if defined(CONFIG_STDOUT_CONSOLE) // 都配置了
         __stdout_hook_install(console_out);
     #endif
     #if defined(CONFIG_PRINTK)
         __printk_hook_install(console_out);
     #endif
     }
    
  3. 所以,真正用来向串口输出的是console_out函数

     static int console_out(int c) {
         if ('\n' == c) {
             uart_poll_out(uart_console_dev, '\r');
         }
         uart_poll_out(uart_console_dev, c);
    
         return c;
     }
    
     static inline void uart_poll_out(
                             const struct device * dev, 
                             unsigned char out_char) {
         compiler_barrier();
         z_impl_uart_poll_out(dev, out_char);
     }
    
     static inline void z_impl_uart_poll_out(
                           const struct device *dev, 
                           unsigned char out_char) {
         const struct uart_driver_api *api =
             (const struct uart_driver_api *)dev->api;
    
         api->poll_out(dev, out_char);
     }
    

    最终本质是调用uart_console_dev->api->poll_out()发送数据,所以这个uart_console_dev是关键

  4. uart_console_devzephyr/drivers/console/uart_console.c 定义

     static const struct device *uart_console_dev;
    
  5. 再次搜索,在zephyr/drivers/console/uart_console.c又找到

     static int uart_console_init(const struct device *arg) {
         ARG_UNUSED(arg);
    
         /* Claim console device */
         uart_console_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
         if (!device_is_ready(uart_console_dev)) {
             return -ENODEV;
         }
    
         uart_console_hook_install();
    
         return 0;
     }    
    
  6. 它是从设备树中/chosen节点中的zephyr_console找到的,这里zephyr_console要换成zephyr,console,在包含gd*的文件中搜索后发现在zephyr/boards/arm/gd32f450i_eval/gd32f450i_eval.dts中有

     / {
         model = "GigaDevice GD32F450I-EVAL";
         compatible = "gd,gd32f450i-eval";
    
         chosen {
             zephyr,sram = &sram0;
             zephyr,flash = &flash0;
             zephyr,console = &usart0;
             zephyr,shell-uart = &usart0;
         };
    
         &usart0 {
             status = "okay";
             current-speed = <115200>;
             pinctrl-0 = <&usart0_default>;
             pinctrl-names = "default";
         };
    

    而且在zephyr/dts/arm/gigadevice/gd32f4xx/gd32f4xx.dtsi中有

     usart0: usart@40011000 {
         compatible = "gd,gd32-usart";
         reg = <0x40011000 0x400>;
         interrupts = <37 0>;
         rcu-periph-clock = <0x1104>;
         status = "disabled";
         label = "USART_0";
     };
    

    这样就把设备树和内核代码关联起来了

uart是如何初始化的呢?

  1. 分析宏定义

     DEVICE_DT_GET(DT_CHOSEN(zephyr_console))
    
     #define DT_CHOSEN(prop) DT_CAT(DT_CHOSEN_, prop)
     #define DT_CAT(a1, a2) a1 ## a2
    

    所以

     DT_CHOSEN(zephyr_console) == DT_CHOSEN_zephyr_console
    

    build/zephyr/include/generated/devicetree_unfixed.h中有

     #define DT_CHOSEN_zephyr_console  DT_N_S_soc_S_usart_40011000
    
  2. DEVICE_DT_GET宏定义的注释可以知道

     #define DEVICE_DT_GET(node_id) (&DEVICE_DT_NAME_GET(node_id))
    
    
     /* --------------------------------
     Returns the name of the global device structure as a C * identifier. 
     The device must be allocated using DEVICE_DT_DEFINE() * 
     or DEVICE_DT_INST_DEFINE() for this to work.
     */
     #define DEVICE_DT_NAME_GET(node_id) DEVICE_NAME_GET(Z_DEVICE_DT_DEV_NAME(node_id))
    

    设备是由DEVICE_DT_DEFINE()DEVICE_DT_INST_DEFINE()完成的

  3. 那么再搜索DEVICE_DT_DEFINEDEVICE_DT_INST_DEFINE关键字,第1个没有结果,在第2个关键字处找到了zephyr/drivers/serial/usart_gd32.c文件中最后

     #define GD32_USART_INIT(n)	\
         PINCTRL_DT_INST_DEFINE(n);	\
         GD32_USART_IRQ_HANDLER(n)		\
         static struct gd32_usart_data usart_gd32_data_##n = {	\
             .baud_rate = DT_INST_PROP(n, current_speed),	\
         }; \
         static const struct gd32_usart_config usart_gd32_config_##n = {	\
             .reg = DT_INST_REG_ADDR(n),	\
             .rcu_periph_clock = DT_INST_PROP(n, rcu_periph_clock),	\
             .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
             .parity = DT_INST_ENUM_IDX_OR(n, parity, UART_CFG_PARITY_NONE),	\
             GD32_USART_IRQ_HANDLER_FUNC_INIT(n)	\
         };	\
         DEVICE_DT_INST_DEFINE(n, &usart_gd32_init,\
                     NULL,\
                     &usart_gd32_data_##n,	\
                     &usart_gd32_config_##n, PRE_KERNEL_1,\
                     CONFIG_SERIAL_INIT_PRIORITY,\
                     &usart_gd32_driver_api);
    
     DT_INST_FOREACH_STATUS_OKAY(GD32_USART_INIT)
    
    • 所以这里的usart_gd32_init就是初始化函数
    • 这里的DT_INST_FOREACH_STATUS_OKAY -> DEVICE_DT_INST_DEFINE -> Z_DEVICE_DEFINE -> Z_INIT_ENTRY_DEFINE 把定义好的gd32_usart设备放到了init_entry段,这样在系统启动时就会初始化这个设备

要查看预处理的中间文件,构建命令为

west build -b gd32f450z_eval zephyr/samples/hello_world --pristine -- -DEXTRA_CFLAGS=-save-temps=obj

启动过程

从gd32的代码可以知道,构建一个bin文件,必须要链接脚本,所以全局搜索*.ld字符串,然后在搜索结果中找到了一个build.ninja的文件,里面就找到了有关线索

  1. 首先找到ld文件
    • build/build.ninja文件中,可以找到*.ld文件,这些文件就是链接脚本,其中有一个

        zephyr-rtos/zephyr/soc/arm/gigadevice/gd32f4xx/linker.ld
      
    • 再找到

        zephyr/include/zephyr/arch/arm/aarch32/cortex_m/scripts/linker.ld
      

      这里面定义了ENTRY(CONFIG_KERNEL_ENTRY)

    • build/zephyr/.config中定义CONFIG_KERNEL_ENTRY="__start"
    • build/zephyr/zephyr_final.map中找到

        .text._reset_section
           0x00000000080010b0   0x2c zephyr/arch/arch/arm/core/aarch32/cortex_m/libarch__arm__core__aarch32__cortex_m.a(reset.S.obj)
           0x00000000080010b0   z_arm_reset
           0x00000000080010b0   __start
      
    • 找到zephyr/arch/arm/core/aarch32/cortex_m/reset.S这就是启动文件了

        SECTION_SUBSEC_FUNC(TEXT,_reset_section,__start)
      
      • 宏的作用就是把__start放到.text._reset_section
      • 下面就是__start的关键内容

          bl z_arm_prep_c
        
  • z_arm_prep_c

    全局搜索这个函数,从zephyr_final.map可知,这里函数在zephyr/arch/arm/core/aarch32/prep_c.c

    void z_arm_prep_c(void) {
        // 设置SCB->VTOR向量表
        relocate_vector_table(); 
    #if defined(CONFIG_CPU_HAS_FPU)
        // 浮点
        z_arm_floating_point_init();
    #endif
    // 清bss段
        z_bss_zero();
        z_data_copy();
    #if ((defined(CONFIG_ARMV7_R) || defined(CONFIG_ARMV7_A)) 
            && defined(CONFIG_INIT_STACKS))
        z_arm_init_stacks();
    #endif
        z_arm_interrupt_init();
        z_cstart();
        CODE_UNREACHABLE;
    }
    

    其中

    void z_bss_zero(void) { 
        z_early_memset(__bss_start, 0, __bss_end - __bss_start);
    }
    
    // 把数据从ROM拷贝到RAM中
    void z_data_copy(void) {
        z_early_memcpy(&__data_region_start, 
                        &__data_region_load_start,
                        __data_region_end - __data_region_start);
        z_early_memcpy(&__ramfunc_start, 
                        &__ramfunc_load_start,
                        (uintptr_t) &__ramfunc_size);
    }
    
    // 确保所有中断的优先级都设置为_EXC_IRQ_DEFAULT_PRIO,而不是0,
    // 当它们退出重置时,会被设置为0。这确保了通过BASEPRI的中断锁定按预期工作
    void z_arm_interrupt_init(void) {
        int irq = 0;
    
        for (; irq < CONFIG_NUM_IRQS; irq++) {
            NVIC_SetPriority((IRQn_Type)irq, _IRQ_PRIO_OFFSET);
        }
    }
    
  • z_cstart

      FUNC_NORETURN void z_cstart(void) {
          /* gcov hook needed to get the coverage report.*/
          // gcov hook需要获得覆盖率报告
          gcov_static_init();
    
          /* perform any architecture-specific initialization */
          arch_kernel_init();
    
          LOG_CORE_INIT();
    
      #if defined(CONFIG_MULTITHREADING)
          /* Note: The z_ready_thread() call in prepare_multithreading() requires
          * a dummy thread even if CONFIG_ARCH_HAS_CUSTOM_SWAP_TO_MAIN=y
          * prepare_multithreading()中的z_ready_thread()调用需要一个虚拟线程,
          * 即使CONFIG_ARCH_HAS_CUSTOM_SWAP_TO_MAIN=y
          */
          struct k_thread dummy_thread;
    
          z_dummy_thread_init(&dummy_thread);
      #endif
          /* do any necessary initialization of static devices */
          z_device_state_init();
    
          /* perform basic hardware initialization */
          z_sys_init_run_level(_SYS_INIT_LEVEL_PRE_KERNEL_1);
          z_sys_init_run_level(_SYS_INIT_LEVEL_PRE_KERNEL_2);
    
      #ifdef CONFIG_STACK_CANARIES
          uintptr_t stack_guard;
    
          z_early_boot_rand_get((uint8_t *)&stack_guard, sizeof(stack_guard));
          __stack_chk_guard = stack_guard;
          __stack_chk_guard <<= 8;
      #endif	/* CONFIG_STACK_CANARIES */
    
      #ifdef CONFIG_TIMING_FUNCTIONS_NEED_AT_BOOT
          timing_init();
          timing_start();
      #endif
    
      #ifdef CONFIG_MULTITHREADING
          // 走这里
          switch_to_main_thread(prepare_multithreading());
      #else
      #ifdef ARCH_SWITCH_TO_MAIN_NO_MULTITHREADING
          /* Custom ARCH-specific routine to switch to main()
          * in the case of no multi-threading.
          */
          ARCH_SWITCH_TO_MAIN_NO_MULTITHREADING(bg_thread_main,
              NULL, NULL, NULL);
      #else
          bg_thread_main(NULL, NULL, NULL);
    
          /* LCOV_EXCL_START
          * We've already dumped coverage data at this point.
          */
          irq_lock();
          while (true) {
          }
          /* LCOV_EXCL_STOP */
      #endif
      #endif /* CONFIG_MULTITHREADING */
    
          /*
          * Compiler can't tell that the above routines won't return and issues
          * a warning unless we explicitly tell it that control never gets this
          * far.
          */
    
          CODE_UNREACHABLE; /* LCOV_EXCL_LINE */
      }
    
    • z_device_state_init(void)

        extern const struct device __device_start[];
        extern const struct device __device_end[];
      
        // 初始化所有静态设备的初始化状态
        void z_device_state_init(void) {
            const struct device *dev = __device_start;
      
            while (dev < __device_end) {
                z_object_init(dev);
                ++dev;
            }
        }
      

      linker.cmd中可以看到__device_start[]的定义

        devices : {
            __device_start = .;
            __device_PRE_KERNEL_1_start = .; 
                  KEEP(*(SORT(.z_device_PRE_KERNEL_1[0-9]_*))); 
                  KEEP(*(SORT(.z_device_PRE_KERNEL_1[1-9][0-9]_*)));
            __device_PRE_KERNEL_2_start = .; 
                  KEEP(*(SORT(.z_device_PRE_KERNEL_2[0-9]_*))); 
                  KEEP(*(SORT(.z_device_PRE_KERNEL_2[1-9][0-9]_*)));
            __device_POST_KERNEL_start = .; 
                  KEEP(*(SORT(.z_device_POST_KERNEL[0-9]_*))); 
                  KEEP(*(SORT(.z_device_POST_KERNEL[1-9][0-9]_*)));
            __device_APPLICATION_start = .; 
                  KEEP(*(SORT(.z_device_APPLICATION[0-9]_*))); 
                  KEEP(*(SORT(.z_device_APPLICATION[1-9][0-9]_*)));
            __device_SMP_start = .; 
                  KEEP(*(SORT(.z_device_SMP[0-9]_*))); 
                  KEEP(*(SORT(.z_device_SMP[1-9][0-9]_*)));
            __device_end = .;
        } > FLASH
      

      这就是uart静态对象放置的地方,可以在usart_gd32.c.i中看到#define GD32_USART_INIT(n)被展开成如下形式:

        __attribute__((__aligned__(__alignof( struct device))))
        struct device __device_dts_ord_49
        __attribute__((__used__))
        __attribute__((__section__(".z_device_" "PRE_KERNEL_1" "55" "_"))) = {
            .name = "USART_0",
            .config = (&usart_gd32_config_0),
            .api = (&usart_gd32_driver_api),
            .state = (&__devstate_dts_ord_49),
            .data = (&usart_gd32_data_0),
            .handles = __devicehdl_DT_N_S_soc_S_usart_40011000,
        };
      

      它被放在zephyr_final.map

        devices 0x0000000008002b5c  0xa8
                0x0000000008002b5c  __device_start = .
                0x0000000008002b5c  __device_PRE_KERNEL_1_start = .
        .z_device_PRE_KERNEL_155_
                0x0000000008002bec  __device_dts_ord_49
                0x0000000008002c04  __device_PRE_KERNEL_2_start = .
      
    • z_sys_init_run_level

      使用指定的级别调用INIT_ENTRY_DEFINE()宏创建的每个init entry对象的初始化程序。链接器脚本按照需要调用的顺序将init entry对象放在内存中,用符号表示一个级别的结束和下一个级别的开始。

        void z_sys_init_run_level(int32_t level) {
            static const struct init_entry *levels[] = {
                __init_PRE_KERNEL_1_start,
                __init_PRE_KERNEL_2_start,
                __init_POST_KERNEL_start,
                __init_APPLICATION_start,
        #ifdef CONFIG_SMP
                __init_SMP_start,
        #endif
                /* End marker */
                __init_end,
            };
            const struct init_entry *entry;
      
            for (entry = levels[level]; entry < levels[level+1]; entry++) {
                const struct device *dev = entry->dev;
                int rc = entry->init(dev);
      
                if (dev != NULL) {
                    /* Mark device initialized.  If initialization
                    * failed, record the error condition.
                    */
                    if (rc != 0) {
                        if (rc < 0) { rc = -rc; }
                        if (rc > UINT8_MAX) { rc = UINT8_MAX; }
                        dev->state->init_res = rc;
                    }
                    dev->state->initialized = true;
                }
            }
        }
      

      其中在zephyr_final.map中可以看到:

        initlevel   0x0000000008002b0c  0x50
                    0x0000000008002b0c  __init_start = .
                    0x0000000008002b0c  __init_PRE_KERNEL_1_start = .
                *(SORT_BY_NAME(SORT_BY_ALIGNMENT(.z_init_PRE_KERNEL_1[0-9]_*)))
                libdrivers__interrupt_controller.a(intc_gd32_exti.c.obj)
                libdrivers__gpio.a(gpio_gd32.c.obj)
                libdrivers__serial.a(usart_gd32.c.obj)
                libdrivers__console.a(uart_console.c.obj)
                ...
            0x0000000008002b54    __init_PRE_KERNEL_2_start = .
                ...
            0x0000000008002b5c    __init_POST_KERNEL_start = .
                ...
            0x0000000008002b5c    __init_APPLICATION_start = .
                ...
            0x0000000008002b5c    __init_SMP_start = .    
                ...    
            0x0000000008002b5c    __init_end = .
      
    • switch_to_main_thread(prepare_multithreading())

      • prepare_multithreading()

        该例程初始化各种内核数据结构,包括init和空闲线程以及任何特定于体系结构的初始化

        注意,“_kernel”的所有字段在输入时都设置为零,这可能是其中许多字段所需的全部初始化。

          __boot_func static char *prepare_multithreading(void) {
              char *stack_ptr;
        
              /* _kernel.ready_q is all zeroes */
              z_sched_init();
        
          #ifndef CONFIG_SMP
              /*
              * prime the cache with the main thread since:
              *
              * - the cache can never be NULL
              * - the main thread will be the one to run first
              * - no other thread is initialized yet and thus their priority fields
              *   contain garbage, which would prevent the cache loading algorithm
              *   to work as intended
              */
              _kernel.ready_q.cache = &z_main_thread;
          #endif
              //z_main_thread = gb_thread_main
              stack_ptr = z_setup_new_thread(
                      &z_main_thread, z_main_stack, CONFIG_MAIN_STACK_SIZE,
                      bg_thread_main, NULL, NULL, NULL,
                      CONFIG_MAIN_THREAD_PRIORITY, K_ESSENTIAL, "main");
              z_mark_thread_as_started(&z_main_thread);
              z_ready_thread(&z_main_thread);
        
              z_init_cpu(0);
        
              return stack_ptr;
          }
        
      • switch_to_main_thread()

          __boot_func static FUNC_NORETURN void switch_to_main_thread(char *stack_ptr)
          {
          #ifdef CONFIG_ARCH_HAS_CUSTOM_SWAP_TO_MAIN
              arch_switch_to_main_thread(&z_main_thread, stack_ptr, bg_thread_main);
          #else
              ARG_UNUSED(stack_ptr);
              /*
              * Context switch to main task (entry function is _main()): the
              * current fake thread is not on a wait queue or ready queue, so it
              * will never be rescheduled in.
              */
              z_swap_unlocked();
          #endif
              CODE_UNREACHABLE; /* LCOV_EXCL_LINE */
          }
        
      • arch_switch_to_main_thread

          __asm__ volatile (
              ...
              "bl z_thread_entry\n\t"	/* z_thread_entry(_main, 0, 0, 0); */
              ...
          )
        
    • gb_thread_main

        static void bg_thread_main(void *unused1, void *unused2, void *unused3) {
            ARG_UNUSED(unused1);
            ARG_UNUSED(unused2);
            ARG_UNUSED(unused3);
      
            z_sys_post_kernel = true;
      
            z_sys_init_run_level(_SYS_INIT_LEVEL_POST_KERNEL);
      
            boot_banner();
      
            /* Final init level before app starts */
            z_sys_init_run_level(_SYS_INIT_LEVEL_APPLICATION);
      
            z_init_static_threads();
      
            extern void main(void);
      
            // 这个main就是我们应用程序写的main函数
            main();
      
            /* Mark nonessential since main() has no more work to do */
            // 标记为不重要,因为main() 没有更多工作要做
            z_main_thread.base.user_options &= ~K_ESSENTIAL;
        }
      

对eeprom驱动的分析

命令

west build -b gd32f450i_eval zephyr/samples/drivers/eeprom --pristine -- -DEXTRA_CFLAGS=-save-temps=obj
  • 首先看build/zephyr/zephyr.dts文件中的eeprom0:

    i2c0: i2c@40005400 {
        compatible = "gd,gd32-i2c";
        reg = < 0x40005400 0x400 >;
        #address-cells = < 0x1 >;
        #size-cells = < 0x0 >;
        clock-frequency = < 0x186a0 >;
        interrupts = < 0x1f 0x0 >, < 0x20 0x0 >;
        interrupt-names = "event", "error";
        rcu-periph-clock = < 0x1015 >;
        status = "okay";
        label = "I2C_0";
        pinctrl-0 = < &i2c0_default >;
        pinctrl-names = "default";
        eeprom0: eeprom@50 {
          compatible = "atmel,at24";
          reg = < 0x50 >;
          status = "okay";
          label = "EEPROM_AT24C02";
          size = < 0x100 >;
          pagesize = < 0x8 >;
          address-width = < 0x8 >;
          timeout = < 0x5 >;
        };
    };
    
    • 它的设备树节点一定有gd,gd32-i2c
    • 它的驱动代码一定有gd_gd32_i2c

main函数

zephyr/samples/drivers/eeprom/src/main.c中,首先获取eeprom的device

const struct device *eeprom = get_eeprom_device();

static const struct device *get_eeprom_device(void) {
	const struct device *dev = DEVICE_DT_GET(DT_ALIAS(eeprom_0));

	if (!device_is_ready(dev)) {
		printk("\nError: Device \"%s\" is not ready; "
		       "check the driver initialization logs for errors.\n",
		       dev->name);
		return NULL;
	}

	printk("Found EEPROM device \"%s\"\n", dev->name);
	return dev;
}

获取eeprom的device

DT_ALIAS(eeprom_0)

DT_ALIAS(eeprom_0)

#define DT_ALIAS(alias) DT_CAT(DT_N_ALIAS_, alias)

DT_ALIAS(eeprom_0) == DT_N_ALIAS_eeprom_0

在文件build/zephyr/include/generated/devicetree_unfixed.h中找到

/*
 * Devicetree node: /soc/i2c@40005400/eeprom@50
 *
 * Node identifier: DT_N_S_soc_S_i2c_40005400_S_eeprom_50
 *
 * Binding (compatible = atmel,at24):
 *   $ZEPHYR_BASE/dts/bindings/mtd/atmel,at24.yaml
 *
 * (Descriptions have moved to the Devicetree Bindings Index in the documentation.)
 */

#define DT_N_ALIAS_eeprom_0    DT_N_S_soc_S_i2c_40005400_S_eeprom_50
#define DT_N_INST_0_atmel_at24 DT_N_S_soc_S_i2c_40005400_S_eeprom_50
#define DT_N_NODELABEL_eeprom0 DT_N_S_soc_S_i2c_40005400_S_eeprom_50

/* 
  可以看到,DT_FOREACH_PROP_ELEM 会展开成单独的一个个fn()函数
  也就是对所有元素应用fn函数
*/ 
#define DT_N_S_soc_S_i2c_40005400_S_eeprom_50_P_status_FOREACH_PROP_ELEM(fn) \
  fn(DT_N_S_soc_S_i2c_40005400_S_eeprom_50, status, 0) \
	fn(DT_N_S_soc_S_i2c_40005400_S_eeprom_50, status, 1) \
	fn(DT_N_S_soc_S_i2c_40005400_S_eeprom_50, status, 2) \
	fn(DT_N_S_soc_S_i2c_40005400_S_eeprom_50, status, 3)

从注释可以看到,节点/soc/i2c@40005400/eeprom@50的绑定文件是$ZEPHYR_BASE/dts/bindings/mtd/atmel,at24.yaml,而它的内容是

description: Atmel AT24 (or compatible) I2C EEPROM

compatible: "atmel,at24"

include: ["atmel,at2x-base.yaml", i2c-device.yaml]

这里 include 的内容是通用内容,也就是列出有什么属性,这些属性可以给任何的设备,但compatible: "atmel,at24"才是指定这些属性是谁的属性。综合起来就是yaml指定了atmel,at24驱动程序可以读取到include指定的各种属性值

DEVICE_DT_GET(DT_ALIAS(eeprom_0))

在文件zephyr/build/zephyr/misc/generated/syscalls_links/include/device.h

DEVICE_DT_GET(DT_ALIAS(eeprom_0)) 
    == DEVICE_DT_GET(DT_N_S_soc_S_i2c_40005400_S_eeprom_50)

#define DEVICE_DT_GET(node_id) (&DEVICE_DT_NAME_GET(node_id))

#define DEVICE_DT_NAME_GET(node_id) DEVICE_NAME_GET(Z_DEVICE_DT_DEV_NAME(node_id))

## __device_name
#define DEVICE_NAME_GET(name) _CONCAT(__device_, name)

#define Z_DEVICE_DT_DEV_NAME(node_id) _CONCAT(dts_ord_, DT_DEP_ORD(node_id))

#define DT_DEP_ORD(node_id) DT_CAT(node_id, _ORD)

再打开文件main.c.i: build/CMakeFiles/app.dir/src/main.c.i

const struct device *dev = (&__device_dts_ord_55);

通过 __device_dts_ord_55可以找到文件build/zephyr/drivers/eeprom/CMakeFiles/drivers__eeprom.dir/eeprom_at2x.c.i

static const struct eeprom_driver_api eeprom_at2x_api = {
    .read = eeprom_at2x_read,
    .write = eeprom_at2x_write,
    .size = eeprom_at2x_size,
};

_Static_assert((8 != 0U) && ((8 & (8 - 1)) == 0U), "" 
                "Page size is not a power of two");

_Static_assert(256 % 8 == 0U, "" "Size is not an integer multiple of page size");

_Static_assert(8 == 8U || 8 == 16U, "" "Unsupported address width");

static const struct eeprom_at2x_config eeprom_at24_config_0 = {
    .bus = {.i2c = {.bus = (&__device_dts_ord_54), .addr = 80}},
    .size = 256,
    .pagesize = 8,
    .addr_width = 8,
    .readonly = 0,
    .timeout = 5,
    .bus_is_ready = eeprom_at24_bus_is_ready,
    .read_fn = eeprom_at24_read,  // 这里的读取,本质上是调用的bus-i2c的读取函数
    .write_fn = eeprom_at24_write,
};

static struct eeprom_at2x_data eeprom_at24_data_0;

static struct device_state __devstate_dts_ord_55 __attribute__((__section__(".z_devstate")));

extern const device_handle_t __devicehdl_DT_N_S_soc_S_i2c_40005400_S_eeprom_50[];

const device_handle_t __attribute__((__aligned__(sizeof(device_handle_t)))) 
  __attribute__((__weak__, __section__(".__device_handles_pass1"))) 
  __devicehdl_DT_N_S_soc_S_i2c_40005400_S_eeprom_50[] = {
    55, 54, (-0x7fff - 1) , (-0x7fff - 1) ,
};

// 把设备放到了.z_device_POST_KERNEL_75_段
const __attribute__((__aligned__(__alignof(struct device)))) 
    struct device __device_dts_ord_55 __attribute__((__used__)) 
      __attribute__((__section__(".z_device_" "POST_KERNEL" "75" "_"))) = {
        .name = "EEPROM_AT24C02",
        .config = (&eeprom_at24_config_0),
        .api = (&eeprom_at2x_api),
        .state = (&__devstate_dts_ord_55),
        .data = (&eeprom_at24_data_0),
        .handles = __devicehdl_DT_N_S_soc_S_i2c_40005400_S_eeprom_50,
};

_Static_assert(sizeof("\"EEPROM_AT24C02\"") <= 48, ""
  "DEVICE_NAME_GET(\"EEPROM_AT24C02\")" " too long");

// 设置好了初始化函数,在启动时就会执行这里的init函数
static const __attribute__((__aligned__(__alignof(struct init_entry)))) 
  struct init_entry __init___device_dts_ord_55 
  __attribute__((__used__)) 
  __attribute__((__section__(
      ".z_init_" "POST_KERNEL" "75" "_"))) = {
          .init = (&eeprom_at2x_init),
          .dev = ((&__device_dts_ord_55)),
};

对应的c代码是

#define EEPROM_AT2X_DEVICE(n, t) \
	ASSERT_PAGESIZE_IS_POWER_OF_2(DT_PROP(INST_DT_AT2X(n, t), pagesize)); \
	ASSERT_SIZE_PAGESIZE_VALID(DT_PROP(INST_DT_AT2X(n, t), size), \
				   DT_PROP(INST_DT_AT2X(n, t), pagesize)); \
	ASSERT_AT##t##_ADDR_W_VALID(DT_PROP(INST_DT_AT2X(n, t), \
					    address_width)); \

  // 这里就是从设备树读取内容的地方 
	static const struct eeprom_at2x_config eeprom_at##t##_config_##n = { \
		.bus = EEPROM_AT##t##_BUS(n, t), \
		EEPROM_AT2X_WP_GPIOS(INST_DT_AT2X(n, t)) \
		.size = DT_PROP(INST_DT_AT2X(n, t), size), \
		.pagesize = DT_PROP(INST_DT_AT2X(n, t), pagesize), \
		.addr_width = DT_PROP(INST_DT_AT2X(n, t), address_width), \
		.readonly = DT_PROP(INST_DT_AT2X(n, t), read_only), \
		.timeout = DT_PROP(INST_DT_AT2X(n, t), timeout), \
		.bus_is_ready = eeprom_at##t##_bus_is_ready, \
		.read_fn = eeprom_at##t##_read, \  // 这里的t是24
		.write_fn = eeprom_at##t##_write, \
	};\
	static struct eeprom_at2x_data eeprom_at##t##_data_##n; \
	DEVICE_DT_DEFINE(INST_DT_AT2X(n, t), &eeprom_at2x_init, \
			    NULL, &eeprom_at##t##_data_##n, \
			    &eeprom_at##t##_config_##n, POST_KERNEL, \
			    CONFIG_EEPROM_INIT_PRIORITY, \
			    &eeprom_at2x_api)

// 实例化
// t = 24, t=24
INST_DT_AT2X_FOREACH(24, EEPROM_AT24_DEVICE);

对I2C驱动的分析

build/zephyr/drivers/eeprom/CMakeFiles/drivers__eeprom.dir/eeprom_at2x.c.i的分析可以知道,对eeprom的读写是调用了函数eeprom_at24_config_0.read_fn = eeprom_at24_read。再跟踪eeprom_at45_read知,它最终是调用了i2c_write_read函数

i2c_write_read(
  config->bus.i2c.bus, bus_addr,
	addr, config->addr_width / 8, 
  buf, len);

调用链如下:

i2c_write_read(config->bus.i2c.bus, ...)
-> i2c_transfer(dev, msg, 2, addr) 
-> z_impl_i2c_transfer(dev, ... )
-> dev->api->transfer(dev, msgs, num_msgs, addr);

这里dev就是bus.i2c.bus这个i2c总线设备,而api是所有对象都具有的接口api,它的内容应该在i2c设备的驱动中找

在从build/zephyr/drivers/eeprom/CMakeFiles/drivers__eeprom.dir/eeprom_at2x.c.i这个文件中可以找到:

static const struct eeprom_at2x_config 
    eeprom_at24_config_0 = {
    .bus = {.i2c = {.bus = (&__device_dts_ord_54), 
    ...
};

它是在i2c_gd32.c中定义的,所以从这个驱动中可以找到api的具体内容:

static struct i2c_driver_api i2c_gd32_driver_api = {
	.configure = i2c_gd32_configure,
	.transfer = i2c_gd32_transfer,
};

那么 api->transfer 就是 i2c_gd32_transfer

分析I2C设备的初始化

#define I2C_GD32_INIT(inst)	\
	PINCTRL_DT_INST_DEFINE(inst);	\
	static void i2c_gd32_irq_cfg_func_##inst(void) {	\
		IRQ_CONNECT(DT_INST_IRQ_BY_NAME(inst, event, irq),		\
			    DT_INST_IRQ_BY_NAME(inst, event, priority),		\
			    i2c_gd32_event_isr,					\
			    DEVICE_DT_INST_GET(inst),				\
			    0);							\
		irq_enable(DT_INST_IRQ_BY_NAME(inst, event, irq));		\
    \
		IRQ_CONNECT(DT_INST_IRQ_BY_NAME(inst, error, irq),		\
			    DT_INST_IRQ_BY_NAME(inst, error, priority),		\
			    i2c_gd32_error_isr,					\
			    DEVICE_DT_INST_GET(inst),				\
			    0);							\
		irq_enable(DT_INST_IRQ_BY_NAME(inst, error, irq)); \
	}	\
	static struct i2c_gd32_data i2c_gd32_data_##inst;	\
	const static struct i2c_gd32_config i2c_gd32_cfg_##inst = {		\
		.reg = DT_INST_REG_ADDR(inst),	\
		.bitrate = DT_INST_PROP(inst, clock_frequency),	\
		.rcu_periph_clock = DT_INST_PROP(inst, rcu_periph_clock),	\
		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),			\
		.irq_cfg_func = i2c_gd32_irq_cfg_func_##inst,			\
	}; \
	I2C_DEVICE_DT_INST_DEFINE(inst,	\
				  i2c_gd32_init, NULL,	\
				  &i2c_gd32_data_##inst, &i2c_gd32_cfg_##inst,	\
				  POST_KERNEL, CONFIG_I2C_INIT_PRIORITY,	\
				  &i2c_gd32_driver_api);	\

DT_INST_FOREACH_STATUS_OKAY(I2C_GD32_INIT)

展开后的内容为:

static const pinctrl_soc_pin_t __pinctrl_state_pins_0__device_dts_ord_54[] = {
    (1121 | ((1U * 0) << 29U) | ((2U * 0) << 29U) | ((1U * 1) << 28U) |
     (0 << 26U)),
    (1137 | ((1U * 0) << 29U) | ((2U * 0) << 29U) | ((1U * 1) << 28U) |
     (0 << 26U)),
};

static const struct pinctrl_state __pinctrl_states__device_dts_ord_54[] = {
    {.id = 0U,
     .pins = __pinctrl_state_pins_0__device_dts_ord_54,
     .pin_cnt = (( size_t)(((int)sizeof(
                      char[1 - 2 * !(!__builtin_types_compatible_p(
                                   __typeof__(__pinctrl_state_pins_0__device_dts_ord_54),
                                   __typeof__(&(
                                       __pinctrl_state_pins_0__device_dts_ord_54)
                                                  [0])))]) -
                  1) +
                 (sizeof(__pinctrl_state_pins_0__device_dts_ord_54) /
                  sizeof((__pinctrl_state_pins_0__device_dts_ord_54)[0]))))}};

static const struct pinctrl_dev_config __pinctrl_dev_config__device_dts_ord_54 = {
    .states = __pinctrl_states__device_dts_ord_54,
    .state_cnt = (( size_t)(((int)sizeof(
                     char[1 -
                          2 * !(!__builtin_types_compatible_p(
                                  __typeof__(__pinctrl_states__device_dts_ord_54),
                                  __typeof__(&(
                                      __pinctrl_states__device_dts_ord_54)
                                                 [0])))]) -
                 1) +
                (sizeof(__pinctrl_states__device_dts_ord_54) /
                 sizeof((__pinctrl_states__device_dts_ord_54)[0])))),
};

static void i2c_gd32_irq_cfg_func_0(void) {
  {
    _Static_assert( 0 || !(0 & (1UL << (0))), ""
        "ZLI interrupt registered but feature is disabled"
    ) ;

    _Static_assert(
        ((0 & (1UL << (0))) && ((1 == 1) || (0 < 1))) ||
            (0 <= ((1UL << (4)) - ((1 + 0)) - 1))
            , ""
        "Invalid interrupt priority. Values must not exceed IRQ_PRIO_LOWEST"
    ) ;

    static __attribute__((__aligned__(__alignof(struct _isr_list))))
        struct _isr_list __attribute__((section(
            ".intList"
            ))) __attribute__((__used__))
        __isr_i2c_gd32_event_isr_irq_0 = {
            31, 0, (void *)&i2c_gd32_event_isr,
            (const void *)(&__device_dts_ord_54)
        };
    z_arm_irq_priority_set(31, 0, 0);
  };

  arch_irq_enable(31);

  {
    _Static_assert( 0 || !(0 & (1UL << (0))) , ""
        "ZLI interrupt registered but feature is disabled"
    ) ;

    _Static_assert( 
      ((0 & (1UL << (0))) && ((1 == 1) || (0 < 1))) ||
      (0 <= ((1UL << (4)) - ((1 + 0)) - 1)) , ""
        "Invalid interrupt priority. Values must not exceed IRQ_PRIO_LOWEST"
    ) ;

    static __attribute__((__aligned__(__alignof( struct _isr_list))))
        struct _isr_list
        __attribute__((section( ".intList"))) __attribute__((__used__))
        __isr_i2c_gd32_error_isr_irq_1 = {
            32, 0, (void *)&i2c_gd32_error_isr,
            (const void *)(&__device_dts_ord_54)};
    z_arm_irq_priority_set(32, 0, 0);
  };

  arch_irq_enable(32);
}

static struct i2c_gd32_data i2c_gd32_data_0;

const static struct i2c_gd32_config i2c_gd32_cfg_0 = {
    .reg = 1073763328,
    .bitrate = 100000,
    .rcu_periph_clock = 4117,
    .pcfg = &__pinctrl_dev_config__device_dts_ord_54,
    .irq_cfg_func = i2c_gd32_irq_cfg_func_0,
};

static struct device_state __devstate_dts_ord_54
    __attribute__((__section__(".z_devstate")));

extern const device_handle_t __devicehdl_DT_N_S_soc_S_i2c_40005400[];

const device_handle_t __attribute__((__aligned__( sizeof(device_handle_t))))
    __attribute__((__weak__, __section__(".__device_handles_pass1")))
    __devicehdl_DT_N_S_soc_S_i2c_40005400[] = {
        54, 7, 22, 53, (-0x7fff - 1) , (-0x7fff - 1) , 55, };

const __attribute__((__aligned__(__alignof( struct device))))
    struct device __device_dts_ord_54
    __attribute__((__used__))
    __attribute__((__section__(".z_device_"
                               "POST_KERNEL"
                               "50"
                               "_"))) = {
        .name = "I2C_0",
        .config = (&i2c_gd32_cfg_0),
        .api = (&i2c_gd32_driver_api),
        .state = (&__devstate_dts_ord_54),
        .data = (&i2c_gd32_data_0),
        .handles = __devicehdl_DT_N_S_soc_S_i2c_40005400,
};

_Static_assert( sizeof("\"I2C_0\"") <= 48 , ""
    "DEVICE_NAME_GET(\"I2C_0\")" " too long") ;

static const __attribute__((__aligned__(__alignof( struct init_entry))))
    struct init_entry __init___device_dts_ord_54
    __attribute__((__used__))
    __attribute__((__section__(".z_init_" "POST_KERNEL" "50" "_"))) = {
        .init = (&i2c_gd32_init),
        .dev = ((&__device_dts_ord_54)),
};

如果您使用 GCC,您可以添加-fdata-sections -ffunction-sections 编译器标志和–gc-sections 链接器标志,以从最终二进制文件中删除未使用的函数和变量。