zephyr 设备树

 

Zephyr 的设备树介绍

Zephyr 的设备树介绍

Introduction to devicetree Zephyr 设备树简介

This is a conceptual overview of devicetree and how Zephyr uses it. For step-by-step guides and examples, see Devicetree HOWTOs.

这是一个设备树的概念概述,以及Zephyr如何使用它。详细步骤和示例请参见Devicetree HOWTOs

A devicetree is a hierarchical data structure that describes hardware. The Devicetree specification defines its source and binary representations. Zephyr uses devicetree to describe the hardware available on its Supported Boards, as well as that hardware’s initial configuration.

设备树是描述硬件的分层数据结构。Devicetree 规范定义了它的源码和二进制表示形式。Zephyr 使用设备树描述其支持的主板上可用的硬件,以及硬件的初始配置。

There are two types of devicetree input files: devicetree sources and devicetree bindings. The sources contain the devicetree itself. The bindings describe its contents, including data types. The build system uses devicetree sources and bindings to produce a generated C header. The generated header’s contents are abstracted by the devicetree.h API, which you can use to get information from your devicetree.

有两种类型的 devicetree 输入文件:devicetree sources 和 devicetree bindings。源文件包含 devicetree 本身。绑定文件描述其内容,包括数据类型。构建系统使用 devicetree 源和绑定来产生一个生成的 C 头。生成的头文件的内容被devicetree.h API抽象化,你可以用它来从你的devicetree中获取信息。

下面是这个过程的一个简化视图:

devtree

All Zephyr and application source code files can include and use devicetree.h. This includes device drivers, applications, tests, the kernel, etc.

所有的Zephyr和应用程序的源代码文件都可以包括和使用devicetree.h。这包括设备驱动程序、应用程序、测试、内核等。

The API itself is based on C macros. The macro names all start with DT_. In general, if you see a macro that starts with DT_ in a Zephyr source file, it’s probably a devicetree.h macro. The generated C header contains macros that start with DT_ as well; you might see those in compiler error messages. You always can tell a generated- from a non-generated macro: generated macros have some lowercased letters, while the devicetree.h macro names have all capital letters.

该API本身是基于C语言的宏。这些宏的名字都以DT_开头。一般来说,如果你在Zephyr源文件中看到一个以DT_开头的宏,它可能是devicetree.h宏。生成的C头也包含以DT_开头的宏;你可能在编译器错误信息中看到这些宏。你总是可以区分生成的-和非生成的宏:生成的宏有一些小写的字母,而devicetree.h的宏名都是大写字母。

Some information defined in devicetree is available via CONFIG_ macros generated from Kconfig. This is often done for backwards compatibility, since Zephyr has used Kconfig for longer than devicetree, and is still in the process of converting some information from Kconfig to devicetree. It is also done to allow Kconfig overrides of default values taken from devicetree. Devicetree information is referenced from Kconfig via Kconfig functions. See Devicetree versus Kconfig for some additional comparisons with Kconfig.

devicetree中定义的一些信息可以通过Kconfig中生成的CONFIG_宏获得。这通常是为了向后兼容,因为Zephyr使用Kconfig的时间比devicetree长,并且仍在将一些信息从Kconfig转换到devicetree。这样做也是为了允许Kconfig覆盖来自devicetree的默认值。Devicetree信息是通过Kconfig函数从Kconfig引用的。请参阅Devicetree与Kconfig的比较,了解与Kconfig的一些其他比较。

Syntax and structure

As the name indicates, a devicetree is a tree. The human-readable text format for this tree is called DTS (for devicetree source), and is defined in the Devicetree specification.

如其名所示,devicetree是一棵树。这种树的人可读文本格式被称为DTS(代表devicetree源),并在Devicetree规范中定义。

Here is an example DTS file:

下面是一个DTS文件的例子:

/dts-v1/;

/ {
        a-node {
                subnode_nodelabel: a-sub-node {
                        foo = <3>;
                        label = "SUBNODE";
                };
        };
};

The /dts-v1/; line means the file’s contents are in version 1 of the DTS syntax, which has replaced a now-obsolete “version 0”.

/dts-v1/; 行表示文件的内容位于 DTS 语法的版本1中,该版本取代了现在已过时的“版本0”。

The tree has three nodes:

该树有三个节点:

  1. A root node: /

    根节点:/

  2. A node named a-node, which is a child of the root node

    一个名为 a-node 的节点,它是根节点的子节点

  3. A node named a-sub-node, which is a child of a-node

    一个名为a-sub-node的节点,它是a-node节点的子节点

Nodes can be assigned node labels, which are unique shorthands that can be used to refer to the labeled node elsewhere in the devicetree. Above, a-sub-node has the node label subnode_nodelabel. A node can have zero, one, or multiple node labels.

节点可以分配节点标签,这些标签是唯一的缩写,可以用来引用设备树中其他地方的标记节点。上面,a-sub-node节点有节点标签subnode_nodelabel。一个节点可以有零个、一个或多个节点标签。

Devicetree nodes have paths identifying their locations in the tree. Like Unix file system paths, devicetree paths are strings separated by slashes (/), and the root node’s path is a single slash: /. Otherwise, each node’s path is formed by concatenating the node’s ancestors’ names with the node’s own name, separated by slashes. For example, the full path to a-sub-node is /a-node/a-sub-node.

设备树节点具有标识其在树中位置的路径。与 Unix文件系统路径一样,devicetree 路径是由斜杠(/)分隔的字符串,根节点的路径是一个斜杠:/。否则,每个节点的路径都是通过将节点的祖先名称与节点自己的名称连接起来形成的,并用斜杠分隔。例如,到a-sub-node节点的完整路径是/a-node/a-sub-node。

Devicetree nodes can also have properties. Properties are name/value pairs. Property values can be any sequence of bytes. In some cases, the values are an array of what are called cells. A cell is just a 32-bit unsigned integer.

Devicetree 节点也可以具有属性。属性是(键-值)对。属性值可以是任何字节序列。在某些情况下,这些值是所谓单元格的数组。单元格只是一个32位无符号整数。

Node a-sub-node has a property named foo, whose value is a cell with value 3. The size and type of foo‘s value are implied by the enclosing angle brackets (< and >) in the DTS.

节点 a-sub-node 有一个名为 foo 的属性,其值是一个值为3的单元格。Foo 的值的大小和类型通过 DTS 中的括号(< 和 >)来表示。

Node a-sub-node has an additional property named label, whose value is a string containing the value “SUBNODE”. Note that the label property is distinct from the node label.

节点 a-sub-node 有一个名为 label 的附加属性,其值是一个包含值“ SUBNODE”的字符串。注意,label 属性与节点 label 不同。

See Writing property values below for more example property values.

有关更多属性值示例,请参见下面的“编写属性值”。

In practice, devicetree nodes usually correspond to some hardware, and the node hierarchy reflects the hardware’s physical layout. For example, let’s consider a board with three I2C peripherals connected to an I2C bus controller on an SoC, like this:

在实践中,设备树节点通常对应于某些硬件,节点层次结构反映了硬件的物理布局。例如,让我们考虑一块板,其中有三个 I2C 外围设备连接到 SoC 上的 I2C 总线控制器,如下所示:

representation of a board with three I2C peripherals

Nodes corresponding to the I2C bus controller and each I2C peripheral would be present in the devicetree. Reflecting the hardware layout, the I2C peripheral nodes would be children of the bus controller node. Similar conventions exist for representing other types of hardware.

与 I2C 总线控制器和每个 I2C 外围设备相对应的节点将出现在设备树中。为了反映硬件布局,I2C 外围节点将是总线控制器节点的子节点。对于表示其他类型的硬件,也存在类似的约定。

The DTS would look something like this:

DTS 看起来是这样的:

/dts-v1/;

/ {
        soc {
                i2c-bus-controller {
                        i2c-peripheral-1 {
                        };
                        i2c-peripheral-2 {
                        };
                        i2c-peripheral-3 {
                        };
                };
        };
};

Properties are used in practice to describe or configure the hardware the node represents. For example, an I2C peripheral’s node has a property whose value is the peripheral’s address on the bus.

属性在实践中用于描述或配置节点所代表的硬件。例如,I2C 外围设备的节点具有一个属性,其值为总线上外围设备的地址。

Here’s a tree representing the same example, but with real-world node names and properties you might see when working with I2C devices.

下面是一个树,它表示相同的示例,但具有在使用 I2C 设备时可能看到的真实节点名和属性。

zephyr_dt_i2c_example.png

Fig. 21 I2C devicetree example with real-world names and properties. Node names are at the top of each node with a gray background. Properties are shown as “name=value” lines.

图21具有真实世界名称和属性的 I2C 设备实例。节点名在每个节点的顶部,背景为灰色。属性显示为“ name = value”行。¶

This is the corresponding DTS:

这是相应的 DTS:

/dts-v1/;

/ {
        soc {
                i2c@40003000 {
                        compatible = "nordic,nrf-twim";
                        label = "I2C_0";
                        reg = <0x40003000 0x1000>;

                        apds9960@39 {
                                compatible = "avago,apds9960";
                                label = "APDS9960";
                                reg = <0x39>;
                        };
                        ti_hdc@43 {
                                compatible = "ti,hdc", "ti,hdc1010";
                                label = "HDC1010";
                                reg = <0x43>;
                        };
                        mma8652fc@1d {
                                compatible = "nxp,fxos8700", "nxp,mma8652fc";
                                label = "MMA8652FC";
                                reg = <0x1d>;
                        };
                };
        };
};

In addition to showing more realistic names and properties, the above example introduces a new devicetree concept: unit addresses. Unit addresses are the parts of node names after an “at” sign (@), like 40003000 in i2c@40003000, or 39 in apds9960@39. Unit addresses are optional: the soc node does not have one.

除了显示更现实的名称和属性外,上面的示例还引入了一个新的设备树概念: 单元地址。单元地址是在“ at”符号(@)之后的节点名称的部分,如 i2c@40003000或 apds9960@39中的40003000。单元地址是可选的: soc 节点没有。

Some more details about unit addresses and important properties follow.

更多关于单元地址和重要属性的详细信息请参阅。

Unit address examples 单元地址示例

In devicetree, unit addresses give a node’s address in the address space of its parent node. Here are some example unit addresses for different types of hardware.

在设备树中,单元地址在其父节点的地址空间中给出节点的地址。下面是一些不同类型硬件的示例单元地址。

  • Memory-mapped peripherals 内存映射的外围设备

    The peripheral’s register map base address. For example, the node named i2c@40003000 represents an I2C controller whose register map base address is 0x40003000.

    外围设备的寄存器映射基地址。例如,名为 I2C@40003000的节点表示一个 I2C 控制器,其寄存器映射基地址为0x40003000。

  • I2C peripherals I2C 外围设备

    The peripheral’s address on the I2C bus. For example, the child node apds9960@39 of the I2C controller in the previous section has I2C address 0x39.

    I2C 总线上外围设备的地址。例如,前一节中 I2C 控制器的子节点 apds9960@39具有 I2C 地址0x39。

  • SPI peripherals SPI 外围设备

    An index representing the peripheral’s chip select line number. (If there is no chip select line, 0 is used.)

    表示外围设备的芯片选择行号的索引。(如果没有芯片选择行,则使用0。)

  • Memory 记忆

    The physical start address. For example, a node named memory@2000000 represents RAM starting at physical address 0x2000000.

    物理开始地址。例如,一个名为 memory@2000000的节点表示从物理地址0x2000000开始的 RAM。

  • Memory-mapped flash 内存映射闪存

    Like RAM, the physical start address. For example, a node named flash@8000000 represents a flash device whose physical start address is 0x8000000.

    像 RAM 一样,物理起始地址。例如,名为 flash@8000000的节点表示物理起始地址为0x800000的闪存设备。

  • Fixed flash partitions 固定闪存分区

    This applies when the devicetree is used to store a flash partition table. The unit address is the partition’s start offset within the flash memory. For example, take this flash device and its partitions:

    当设备树用于存储一个闪存分区表时,这种情况就会出现。单元地址是闪存中分区的开始偏移量。例如,以这个闪存设备及其分区为例:

      flash@8000000 {
          /* ... */
          partitions {
                  partition@0 { /* ... */ };
                  partition@20000 {  /* ... */ };
                  /* ... */
          };
      }; 
    

    The node named partition@0 has offset 0 from the start of its flash device, so its base address is 0x8000000. Similarly, the base address of the node named partition@20000 is 0x8020000.

    名为 partition@0的节点从其 flash 设备的开始偏移了0,因此它的基地址是0x8000000。类似地,名为 partition@20000的节点的基地址是0x8020000。

Important properties 重要特性

Some important properties are:

一些重要的属性是:

  • compatible 兼容的

    The name of the hardware device the node represents.节点所代表的硬件设备的名称。

    The recommended format is "vendor,device", like "avago,apds9960", or a sequence of these, like "ti,hdc", "ti,hdc1010". The vendor part is an abbreviated name of the vendor. The file dts/bindings/vendor-prefixes.txt contains a list of commonly accepted vendor names. The device part is usually taken from the datasheet.

    推荐的格式是“ vendor,device”,比如“ avago,apds9960”,或者一系列这样的格式,比如“ ti,hdc”,“ ti,hdc1010”。供应商部分是供应商名称的缩写。Dts/bindings/vendor-prefixes.txt 文件包含一个常用的供应商名称列表。设备部分通常取自数据表。

    It is also sometimes a value like gpio-keys, mmio-sram, or fixed-clock when the hardware’s behavior is generic.

    当硬件的行为是通用的时候,它有时也是一个类似于 gpio-keys、 mmio-sram 或 fixed-clock 的值。

    The build system uses the compatible property to find the right bindings for the node. Device drivers use devicetree.h to find nodes with relevant compatibles, in order to determine the available hardware to manage.

    构建系统使用兼容属性查找节点的正确绑定。设备驱动程序使用 devicetree.h 查找具有相关兼容性的节点,以确定要管理的可用硬件。

    The compatible property can have multiple values. Additional values are useful when the device is a specific instance of a more general family, to allow the system to match from most- to least-specific device drivers.

    兼容的属性可以有多个值。当设备是一个更一般系列的特定实例时,附加值非常有用,以允许系统匹配从最特定到最不特定的设备驱动程序。

    Within Zephyr’s bindings syntax, this property has type string-array.

    在 Zephyr 的绑定语法中,此属性具有字符串数组类型。

  • label 标签

    The device’s name according to Zephyr’s Device Driver Model. The value can be passed to device_get_binding() to retrieve the corresponding driver-level struct device*. This pointer can then be passed to the correct driver API by application code to interact with the device. For example, calling device_get_binding("I2C_0") would return a pointer to a device structure which could be passed to I2C API functions like i2c_transfer(). The generated C header will also contain a macro which expands to this string.

    该设备的名称根据 Zephyr 的设备驱动程序模型。该值可以传递给 device_get_binding() 以检索相应的驱动程序级 struct device*。这个指针可以通过应用程序代码传递给正确的驱动程序 API 以与设备交互。例如,调用 device_get_binding(“I2C_0”) 将返回一个指向设备结构的指针,该指针可以传递给 I2C API 函数,如 i2c_transfer()。生成的 c 头还将包含一个宏,该宏将展开为此字符串。Note that the label property is distinct from the node label.注意,label 属性与节点 label 不同。

  • reg 寄存器块

    Information used to address the device. The value is specific to the device (i.e. is different depending on the compatible property).

    用于寻址设备的信息。这个值是特定于设备的(也就是说,根据兼容的属性不同)。

    The reg property is a sequence of (address, length) pairs. Each pair is called a “register block”. Here are some common patterns:

    reg 属性是一个(地址,长度)对的序列。每一对被称为“寄存器块”。以下是一些常见的模式通过内存映射 I/O 寄存器访问的设备(如 i2c@40003000) :

    • Devices accessed via memory-mapped I/O registers (like i2c@40003000): address is usually the base address of the I/O register space, and length is the number of bytes occupied by the registers.

      通过内存映射的I/O寄存器(如i2c@40003000)访问的设备:address通常是I/O寄存器空间的基址,而length是寄存器占用的字节数。

    • I2C devices (like apds9960@39 and its siblings): address is a slave address on the I2C bus. There is no length value.

      I2C 设备(如 apds9960@39及其兄弟设备) ,address是I2C总线上的一个从地址。没有长度值。

    • SPI devices: address is a chip select line number; there is no length.

      SPI 设备: 地址是芯片选择行号,没有长度。

    You may notice some similarities between the reg property and common unit addresses described above. This is not a coincidence. The reg property can be seen as a more detailed view of the addressable resources within a device than its unit address.

    您可能会注意到上面描述的 reg 属性和通用单元地址之间的一些相似之处。这不是巧合。Reg 属性可以看作是设备中可寻址资源的比单位地址更详细的视图。

    Information used to address the device. The value is specific to the device (i.e. is different depending on the compatible property).

    用于寻址设备的信息。该值是特定于设备的(即,根据compatible属性的不同)。

  • status 状态

    A string which describes whether the node is enabled.

    一个字符串,用于描述是否启用节点。

    The devicetree specification allows this property to have values "okay", "disabled", "reserved", "fail", and "fail-sss". Only the values "okay" and "disabled" are currently relevant to Zephyr; use of other values currently results in undefined behavior.

    Devicetree 规范允许此属性具有“ okay”、“ disabled”、“ reserved”、“ fail”和“ fail-sss”值。目前只有“ ok”和“ disabled”的值与Zephyr相关,使用其他值会导致未定义行为。

    A node is considered enabled if its status property is either "okay" or not defined (i.e. does not exist in the devicetree source). Nodes with status "disabled" are explicitly disabled. (For backwards compatibility, the value "ok" is treated the same as "okay", but this usage is deprecated.) Devicetree nodes which correspond to physical devices must be enabled for the corresponding struct device in the Zephyr driver model to be allocated and initialized.

    如果一个节点的状态属性是“ okay”或者没有定义(也就是说在设备源中不存在) ,则认为该节点已启用。显式禁用状态为”disabled”的节点。(为了向后兼容,“ ok”的值被视为“ okay”,但是这个用法不推荐使用。)必须启用与物理设备对应的 Devicetree 节点,才能分配和初始化 Zephyr 驱动程序模型中的相应结构设备。

  • interrupts 中断

    Information about interrupts generated by the device, encoded as an array of one or more interrupt specifiers. Each interrupt specifier has some number of cells. See section 2.4, Interrupts and Interrupt Mapping, in the Devicetree Specification release v0.3 for more details.

    关于设备生成的中断的信息,编码为一个或多个中断指定符的数组。每个中断指定符有一定数量的单元格。详细信息请参阅 Devicetree Specification 版本 v0.3中的2.4节—- 中断和中断映射。

    Zephyr’s devicetree bindings language lets you give a name to each cell in an interrupt specifier.

    Zephyr 的 devicetree 绑定语言允许您为中断指定符中的每个单元格命名。

Writing property values 写入属性值

This section describes how to write property values in DTS format. The property types in the table below are described in detail in Devicetree bindings.

本节描述如何以 DTS 格式写入属性值。下表中的属性类型在 Devicetree 绑定中有详细描述。

Some specifics are skipped in the interest of keeping things simple; if you’re curious about details, see the devicetree specification.

为了简单起见,跳过了一些细节; 如果您对细节感兴趣,请参阅设备树规范。

Property type How to write Example
string Double quoted双引号 a-string = "hello, world!";
int between angle brackets (< and >) an-int = <1>;
boolean for true, with no value (for false, use /delete-property/) my-true-boolean;
array between angle brackets (< and >), separated by spaces
在尖括号之间(< 和 >) ,用空格分隔
foo = <0xdeadbeef 1234 0>;
uint8-array in hexadecimal without leading 0x, between square brackets ([ and ]).
以十六进制表示,前面没有前导0x,在方括号中([和])。
a-byte-array = [00 01 ab];
string-array separated by commas用逗号分隔 a-string-array = "string one", "string two", "string three";
phandle between angle brackets (< and >)尖括号(< 及 >) a-phandle = <&mynode>;
phandles between angle brackets (< and >), separated by spaces在尖括号之间(< 和 >) ,用空格分隔 some-phandles = <&mynode0 &mynode1 &mynode2>;
phandle-array between angle brackets (< and >), separated by spaces在尖括号之间(< 和 >) ,用空格分隔 a-phandle-array = <&mynode0 1 2 &mynode1 3 4>;

Additional notes on the above:

关于上述问题的补充说明:

  • Boolean properties are true if present. They should not have a value. A boolean property is only false if it is completely missing in the DTS.

    如果存在,则布尔属性为 true。它们不应该有价值。只有在 DTS 中完全缺少布尔属性时,该属性才是 false。

  • The foo property value above has three cells with values 0xdeadbeef, 1234, and 0, in that order. Note that hexadecimal and decimal numbers are allowed and can be intermixed. Since Zephyr transforms DTS to C sources, it is not necessary to specify the endianness of an individual cell here.

    上面的 foo 属性值有三个单元格,分别是值0xdeadbeef、1234和0,顺序如下。请注意,十六进制和十进制数字是允许的,并且可以混合使用。由于 Zephyr 将 DTS 转换为 c 源码,因此不必在此指定单个单元的 大小端。

  • 64-bit integers are written as two 32-bit cells in big-endian order. The value 0xaaaa0000bbbb1111 would be written <0xaaaa0000 0xbbbb1111>.

    64位整数以 big-endian 顺序写成两个32位单元格。

  • The a-byte-array property value is the three bytes 0x00, 0x01, and 0xab, in that order.

    字节数组属性值是三个字节0x00、0x01和0xab,顺序如下。

  • Parentheses, arithmetic operators, and bitwise operators are allowed. The bar property contains a single cell with value 64:

    圆括号、算术运算符和位运算符都是允许的。 bar 属性包含一个值为64的单元格:

    bar = <(2 * (1 << 5))>;
    

    Note that the entire expression must be parenthesized.

    请注意,整个表达式必须用圆括号括起来。

  • Property values refer to other nodes in the devicetree by their phandles. You can write a phandle using &foo, where foo is a node label. Here is an example devicetree fragment:

    属性值通过它们的 phandles 指代设备树中的其他节点。可以使用 & foo 编写 phandle,其中 foo 是一个节点标签。下面是一个设备树片段的例子:

    foo: device@0 { };
    device@1 {
            sibling = <&foo 1 2>;
    };
    

    The sibling property of node device@1 contains three cells, in this order:

    节点设备@1的同级属性包含三个单元格,顺序如下:

    1. The device@0 node’s phandle, which is written here as &foo since the device@0 node has a node label foo

      device@0 节点的 phandle(句柄指针),这里写作 & foo,因为 device@0节点有一个节点标签 foo

    2. The value 1

    3. The value 2

    In the devicetree, a phandle value is a cell – which again is just a 32-bit unsigned int. However, the Zephyr devicetree API generally exposes these values as node identifiers. Node identifiers are covered in more detail in Devicetree access from C/C++.

    在设备树中,一个 phandle 值是一个单元格,也是一个32位无符号整型数。然而,Zephyr 设备树 API 通常将这些值作为节点标识符公开。节点标识符在 Devicetree 访问 c/c + + 中有更详细的介绍。

  • Array and similar type property values can be split into several <> blocks, like this:

    数组和类似类型的属性值可以分成几个 < > 块,像这样:

    foo = <1 2>, <3 4>;                         // Okay for 'type: array'
    foo = <&label1 &label2>, <&label3 &label4>; // Okay for 'type: phandles'
    foo = <&label1 1 2>, <&label2 3 4>;         // Okay for 'type: phandle-array'
    

    This is recommended for readability when possible if the value can be logically grouped into blocks of sub-values.

    如果值可以逻辑地分组为子值块,则建议在可读性方面使用这种方法。

d

Aliases and chosen nodes 别名和选择的节点

There are two additional ways beyond node labels to refer to a particular node without specifying its entire path: by alias, or by chosen node.

除了节点标签之外,还有两种方法可以在不指定整个路径的情况下引用特定节点: 通过别名或选择的节点。

Here is an example devicetree which uses both:

下面是一个使用两者的设备例子:

/dts-v1/;

/ {
     chosen {
             zephyr,console = &uart0;
     };

     aliases {
             my-uart = &uart0;
     };

     soc {
             uart0: serial@12340000 {
                     ...
             };
     };
};

The /aliases and /chosen nodes do not refer to an actual hardware device. Their purpose is to specify other nodes in the devicetree.

/aliases 和 /chosen 的节点并不引用实际的硬件设备。它们的目的是指定设备树中的其他节点。

Above, my-uart is an alias for the node with path /soc/serial@12340000. Using its node label uart0, the same node is set as the value of the chosen zephyr,console node.

上面,my-uart 是具有 path/soc/serial@12340000的节点的别名。使用其节点标签 uart0,将同一节点设置为所选择的 zephyr,console 节点的值。

Zephyr sample applications sometimes use aliases to allow overriding the particular hardware device used by the application in a generic way. For example, Blinky uses this to abstract the LED to blink via the led0 alias.

Zephyr 示例应用程序有时使用别名允许以通用方式覆盖应用程序使用的特定硬件设备。例如,Blinky 通过 led0别名 来提取 LED 闪烁。

The /chosen node’s properties are used to configure system- or subsystem-wide values. See Chosen nodes for more information.

所选节点的属性用于配置系统或子系统范围的值。有关详细信息,请参阅所选节点。

Input and output files 输入和输出文件

This section describes the input and output files shown in the figure at the top of this introduction in more detail.

本节将更详细地描述本介绍开头的图中所示的输入和输出文件。

zephyr_dt_inputs_outputs.svg

Fig. 22 Devicetree input (green) and output (yellow) files

图22设备输入(绿色)和输出(黄色)文件

Input files 输入文件

There are four types of devicetree input files:

有四种类型的设备树输入文件:

  • sources (.dts)

    源代码(. dts)

  • includes (.dtsi)

    包括(. dtsi)

  • overlays (.overlay)

    覆盖(. 覆盖)

  • bindings (.yaml)

    绑定(. yaml)

The devicetree files inside the zephyr directory look like this:

Zephyr 目录中的 devicetree 文件如下所示:

boards/<ARCH>/<BOARD>/<BOARD>.dts
dts/common/skeleton.dtsi
dts/<ARCH>/.../<SOC>.dtsi
dts/bindings/.../binding.yaml

Generally speaking, every supported board has a BOARD.dts file describing its hardware. For example, the reel_board has boards/arm/reel_board/reel_board.dts.

一般来说,每个支持的主板都有一个用于描述其硬件的 board. dts 文件,例如,reel_board有一个 board/arm/reel board/reel board. dts。

BOARD.dts includes one or more .dtsi files. These .dtsi files describe the CPU or system-on-chip Zephyr runs on, perhaps by including other .dtsi files. They can also describe other common hardware features shared by multiple boards. In addition to these includes, BOARD.dts also describes the board’s specific hardware.

BOARD.dts 包括一个或多个 .dtsi 文件。这些。.dtsi 文件描述 CPU 或Zephyr 在其上运行的Soc片上系统 ,也许包括其他.dtsi文件。它们还可以描述多块板共享的其他常见硬件特性。除了包含这些,BOARD.dts 还描述了主板的具体硬件。

The dts/common directory contains skeleton.dtsi, a minimal include file for defining a complete devicetree. Architecture-specific subdirectories (dts/<ARCH>) contain .dtsi files for CPUs or SoCs which extend skeleton.dtsi.

dts/common 目录包含 skeleton.dtsi,这是一个用于定义完整设备树的最小包含文件。体系结构特定的子目录(dts/)扩展skeleton.dtsi的cpu或soc的.dtsi文件。

The C preprocessor is run on all devicetree files to expand macro references, and includes are generally done with #include <filename> directives, even though DTS has a /include/ "<filename>" syntax.

C预处理器在所有devicetree文件上运行,以展开宏引用,并且通常使用#include <filename>指令执行include,即使DTS具有/include/" <filename>"语法。

BOARD.dts can be extended or modified using overlays. Overlays are also DTS files; the .overlay extension is just a convention which makes their purpose clear. Overlays adapt the base devicetree for different purposes:

BOARD.dts 可以通过overlays来扩展或修改。overlays也是 DTS 文件; .overlay扩展只是一个约定,它的目的很明确。overlay可以根据不同的目的调整基础设备:

  • Zephyr applications can use overlays to enable a peripheral that is disabled by default, select a sensor on the board for an application specific purpose, etc. Along with Configuration System (Kconfig), this makes it possible to reconfigure the kernel and device drivers without modifying source code.

    Zephyr 应用程序可以使用overlay来启用默认禁用的外设,为应用程序特定的目的在板上选择传感器,等等。与 配置系统 (Kconfig)一起,这使得不需要修改源代码就可以重新配置内核和设备驱动程序成为可能。

  • Overlays are also used when defining Shields.

    覆盖也用于定义盾牌。

The build system automatically picks up .overlay files stored in certain locations. It is also possible to explicitly list the overlays to include, via the DTC_OVERLAY_FILE CMake variable. See Set devicetree overlays for details.

构建系统自动拾取.overlay文件存储在某些位置。也可以通过DTC_OVERLAY_FILE CMake变量显式列出要包括的overlay。详见设置设备树覆盖。

The build system combines BOARD.dts and any .overlay files by concatenating them, with the overlays put last. This relies on DTS syntax which allows merging overlapping definitions of nodes in the devicetree. See Example: FRDM-K64F and Hexiwear K64 for an example of how this works (in the context of .dtsi files, but the principle is the same for overlays). Putting the contents of the .overlay files last allows them to override BOARD.dts.

构建系统通过连接它们来组合BOARD.dts和任何.overlay文件,overlay放在最后。这依赖于DTS语法,它允许合并设备树中节点的重叠定义。参见示例:FRDM-K64F和Hexiwear K64,以了解其工作原理(在.dtsi文件的上下文中,但原理是相同)。把.overlay文件的内容放在最后允许他们覆盖BOARD.dts。

Devicetree bindings (which are YAML files) are essentially glue. They describe the contents of devicetree sources, includes, and overlays in a way that allows the build system to generate C macros usable by device drivers and applications. The dts/bindings directory contains bindings.

Devicetree 绑定(YAML 文件)本质上是粘合的。它们描述了设备树源代码、包含文件和图层文件的内容,以允许构建系统生成设备驱动程序和应用程序可用的 c 宏。dts/bindings 目录包含绑定。

Zephyr currently uses dts_fixup.h files to rename macros in devicetree_unfixed.h to names that are currently in use by C code. The build system looks for fixup files in the zephyr/boards/ and zephyr/soc/ directories by default. Fixup files exist for historical reasons. New code should generally avoid them.

Zephyr 目前使用 dts_fixup.h 文件将 devicetree_unfixed.h 中的宏重命名为 c 代码当前使用的名称。构建系统默认在 zephyr/boards/和 zephyr/soc/目录中查找修复文件。修复文件的存在是由于历史原因。新的代码通常应该避免使用它们。

Scripts and tools 脚本和工具

The following libraries and scripts, located in scripts/dts/, create output files from input files. Their sources have extensive documentation.

下面的库和脚本位于 scripts/dts/中,从输入文件创建输出文件。

  • dtlib.py

    A low-level DTS parsing library.一个低级 DTS 解析库。

  • edtlib.py

    A library layered on top of dtlib that uses bindings to interpret properties and give a higher-level view of the devicetree. Uses dtlib to do the DTS parsing.在 dtlib 之上的一个分层库,它使用绑定来解释属性并提供设备树的更高级视图。使用 dtlib 进行 DTS 解析。

  • gen_defines.py

    A script that uses edtlib to generate C preprocessor macros from the devicetree and bindings.使用 edtlib 从 devicetree 和 bindings 生成 c 预处理器宏的脚本。

In addition to these, the standard dtc (devicetree compiler) tool is run on the final devicetree if it is installed on your system. This is just to catch errors or warnings. The output is unused. Boards may need to pass dtc additional flags, e.g. for warning suppression. Board directories can contain a file named pre_dt_board.cmake which configures these extra flags, like this:

除此之外,如果标准 dtc (devicetree 编译器)工具安装在您的系统上,那么它将在最终的 devicetree 上运行。这只是为了捕捉错误或警告。输出未使用。主板可能需要传递 dtc 附加的标志,例如用于警告抑制。主板目录可以包含一个文件命名为pre_dt_board.cmake 来配置这些额外的标志,像这样:

list(APPEND EXTRA_DTC_FLAGS "-Wno-simple_bus_reg")

Output files 输出文件

These are created in your application’s build directory.

这些是在应用程序的构建目录中创建的。

Warning

Don’t include the header files directly. Devicetree access from C/C++ explains what to do instead.

不要直接包含头文件。 c/c++ 中的 Devicetree 访问解释了如何代替头文件。

  • <build>/zephyr/zephyr.dts.pre

    The preprocessed DTS source. This is an intermediate output file, which is input to gen_defines.py and used to create zephyr.dts and devicetree_unfixed.h.

    预处理 DTS 源。这是一个中间输出文件,输入到 gen_defines.py,用于创建 zephyr.dts 和 devicetree_unfixed.h。

  • <build>/zephyr/include/generated/devicetree_unfixed.h

    The generated macros and additional comments describing the devicetree. Included by devicetree.h.

    生成的宏和描述设备树的附加注释。被 devicetree.h 包含

  • <build>/zephyr/include/generated/devicetree_fixups.h

    The concatenated contents of any dts_fixup.h files. Included by devicetree.h.

    任何 dts_fixup.h 文件的连接内容,由 devicetree.h 包含。

  • <build>/zephyr/zephyr.dts

    The final merged devicetree. This file is output by gen_defines.py. It is useful for debugging any issues. If the devicetree compiler dtc is installed, it is also run on this file, to catch any additional warnings or errors.

    最后合并的设备树。此文件由 gen_defines.py 输出。它对于调试任何问题都很有用。如果已经安装了 devicetree 编译器 dtc,那么它也将在该文件上运行,以捕获任何其他警告或错误。

Design goals 设计目标

Zephyr’s use of devicetree has evolved significantly over time, and further changes are expected. The following are the general design goals, along with specific examples about how they impact Zephyr’s source code, and areas where more work remains to be done.

随着时间的推移,Zephyr 对设备的使用已经发生了显著的变化,预计还会有进一步的变化。以下是一般的设计目标,以及关于它们如何影响 Zephyr 源代码的具体例子,以及还有更多工作要做的领域。

Single source for all hardware information

所有硬件信息的单一来源

Zephyr shall obtain its hardware descriptions exclusively from devicetree.

Zephyr 应完全从设备树上获得其硬件说明

Examples 例子

  • New device drivers shall use devicetree APIs to determine which devices to create if possible.

    如果可能的话,新的设备驱动程序应该使用设备树 api 来决定创建哪些设备

  • In-tree sample applications shall use aliases to determine which of multiple possible generic devices of a given type will be used in the current build. For example, the Blinky uses this to determine the LED to blink.

    树内示例应用程序应该使用别名来确定在当前构建中将使用给定类型的多个可能的通用设备中的哪一个。例如,Blinky 使用这个来决定 LED 是否闪烁。

  • Boot-time pin muxing and pin control can be accomplished via devicetree.

    开机时引脚的复用和引脚控制可以通过设备树实现

Example remaining work 示例剩余工作

  • Zephyr’s Test Runner (Twister) currently use board.yaml files to determine the hardware supported by a board. This should be obtained from devicetree instead.

    Zephyr 的 Test Runner (Twister)目前使用 board.yaml 文件来确定主板支持的硬件。这应该从设备树中获得。

  • Various device drivers currently use Kconfig to determine which instances of a particular compatible are enabled. This can and should be done with devicetree overlays instead.

    各种设备驱动程序目前使用 Kconfig 来确定哪些特定的兼容实例被启用。这可以而且应该通过设备树图层来完成。

  • Board-level documentation still contains tables of hardware support which are generated and maintained by hand. This can and should be obtained from the board level devicetree instead.

    板级文档仍然包含由手工生成和维护的硬件支持表。这可以而且应该从板级设备树获得。

  • Runtime determination of struct device relationships should be done using information obtained from devicetree, e.g. for device power management.

    运行时确定 struct device 关系应该使用从设备树获得的信息,例如设备电源管理。

Source compatibility with other operating systems

与其他操作系统的源代码兼容性

Zephyr’s devicetree tooling is based on a generic layer which is interoperable with other devicetree users, such as the Linux kernel.

Zephyr 的设备树工具基于一个通用层,该层可与其他设备树用户(如 Linux 内核)互操作。

Zephyr’s binding language semantics can support Zephyr-specific attributes, but shall not express Zephyr-specific relationships.

Zephyr 的绑定语言语义学可以支持Zephyr的特定属性,但不能表达Zephyr的特定关系。

Examples 例子

  • Zephyr’s devicetree source parser, dtlib.py, is source-compatible with other tools like dtc in both directions: dtlib.py can parse dtc output, and dtc can parse dtlib.py output.

    Zephyr 的 devicetree 源代码解析器 dtlib.py 在两个方向上都与 dtc 等其他工具具有源代码兼容性: dtlib.py 可以解析 dtc 输出,dtc 可以解析 dtlib.py 输出。

  • Zephyr’s “extended dtlib” library, edtlib.py, shall not include Zephyr-specific features. Its purpose is to provide a higher-level view of the devicetree for common elements like interrupts and buses.

    Zephyr 的“扩展 dtlib”库 edtlib.py 不应包含 Zephyr 特定的特性。其目的是为中断和总线等公共元素提供更高层次的设备代码视图。

    Only the high-level gen_defines.py script, which is built on top of edtlib.py, contains Zephyr-specific knowledge and features.

    只有基于 edtlib.py 的高级 gen_defines.py 脚本包含与 zephyr 相关的知识和特性。

Example remaining work 示例剩余工作

  • Zephyr has a custom Devicetree bindings language syntax. While Linux’s dtschema does not yet meet Zephyr’s needs, we should try to follow what it is capable of representing in Zephyr’s own bindings.

    Zephyr 有一个自定义 Devicetree 绑定语言语法。虽然 Linux 的 dtschema 还不能满足 Zephyr 的需要,但是我们应该尝试遵循它能够在 Zephyr 自己的绑定中表示什么。

  • Due to inflexibility in the bindings language, Zephyr cannot support the full set of bindings supported by Linux.

    由于绑定语言不够灵活,Zephyr 不能支持 Linux 支持的全部绑定集。

  • Devicetree source sharing between Zephyr and Linux is not done.

    Devicetree 源码在 Zephyr 和 Linux 之间共享还没有完成。

Devicetree bindings 设备树绑定

A devicetree on its own is only half the story for describing hardware, as it is a relatively unstructured format. Devicetree bindings provide the other half.

设备树本身只是描述硬件的一半,因为它是一种相对非结构化的格式。设备树绑定提供了另一半。

A devicetree binding declares requirements on the contents of nodes, and provides semantic information about the contents of valid nodes. Zephyr devicetree bindings are YAML files in a custom format (Zephyr does not use the dt-schema tools used by the Linux kernel).

一个 devicetree 绑定声明节点内容的需求,并提供有效节点内容的语义信息。Zephyr devicetree 绑定是自定义格式的 YAML 文件(Zephyr 不使用 Linux 内核使用的 dt-schema 工具)。

This page introduces bindings, describes what they do, notes where they are found, and explains their data format.

本页介绍了绑定,描述了它们的作用,说明了它们的位置,并解释了它们的数据格式。

Note 注意

See the Bindings index for reference information on bindings built in to Zephyr.

有关构建在 Zephyr 的绑定的参考信息,请参阅 Bindings 索引。

Introduction 引言

Devicetree nodes are matched to bindings using their compatible properties.

Devicetree 节点使用其兼容属性与绑定进行匹配

During the Configuration Phase, the build system tries to match each node in the devicetree to a binding file. When this succeeds, the build system uses the information in the binding file both when validating the node’s contents and when generating macros for the node.

在配置阶段,构建系统尝试将 devicetree 中的每个节点与绑定文件匹配。成功后,构建系统在验证节点的内容和为节点生成宏时使用绑定文件中的信息。

A simple example 一个简单的例子

Here is an example devicetree node:

下面是一个设备树节点的示例:

/* Node in a DTS file */
bar-device {
     compatible = "foo-company,bar-device";
     num-foos = <3>;
};

Here is a minimal binding file which matches the node:

下面是一个最小绑定文件,它与节点匹配:

# A YAML binding matching the node

compatible: "foo-company,bar-device"

properties:
  num-foos:
    type: int
    required: true

The build system matches the bar-device node to its YAML binding because the node’s compatible property matches the binding’s compatible: line.

构建系统将 bar-device 节点与其 YAML 绑定匹配,因为节点的兼容属性与绑定的兼容属性匹配

What the build system does with bindings构建系统使用绑定做什么

The build system uses bindings both to validate devicetree nodes and to convert the devicetree’s contents into the generated devicetree_unfixed.h header file.

构建系统使用绑定来验证设备参数节点,并将设备参数的内容转换为生成的 devicetree_unfixed.h 头文件

For example, the build system would use the above binding to check that the required num-foos property is present in the bar-device node, and that its value, <3>, has the correct type

例如,构建系统将使用上面的绑定来检查 bar-device 节点中是否存在所需的 num-foos 属性,以及它的值 < 3 > 是否具有正确的类型

The build system will then generate a macro for the bar-device node’s num-foos property, which will expand to the integer literal 3. This macro lets you get the value of the property in C code using the API which is discussed later in this guide in Devicetree access from C/C++.

然后,构建系统将为 bar-device 节点的 num-foos 属性生成一个宏,该属性将扩展为 3。这个宏允许您使用本指南后面在 Devicetree 访问 c/c++ 中讨论的 API 在 c 代码中获取属性值

For another example, the following node would cause a build error, because it has no num-foos property, and this property is marked required in the binding:

另一个例子,下面的节点会导致一个构建错误,因为它没有 num-foos 属性,并且这个属性在 binding 中被标记为必需的:

bad-node {
     compatible = "foo-company,bar-device";
};

Other ways nodes are matched to bindings其他节点与绑定匹配的方式

If a node has more than one string in its compatible property, the build system looks for compatible bindings in the listed order and uses the first match.

如果一个节点的兼容属性中包含多个字符串,则生成系统将按照列出的顺序查找兼容绑定,并使用第一个匹配项。

Take this node as an example:

以这个节点为例:

baz-device {
     compatible = "foo-company,baz-device", "generic-baz-device";
};

The baz-device node would get matched to a binding with a compatible: "generic-baz-device" line if the build system can’t find a binding with a compatible: "foo-company,baz-device" line.

如果构建系统无法找到与foo-company,baz-device兼容的绑定,baz-device 节点将被匹配到具有兼容的绑定: “generic-baz-device”行。

Nodes without compatible properties can be matched to bindings associated with their parent nodes. These are called “child bindings”. If a node describes hardware on a bus, like I2C or SPI, then the bus type is also taken into account when matching nodes to bindings. (The Bindings file syntax section below describes how to write child bindings and bus-specific bindings.)

不具有兼容属性的节点可以匹配与其父节点关联的绑定。这些被称为“子绑定”。如果一个节点描述总线上的硬件,比如 I2C 或 SPI,那么在将节点匹配到绑定时也会考虑总线类型。(下面的 Bindings 文件语法部分描述了如何编写子绑定和总线特定的绑定)

Some special nodes without compatible properties are matched to Inferred bindings. For these nodes, the build system generates macros based on the properties in the final devicetree.

一些没有兼容属性的特殊节点与推断绑定匹配。对于这些节点,构建系统根据最终设备树中的属性生成宏

Where bindings are located 绑定装置位于何处

Binding file names usually match their compatible: lines. For example, the above example binding would be named foo-company,bar-device.yaml by convention.

绑定文件名通常匹配它们的 compatible:行。例如,上面的示例绑定按约定命名为 foo-company,bar-device.yaml

The build system looks for bindings in dts/bindings subdirectories of the following places:

构建系统在以下位置的 dts/bindings 子目录中查找绑定:

The build system will consider any YAML file in any of these, including in any subdirectories, when matching nodes to bindings. A file is considered YAML if its name ends with .yaml or .yml.

在将节点与绑定进行匹配时,构建系统将考虑其中任何一个目录(包括任何子目录)中的任何 YAML 文件。如果一个文件的名称以.Yaml.yml结尾,则该文件被认为是YAML

Warning 警告

The binding files must be located somewhere inside the dts/bindings subdirectory of the above places.

绑定文件必须位于上述位置的 dts/bindings 子目录中的某个位置

For example, if my-app is your application directory, then you must place application-specific bindings inside my-app/dts/bindings. So my-app/dts/bindings/serial/my-company,my-serial-port.yaml would be found, but my-app/my-company,my-serial-port.yaml would be ignored.

例如,如果 my-app 是您的应用程序目录,那么您必须将特定于应用程序的绑定放在 my-app/dts/bindings 中。所以 my-app/dts/bindings/serial/my-company,my-serial-port.yaml 会被找到,但是 my-app/my-company,my-serial-port.yaml 会被忽视的。

Bindings file syntax 绑定文件语法

Zephyr bindings files are YAML files. The top-level value in the file is a mapping. A simple example is given above.

Zephyr 绑定文件是 YAML 文件。文件中的顶级值是一个映射。上面给出了一个简单的例子.

The top-level keys in the mapping look like this:

映射中的顶级键如下所示:

# A high level description of the device the binding applies to:
description: |
   This is the Vendomatic company's foo-device.

   Descriptions which span multiple lines (like this) are OK,
   and are encouraged for complex bindings.

   See https://yaml-multiline.info/ for formatting help.

# You can include definitions from other bindings using this syntax:
include: other.yaml

# Used to match nodes to this binding as discussed above:
# 用于将节点匹配到上面讨论的绑定
compatible: "manufacturer,foo-device"

properties:
  # Requirements for and descriptions of the properties that this
  # binding's nodes need to satisfy go here.

child-binding:
  # You can constrain the children of the nodes matching this binding
  # using this key.

# If the node describes bus hardware, like an SPI bus controller
# on an SoC, use 'bus:' to say which one, like this:
bus: spi

# If the node instead appears as a device on a bus, like an external
# SPI memory chip, use 'on-bus:' to say what type of bus, like this.
# Like 'compatible', this key also influences the way nodes match
# bindings.
on-bus: spi

foo-cells:
  # "Specifier" cell names for the 'foo' domain go here; example 'foo'
  # values are 'gpio', 'pwm', and 'dma'. See below for more information.

The following sections describe these keys in more detail:

以下各节将更详细地描述这些关键字:

The include: key usually appears early in the binding file, but it is documented last here because you need to know how the other keys work before understanding include:.

Include: key 通常在绑定文件的早期出现,但是它在这里是最后一个文档,因为您需要知道其他键是如何工作的,然后才能理解

Description 描述

A free-form description of node hardware goes here. You can put links to datasheets or example nodes or properties as well.

节点硬件的自由格式描述在这里。您还可以放置指向数据表或示例节点或属性的链接。

Compatible 兼容

This key is used to match nodes to this binding as described above. It should look like this in a binding file:

如上所述,此键用于将节点匹配到此绑定。在绑定文件中应该是这样的:

# Note the comma-separated vendor prefix and device name
# 注意以逗号分隔的厂商前缀和设备名称
compatible: "manufacturer,device"

This devicetree node would match the above binding:

这个设备树节点将匹配上面的绑定:

device {
     compatible = "manufacturer,device";
};

Assuming no binding has compatible: "manufacturer,device-v2", it would also match this node:

假设绑定没有: compatible: "manufacturer,device-v2",它也会匹配这个节点:

device-2 {
    compatible = "manufacturer,device-v2", "manufacturer,device";
};

Each node’s compatible property is tried in order. The first matching binding is used. The on-bus: key can be used to refine the search.

按顺序尝试每个节点的兼容属性。使用第一个匹配绑定。可以使用 on-bus: key 来优化搜索。

If more than one binding for a compatible is found, an error is raised.

如果找到一个兼容的多个绑定,则引发错误。

The manufacturer prefix identifies the device vendor. See dts/bindings/vendor-prefixes.txt for a list of accepted vendor prefixes. The device part is usually from the datasheet.

制造商前缀标识设备供应商。请参阅 dts/bindings/vendor-prefixes.txt 以获得可接受的 vendor 前缀列表。设备部分通常来自数据表。

Some bindings apply to a generic class of devices which do not have a specific vendor. In these cases, there is no vendor prefix. One example is the gpio-leds compatible which is commonly used to describe board LEDs connected to GPIOs.

有些绑定适用于没有特定供应商的通用设备类。在这些情况下,没有供应商前缀。一个例子是与 gpio 兼容的 gpio-leds,它通常用于描述连接到 gpio 上的主板 led。

If more than one binding for a compatible is found, an error is raised.

如果找到一个兼容的多个绑定,则引发错误。

Properties 属性

The properties: key describes the properties that nodes which match the binding can contain.

属性: key 描述匹配绑定的节点可以包含的属性

For example, a binding for a UART peripheral might look something like this:

例如,UART 外围设备的绑定可能是这样的:

compatible: "manufacturer,serial"

properties:
  reg:
    type: array
    description: UART peripheral MMIO register space
    required: true
  current-speed:
    type: int
    description: current baud rate
    required: true
  label:
    type: string
    description: human-readable name
    required: false

The properties in the following node would be validated by the above binding:

下面节点中的属性将通过上面的绑定进行验证:

my-serial@deadbeef {
     compatible = "manufacturer,serial";
     reg = <0xdeadbeef 0x1000>;
     current-speed = <115200>;
     label = "UART_0";
};

This is used to check that required properties appear, and to control the format of output generated for them.

这用于检查所需的属性是否出现,并控制为其生成的输出格式。

Except for some special properties, like reg, whose meaning is defined by the devicetree specification itself, only properties listed in the properties: key will have generated macros.

除了一些特殊属性,比如 reg,它的含义是由 devicetree 规范本身定义的,只有在 properties: 键中列出的属性才会生成宏

Example property definitions 属性定义示例

Here are some more examples.

这里有更多的例子。

properties:
    # Describes a property like 'current-speed = <115200>;'. We pretend that
    # it's obligatory for the example node and set 'required: true'.
    # 描述类似于'current-speed = <115200>;'的属性。
    # 我们假设它对于示例节点是必须的,并设置'required: true'。
    current-speed:
        type: int
        required: true
        description: Initial baud rate for bar-device

    # Describes an optional property like 'keys = "foo", "bar";'
    # 描述一个可选属性,比如keys = "foo", "bar";
    keys:
        type: string-array
        required: false
        description: Keys for bar-device

    # Describes an optional property like 'maximum-speed = "full-speed";'
    # the enum specifies known values that the string property may take
    maximum-speed:
        type: string
        required: false
        description: Configures USB controllers to work up to a specific speed.
        enum:
           - "low-speed"
           - "full-speed"
           - "high-speed"
           - "super-speed"

    # Describes an optional property like 'resolution = <16>;'
    # the enum specifies known values that the int property may take
    resolution:
      type: int
      required: false
      enum: # 设备树节点的属性值必须是下面的一个
       - 8
       - 16
       - 24
       - 32

    # Describes a required property '#address-cells = <1>';  the const
    # specifies that the value for the property is expected to be the value 1
    "#address-cells":
        type: int
        required: true
        const: 1

    int-with-default:
        type: int
        required: false
        default: 123
        description: Value for int register, default is power-up configuration.

    array-with-default:
        type: array
        required: false
        default: [1, 2, 3] # Same as 'array-with-default = <1 2 3>'

    string-with-default:
        type: string
        required: false
        default: "foo"

    string-array-with-default:
        type: string-array
        required: false
        default: ["foo", "bar"] # Same as 'string-array-with-default = "foo", "bar"'

    uint8-array-with-default:
        type: uint8-array
        required: false
        default: [0x12, 0x34] # Same as 'uint8-array-with-default = [12 34]'

Property entry syntax 属性条目语法

As shown by the above examples, each property entry in a binding looks like this:

如上面的例子所示,绑定中的每个属性条目如下所示:

<property name>:
  required: <true | false>
  type: <string | int | boolean | array | uint8-array | string-array |
         phandle | phandles | phandle-array | path | compound>
  deprecated: <true | false>
  default: <default>
  description: <description of the property>
  enum:
    - <item1>
    - <item2>
    ...
    - <itemN>
  const: <string | int>

Required properties 必需属性

If a node matches a binding but is missing any property which the binding defines with required: true, the build fails.

如果一个节点匹配一个绑定,但是缺少绑定用 required: true 定义的任何属性,则构建失败。

Property types 属性类型

The type of a property constrains its values. The following types are available. See Writing property values for more details about writing values of each type in a DTS file.

属性的类型约束其值。以下类型可用。有关在 DTS 文件中写入每种类型的值的详细信息,请参阅写入属性值。

Type类型 Description描述 Example in DTS
string exactly one string label = "UART_0";
int exactly one 32-bit value (cell)正好是一个32位值(单元格) current-speed = <115200>;现时速度 = < 115200 > ;
boolean flags that don’t take a value when true, and are absent if false hw-flow-control;流量控制;
array数组 zero or more 32-bit values (cells)零个或多个32位值(单元格) offsets = <0x100 0x200 0x300>;
uint8-array zero or more bytes, in hex (‘bytestring’ in the Devicetree specification)
零个或更多字节,以十六进制表示(Devicetree 规范中的“字节字符串”)
local-mac-address = [de ad be ef 12 34];
string-array字符串数组 zero or more strings dma-names = "tx", "rx";
phandle exactly one phandle interrupt-parent = <&gic>;
phandles zero or more phandles pinctrl-0 = <&usart2_tx_pd5 &usart2_rx_pd6>;
phandle-array a list of phandles and 32-bit cells (usually specifiers) dmas = <&dma0 2>, <&dma0 3>;
path a path to a node as a phandle path reference or path string
作为 phandle 路径引用或路径字符串指向节点的路径
zephyr,bt-c2h-uart = &uart0; or foo = "/path/to/some/node";
compound a catch-all for more complex types (no macros will be generated)
为更复杂的类型提供了一个捕获全部(不会生成宏)
foo = <&label>, [01 02];

Deprecated properties 不推荐的属性

A property with deprecated: true indicates to both the user and the tooling that the property is meant to be phased out.

带有 deprecated: true 的属性向用户和工具表明该属性将被淘汰。

The tooling will report a warning if the devicetree includes the property that is flagged as deprecated. (This warning is upgraded to an error in the Test Runner (Twister) for upstream pull requests.)

如果设备树包含标记为已弃用的属性,则工具将报告警告。(对于上游拉请求,此警告升级为测试运行器(Twister)中的错误。)

Default values for properties 属性的默认值

The optional default: setting gives a value that will be used if the property is missing from the devicetree node.

可选的 default: 设置提供了一个值,如果设备节点中缺少属性,该值将被使用

For example, with this binding fragment:

例如,使用这个绑定片段:

properties:
  foo:
    type: int
    default: 3

If property foo is missing in a matching node, then the output will be as if foo = <3>; had appeared in the DTS (except YAML data types are used for the default value).

如果在匹配的节点中缺少属性 foo,那么输出就像 foo = <3>; 出现在 DTS 中一样(除了使用 YAML 数据类型作为默认值)。

Note that it only makes sense to combine default: with required: false. Combining it with required: true will raise an error.

请注意,只有将 default: 与 required: false 结合起来才有意义。将它与 required: true 结合会产生错误。

There is a risk in using default: when the value in the binding may be incorrect for a particular board or hardware configuration. For example, defaulting the capacity of the connected power cell in a charging IC binding is likely to be incorrect. For such properties it’s better to make the property required: true, forcing the devicetree maintainer into an explicit and witting choice.

使用默认值是有风险的: 当绑定中的值对于特定板或硬件配置可能不正确时。例如,在充电集成电路连接中缺省连接电池的容量很可能是不正确的。对于这些属性,最好使用必需的属性: true,这样可以强制 devicetree 维护者进行明确和有意义的选择。

Driver developers should use their best judgment as to whether a value can be safely defaulted. Candidates for default values include:

驱动程序开发人员应该使用他们最好的判断值是否可以安全违约。默认值的候选项包括:

  • delays that would be different only under unusual conditions (such as intervening hardware)

    只有在不寻常的情况下(例如介入硬件)才会有不同的延迟

  • configuration for devices that have a standard initial configuration (such as a USB audio headset)

    配置为具有标准初始配置的设备(如 USB 音频耳机)

  • defaults which match the vendor-specified power-on reset value (as long as they are independent from other properties)

    默认值匹配供应商指定的电源重置值(只要它们独立于其他属性)

Power-on reset values may be used for defaults as long as they’re independent. If changing one property would require changing another to create a consistent configuration, then those properties should be made required.

开机重置值可用于默认值,只要它们是独立的。如果更改一个属性需要更改另一个属性来创建一致的配置,那么应该需要这些属性。

In any case where default: is used, the property documentation should explain why the value was selected and any conditions that would make it necessary to provide a different value. (This is mandatory for built-in bindings.)

在任何使用 default: 的情况下,属性文档都应该解释为什么选择该值,以及需要提供不同值的任何条件。(这对于内置绑定是强制性的。)

See Example property definitions for examples. Putting default: on any property type besides those used in the examples will raise an error.

有关示例,请参见示例属性定义。在除了示例中使用的属性类型之外的任何属性类型上设置 default: 都会引发错误。

Enum values

The enum: line is followed by a list of values the property may contain. If a property value in DTS is not in the enum: list in the binding, an error is raised. See Example property definitions for examples.

enum:行后面跟着属性可能包含的值列表。如果 DTS 中的属性值不在绑定中的enum: 列表中,则会引发错误。有关示例,请参见示例属性定义。

Const

This specifies a constant value the property must take. It is mainly useful for constraining the values of common properties for a particular piece of hardware.

这指定了属性必须采用的常量值。它主要用于约束特定硬件的公共属性值。

Child-binding 子绑定

child-binding can be used when a node has children that all share the same properties. Each child gets the contents of child-binding as its binding, though an explicit compatible = ... on the child node takes precedence, if a binding is found for it.

当一个节点具有共享相同属性的子节点时,可以使用子绑定。每个子节点都获取子绑定的内容作为其绑定,但如果为子节点找到绑定,则子节点上的显式 compatible = ... 优先

Consider a binding for a PWM LED node like this one, where the child nodes are required to have a pwms property:

考虑一个像这样的 PWM LED 节点的绑定,其中子节点需要具有 pwms 属性:

pwmleds {
        compatible = "pwm-leds";

        red_pwm_led {
                pwms = <&pwm3 4 15625000>;
        };
        green_pwm_led {
                pwms = <&pwm3 0 15625000>;
        };
        /* ... */
};

The binding would look like this:

绑定是这样的:

compatible: "pwm-leds"

child-binding:
  description: LED that uses PWM

  properties:
    pwms:
      type: phandle-array
      required: true

child-binding also works recursively. For example, this binding:

子绑定也是递归的。例如,这个绑定:

compatible: foo

child-binding:
  child-binding:
    properties:
      my-property:
        type: int
        required: true

will apply to the grandchild node in this DTS:

将应用于此 DTS 中的孙节点:

parent {
        compatible = "foo";
        child {
                grandchild {
                        my-property = <123>;
                };
        };
};

bus 总线

If the node is a bus controller, use bus: in the binding to say what type of bus. For example, a binding for a SPI peripheral on an SoC would look like this:

如果节点是一个总线控制器,使用 bus: 在绑定中说明总线的类型。例如,一个 SoC 上 SPI 外围设备的绑定看起来是这样的:

compatible: "manufacturer,spi-peripheral"
bus: spi
# ...

The presence of this key in the binding informs the build system that the children of any node matching this binding appear on this type of bus.

在绑定中出现的这个键通知构建系统,与该绑定匹配的任何节点的子节点都出现在这种类型的总线上。

This in turn influences the way on-bus: is used to match bindings for the child nodes.

这进而影响了使用on-bus:匹配子节点绑定的方式。

On-bus

If the node appears as a device on a bus, use on-bus: in the binding to say what type of bus.

如果节点作为设备出现在总线上,请使用 on-bus: 来说明总线的类型。

For example, a binding for an external SPI memory chip should include this line:

例如,一个外部 SPI 内存芯片的绑定应该包括这一行:

on-bus: spi

And a binding for an I2C based temperature sensor should include this line:

基于 I2C 的温度传感器的绑定应该包括这一行:

on-bus: i2c

When looking for a binding for a node, the build system checks if the binding for the parent node contains bus: <bus type>. If it does, then only bindings with a matching on-bus: <bus type> and bindings without an explicit on-bus are considered. Bindings with an explicit on-bus: <bus type> are searched for first, before bindings without an explicit on-bus. The search repeats for each item in the node’s compatible property, in order.

在查找节点的绑定时,构建系统会检查父节点的绑定是否包含bus: <bus type>。如果是,那么只考虑具有匹配 on-bus: <bus type>的绑定和没有显式on-bus 的绑定。带有显式 on-bus: <bus type>的绑定首先被搜索,然后是没有显式 on-bus 的绑定。对节点compatible属性中的每个项目重复搜索

This feature allows the same device to have different bindings depending on what bus it appears on. For example, consider a sensor device with compatible manufacturer,sensor which can be used via either I2C or SPI.

这个特性允许同一个设备有不同的绑定,这取决于它出现在哪个总线上。例如,考虑一个传感器设备与兼容的制造商,传感器可以通过 I2C 或 SPI 使用。

The sensor node may therefore appear in the devicetree as a child node of either an SPI or an I2C controller, like this:

因此,传感器节点可能作为 SPI 或 I2C 控制器的子节点出现在设备树中,如下所示:

spi-bus@0 {
   /* ... some compatible with 'bus: spi', etc. ... */

   sensor@0 {
       compatible = "manufacturer,sensor";
       reg = <0>;
       /* ... */
   };
};

i2c-bus@0 {
   /* ... some compatible with 'bus: i2c', etc. ... */

   sensor@79 {
       compatible = "manufacturer,sensor";
       reg = <79>;
       /* ... */
   };
};

You can write two separate binding files which match these individual sensor nodes, even though they have the same compatible:

你可以编写两个独立的绑定文件来匹配这些独立的传感器节点,即使它们具有相同的兼容性:

# manufacturer,sensor-spi.yaml, which matches sensor@0 on the SPI bus:
compatible: "manufacturer,sensor"
on-bus: spi

# manufacturer,sensor-i2c.yaml, which matches sensor@79 on the I2C bus:
compatible: "manufacturer,sensor"
properties:
  uses-clock-stretching:
    type: boolean
    required: false
on-bus: i2c

Only sensor@79 can have a use-clock-stretching property. The bus-sensitive logic ignores manufacturer,sensor-i2c.yaml when searching for a binding for sensor@0.

只有 sensor@79可以有 use-clock-stretching 属性。总线敏感逻辑在搜索 sensor@0的绑定时忽略 manufacturer,sensor-i2c.yaml

Specifier cell names (-cells) 说明符单元格名称(-单元格)

Specifier cells are usually used with phandle-array type properties briefly introduced above.

说明符单元格通常与上面简要介绍的 phandle-array 类型的属性一起使用。

To understand the purpose of *-cells, assume that some node has the following pwms property with type phandle-array:

为了理解*-cells的用途,假设某个节点具有以下类型为 phandle-array 的 pwms 属性:

my-device {
     pwms = <&pwm0 1 2>, <&pwm3 4>;
};

The tooling strips the final s from the property name of such properties, resulting in pwm. Then the value of the #pwm-cells property is looked up in each of the PWM controller nodes pwm0 and pwm3, like so:

工具会从属性名中删除最终的s,得到pwm。然后在每个 PWM 控制器节点 pwm0和 pwm3中查找 #pwm-cells属性的值,如下所示:

pwm0: pwm@0 {
     compatible = "foo,pwm";
     #pwm-cells = <2>;
};

pwm3: pwm@3 {
     compatible = "bar,pwm";
     #pwm-cells = <1>;
};

The &pwm0 1 2 part of the property value has two cells, 1 and 2, which matches #pwm-cells = <2>;, so these cells are considered the specifier associated with pwm0 in the phandle array.

属性值的 &pwm0 1 2部分有两个单元,1和2,匹配 #pwm-cells = <2>;, 因此这些单元被认为是与 phandle 阵列中的 pwm0相关联的指定单元。

Similarly, the cell 4 is the specifier associated with pwm3.

类似地,单元 4 是与 pwm3 关联的说明符

The number of PWM cells in the specifiers in pwms must match the #pwm-cells values, as shown above. If there is a mismatch, an error is raised. For example, this node would result in an error:

PWM 单元的数量在指定的 pwms 必须匹配 #pwm-cells的值,如上所示。如果存在不匹配,则引发错误。例如,这个节点会导致一个错误:

my-bad-device {
     /* wrong: 2 cells given in the specifier, but #pwm-cells is 1 in pwm3. */
     pwms = <&pwm3 5 6>;
};

The binding for each PWM controller must also have a *-cells key, in this case pwm-cells, giving names to the cells in each specifier:

每个 PWM 控制器的绑定还必须有一个 *-cells 键,在这种情况下是 pwm-cells,给每个指定器中的单元命名:

# foo,pwm.yaml
compatible: "foo,pwm"
...
pwm-cells:
  - channel
  - period

# bar,pwm.yaml
compatible: "bar,pwm"
...
pwm-cells:
  - period

A *-names (e.g. pwm-names) property can appear on the node as well, giving a name to each entry.

一个 *-names (例如 pwm-names)属性也可以出现在节点上,给每个条目一个名称。

This allows the cells in the specifiers to be accessed by name, e.g. using APIs like DT_PWMS_CHANNEL_BY_NAME.

这允许通过名称访问说明符中的单元格,例如使用像 DT_PWMS_CHANNEL_BY_NAME 这样的 api。

Because other property names are derived from the name of the property by removing the final s, the property name must end in s. An error is raised if it doesn’t.

因为其他属性名是通过删除最后的 s 从属性名派生的,所以属性名必须以 s 结尾。如果没有引发错误,则引发错误。

An alternative is using a specifier-space property to indicate the base property name for *-names and *-cells.

另一种方法是使用specifier-space属性来指示 *-names 和 *-cells 的基属性名称。

*-gpios properties are special-cased so that e.g. foo-gpios resolves to #gpio-cells rather than #foo-gpio-cells.

*-gpios 属性是特殊格式的,因此,例如 foo-gpios 解析为 #gpio-cells 而不是 #foo-gpio-cells

If the specifier is empty (e.g. #clock-cells = <0>), then *-cells can either be omitted (recommended) or set to an empty array. Note that an empty array is specified as e.g. clock-cells: [] in YAML.

如果说明符是空的(例如 #clock-cells = <0>) ,那么 *-cells 可以省略(建议)或设置为空数组。注意,在 YAML 中指定一个空数组,例如clock-cells: []

All phandle-array type properties support mapping through *-map properties, e.g. gpio-map, as defined by the Devicetree specification.

所有 phandle-array 类型的属性都支持通过 *-map 属性进行映射,如 Devicetree 规范定义的 gpio-map。

Include

Bindings can include other files, which can be used to share common property definitions between bindings. Use the include: key for this. Its value is either a string or a list.

绑定可以包括其他文件,这些文件可用于在绑定之间共享公共属性定义。使用 include: 。它的值要么是一个字符串,要么是一个列表。

In the simplest case, you can include another file by giving its name as a string, like this:

在最简单的情况下,您可以通过将其名称作为字符串来包含另一个文件,如下所示:

include: foo.yaml

If any file named foo.yaml is found (see Where bindings are located for the search process), it will be included into this binding.

如果找到任何名为 foo.yaml 的文件(请参阅搜索过程中的绑定位置) ,它将被包含到这个绑定中。

Included files are merged into bindings with a simple recursive dictionary merge. The build system will check that the resulting merged binding is well-formed.

使用简单的递归字典合并将包含的文件合并为绑定。构建系统将检查合并后的绑定是否格式良好。

It is an error if a key appears with a different value in a binding and in a file it includes, with one exception: a binding can have required: true for a property definition for which the included file has required: false. The required: true takes precedence, allowing bindings to strengthen requirements from included files.

如果一个键在绑定和它包含的文件中出现了不同的值,则会报错,但有一个例外:绑定中包含的文件为required: false时,绑定可以为required: truerequired: true优先,允许绑定加强所包含文件的需求。

Note that weakening requirements by having required: false where the included file has required: true is an error. This is meant to keep the organization clean.

注意,当包含的文件为required: true时,用required: false弱化需求是错误的。这是为了保持组织的干净。

The file base.yaml contains definitions for many common properties. When writing a new binding, it is a good idea to check if base.yaml already defines some of the needed properties, and include it if it does.

文件 base.yaml 包含许多常见属性的定义。在编写新的绑定时,最好检查 base.yaml 是否已经定义了一些所需的属性,如果已经定义了,就包含它。

Note that you can make a property defined in base.yaml obligatory like this, taking reg as an example:

请注意,您可以像下面这样使定义在 base.yaml 中的属性成为必需,以 reg 为例:

reg:
  required: true

This relies on the dictionary merge to fill in the other keys for reg, like type.

这依赖于字典合并来填充 reg 的其他键,比如 type

To include multiple files, you can use a list of strings:

要包含多个文件,可以使用一个字符串列表:

include:
  - foo.yaml
  - bar.yaml

This includes the files foo.yaml and bar.yaml. (You can write this list in a single line of YAML as include: [foo.yaml, bar.yaml].)

这包括 foo.yamlbar.yaml 文件。(您可以在 YAML 的单行代码中编写这个列表,include: [ foo.YAML,bar.YAML ] .)

When including multiple files, any overlapping required keys on properties in the included files are ORed together. This makes sure that a required: true is always respected.

当包含多个文件时,所包含文件中属性上的任何重叠的必需键都会被连接在一起。这确保了 required: true 总是得到尊重。

In some cases, you may want to include some property definitions from a file, but not all of them. In this case, include: should be a list, and you can filter out just the definitions you want by putting a mapping in the list, like this:

在某些情况下,您可能希望包含文件中的一些属性定义,但不是全部。在这种情况下,include: 应该是一个列表,你可以通过在列表中添加映射来过滤出你想要的定义,像这样:

include:
  - name: foo.yaml
    property-allowlist:
      - i-want-this-one
      - and-this-one
  - name: bar.yaml
    property-blocklist:
      - do-not-include-this-one
      - or-this-one

Each map element must have a name key which is the filename to include, and may have property-allowlist and property-blocklist keys that filter which properties are included.

每个映射元素必须有一个 name 键,这是要包括的文件名,并可能有属性允许列表和属性封锁列表键,过滤哪些属性包括在内。

You cannot have a single map element with both property-allowlist and property-blocklist keys. A map element with neither property-allowlist nor property-blocklist is valid; no additional filtering is done.

你不能同时拥有一个带有属性允许列表和属性封锁列表键的映射元素。一个既没有属性允许列表也没有属性阻止列表的映射元素是有效的; 没有进行任何额外的过滤。

You can freely intermix strings and mappings in a single include: list:

你可以在一个单独的 include: list 中自由地混合字符串和映射:

include:
  - foo.yaml
  - name: bar.yaml
    property-blocklist:
      - do-not-include-this-one
      - or-this-one

Finally, you can filter from a child binding like this:

最后,你可以像这样过滤子绑定:

include:
  - name: bar.yaml
    child-binding:
      property-allowlist:
        - child-prop-to-allow

Rules for mainline bindings 主线绑定规则

This section includes general rules for writing bindings that you want to submit to the mainline Zephyr Project. (You don’t need to follow these rules for bindings you don’t intend to contribute to the Zephyr Project, but it’s a good idea.)

本节包括编写希望提交给主线 Zephyr 项目的绑定的一般规则。(对于绑定,您不需要遵循这些规则,您不打算为 Zephyr 项目做出贡献,但这是一个好主意。)

Decisions made by the Zephyr devicetree maintainer override the contents of this section. If that happens, though, please let them know so they can update this page, or you can send a patch yourself.

由 Zephyr 设备维护人员作出的决定超越了本节的内容。如果发生这种情况,请让他们知道,这样他们就可以更新这个页面,或者你可以自己发送一个补丁。

Check for existing bindings 检查现有绑定

Zephyr aims for devicetree Source compatibility with other operating systems. Therefore, if there is an existing binding for your device in an authoritative location, you should try to replicate its properties when writing a Zephyr binding, and you must justify any Zephyr-specific divergences.

Zephyr 的目标是设备树的代码与其他操作系统兼容。因此,如果您的设备有一个权威位置的现有绑定,那么在编写 Zephyr 绑定时应该尝试复制它的属性,并且必须证明任何 Zephyr 特定的分歧。

In particular, this rule applies if:

特别是,这条规则适用于以下情况:

  • There is an existing binding in the mainline Linux kernel. See Documentation/devicetree/bindings in Linus’s tree for existing bindings and the Linux devicetree documentation for more information.

    在主线 Linux 内核中有一个现有的绑定。有关现有绑定和 Linux devicetree 文档,请参见 Linus 树中的 Documentation/devicetree/bindings,以获得更多信息。

  • Your hardware vendor provides an official binding outside of the Linux kernel.

    您的硬件供应商在 Linux 内核之外提供了正式的绑定。

General rules 一般规则

  • Bindings which match a compatible must have file names based on the compatible.

    匹配兼容的绑定必须具有基于兼容的文件

    • For example, a binding for compatible vnd,foo must be named vnd,foo.yaml.

      例如,compatible 值为 vnd,foo 的绑定必须名为 vnd,foo.yaml

    • If the binding is bus-specific, you can append the bus to the file name; for example, if the binding YAML has on-bus: bar, you may name the file vnd,foo-bar.yaml.

      如果绑定是特定于总线的,则可以将总线附加到文件名; 例如,如果 YAML 的绑定是 on-bus: bar,则可以将文件命名为 vnd,foo-bar.yaml

  • All recommendations in Default values for properties are requirements when submitting the binding.

    当提交绑定时,属性的默认值中的所有建议都是需求。

    In particular, if you use the default: feature, you must justify the value in the property’s description.

    特别是,如果使用 default:,则必须对属性描述中的值进行调整。

  • There are two ways to write property description: strings that are always OK.

    有两种方法来编写属性描述: 始终为 OK 的字符串。

    If your description is short, it’s fine to use this style:

    如果你的描述很简短,那么可以使用下面的风格:

    description: my short string
    

    If your description is long or spans multiple lines, you must use this style:

    如果你的描述很长或者跨越多行,你必须使用这个样式:

    description: |
      My very long string
      goes here.
      Look at all these lines!
    

    This | style prevents YAML parsers from removing the newlines in multi-line descriptions. This in turn makes these long strings display properly in the Bindings index.

    这种 | 样式可以防止 YAML 解析器删除多行描述中的换行。这反过来又使这些长字符串在 Bindings 索引中正确显示。

    Do not use any other style for long or multi-line strings.

    不要对长或多行字符串使用任何其他样式。

Vendor prefixes 供应商前缀

The following general rules apply to vendor prefixes in compatible properties.

下面的一般规则适用于兼容属性中的供应商前缀。

  • If your device is manufactured by a specific vendor, then its compatible should have a vendor prefix.

    如果您的设备是由特定的厂商生产的,那么它的兼容性应该有厂商前缀

    If your binding describes hardware with a well known vendor from the list in dts/bindings/vendor-prefixes.txt, you must use that vendor prefix.

    如果绑定描述了 dts/bindings/vendor-prefixes.txt 列表中的知名厂商的硬件,则必须使用该厂商前缀

  • If your device is not manufactured by a specific hardware vendor, do not invent a vendor prefix. Vendor prefixes are not mandatory parts of compatible properties, and compatibles should not include them unless they refer to an actual vendor. There are some exceptions to this rule, but the practice is strongly discouraged.

    如果您的设备不是由特定的硬件供应商制造的,则不要发明供应商前缀。供应商前缀不是兼容属性的强制部分,兼容不应该包括它们,除非它们指向实际的供应商。这个规则也有一些例外,但是这种做法是强烈反对的。

  • Do not submit additions to Zephyr’s dts/bindings/vendor-prefixes.txt file unless you also include users of the new prefix. This means at least a binding and a devicetree using the vendor prefix, and should ideally include a device driver handling that compatible.

    不要向 Zephyr 的 dts/bindings/vendor-prefixes.txt 文件提交附加内容,除非您还包括新前缀的用户。这意味着至少有一个使用供应商前缀的绑定和设备树,并且理想情况下应该包括一个处理兼容的设备驱动程序。

    For custom bindings, you can add a custom dts/bindings/vendor-prefixes.txt file to any directory in your DTS_ROOT. The devicetree tooling will respect these prefixes, and will not generate warnings or errors if you use them in your own bindings or devicetrees.

    对于自定义绑定,可以向 DTS_ROOT 中的任何目录添加自定义 DTS/bindings/vendor-prefixes.txt 文件。Devicetree 工具将尊重这些前缀,如果您在自己的绑定或设备树中使用它们,则不会生成警告或错误。

  • We sometimes synchronize Zephyr’s vendor-prefixes.txt file with the Linux kernel’s equivalent file; this process is exempt from the previous rule.

    我们有时将 Zephyr 的 vendor-prefixes.txt 文件与 Linux 内核的等效文件同步; 这个过程不受前面规则的约束。

  • If your binding is describing an abstract class of hardware with Zephyr specific drivers handling the nodes, it’s usually best to use zephyr as the vendor prefix. See Zephyr-specific binding (zephyr) for examples.

    如果您的绑定描述的是一个具有 Zephyr 特定驱动程序处理节点的抽象硬件类,那么通常最好使用 Zephyr 作为供应商前缀。见 Zephyr 特异性绑定( Zephyr )的例子。

Inferred bindings 推断绑定

Zephyr’s devicetree scripts can “infer” a binding for the special /zephyr,user node based on the values observed in its properties.

Zephyr 的设备树脚本可以根据在其属性中观察到的值“推断”特殊 /Zephyr,user 节点的绑定。

This node matches a binding which is dynamically created by the build system based on the values of its properties in the final devicetree. It does not have a compatible property.

该节点匹配由构建系统根据其在最终设备树中的属性值动态创建的绑定。它没有兼容的属性。

This node is meant for sample code and applications. The devicetree API provides it as a convenient container when only a few simple properties are needed, such as storing a hardware-dependent value, phandle(s), or GPIO pin.

此节点用于示例代码和应用程序。当只需要一些简单属性(如存储与硬件相关的值、 phandle (s)或 GPIO pin)时,设备树 API 提供了一个方便的容器。

For example, with this DTS fragment:

例如,使用这个 DTS 片段:

#include <zephyr/dt-bindings/gpio/gpio.h>

/ {
     zephyr,user {
             boolean;
             bytes = [81 82 83];
             number = <23>;
             numbers = <1>, <2>, <3>;
             string = "text";
             strings = "a", "b", "c";

             handle = <&gpio0>;
             handles = <&gpio0>, <&gpio1>;
             signal-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
     };
};

You can get the simple values like this:

你可以得到如下简单的值:

#define ZEPHYR_USER_NODE DT_PATH(zephyr_user)

DT_PROP(ZEPHYR_USER_NODE, boolean) // 1
DT_PROP(ZEPHYR_USER_NODE, bytes)   // {0x81, 0x82, 0x83}
DT_PROP(ZEPHYR_USER_NODE, number)  // 23
DT_PROP(ZEPHYR_USER_NODE, numbers) // {1, 2, 3}
DT_PROP(ZEPHYR_USER_NODE, string)  // "text"
DT_PROP(ZEPHYR_USER_NODE, strings) // {"a", "b", "c"}

You can convert the phandles in the handle and handles properties to device pointers like this:

你可以将句柄中的 phandles 转换为设备指针的属性,如下所示:

/*
 * Same thing as:
 *
 * ... my_dev = DEVICE_DT_GET(DT_NODELABEL(gpio0));
 */
const struct device *my_device =
     DEVICE_DT_GET(DT_PROP(ZEPHYR_USER_NODE, handle));

#define PHANDLE_TO_DEVICE(node_id, prop, idx) \
     DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)),

/*
 * Same thing as:
 *
 * ... *my_devices[] = {
 *         DEVICE_DT_GET(DT_NODELABEL(gpio0)),
 *         DEVICE_DT_GET(DT_NODELABEL(gpio1)),
 * };
 */
const struct device *my_devices[] = {
     DT_FOREACH_PROP_ELEM(ZEPHYR_USER_NODE, handles, PHANDLE_TO_DEVICE)
};

And you can convert the pin defined in signal-gpios to a struct gpio_dt_spec, then use it like this:

您可以将 signal-gpios 中定义的引脚转换为 struct gpio_dt_spec,然后像这样使用它:

#include <zephyr/drivers/gpio.h>

#define ZEPHYR_USER_NODE DT_PATH(zephyr_user)

const struct gpio_dt_spec signal =
        GPIO_DT_SPEC_GET(ZEPHYR_USER_NODE, signal_gpios);

/* Configure the pin */
gpio_pin_configure_dt(&signal, GPIO_OUTPUT_INACTIVE);

/* Set the pin to its active level */
gpio_pin_set_dt(&signal, 1);

(See gpio_dt_spec, GPIO_DT_SPEC_GET, and gpio_pin_configure_dt() for details on these APIs.)

(有关这些 api 的详细信息,请参阅 gpio_dt_specGPIO_DT_SPEC_GETgpio_pin_configure_dt())

Devicetree access from C/C++

从 c/c++ 访问设备(2)

This guide describes Zephyr’s <devicetree.h> API for reading the devicetree from C source files. It assumes you’re familiar with the concepts in Introduction to devicetree and Devicetree bindings. See Reference for reference material.

本指南介绍了 Zephyr 用于从 c 源文件读取设备树的 < devicetree.h > API。它假设您熟悉设备树和 Devicetree 绑定介绍中的概念。参考资料见参考资料。

A note for Linux developers 给 Linux 开发者的提示

Linux developers familiar with devicetree should be warned that the API described here differs significantly from how devicetree is used on Linux.

熟悉 devicetree 的 Linux 开发人员应该注意,这里描述的 API 与在 Linux 上使用 devicetree 的方式有很大不同。

Instead of generating a C header with all the devicetree data which is then abstracted behind a macro API, the Linux kernel would instead read the devicetree data structure in its binary form. The binary representation is parsed at runtime, for example to load and initialize device drivers.

Linux内核将以二进制形式读取设备树数据结构,而不是生成一个包含所有设备树数据的C头文件,然后将其抽象到一个宏API中。二进制表示在运行时进行解析,例如加载和初始化设备驱动程序。

Zephyr does not work this way because the size of the devicetree binary and associated handling code would be too large to fit comfortably on the relatively constrained devices Zephyr supports.

Zephyr 不能以这种方式工作,因为设备树二进制代码和相关处理代码的大小太大,无法适应 Zephyr 支持的相对受限的设备。

Node identifiers 节点标识符

To get information about a particular devicetree node, you need a node identifier for it. This is a just a C macro that refers to the node.

需要有一个节点标识符来获取关于特定设备树节点的信息。这只是一个引用节点的 C 宏。

These are the main ways to get a node identifier:

以下是获得节点标识符的主要方法:

  • By path 路径

    Use DT_PATH() along with the node’s full path in the devicetree, starting from the root node. This is mostly useful if you happen to know the exact node you’re looking for.

    从根节点开始,在设备树中使用 DT_PATH()和节点的完整路径。如果您恰好知道正在寻找的确切节点,这将非常有用。

  • By node label 按节点标签

    Use DT_NODELABEL() to get a node identifier from a node label. Node labels are often provided by SoC .dtsi to give nodes names that match the SoC datasheet, like i2c1, spi2, etc.

    使用DT_NODELABEL()从节点标签获取节点标识符。节点标签通常由SoC .dtsi提供,以提供与SoC数据表匹配的节点名称,如i2c1, spi2等。

  • By alias 化名

    Use DT_ALIAS() to get a node identifier for a property of the special /aliases node. This is sometimes done by applications (like blinky, which uses the led0 alias) that need to refer to some device of a particular type (“the board’s user LED”) but don’t care which one is used.

    使用 DT_ALIAS()获取特定的 /aliases 节点的属性的节点标识符。有些应用程序(比如 blinky,它使用 led0 别名)需要引用某种特定类型的设备(“主板的用户 LED”) ,但是不在乎使用哪种设备。

  • By instance number 按实例编号

    This is done primarily by device drivers, as instance numbers are a way to refer to individual nodes based on a matching compatible. Get these with DT_INST(), but be careful doing so. See below.

    这主要由设备驱动程序完成,因为实例号是基于匹配兼容性引用单个节点的一种方式。使用 DT_INST()获得这些,但是这样做要小心。见下文。

  • By chosen node 按选择的节点

    Use DT_CHOSEN() to get a node identifier for /chosen node properties.

    使用 DT_CHOSEN()获取/chosen的节点属性的节点标识符。

  • By parent/child 按父母/子女划分

    Use DT_PARENT() and DT_CHILD() to get a node identifier for a parent or child node, starting from a node identifier you already have.

    使用 DT_PARENT()和 DT_CHILD()获取父节点或子节点的节点标识符,从已有的节点标识符开始。

Two node identifiers which refer to the same node are identical and can be used interchangeably.

引用同一节点的两个节点标识符是相同的,可以互换使用。

Here’s a DTS fragment for some imaginary hardware we’ll return to throughout this file for examples:

下面是我们将在整个文件中返回的一些虚拟硬件的 DTS 片段,例如:

/dts-v1/;

/ {

	aliases {
		sensor-controller = &i2c1;
	};

	soc {
		i2c1: i2c@40002000 {
			compatible = "vnd,soc-i2c";
			label = "I2C_1";
			reg = <0x40002000 0x1000>;
			status = "okay";
			clock-frequency = < 100000 >;
		};
	};
};

Here are a few ways to get node identifiers for the i2c@40002000 node:

以下是获取 i2c@40002000 节点的节点标识符的几种方法:

  • DT_PATH(soc, i2c_40002000)

  • DT_NODELABEL(i2c1)

  • DT_ALIAS(sensor_controller)

  • DT_INST(x, vnd_soc_i2c) for some unknown number x. See the DT_INST() documentation for details.

    DT_INST(x,vnd_soc_i2c)表示某个未知数 x. 详细信息请参阅 DT_INST()文档。

Important 很重要

Non-alphanumeric characters like dash (-) and the at sign (@) in devicetree names are converted to underscores (_). The names in a DTS are also converted to lowercase.

非字母数字字符,如 dash (-)和 devicetree 名称中的 at 符号 (@)转换为下划线(_)。DTS 中的名称也将转换为小写。(2)

Node identifiers are not values 节点标识符不是值

There is no way to store one in a variable. You cannot write:

没有办法将其存储在变量中。您不能写:

/* These will give you compiler errors: */

void *i2c_0 = DT_INST(0, vnd_soc_i2c);
unsigned int i2c_1 = DT_INST(1, vnd_soc_i2c);
long my_i2c = DT_NODELABEL(i2c1);

If you want something short to save typing, use C macros:

如果你想要一些简短的东西来保存输入,使用 c 宏:

/* Use something like this instead: */

#define MY_I2C DT_NODELABEL(i2c1)

#define INST(i) DT_INST(i, vnd_soc_i2c)
#define I2C_0 INST(0)
#define I2C_1 INST(1)

Property access

The right API to use to read property values depends on the node and property.

用于读取属性值的正确 API 取决于节点和属性。

Checking properties and values 检查属性和值

You can use DT_NODE_HAS_PROP() to check if a node has a property. For the example devicetree above:

可以使用 DT_NODE_HAS_PROP()检查节点是否具有属性:

DT_NODE_HAS_PROP(DT_NODELABEL(i2c1), clock_frequency)  /* expands to 1 */
DT_NODE_HAS_PROP(DT_NODELABEL(i2c1), not_a_property)   /* expands to 0 */

Simple properties 简单的属性

Use DT_PROP(node_id, property) to read basic integer, boolean, string, numeric array, and string array properties.

使用 DT_PROP(node_id,property)读取基本的整数、布尔值、字符串、数值数组和字符串数组属性。

For example, to read the clock-frequency property’s value in the above example:

例如,要读取上面示例中的时钟频率属性值:

DT_PROP(DT_PATH(soc, i2c_40002000), clock_frequency)  /* This is 100000, */
DT_PROP(DT_NODELABEL(i2c1), clock_frequency)          /* and so is this, */
DT_PROP(DT_ALIAS(sensor_controller), clock_frequency) /* and this. */

Important 很重要

The DTS property clock-frequency is spelled clock_frequency in C. That is, properties also need special characters converted to underscores. Their names are also forced to lowercase.

DTS 属性时钟频率在 c 中拼写为时钟频率。也就是说,属性还需要将特殊字符转换为下划线。他们的名字也被强制小写。

Properties with string and boolean types work the exact same way. The DT_PROP() macro expands to a string literal in the case of strings, and the number 0 or 1 in the case of booleans. For example:

具有字符串和布尔类型的属性的工作方式完全相同。DT_PROP()宏在字符串的情况下扩展为字符串文字,在布尔值的情况下扩展为数字0或1。例如:

#define I2C1 DT_NODELABEL(i2c1)

DT_PROP(I2C1, status)  /* expands to the string literal "okay" */

Note

Don’t use DT_NODE_HAS_PROP() for boolean properties. Use DT_PROP() instead as shown above. It will expand to either 0 or 1 depending on if the property is present or absent.

不要使用 DT_NODE_HAS_PROP()表示布尔属性。如上所示,改用 DT_PROP()。它将扩大到0或1,这取决于财产是否存在或缺席。

Properties with type array, uint8-array, and string-array work similarly, except DT_PROP() expands to an array initializer in these cases. Here is an example devicetree fragment:

类型 array、 uint8-array 和 string-array 的属性的工作方式类似,只不过在这些情况下 DT_PROP()扩展为数组初始值设定项。下面是一个设备树片段的例子:(2)

foo: foo@1234 {
        a = <1000 2000 3000>; /* array */
        b = [aa bb cc dd];    /* uint8-array */
        c = "bar", "baz";     /* string-array */
};

Its properties can be accessed like this:

它的属性可以这样访问:

#define FOO DT_NODELABEL(foo)

int a[] = DT_PROP(FOO, a);           /* {1000, 2000, 3000} */
unsigned char b[] = DT_PROP(FOO, b); /* {0xaa, 0xbb, 0xcc, 0xdd} */
char* c[] = DT_PROP(FOO, c);         /* {"foo", "bar"} */

You can use DT_PROP_LEN() to get logical array lengths in number of elements.

您可以使用 DT_PROP_LEN()以元素个数获取逻辑数组长度。

size_t a_len = DT_PROP_LEN(FOO, a); /* 3 */
size_t b_len = DT_PROP_LEN(FOO, b); /* 4 */
size_t c_len = DT_PROP_LEN(FOO, c); /* 2 */

DT_PROP_LEN() cannot be used with the special reg or interrupts properties. These have alternative macros which are described next.

DT_PROP_LEN()不能与特殊的 reg 或中断属性一起使用。

reg properties 寄存器属性

See Important properties for an introduction to reg.

有关 reg 的介绍,请参见重要属性。

Given a node identifier node_id, DT_NUM_REGS(node_id) is the total number of register blocks in the node’s reg property.

给定一个节点标识符node_id,DT_NUM_REGS(node_id)是该节点的 reg 属性中寄存器块的总数。

You cannot read register block addresses and lengths with DT_PROP(node, reg). Instead, if a node only has one register block, use DT_REG_ADDR() or DT_REG_SIZE():

使用 DT_PROP(node,reg)无法读取寄存器块地址和长度。相反,如果一个节点只有一个寄存器块,使用 DT_REG_ADDR()或 DT_REG_SIZE() :

  • DT_REG_ADDR(node_id): the given node’s register block address

    DT_REG_ADDR (node_id) : 给定节点的寄存器块地址

  • DT_REG_SIZE(node_id): its size

    DT_REG_SIZE (node_id) : 其大小

Use DT_REG_ADDR_BY_IDX() or DT_REG_SIZE_BY_IDX() instead if the node has multiple register blocks:

如果节点有多个寄存器块,则使用 DT_REG_ADDR_BY_IDX()或 DT_REG_SIZE_BY_IDX() :

  • DT_REG_ADDR_BY_IDX(node_id, idx): address of register block at index idx

    DT_REG_ADDR_BY_IDX (node_id,idx) : 索引 idx 处寄存器块的地址

  • DT_REG_SIZE_BY_IDX(node_id, idx): size of block at index idx

    DT_reg_size_by_idx (node_id,idx) : 索引 idx 处块的大小

The idx argument to these must be an integer literal or a macro that expands to one without requiring any arithmetic. In particular, idx cannot be a variable. This won’t work:

它们的 idx 参数必须是一个整数字面值或一个不需要任何算术就能展开为一的宏。特别是,idx 不能是一个变量。这是行不通的:

/* This will cause a compiler error. */

for (size_t i = 0; i < DT_NUM_REGS(node_id); i++) {
        size_t addr = DT_REG_ADDR_BY_IDX(node_id, i);
}

interrupts properties 中断属性

See Important properties for a brief introduction to interrupts.

有关中断的简要介绍,请参阅重要属性。

Given a node identifier node_id, DT_NUM_IRQS(node_id) is the total number of interrupt specifiers in the node’s interrupts property.

给定一个节点标识符node_id,DT_NUM_IRQS (node_id)是节点的中断属性中中断指定符的总数。

The most general purpose API macro for accessing these is DT_IRQ_BY_IDX():

访问它们的最通用的 API 宏是 DT_IRQ_BY_IDX() :

DT_IRQ_BY_IDX(node_id, idx, val)

Here, idx is the logical index into the interrupts array, i.e. it is the index of an individual interrupt specifier in the property. The val argument is the name of a cell within the interrupt specifier. To use this macro, check the bindings file for the node you are interested in to find the val names.

在这里,idx 是中断数组的逻辑索引,也就是说,它是属性中单个中断指定符的索引。Val 参数是中断指定符中单元格的名称。要使用这个宏,请检查您感兴趣的节点的 bindings 文件以查找 val 名称。

Most Zephyr devicetree bindings have a cell named irq, which is the interrupt number. You can use DT_IRQN() as a convenient way to get a processed view of this value.

大多数 Zephyr 设备绑定都有一个名为 irq 的单元,这是中断号。您可以使用 DT_irqn()作为获得此值的处理视图的方便方法。

Warning 警告

Here, “processed” reflects Zephyr’s devicetree Scripts and tools, which change the irq number in zephyr.dts to handle hardware constraints on some SoCs and in accordance with Zephyr’s multilevel interrupt numbering.

在这里,“处理”反映了 Zephyr 的设备脚本和工具,它改变 Zephyr.dts 中的 irq 编号,以处理某些 soc 上的硬件约束,并与 Zephyr 的多级中断编号相一致。

This is currently not very well documented, and you’ll need to read the scripts’ source code and existing drivers for more details if you are writing a device driver.

目前还没有很好的文档说明,如果要编写设备驱动程序,需要阅读脚本的源代码和现有驱动程序以获得更多细节。

phandle properties

Property values can refer to other nodes using the &another-node phandle syntax introduced in Writing property values. Properties which contain phandles have type phandle, phandles, or phandle-array in their bindings. We’ll call these “phandle properties” for short.

属性值可以使用在编写属性值中引入的 &another-node 句柄语法引用其他节点。包含句柄的属性在其绑定中具有类型句柄、句柄或句柄数组。我们简称这些“句柄属性”。

You can convert a phandle to a node identifier using DT_PHANDLE(), DT_PHANDLE_BY_IDX(), or DT_PHANDLE_BY_NAME(), depending on the type of property you are working with.

您可以使用 DT_PHANDLE()、 DT_PHANDLE_BY_IDX()或 DT_PHANDLE_BY_NAME()将一个句柄转换为节点标识符,具体取决于您使用的属性类型。

One common use case for phandle properties is referring to other hardware in the tree. In this case, you usually want to convert the devicetree-level phandle to a Zephyr driver-level struct device. See Get a struct device from a devicetree node for ways to do that.

Phandle 属性的一个常见用例是引用树中的其他硬件。在这种情况下,您通常希望将设备树级别的 phandle 转换为 Zephyr 驱动程序级别的 struct 设备。请参阅从设备/节点获取一个结构设备,以了解实现该目的的方法。

Another common use case is accessing specifier values in a phandle array. The general purpose APIs for this are DT_PHA_BY_IDX() and DT_PHA(). There are also hardware-specific shorthands like DT_GPIO_CTLR_BY_IDX(), DT_GPIO_CTLR(), DT_GPIO_LABEL_BY_IDX(), DT_GPIO_LABEL(), DT_GPIO_PIN_BY_IDX(), DT_GPIO_PIN(), DT_GPIO_FLAGS_BY_IDX(), and DT_GPIO_FLAGS().

另一个常见的用例是访问句柄数组中的说明符值。用于此目的的通用 API 是 DT_PHA_BY_IDX()和 DT_PHA()。还有一些特定于硬件的简写,如 DT_GPIO_CTLR_BY_IDX()、 DT_GPIO_CTLR()、 DT_GPIO_LABEL_BY_IDX()、 DT_GPIO_LABEL()、 DT_GPIO_PIN_BY_IDX()、 DT_GPIO_PIN()、 DT_GPIO_FLAGS_BY_IDX()和 DT_GPIO_FLAGS()。

See DT_PHA_HAS_CELL_AT_IDX() and DT_PROP_HAS_IDX() for ways to check if a specifier value is present in a phandle property.

参见 DT_pha_has_cell_at_idx()和 DT_prop_has_idx()检查 phandle 属性中是否存在指定符值。

Other APIs

Here are pointers to some other available APIs.

下面是一些其他可用 api 的指针。

  • DT_CHOSEN(), DT_HAS_CHOSEN(): for properties of the special /chosen node

    DT_chosen() ,DT_has_chosen() : 用于特殊/选择节点的属性

  • DT_HAS_COMPAT_STATUS_OKAY(), DT_NODE_HAS_COMPAT(): global- and node-specific tests related to the compatible property

    DT_has_compat_status_okay() ,DT_node_has_compat() : 与兼容属性相关的全局和节点特定测试

  • DT_BUS(): get a node’s bus controller, if there is one

    DT_bus() : 获取一个节点的总线控制器,如果有的话

  • DT_ENUM_IDX(): for properties whose values are among a fixed list of choices

    DT_enum_idx() : 用于其值在固定选项列表中的属性

  • Fixed flash partitions: APIs for managing fixed flash partitions. Also see Flash map, which wraps this in a more user-friendly API.

    固定闪存分区: 管理固定闪存分区的 api。还可以参见 Flash map,它包含了一个更加用户友好的 API。

Device driver conveniences 方便设备驱动程序

Special purpose macros are available for writing device drivers, which usually rely on instance identifiers.

特殊用途的宏可用于编写设备驱动程序,这些驱动程序通常依赖于实例标识符。

To use these, you must define DT_DRV_COMPAT to the compat value your driver implements support for. This compat value is what you would pass to DT_INST().

要使用它们,您必须将 DT_drv_compat 定义为您的驱动程序实现支持的 compat 值。这个 compat 值就是传递给 DT_inst()的值。

If you do that, you can access the properties of individual instances of your compatible with less typing, like this:

如果你这样做,你可以访问你的兼容的单个实例的属性,更少的输入,像这样:

#include <zephyr/devicetree.h>

#define DT_DRV_COMPAT my_driver_compat

/* This is same thing as DT_INST(0, my_driver_compat): */
DT_DRV_INST(0)

/*
 * This is the same thing as
 * DT_PROP(DT_INST(0, my_driver_compat), clock_frequency)
 */
DT_INST_PROP(0, clock_frequency)

See Instance-based APIs for a generic API reference.

有关通用 API 引用,请参见基于实例的 API。

Hardware specific APIs 特定于硬件的 API

Convenience macros built on top of the above APIs are also defined to help readability for hardware specific code. See Hardware specific APIs for details.

还定义了构建在上述 api 之上的方便宏,以帮助硬件特定代码的可读性。有关详细信息,请参阅硬件特定 api。

Generated macros

While the devicetree.h API is not generated, it does rely on a generated C header which is put into every application build directory: devicetree_unfixed.h. This file contains macros with devicetree data.

虽然不会生成 devicetree.h API,但是它依赖于生成的 c 头文件,该头文件被放到每个应用程序构建目录中: devicetree_unfixed.h。这个文件包含带有设备树数据的宏。

These macros have tricky naming conventions which the Devicetree API API abstracts away. They should be considered an implementation detail, but it’s useful to understand them since they will frequently be seen in compiler error messages.

这些宏有一些被 devicetreeapi 抽象出来的复杂的命名约定。它们应该被视为实现细节,但是理解它们是有用的,因为它们经常出现在编译器错误消息中。

This section contains an Augmented Backus-Naur Form grammar for these generated macros, with examples and more details in comments. See RFC 7405 (which extends RFC 5234) for a syntax specification.

本节包含了这些生成宏的 Augmented Backus-Naur Form 语法,并在注释中提供了示例和更多细节。有关语法规范,请参见 RFC 7405(它扩展了 RFC 5234)。

Devicetree HOWTOs

This page has step-by-step advice for getting things done with devicetree.

本页面提供了使用 devicetree 完成工作的分步建议。

See Troubleshooting devicetree for troubleshooting advice.

有关故障排除建议,请参阅故障排除设备。

Get your devicetree and generated header

获取您的设备树并生成 header

A board’s devicetree (BOARD.dts) pulls in common node definitions via #include preprocessor directives. This at least includes the SoC’s .dtsi. One way to figure out the devicetree’s contents is by opening these files, e.g. by looking in dts/<ARCH>/<vendor>/<soc>.dtsi, but this can be time consuming.

主板的 devicetree (BOARD.dts)通过 #include 预处理器指令提取公共节点定义。这至少包括 SoC 的 .dtsi.计算设备承载体内容的一种方法是打开这些文件,例如查找 dts/<ARCH>/<vendor>/<soc>.dtsi 。但这可能很费时间。

If you just want to see the “final” devicetree for your board, build an application and open the zephyr.dts file in the build directory.

如果您只想看到您的主板的“final”devicetree,那么构建一个应用程序并在构建目录中打开 zephyr.dts 文件。

Tip 小贴士

You can build Hello World to see the “base” devicetree for your board without any additional changes from overlay files.

您可以构建 HelloWorld 来查看电路板的“基本”设备树,而不需要对图层文件进行任何其他更改

For example, using the ARM Cortex-M3 Emulation (QEMU) board to build Hello World:

例如,使用 ARM Cortex-M3仿真(QEMU)板构建 Hello World:

# --cmake-only here just forces CMake to run, skipping the
# build process to save time.
west build -b qemu_cortex_m3 -s samples/hello_world --cmake-only

You can change qemu_cortex_m3 to match your board.

您可以更改 qemu_cortex_m3以匹配您的电路板。

CMake prints the input and output file locations like this:

打印输入和输出文件位置,如下:

-- Found BOARD.dts: .../zephyr/boards/arm/qemu_cortex_m3/qemu_cortex_m3.dts
-- Generated zephyr.dts: .../zephyr/build/zephyr/zephyr.dts
-- Generated devicetree_unfixed.h: .../zephyr/build/zephyr/include/generated/devicetree_unfixed.h

The zephyr.dts file is the final devicetree in DTS format.

zephyr.dts 文件是 DTS 格式的最终设备树。

The devicetree_unfixed.h file is the corresponding generated header.

devicetree_unfixed.h 文件是相应生成的头文件。

See Input and output files for details about these files.

有关这些文件的详细信息,请参阅输入和输出文件。

Get a struct device from a devicetree node

从设备树节点获取 struct device

When writing Zephyr applications, you’ll often want to get a driver-level struct device corresponding to a devicetree node.

在编写 Zephyr 应用程序时,您通常希望获得与设备树节点对应的驱动程序级struct device

For example, with this devicetree fragment, you might want the struct device for serial@40002000:

例如,对于这个 devicetree 片段,您可能需要用于 Series@40002000的 struct device:

/ {
        soc {
                serial0: serial@40002000 {
                        status = "okay";
                        current-speed = <115200>;
                        /* ... */
                };
        };

        aliases {
                my-serial = &serial0;
        };

        chosen {
                zephyr,console = &serial0;
        };
};

Start by making a node identifier for the device you are interested in. There are different ways to do this; pick whichever one works best for your requirements. Here are some examples:

首先为您感兴趣的设备制作一个节点标识符。有不同的方法可以做到这一点; 选择最适合您的需求的方法。下面是一些例子:

/* Option 1: by node label */
#define MY_SERIAL DT_NODELABEL(serial0)

/* Option 2: by alias */
#define MY_SERIAL DT_ALIAS(my_serial)

/* Option 3: by chosen node */
#define MY_SERIAL DT_CHOSEN(zephyr_console)

/* Option 4: by path */
#define MY_SERIAL DT_PATH(soc, serial_40002000)

Once you have a node identifier there are two ways to proceed. One way to get a device is to use DEVICE_DT_GET():

有了节点标识符之后,有两种方法可以继续。获得设备的一种方法是使用 DEVICE_DT_GET() :

const struct device *uart_dev = DEVICE_DT_GET(MY_SERIAL);

if (!device_is_ready(uart_dev)) {
        /* Not ready, do not use */
        return -ENODEV;
}

There are variants of DEVICE_DT_GET() such as DEVICE_DT_GET_OR_NULL(), DEVICE_DT_GET_ONE() or DEVICE_DT_GET_ANY(). This idiom fetches the device pointer at build-time, which means there is no runtime penalty. This method is useful if you want to store the device pointer as configuration data. But because the device may not be initialized, or may have failed to initialize, you must verify that the device is ready to be used before passing it to any API functions. (This check is done for you by device_get_binding().)

DEVICE_DT_GET()有多种变体,例如 DEVICE_DT_GET_OR_NULL()、 DEVICE_DT_GET_ONE()或 DEVICE_DT_GET_ANY()。这个习惯用法在构建时获取设备指针,这意味着没有运行时损失。如果要将设备指针存储为配置数据,则此方法非常有用。但是,由于设备可能没有被初始化,或者可能未能初始化,因此在将设备传递给任何 API 函数之前,必须验证设备是否已准备好可以使用。(这个检查是通过 device_get_binding()完成的。)

In some situations the device cannot be known at build-time, e.g., if it depends on user input like in a shell application. In this case you can get the struct device by combining DT_LABEL() with device_get_binding():

在某些情况下,在构建时无法知道设备,例如,如果它依赖于用户输入,比如在 shell 应用程序中。在这种情况下,您可以通过将 DT_LABEL()和 device_get_binding()组合起来获得 struct 设备:

const struct device *uart_dev = device_get_binding(DT_LABEL(MY_SERIAL));

You can then use uart_dev with UART API functions like uart_configure(). Similar code will work for other device types; just make sure you use the correct API for the device.

然后可以将 uart_dev 与 UART API 函数(如 uart_configure())一起使用。类似的代码也适用于其他类型的设备; 只要确保为设备使用了正确的 API 即可。

There’s no need to override the label property to something else: just make a node identifier and pass it to DT_LABEL to get the right string to pass to device_get_binding().

不需要将 label 属性覆盖到其他属性: 只需创建一个节点标识符并将其传递给 DT_LABEL,就可以获得传递给 device_get_binding()的正确字符串。

If you’re having trouble, see Troubleshooting devicetree. The first thing to check is that the node has status = "okay", like this:

如果遇到麻烦,请参见设备树故障排除。首先要检查的是节点的 status = “ okay”,如下所示:

#define MY_SERIAL DT_NODELABEL(my_serial)

#if DT_NODE_HAS_STATUS(MY_SERIAL, okay)
const struct device *uart_dev = DEVICE_DT_GET(MY_SERIAL);
#else
#error "Node is disabled"
#endif

If you see the #error output, make sure to enable the node in your devicetree. In some situations your code will compile but it will fail to link with a message similar to:

如果看到 #error 输出,请确保启用设备树中的节点。在某些情况下,你的代码可以编译,但是不能链接,得到一个类似以下的消息:

...undefined reference to `__device_dts_ord_N'
collect2: error: ld returned 1 exit status

This likely means there’s a Kconfig issue preventing the device driver from being built, resulting in a reference that does not exist. If your code compiles successfully, the last thing to check is if the device is ready, like this:

这可能意味着存在 Kconfig 问题,阻止了设备驱动程序的构建,导致引用不存在。如果你的代码编译成功,最后要检查的是设备是否准备好了,像这样:

if (!device_is_ready(uart_dev)) {
     printk("Device not ready\n");
}

If you find that the device is not ready, it likely means that the device’s initialization function failed. Enabling logging or debugging driver code may help in such situations. Note that you can also use device_get_binding() to obtain a reference at runtime. If it returns NULL it can either mean that device’s driver failed to initialize or that it does not exist.

如果您发现设备没有就绪,这可能意味着设备的初始化功能失败。在这种情况下,启用日志记录或调试驱动程序代码可能有所帮助。注意,您还可以使用 device_get_binding() 在运行时获得引用。如果返回 NULL,则可能意味着设备驱动程序初始化失败,或者设备驱动程序不存在。

Find a devicetree binding 找到设备树绑定

Devicetree bindings are YAML files which declare what you can do with the nodes they describe, so it’s critical to be able to find them for the nodes you are using.

Devicetree 绑定是 YAML 文件,它们声明您可以如何使用它们所描述的节点,因此能够找到它们对于您正在使用的节点是至关重要的。

If you don’t have them already, Get your devicetree and generated header. To find a node’s binding, open the generated header file, which starts with a list of nodes in a block comment:

如果你还没有设备,那么获取你的设备代码并生成头文件。要查找一个节点的绑定,打开生成的头文件,它以一个块注释中的节点列表开始:

/*
 * [...]
 * Nodes in dependency order (ordinal and path):
 *   0   /
 *   1   /aliases
 *   2   /chosen
 *   3   /flash@0
 *   4   /memory@20000000
 *          (etc.)
 * [...]
 */

Make note of the path to the node you want to find, like /flash@0. Search for the node’s output in the file, which starts with something like this if the node has a matching binding:

记下要查找的节点的路径,如/flash@0。在文件中搜索该节点的输出,如果该节点具有匹配的绑定,则以下面的内容开始:

/*
 * Devicetree node:
 *   /flash@0
 *
 * Binding (compatible = soc-nv-flash):
 *   $ZEPHYR_BASE/dts/bindings/mtd/soc-nv-flash.yaml
 * [...]
 */

See Check for missing bindings for troubleshooting.

有关故障诊断,请参见检查丢失的绑定。

Set devicetree overlays 设置工具树图层

Devicetree overlays are explained in Introduction to devicetree. The CMake variable DTC_OVERLAY_FILE contains a space- or semicolon-separated list of overlay files to use. If DTC_OVERLAY_FILE specifies multiple files, they are included in that order by the C preprocessor.

工具树覆盖图在工具简介中有所解释。CMake 变量 DTC_OVERLAY_FILE 包含要使用的重叠文件的空格或分号分隔列表。如果 DTC_OVERLAY_FILE 指定了多个文件,那么 c 预处理器将按照这个顺序包含它们。

You can set DTC_OVERLAY_FILE to contain exactly the files you want to use. Here is an example using using west build.

可以将 DTC_OVERLAY_FILE 设置为恰好包含要使用的文件。下面是一个使用 west build 的示例。

If you don’t set DTC_OVERLAY_FILE, the build system will follow these steps, looking for files in your application source directory to use as devicetree overlays:

如果您没有设置 DTC_OVERLAY_FILE,构建系统将遵循以下步骤,在您的应用程序源目录中查找用作设备树覆盖的文件:

  1. If the file boards/<BOARD>.overlay exists, it will be used.

    如果 boards/<BOARD>.overlay 存在,将使用它。

  2. If the current board has multiple revisions and boards/<BOARD>_<revision>.overlay exists, it will be used. This file will be used in addition to boards/<BOARD>.overlay if both exist.

    如果当前板块有多个修订版,以及boards/<BOARD>_<revision>.overlay存在,它将被使用。如果boards/<BOARD>.overlay同时存在,这个文件将被用来补充

  3. If one or more files have been found in the previous steps, the build system stops looking and just uses those files.

    如果在前面的步骤中找到一个或多个文件,则生成系统将停止查找,只使用这些文件。

  4. Otherwise, if <BOARD>.overlay exists, it will be used, and the build system will stop looking for more files.

    否则,如果 <BOARD>.overlay 存在,它将被使用,构建系统将停止查找更多文件。

  5. Otherwise, if app.overlay exists, it will be used.

    否则,如果 app.overlay 存在,将使用它。

Using Shields will also add devicetree overlay files.

使用 Shields 还将添加设备树覆盖文件。

The DTC_OVERLAY_FILE value is stored in the CMake cache and used in successive builds.

DTC_OVERLAY_FILE 值存储在 CMake 缓存中,并在连续的构建中使用。

The build system prints all the devicetree overlays it finds in the configuration phase, like this:

构建系统会打印出在配置阶段找到的所有设备树覆盖图,如下所示:

-- Found devicetree overlay: .../some/file.overlay

Use devicetree overlays 使用工具树覆盖

See Set devicetree overlays for how to add an overlay to the build.

请参阅设置设备树覆盖如何添加覆盖到构建

Overlays can override node property values in multiple ways. For example, if your BOARD.dts contains this node:

覆盖可以以多种方式覆盖节点属性值。例如,如果您的 BOARD.dts 包含以下节点:

/ {
        soc {
                serial0: serial@40002000 {
                        status = "okay";
                        current-speed = <115200>;
                        /* ... */
                };
        };
};

These are equivalent ways to override the current-speed value in an overlay:

这些都是在覆盖层中重写电流速度值的等效方法:

/* Option 1 */
&serial0 {
     current-speed = <9600>;
};

/* Option 2 */
&{/soc/serial@40002000} {
     current-speed = <9600>;
};

We’ll use the &serial0 style for the rest of these examples.

我们将在其余的示例中使用 &seral0 样式。

You can add aliases to your devicetree using overlays: an alias is just a property of the /aliases node. For example:

您可以使用覆盖层将别名添加到设备树中: 别名只是 /aliases 节点的属性。例如:

/ {
     aliases {
             my-serial = &serial0;
     };
};

Chosen nodes work the same way. For example:

选择的节点以相同的方式工作。例如:

/ {
     chosen {
             zephyr,console = &serial0;
     };
};

To delete a property (in addition to deleting properties in general, this is how to set a boolean property to false if it’s true in BOARD.dts):

要删除一个属性(除了通常删除属性之外,如果在 BOARD.dts 中布尔属性为真,这里还提供了如何将其设置为 false 的方法) :

&serial0 {
     /delete-property/ some-unwanted-property;
};

You can add subnodes using overlays. For example, to configure a SPI or I2C child device on an existing bus node, do something like this:

您可以使用覆盖来添加子节点。例如,要在现有的总线节点上配置 SPI 或 I2C 子设备,可以这样做:

/* SPI device example */
&spi1 {
     my_spi_device: temp-sensor@0 {
             compatible = "...";
             label = "TEMP_SENSOR_0";
             /* reg is the chip select number, if needed;
              * reg 是芯片选择编号,如果需要的话。
              * If present, it must match the node's unit address. */
             reg = <0>;

             /* Configure other SPI device properties as needed.
              * Find your device's DT binding for details. */
             spi-max-frequency = <4000000>;
     };
};

/* I2C device example */
&i2c2 {
     my_i2c_device: touchscreen@76 {
             compatible = "...";
             label = "TOUCHSCREEN";
             /* reg is the I2C device address.
              * It must match the node's unit address. */
             reg = <76>;

             /* Configure other I2C device properties as needed.
              * Find your device's DT binding for details. */
     };
};

Other bus devices can be configured similarly:

其他总线设备也可以进行类似的配置:

  • create the device as a subnode of the parent bus

    创建设备作为父总线的子节点

  • set its properties according to its binding

    根据它的结合来设置它的属性

Assuming you have a suitable device driver associated with the my_spi_device and my_i2c_device compatibles, you should now be able to enable the driver via Kconfig and get the struct device for your newly added bus node, then use it with that driver API.

假设你有一个合适的设备驱动程序与 my_spi_devicemy_i2c_device 的 compatible 相关,你现在应该能够通过 Kconfig 启用驱动程序,并获得你新添加的总线节点的结构设备,然后使用它与驱动 API。

Write device drivers using devicetree APIs

使用设备树 API 编写设备驱动程序

“Devicetree-aware” device drivers should create a struct device for each status = "okay" devicetree node with a particular compatible (or related set of compatibles) supported by the driver.

“Devicetree-aware”设备驱动程序应该为每个status = "okay"的devicetree节点创建一个struct device,该节点具有驱动程序支持的特定compatible(或相关的compatibles集)。

Writing a devicetree-aware driver begins by defining a devicetree binding for the devices supported by the driver. Use existing bindings from similar drivers as a starting point. A skeletal binding to get started needs nothing more than this:

编写Devicetree-aware驱动,首先要为驱动所支持的设备定义一个devicetree binding 。使用类似驱动程序的现有绑定作为一个起点。一个骨架式的绑定开始时只需要这样做即可:

description: <Human-readable description of your binding>
compatible: "foo-company,bar-device"
include: base.yaml

See Find a devicetree binding for more advice on locating existing bindings.

有关定位现有绑定的更多建议,请参见查找设备树绑定

After writing your binding, your driver C file can then use the devicetree API to find status = "okay" nodes with the desired compatible, and instantiate a struct device for each one. There are two options for instantiating each struct device: using instance numbers, and using node labels.

编写绑定之后,驱动程序 c 文件可以使用 devicetree API 查找所需兼容的 status = “ okay”节点,并为每个节点实例化一个 struct 设备。实例化每个结构设备有两个选项: 使用实例编号和使用节点标签。

In either case: 不管是哪种情况:

  • Each struct device‘s name should be set to its devicetree node’s label property. This allows the driver’s users to Get a struct device from a devicetree node in the usual way.

    每个struct device的名称都应该设置为其设备节点的 label 属性。这允许驱动程序的用户以通常的方式从设备树节点获取一个结构设备。

  • Each device’s initial configuration should use values from devicetree properties whenever practical. This allows users to configure the driver using devicetree overlays.

    只要可行,每个设备的初始配置都应该使用来自设备树属性的值。这允许用户使用设备树覆盖来配置驱动程序。

Examples for how to do this follow. They assume you’ve already implemented the device-specific configuration and data structures and API functions, like this:

下面是如何做到这一点的示例。它们假设您已经实现了特定于设备的配置、数据结构和 API 函数,如下所示:

/* my_driver.c */
#include <zephyr/drivers/some_api.h>

/* Define data (RAM) and configuration (ROM) structures: */
struct my_dev_data {
     /* per-device values to store in RAM */
};
struct my_dev_cfg {
     uint32_t freq; /* Just an example: initial clock frequency in Hz */
     /* other configuration to store in ROM */
};

/* Implement driver API functions (drivers/some_api.h callbacks): */
static int my_driver_api_func1(const struct device *dev, uint32_t *foo) { /* ... */ }
static int my_driver_api_func2(const struct device *dev, uint64_t bar) { /* ... */ }
static struct some_api my_api_funcs = {
     .func1 = my_driver_api_func1,
     .func2 = my_driver_api_func2,
};

Option 1: create devices using instance numbers

选项1: 使用实例号创建设备

Use this option, which uses Instance-based APIs, if possible. However, they only work when devicetree nodes for your driver’s compatible are all equivalent, and you do not need to be able to distinguish between them.

如果可能,请使用此选项,该选项使用基于实例的 API。但是,它们只有在驱动程序兼容的设备树节点都是等价的时候才能工作,而且您不需要能够区分它们。

To use instance-based APIs, begin by defining DT_DRV_COMPAT to the lowercase-and-underscores version of the compatible that the device driver supports. For example, if your driver’s compatible is "vnd,my-device" in devicetree, you would define DT_DRV_COMPAT to vnd_my_device in your driver C file:

要使用基于实例的 API,首先将 DT_DRV_COMPAT 定义为设备驱动程序支持的兼容版本的小写和下划线版本。例如,如果您的驱动程序兼容的是 devicetree 中的“ vnd,my-device”,那么您可以在驱动程序 C 文件中将 DT_DRV_COMPAT 定义为 vnd_my_device:

/*
 * Put this near the top of the file. After the includes is a good place.
 * (Note that you can therefore run "git grep DT_DRV_COMPAT drivers" in
 * the zephyr Git repository to look for example drivers using this style).
 * 把它放在文件的顶部附近。在includes之后是个好地方。
 * (注意,因此你可以在zephyr Git仓库中运行 "git grep DT_DRV_COMPAT drivers"
 *  来寻找使用这种风格的驱动实例)
 */
#define DT_DRV_COMPAT vnd_my_device

Important 重要事项

As shown, the DT_DRV_COMPAT macro should have neither quotes nor special characters. Remove quotes and convert special characters to underscores when creating DT_DRV_COMPAT from the compatible property.

如图所示,DT_DRV_COMPAT 宏既不应该有引号也不应该有特殊字符。在从兼容属性创建 DT_DRV_COMPAT 时,删除引号并将特殊字符转换为下划线。

Finally, define an instantiation macro, which creates each struct device using instance numbers. Do this after defining my_api_funcs.

最后,定义一个实例化宏,它使用实例数创建每个结构设备。

/*
 * This instantiation macro is named "CREATE_MY_DEVICE".
 * Its "inst" argument is an arbitrary instance number.
 *
 * Put this near the end of the file, e.g. after defining "my_api_funcs".
 */
#define CREATE_MY_DEVICE(inst)                                       \
     static struct my_dev_data my_data_##inst = {                    \
             /* initialize RAM values as needed, e.g.: */            \
             .freq = DT_INST_PROP(inst, clock_frequency),            \
     };                                                              \
     static const struct my_dev_cfg my_cfg_##inst = {                \
             /* initialize ROM values as needed. */                  \
     };                                                              \
     DEVICE_DT_INST_DEFINE(inst,                                     \
                           my_dev_init_function,                     \
                           NULL,                                     \
                           &my_data_##inst,                          \
                           &my_cfg_##inst,                           \
                           MY_DEV_INIT_LEVEL, MY_DEV_INIT_PRIORITY,  \
                           &my_api_funcs);

Notice the use of APIs like DT_INST_PROP() and DEVICE_DT_INST_DEFINE() to access devicetree node data. These APIs retrieve data from the devicetree for instance number inst of the node with compatible determined by DT_DRV_COMPAT.

注意使用像 DT_INST_PROP()和 DEVICE_DT_INST_DEFINE()这样的 api 来访问设备树节点数据。这些 api 从设备树中检索节点实例数的数据,这些数据由 DT_DRV_COMPAT 确定。

Finally, pass the instantiation macro to DT_INST_FOREACH_STATUS_OKAY():

最后,将实例化宏传递给 DT_INST_FOREACH_STATUS_OKAY() :

/* Call the device creation macro for each instance: */
DT_INST_FOREACH_STATUS_OKAY(CREATE_MY_DEVICE)

DT_INST_FOREACH_STATUS_OKAY expands to code which calls CREATE_MY_DEVICE once for each enabled node with the compatible determined by DT_DRV_COMPAT. It does not append a semicolon to the end of the expansion of CREATE_MY_DEVICE, so the macro’s expansion must end in a semicolon or function definition to support multiple devices.

DT_INST_FOREACH_STATUS_OKAY扩展为代码,为每个启用的节点调用CREATE_MY_DEVICE一次,其兼容由DT_DRV_COMPAT决定。它不会在CREATE_MY_DEVICE的扩展末尾加上分号,所以宏的扩展必须以分号或函数定义结束,以支持多个设备。

Option 2: create devices using node labels

选项2: 使用节点标签创建设备

Some device drivers cannot use instance numbers. One example is an SoC peripheral driver which relies on vendor HAL APIs specialized for individual IP blocks to implement Zephyr driver callbacks. Cases like this should use DT_NODELABEL() to refer to individual nodes in the devicetree representing the supported peripherals on the SoC. The devicetree.h Generic APIs can then be used to access node data.

一些设备驱动程序不能使用实例号。一个例子是SoC外围驱动器,它依赖于供应商的HAL API,专门用于单个IP块来实现Zephyr驱动器的回调。像这样的情况应该使用DT_NODELABEL()来引用devicetree中代表SoC上支持的外设的单个节点。然后可以使用devicetree.h Generic APIs来访问节点数据。

For this to work, your SoC’s dtsi file must define node labels like mydevice0, mydevice1, etc. appropriately for the IP blocks your driver supports. The resulting devicetree usually looks something like this:

为了使其发挥作用,你的SoC的dtsi文件必须为你的驱动程序支持的IP块适当地定义节点标签,如mydevice0mydevice1,等等。由此产生的devicetree通常看起来像这样。

/ {
        soc {
                mydevice0: dev@0 {
                        compatible = "vnd,my-device";
                };
                mydevice1: dev@1 {
                        compatible = "vnd,my-device";
                };
        };
};

The driver can use the mydevice0 and mydevice1 node labels in the devicetree to operate on specific device nodes:

驱动程序可以使用设备树中的 mydevice0mydevice1 节点标签来操作特定的设备节点:

/*
 * This is a convenience macro for creating a node identifier for
 * the relevant devices. An example use is MYDEV(0) to refer to
 * the node with label "mydevice0".
 */
#define MYDEV(idx) DT_NODELABEL(mydevice ## idx)

/*
 * Define your instantiation macro; "idx" is a number like 0 for mydevice0
 * or 1 for mydevice1. It uses MYDEV() to create the node label from the
 * index.
 */
#define CREATE_MY_DEVICE(idx)                                        \
     static struct my_dev_data my_data_##idx = {                     \
             /* initialize RAM values as needed, e.g.: */            \
             .freq = DT_PROP(MYDEV(idx), clock_frequency),           \
     };                                                              \
     static const struct my_dev_cfg my_cfg_##idx = { /* ... */ };    \
     DEVICE_DT_DEFINE(MYDEV(idx),                                    \
                     my_dev_init_function,                           \
                     NULL,                                           \
                     &my_data_##idx,                                 \
                     &my_cfg_##idx,                                  \
                     MY_DEV_INIT_LEVEL, MY_DEV_INIT_PRIORITY,        \
                     &my_api_funcs)

Notice the use of APIs like DT_PROP() and DEVICE_DT_DEFINE() to access devicetree node data.

注意使用DT_PROP()DEVICE_DT_DEFINE()等API来访问devicetree节点数据。

Finally, manually detect each enabled devicetree node and use CREATE_MY_DEVICE to instantiate each struct device:

最后,手动检测每个启用的设备树节点,并使用 CREATE_MY_DEVICE 实例化每个结构设备:

#if DT_NODE_HAS_STATUS(DT_NODELABEL(mydevice0), okay)
CREATE_MY_DEVICE(0)
#endif

#if DT_NODE_HAS_STATUS(DT_NODELABEL(mydevice1), okay)
CREATE_MY_DEVICE(1)
#endif

Since this style does not use DT_INST_FOREACH_STATUS_OKAY(), the driver author is responsible for calling CREATE_MY_DEVICE() for every possible node, e.g. using knowledge about the peripherals available on supported SoCs.

由于这种风格不使用DT_INST_FOREACH_STATUS_OKAY(),驱动作者负责为每个可能的节点调用CREATE_MY_DEVICE(),例如,使用关于支持的SoC上可用的外设知识。

Device drivers that depend on other devices

依赖于其他设备的设备驱动程序

At times, one struct device depends on another struct device and requires a pointer to it. For example, a sensor device might need a pointer to its SPI bus controller device. Some advice:

有时,一个结构设备依赖于另一个结构设备,并需要一个指向它的指针。例如,传感器设备可能需要指向其 SPI 总线控制器设备的指针。一些建议:

  • Write your devicetree binding in a way that permits use of Hardware specific APIs from devicetree.h if possible.

    如果可能的话,以允许使用 devicetree.h 中的硬件特定 API 的方式编写设备树绑定。

  • In particular, for bus devices, your driver’s binding should include a file like dts/bindings/spi/spi-device.yaml which provides common definitions for devices addressable via a specific bus. This enables use of APIs like DT_BUS() to obtain a node identifier for the bus node. You can then Get a struct device from a devicetree node for the bus in the usual way.

    特别是,对于总线设备,你的驱动程序的绑定应该包括一个像dts/bindings/spi/spi-device.yaml这样的文件,它为可通过特定总线寻址的设备提供通用定义。这样就可以使用像DT_BUS()这样的API来获取总线节点的节点标识符。然后你可以用通常的方法为总线Get a struct device from a devicetree node

Search existing bindings and device drivers for examples.

搜索现有绑定和设备驱动程序的示例。

Applications that depend on board-specific devices

依赖于板上特定设备的应用程序

One way to allow application code to run unmodified on multiple boards is by supporting a devicetree alias to specify the hardware specific portions, as is done in the Blinky. The application can then be configured in BOARD.dts files or via devicetree overlays.

允许应用程序代码在多个板子上不加修改地运行的一种方法是支持devicetree别名来指定硬件的特定部分,正如在Blinky中所做的那样。然后可以在BOARD.dts文件中或通过devicetree overlays对应用程序进行配置。

Troubleshooting devicetree 设备故障排除

Here are some tips for fixing misbehaving devicetree related code.

这里有一些修复错误行为的设备/树相关代码的技巧。

See Devicetree HOWTOs for other “HOWTO” style information.

参见 Devicetree HOWTOs 的其他“ HOWTO”风格的信息。

Try again with a pristine build directory

再次尝试使用原始构建目录

Important 很重要

Try this first, before doing anything else.

在做其他事情之前,先试试这个。

See Pristine Builds for examples, or just delete the build directory completely and retry.

有关示例,请参见“纯粹构建”,或者直接删除构建目录,然后重试。

This is general advice which is especially applicable to debugging devicetree issues, because the outputs are created during the CMake configuration phase, and are not always regenerated when one of their inputs changes.

这是一般性的建议,尤其适用于调试设备树代码问题,因为输出是在 CMake 配置阶段创建的,并不总是在输入发生变化时重新生成。

Make sure devicetree.h is included

确保包含 devicetree.h

Unlike Kconfig symbols, the devicetree.h header must be included explicitly.

与 Kconfig 符号不同,devicetree.h 头文件必须显式包含。

Many Zephyr header files rely on information from devicetree, so including some other API may transitively include devicetree.h, but that’s not guaranteed.

许多 Zephyr 头文件依赖于来自设备树的信息,因此包含其他 API 可能会传递性地包含 devicetree.h,但这并不能保证。

Make sure you’re using the right names

确保你用对了名字

Remember that: 记住:

  • In C/C++, devicetree names must be lowercased and special characters must be converted to underscores. Zephyr’s generated devicetree header has DTS names converted in this way into the C tokens used by the preprocessor-based <devicetree.h> API.

    在 c/c++ 中,设备树名称必须小写,特殊字符必须转换为下划线。Zephyr 生成的 devicetree header 将 DTS 名称以这种方式转换为基于预处理器的 < devicetree.h > API 使用的 c 标记。

  • In overlays, use devicetree node and property names the same way they would appear in any DTS file. Zephyr overlays are just DTS fragments.

    在覆盖中,使用 devicetree 节点和属性名称的方式与它们在任何 DTS 文件中出现的方式相同。Zephyr的覆盖只是 DTS 的片段。

For example, if you’re trying to get the clock-frequency property of a node with path /soc/i2c@12340000 in a C/C++ file:

例如,如果您试图在 c/c++ 文件中获得具有 path/soc/i2c@12340000的节点的时钟频率属性:

/*
 * foo.c: lowercase-and-underscores names
 */

/* Don't do this: */
#define MY_CLOCK_FREQ DT_PROP(DT_PATH(soc, i2c@1234000), clock-frequency)
/*                                            ^               ^
 *                                        @ should be _     - should be _  */

/* Do this instead: */
#define MY_CLOCK_FREQ DT_PROP(DT_PATH(soc, i2c_1234000), clock_frequency)
/*                                           ^               ^           */

And if you’re trying to set that property in a devicetree overlay:

如果你想在设备树覆盖中设置属性:

/*
 * foo.overlay: DTS names with special characters, etc.
 */

/* Don't do this; you'll get devicetree errors. */
&{/soc/i2c_12340000/} {
     clock_frequency = <115200>;
};

/* Do this instead. Overlays are just DTS fragments. */
&{/soc/i2c@12340000/} {
     clock-frequency = <115200>;
};

Look at the preprocessor output 查看预处理器输出

To save preprocessor output when using GCC-based toolchains, add -save-temps=obj to the EXTRA_CFLAGS CMake variable. For example, to build Hello World with west with this option set, use:

在使用基于GCC的工具链时,要保存预处理器的输出,可在EXTRA_CFLAGSCMake变量中添加save-temps=obj。例如,在设置此选项的情况下,用west构建Hello World,使用

west build -b BOARD samples/hello_world -- -DEXTRA_CFLAGS=-save-temps=obj

This will create a preprocessor output file named foo.c.i in the build directory for each source file foo.c.

这将在构建目录中为每个源文件 foo.c 创建一个名为 foo.c.i 的预处理器输出文件。

You can then search for the file in the build directory to see what your devicetree macros expanded to. For example, on macOS and Linux, using find to find main.c.i:

然后,您可以在 build 目录中搜索该文件,以查看您的 devicetree 宏扩展为什么。例如,在 macOS 和 Linux 上,使用 find 来查找 main.c.c.i:

$ find build -name main.c.i
build/CMakeFiles/app.dir/src/main.c.i

It’s usually easiest to run a style formatter on the results before opening them. For example, to use clang-format to reformat the file in place:

在打开结果之前运行样式格式化程序通常是最简单的。例如,使用 clang-format 重新格式化文件:

clang-format -i build/CMakeFiles/app.dir/src/main.c.i

You can then open the file in your favorite editor to view the final C results after preprocessing.

然后,您可以在您喜欢的编辑器中打开该文件,以便在预处理后查看最终的 C 结果。

Validate properties 验证属性

If you’re getting a compile error reading a node property, check your node identifier and property. For example, if you get a build error on a line that looks like this:

如果读取节点属性时出现编译错误,请检查节点标识符和属性。例如,如果你在一行上看到一个构建错误,如下所示:

int baud_rate = DT_PROP(DT_NODELABEL(my_serial), current_speed);

Try checking the node by adding this to the file and recompiling:

尝试通过将其添加到文件并重新编译来检查节点:

#if !DT_NODE_EXISTS(DT_NODELABEL(my_serial))
#error "whoops"
#endif

If you see the “whoops” error message when you rebuild, the node identifier isn’t referring to a valid node. Get your devicetree and generated header and debug from there.

如果在重新生成时看到“whoops”错误消息,则节点标识符不指向有效的节点。从中获取设备树和生成的头文件并进行调试。

Some hints for what to check next if you don’t see the “whoops” error message:

如果没有看到“哎呀”错误消息,下面是一些关于下一步检查的提示:

Check for missing bindings 检查缺少的绑定

See Devicetree bindings for information about bindings, and Bindings index for information on bindings built into Zephyr.

有关绑定的信息,请参见 Devicetree 绑定,有关内置到 Zephyr 中的绑定的信息,请参见 Bindings 索引。

If the build fails to Find a devicetree binding for a node, then either the node’s compatible property is not defined, or its value has no matching binding. If the property is set, check for typos in its name. In a devicetree source file, compatible should look like "vnd,some-device"Make sure you’re using the right names.

如果构建时未能为一个节点找到设备树绑定,那么要么该节点的compatible属性没有定义,要么其值没有匹配的绑定。如果该属性被设置,请检查其名称中是否有错别字。在devicetree源文件中,compatible应该看起来像"vnd,some-device" - 确保你使用正确的名字

If your binding file is not under zephyr/dts, you may need to set DTS_ROOT; see Where bindings are located.

如果绑定文件不在 zephyr/DTS 下,则可能需要设置 DTS_ROOT; 请参见绑定位于何处。

Errors with DT_INST_() APIs DT_INST_() API 的错误

If you’re using an API like DT_INST_PROP(), you must define DT_DRV_COMPAT to the lowercase-and-underscores version of the compatible you are interested in. See Option 1: create devices using instance numbers.

如果使用类似于 DT_INST_PROP()的 API,则必须将 DT_DRV_COMPAT 定义为您感兴趣的兼容的小写和下划线版本。请参阅选项1: 使用实例号创建设备。

Devicetree versus Kconfig

Along with devicetree, Zephyr also uses the Kconfig language to configure the source code. Whether to use devicetree or Kconfig for a particular purpose can sometimes be confusing. This section should help you decide which one to use.

除了 devicetree 之外,Zephyr 还使用 Kconfig 语言来配置源代码。是否使用设备树或 Kconfig 用于特定用途有时会令人困惑。这一部分将帮助您决定使用哪一个。

In short: 简而言之:

  • Use devicetree to describe hardware and its boot-time configuration. Examples include peripherals on a board, boot-time clock frequencies, interrupt lines, etc.

    使用 devicetree 来描述硬件及其启动时配置。例如,板上的外围设备、启动时钟频率、中断线等。

  • Use Kconfig to configure software support to build into the final image. Examples include whether to add networking support, which drivers are needed by the application, etc.

    使用 Kconfig 配置软件支持以构建到最终的映像中。示例包括是否添加网络支持,应用程序需要哪些驱动程序等。

In other words, devicetree mainly deals with hardware, and Kconfig with software.

换句话说,设备树主要处理硬件,Kconfig 主要处理软件。

For example, consider a board containing a SoC with 2 UART, or serial port, instances.

例如,考虑一块包含有2个 UART 或串行端口的 SoC 的板。

  • The fact that the board has this UART hardware is described with two UART nodes in the devicetree. These provide the UART type (via the compatible property) and certain settings such as the address range of the hardware peripheral registers in memory (via the reg property).

    在设备树中用两个 UART 节点描述了板上有这个 UART 硬件的事实。它们提供 UART 类型(通过兼容属性)和某些设置,例如内存中硬件外设寄存器的地址范围(通过 reg 属性)。

  • Additionally, the UART boot-time configuration is also described with devicetree. This could include configuration such as the RX IRQ line’s priority and the UART baud rate. These may be modifiable at runtime, but their boot-time configuration is described in devicetree.

    此外,UART 引导时配置也使用设备树进行描述。这可能包括配置,如 RX IRQ 线的优先级和 UART 波特率。这些在运行时可以修改,但是它们的引导时配置在设备树中描述。

  • Whether or not to include software support for UART in the build is controlled via Kconfig. Applications which do not need to use the UARTs can remove the driver source code from the build using Kconfig, even though the board’s devicetree still includes UART nodes.

    是否在构建版本中包含对 UART 的软件支持是通过 Kconfig 控制的。不需要使用 UART 的应用程序可以使用 Kconfig 从构建中删除驱动程序源代码,即主板的设备树仍然包含 UART 节点。

As another example, consider a device with a 2.4GHz, multi-protocol radio supporting both the Bluetooth Low Energy and 802.15.4 wireless technologies.

另一个例子是一个2.4 GHz 的设备,多协议无线电支持低耗电蓝牙和802.15.4无线技术。

  • Devicetree should be used to describe the presence of the radio hardware, what driver or drivers it’s compatible with, etc.

    设备树应该被用来描述无线电硬件的存在,它与什么驱动程序或驱动程序兼容,等等。

  • Boot-time configuration for the radio, such as TX power in dBm, should also be specified using devicetree.

    还应该使用设备树指定无线电的启动时配置,例如 dBm 中的 TX 电源。

  • Kconfig should determine which software features should be built for the radio, such as selecting a BLE or 802.15.4 protocol stack.

    Kconfig 应该确定应该为无线电构建哪些软件特性,例如选择 BLE 或802.15.4协议栈。

As another example, Kconfig options that formerly enabled a particular instance of a driver (that is itself enabled by Kconfig) have been removed. The devices are selected individually using devicetree’s status keyword on the corresponding hardware instance.

再比如,以前启用某个驱动的特定实例的Kconfig选项(该实例本身是由Kconfig启用的)已经被删除。在相应的硬件实例上使用devicetree的status关键字单独选择设备。

There are exceptions to these rules:

这些规则也有例外:

  • Because Kconfig is unable to flexibly control some instance-specific driver configuration parameters, such as the size of an internal buffer, these options may be defined in devicetree. However, to make clear that they are specific to Zephyr drivers and not hardware description or configuration these properties should be prefixed with zephyr,, e.g. zephyr,random-mac-address in the common Ethernet devicetree properties.

    由于 Kconfig 无法灵活地控制某些特定于实例的驱动程序配置参数,如内部缓冲区的大小,因此可以在设备树中定义这些选项。然而,为了表明这些属性是针对 Zephyr 驱动程序的,而不是针对硬件描述或配置,这些属性应该以 zephyr, 为前缀,例如,在通用以太网设备树属性中,属性名为zephyr,random-mac-address

  • Devicetree’s chosen keyword, which allows the user to select a specific instance of a hardware device to be used for a particular purpose. An example of this is selecting a particular UART for use as the system’s console.

    Devicetree 的 chosen 关键字,允许用户选择硬件设备的特定实例用于特定用途。这方面的一个例子是选择一个特定的 UART 用作系统的控制台。