设备树绑定声明对节点内容的要求,主要说明了内容了数据类型和键。Zephyr设备树绑定是自定义格式的YAML 文件。
介绍
设备树节点使用compatible属性与绑定匹配。
在配置阶段,生成系统尝试将设备树中的每个节点与绑定文件匹配。如果此操作成功,生成系统将在验证节点的内容和为节点生成宏时使用绑定文件中的信息。
示例
设备树:
/* Node in a DTS file */bar-device {compatible = "foo-company,bar-device";num-foos = <3>;};
绑定文件:
# A YAML binding matching the nodecompatible: "foo-company,bar-device"properties:num-foos:type: intrequired: true
生成系统将bar-device节点的compatible与其YAML文件里的compatible匹配,节点的属性与绑定文件里的行匹配。
生成系统对绑定执行的操作
生成系统使用绑定文件来验证设备树节点,并将设备树的内容转换为devicetree_unfixed.h头文件。
例如,生成系统将使用绑定文件来检查节点中是否存在所需的属性,以及其值是否具有正确的类型。然后,生成系统将为节点的属性生成一个宏。此宏允许使用API在代码中获取属性的值。
对于另一个示例,以下节点将导致生成错误,因为它没有属性,并且num-foos属性在绑定中标记为必需:
bad-node {compatible = "foo-company,bar-device";};
节点与绑定匹配的其他方式
如果节点的compatible属性中有多个字符串,则生成系统将按列出的顺序查找兼容的绑定,并使用第一个匹配项。
以下节点为例:
baz-device {compatible = "foo-company,baz-device", "generic-baz-device";};
如果生成系统找不到带有compatible: "generic-baz-device"行的绑定文件,则该节点将与具有compatible: "foo-company,baz-device"行的绑定文件匹配。
没有compatible属性的节点可以与与其父节点关联的绑定进行匹配。这些称为子绑定。如果节点描述总线上的硬件(如I2C或SPI),则在将节点与绑定匹配时也会考虑总线类型。
一些没有compatible属性的特殊节点与推断绑定匹配。对于这些节点,生成系统根据最终设备树中的属性生成宏。
绑定文件存放位置
生成系统在以下位置的子目录中查找dts/bindings绑定文件:
- zephyr 存储库
- 应用程序目录
- 自定义板子目录
- 指定的
DTS_ROOT变量中的任何目录 - 在其
构建设置中定义dts_root的任何模块
绑定文件是以.yml或.yaml结尾的YAML文件。
绑定文件必须位于上述位置的dts/bindings的子目录中。
绑定文件语法
Zephyr的绑定文件是YAML类型的文件。在YAML文件由多个键值对组成。
以下是绑定文件所有的键:
# 绑定所应用的设备的顶层描述: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.# 包含其他的绑定文件的定义include: other.yaml# 匹配节点到这个绑定文件上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.# 如果节点描述了总线硬件,比如SPI总线控制器,在SoC上使用'bus:'来表示是哪个bus: spi# 如果节点作为总线上的设备出现,比如外部设备SPI内存芯片,# 使用'on-bus:'来表示什么类型的总线。和compatible一样,这个键也会影响节点绑定匹配的方式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.
description键
主要写的是节点硬件的描述。
compatible键
此键用于将节点与绑定文件进行匹配。它在绑定文件中应如下所示:
# Note the comma-separated vendor prefix and device namecompatible: "manufacturer,device"
此设备树节点将与上述绑定匹配:
device {compatible = "manufacturer,device";};
如果找到多个兼容的绑定文件,则会引发错误。
properties键
该键描述与绑定匹配的节点可以包含的属性。
例如,UART外设的绑定可能如下所示:
compatible: "manufacturer,serial"properties:reg:type: arraydescription: UART peripheral MMIO register spacerequired: truecurrent-speed:type: intdescription: current baud raterequired: truelabel:type: stringdescription: human-readable namerequired: false
以下节点中的属性将通过上面的绑定文件的验证:
my-serial@deadbeef {compatible = "manufacturer,serial";reg = <0xdeadbeef 0x1000>;current-speed = <115200>;label = "UART_0";};
除了某些特殊属性(如reg)之外,只有绑定文件中列出的属性才会生成宏。
属性的示例
properties:# Describes a property like 'current-speed = <115200>;'. We pretend that# it's obligatory for the example node and set 'required: true'.current-speed:type: intrequired: truedescription: Initial baud rate for bar-device# Describes an optional property like 'keys = "foo", "bar";'keys:type: string-arrayrequired: falsedescription: Keys for bar-device# Describes an optional property like 'maximum-speed = "full-speed";'# the enum specifies known values that the string property may takemaximum-speed:type: stringrequired: falsedescription: 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 takeresolution:type: intrequired: falseenum:- 8- 16- 24- 32# Describes a required property '#address-cells = <1>'; the const# specifies that the value for the property is expected to be the value 1"#address-cells":type: intrequired: trueconst: 1int-with-default:type: intrequired: falsedefault: 123description: Value for int register, default is power-up configuration.array-with-default:type: arrayrequired: falsedefault: [1, 2, 3] # Same as 'array-with-default = <1 2 3>'string-with-default:type: stringrequired: falsedefault: "foo"string-array-with-default:type: string-arrayrequired: falsedefault: ["foo", "bar"] # Same as 'string-array-with-default = "foo", "bar"'uint8-array-with-default:type: uint8-arrayrequired: falsedefault: [0x12, 0x34] # Same as 'uint8-array-with-default = [12 34]'
属性的语法
<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: 代表此节点属性是否是必须的type: 代表节点属性的类型
属性的类型如下:
| 类型 | 描述 | 示例 |
|---|---|---|
| string | 字符串 | label = "UART_0"; |
| int | 32位整数 | current-speed = <115200>; |
| boolean | bool类型 | hw-flow-control; |
| array | 32位整数数组 | offsets = <0x100 0x200 0x300>; |
| uint8-array | 字节数组 | local-mac-address = [de ad be ef 12 34]; |
| string-array | 字符串数组 | dma-names = "tx", "rx"; |
| phandle | 节点 | interrupt-parent = <&gic>; |
| phandles | 节点数组 | pinctrl-0 = <&usart2_tx_pd5 &usart2_rx_pd6>; |
| phandle-array | 节点和32位整数的数组 | dmas = <&dma0 2>, <&dma0 3>; |
| path | 节点路径引用或路径字符串 | zephyr,bt-c2h-uart = &uart0;或foo = "/path/to/some/node"; |
| compound | 更复杂的类型,一个包罗万象的类型,但不会生成任何宏 | foo = <&label>, [01 02]; |
deprecated: 具有deprecated: true的属性向用户和工具指示该属性将被逐步淘汰。default: 可选的,提供一个值,如果设备树节点中缺少该属性,将使用该值。enum: 该属性可能包含的值的列表。如果设备树中的属性值不在列表中,则会引发错误。const: 这指定了属性必须采用的常量值。
child-binding键
当节点具有所有相同属性的子节点时,可以使用child-binding键。每个子节点都获取child-binding键的内容作为其绑定,但如果子节点匹配到了绑定文件,则优先使用绑定文件。
一个PWM LED节点的绑定如下所示,其中子节点需要具有一个pwms属性:
pwmleds {compatible = "pwm-leds";red_pwm_led {pwms = <&pwm3 4 15625000>;};green_pwm_led {pwms = <&pwm3 0 15625000>;};/* ... */};
绑定文件如下:
compatible: "pwm-leds"child-binding:description: LED that uses PWMproperties:pwms:type: phandle-arrayrequired: true
child-binding也可以以递归方式工作。例如:
compatible: foochild-binding:child-binding:properties:my-property:type: intrequired: true
将应用于设备树中的grandchild节点:
parent {compatible = "foo";child {grandchild {my-property = <123>;};};};
bus键
如果节点是总线控制器,请在绑定中使用bus来说明总线的类型。例如,SoC上SPI控制器的绑定如下所示:
compatible: "manufacturer,spi-peripheral"bus: spi
绑定文件中存在此键会通知生成系统与此绑定匹配的任何节点的子节点出现在此类型的总线上。
on-bus键
如果节点是总线上的设备,在绑定文件中使用on-bus说明总线的类型。
例如,外部SPI存储器芯片的绑定应包括以下行:
on-bus: spi
查找节点的绑定时,生成系统会检查父节点的绑定是否包含bus: <bus type>。如果是这样,则仅考虑具有匹配bus: <bus type>的绑定文件。首先搜索具有显式on-bus: <bus type>的绑定,然后搜索没有显式on-bus的绑定。按顺序对节点属性中的每个项重复搜索。
此功能允许同一设备具有不同的绑定,具体取决于它出现在哪个总线上。例如,考虑一个兼容的传感器器件,可以通过I2C或SPI使用。
因此,传感器节点可能在器件树中显示为SPI或I2C控制器的子节点,如下所示:
spi-bus@0 {/* ... some compatible with 'bus: spi', etc. ... */sensor@0 {compatible = "manufacturer,sensor";reg = <0>;/* ... */};};i2c-bus@0 {/* ... some compatible with 'bus: i2c', etc. ... */sensor@79 {compatible = "manufacturer,sensor";reg = <79>;/* ... */};};
可以编写两个单独的绑定文件,这些文件与这些单独的传感器节点匹配,即使它们具有相同的兼容性:
# 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: booleanrequired: falseon-bus: i2c
foo-cells键
说明符单元格通常与属性类型为phandle-array时一起使用。
假定某个节点具有以下类型属性:
pwm0: pwm@0 {compatible = "foo,pwm";#pwm-cells = <2>;};pwm3: pwm@3 {compatible = "bar,pwm";#pwm-cells = <1>;};my-device {pwms = <&pwm0 1 2>, <&pwm3 4>;};
# foo,pwm.yamlcompatible: "foo,pwm"...pwm-cells:- channel- period# bar,pwm.yamlcompatible: "bar,pwm"...pwm-cells:- period
可以看到pwm0的#pwm-cells的属性为2,这就意味着说明符单元格的列表数量为2,因此<&pwm0 1 2>中1和2的含义就由单元格说明符来定义,分别代表channel和period。
include键
绑定可以包含其他文件,这些文件可用于在绑定之间共享公共属性定义。
包含单个绑定文件
include: foo.yaml
如果找到任何名为foo.yaml的文件,则该文件将包含在此绑定中。
如果键在绑定文件和包含绑定文件中以不同的值出现,则直接报错,但required是个例外。
基础绑定文件
base.yaml文件包含许多常见属性的定义。编写新绑定时,最好检查是否已定义了某些必需的属性,如果定义了,则将其包括在内。
# Common fields for all devicesproperties:status:type: stringrequired: falsedescription: indicates the operational status of a deviceenum:- "ok" # Deprecated form- "okay"- "disabled"- "reserved"- "fail"- "fail-sss"compatible:type: string-arrayrequired: truedescription: compatible stringsreg:type: arraydescription: register spacerequired: falsereg-names:type: string-arraydescription: name of each register spacerequired: falseinterrupts:type: arrayrequired: falsedescription: interrupts for device# Does not follow the 'type: phandle-array' scheme, but gets type-checked# by the code. Declare it here just so that other bindings can make it# 'required: true' easily if they want to.interrupts-extended:type: compoundrequired: falsedescription: extended interrupt specifier for deviceinterrupt-names:type: string-arrayrequired: falsedescription: name of each interruptinterrupt-parent:type: phandlerequired: falsedescription: phandle to interrupt controller nodelabel:type: stringrequired: falsedescription: Human readable string describing the device (used as device_get_binding() argument)clocks:type: phandle-arrayrequired: falsedescription: Clock gate informationclock-names:type: string-arrayrequired: falsedescription: name of each clock"#address-cells":type: intrequired: falsedescription: number of address cells in reg property"#size-cells":type: intrequired: falsedescription: number of size cells in reg propertydmas:required: falsetype: phandle-arraydescription: DMA channels specifiersdma-names:required: falsetype: string-arraydescription: Provided names of DMA channel specifiersio-channels:required: falsetype: phandle-arraydescription: IO channels specifiersio-channel-names:required: falsetype: string-arraydescription: Provided names of IO channel specifiers
包含多个绑定文件
include:- foo.yaml- bar.yaml
包括多个文件时,所包含文件中属性上的任何重叠键都将放在一起。
过滤属性定义
在某些情况下,您可能希望包括文件中的某些属性定义,但不是全部。在这种情况下,应该是一个列表,您可以通过在列表中过滤掉所需的定义,如下所示:
include:- name: foo.yamlproperty-allowlist:- i-want-this-one- and-this-one- name: bar.yamlproperty-blocklist:- do-not-include-this-one- or-this-one
混合包含
您可以在单个列表包含文件和属性定义:
include:- foo.yaml- name: bar.yamlproperty-blocklist:- do-not-include-this-one- or-this-one
过滤子绑定的属性定义
include:- name: bar.yamlchild-binding:property-allowlist:- child-prop-to-allow
推断绑定
Zephyr的设备树脚本可以根据其/zephyr,user属性中观察到的值推断特殊节点的绑定。
此节点匹配绑定,该绑定由生成系统根据其在最终设备树中的属性值动态创建。它没有compatible属性。
例如:
#include <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>;};};
可以获取到如下的值:
#define ZEPHYR_USER_NODE DT_PATH(zephyr_user)DT_PROP(ZEPHYR_USER_NODE, boolean) // 1DT_PROP(ZEPHYR_USER_NODE, bytes) // {0x81, 0x82, 0x83}DT_PROP(ZEPHYR_USER_NODE, number) // 23DT_PROP(ZEPHYR_USER_NODE, numbers) // {1, 2, 3}DT_PROP(ZEPHYR_USER_NODE, string) // "text"DT_PROP(ZEPHYR_USER_NODE, strings) // {"a", "b", "c"}
可以通过以下方式获取gpio0节点:
/** 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)};
可以通过以下方式获取signal-gpios中的引脚:
#include <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);
