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.

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.

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.

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

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.

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.

Here is an example DTS file:

/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.

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.

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.

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.

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:

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.

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.

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

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.

This is the corresponding 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.

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.

• 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.

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 ]).

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.

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.

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

### 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 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.

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.

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.

• 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:

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.

• <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 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 关系应该使用从设备树获得的信息，例如设备电源管理。

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

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).

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.

## 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.

### 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.

### 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.

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

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++.

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:

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.

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.)

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.

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

• the zephyr repository

zephyr 仓库

• 您的应用程序源目录

• your board directory

你的主板名目录

• any directories in the DTS_ROOT CMake variable

变量 DTS_ROOT CMake 中的任何目录

• any module that defines a dts_root in its Build settings

在其 Build 设置中定义 dts_root 的任何模块

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.

Warning 警告

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

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.

## 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:

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.

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.

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.

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.

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

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
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.

#### 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
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.

#### 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.

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)

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

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.

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.)

#### Default values for properties 属性的默认值¶

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

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).

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

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.

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.)

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

#### 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.

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

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:

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:

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¶

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

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

on-bus: spi


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

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.

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.

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

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.

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

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

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

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:

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.

Similarly, the cell 4 is the specifier associated with 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:

# 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.

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

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.

An alternative is using a specifier-space property to indicate the base property name for *-names and *-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.

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

### 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.

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.

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.

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.

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.

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

reg:
required: true


This relies on the dictionary merge to fill in the other keys for reg, like 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].)

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.

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:
- 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.

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:
- 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.)

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.

### 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.

For example, with this DTS fragment:

#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:

/*
* 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:

#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())

# 从 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.

## 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.

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.

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-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:

• 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.

## 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:

/* 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.

### 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_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.

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:

#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.

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:

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.

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.

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

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_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(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:

/* This will cause a compiler error. */

for (size_t i = 0; i < DT_NUM_REGS(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.

The most general purpose API macro for accessing these is 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.

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.

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.

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.

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.

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().

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.

## Other APIs¶

Here are pointers to some other available APIs.

## 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().

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.

## 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.

## 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.

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.

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.

# Devicetree HOWTOs ¶

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

See Troubleshooting devicetree for troubleshooting advice.

## Get your devicetree and generated 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.

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.

Tip 小贴士

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

For example, using the ARM Cortex-M3 Emulation (QEMU) board to build 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.

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.

## 从设备树节点获取 struct device

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

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

/ {
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():

const struct device *uart_dev = DEVICE_DT_GET(MY_SERIAL);

/* 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():

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.

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().

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

#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:

...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:

if (!device_is_ready(uart_dev)) {
}


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.

## 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:

/*
* 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 总线控制器设备的指针。一些建议: 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 -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.

## 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.

Some hints for what to check next if you don’t see the “whoops” error message:

• 你确定你用的是正确的名字吗？

• does the property exist?

这属性真的存在吗？

• does the node have a matching binding?

节点是否有匹配的绑定？

• does the binding define the property?

绑定是否定义了属性？

## Check for missing bindings 检查缺少的绑定¶

See Devicetree bindings for information about bindings, and Bindings index for information on bindings built into Zephyr.

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.

If your binding file is not under zephyr/dts, you may need to set DTS_ROOT; see Where bindings are located.

## 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.

# 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.

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.

For example, consider a board containing a SoC with 2 UART, or serial port, instances.

• 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.

• 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.

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 用作系统的控制台。