Node identifiers 节点标识符
以下是获得节点标识符的主要方法:
-
By path 路径
从根节点开始,在设备树中使用DT_PATH()
和节点的完整路径。如果您恰好知道正在寻找的确切节点,这将非常有用。 -
By node label 按节点标签
使用DT_NODELABEL()
从节点标签获取节点标识符。节点标签通常由SoC .dtsi提供,以提供与SoC数据表匹配的节点名称,如i2c1
,spi2
等。 -
By alias 化名
使用DT_ALIAS()
获取特定的/aliases
节点的属性的节点标识符。有些应用程序(比如 blinky,它使用 led0 别名)需要引用某种特定类型的设备(“主板的用户 LED”) ,但是不在乎使用哪种设备。 -
By instance number 按实例编号
这主要由设备驱动程序完成,因为实例号是基于匹配兼容性引用单个节点的一种方式。使用DT_INST()
获得这些,但是这样做要小心。见下文。 -
By chosen node 按选择的节点
使用DT_CHOSEN()
获取/chosen
的节点属性的节点标识符。 -
By parent/child 按父母/子女划分
使用DT_PARENT()
和DT_CHILD()
获取父节点或子节点的节点标识符,从已有的节点标识符开始。
/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 >;
};
};
};
以下是获取 i2c@40002000
节点的节点标识符的几种方法:
DT_PATH(soc, i2c_40002000)
DT_NODELABEL(i2c1)
DT_ALIAS(sensor_controller)
DT_INST(x,vnd_soc_i2c)
表示某个未知数 x. 详细信息请参阅 DT_INST()文档。
非字母数字字符,如 dash (-)和 devicetree 名称中的 at 符号 (@)转换为下划线(_)。DTS 中的名称也将转换为小写。
Property access
用于读取属性值的正确 API 取决于节点和属性。
- Checking properties and values
检查属性和值 - Simple properties
简单的属性 - reg properties
寄存器属性 - interrupts properties
中断属性 - phandle properties
Phandle 属性
使用 DT_PROP(node_id,property)读取基本的整数、布尔值、字符串、数值数组和字符串数组属性。
例如,要读取上面示例中的时钟频率属性值:
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. */
类型 array、 uint8-array 和 string-array 的属性的工作方式类似,只不过在这些情况下 DT_PROP()扩展为数组初始值设定项。下面是一个设备树片段的例子:(2)
foo: foo@1234 {
a = <1000 2000 3000>; /* array */
b = [aa bb cc dd]; /* uint8-array */
c = "bar", "baz"; /* string-array */
};
它的属性可以这样访问:
#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"} */
您可以使用 DT_PROP_LEN()以元素个数获取逻辑数组长度。
size_t a_len = DT_PROP_LEN(FOO, a); /* 3 */
size_t b_len = DT_PROP_LEN(FOO, b); /* 4 */
size_t c_len = DT_PROP_LEN(FOO, c); /* 2 */
DT_PROP_LEN()不能与特殊的 reg 或中断属性一起使用。
reg properties 寄存器属性
有关 reg 的介绍,请参见重要属性。
给定一个节点标识符 node_id
,DT_NUM_REGS(node_id)
是该节点的 reg 属性中寄存器块的总数。
使用 DT_PROP(node,reg)
无法读取寄存器块地址和长度。相反,如果一个节点只有一个寄存器块,使用 DT_REG_ADDR()
或 DT_REG_SIZE()
:
DT_REG_ADDR (node_id)
: 给定节点的寄存器块地址DT_REG_SIZE (node_id)
: 其大小
如果节点有多个寄存器块,则使用 DT_REG_ADDR_BY_IDX()
或 DT_REG_SIZE_BY_IDX()
:
DT_REG_ADDR_BY_IDX (node_id,idx)
: 索引 idx 处寄存器块的地址DT_reg_size_by_idx (node_id,idx)
: 索引 idx 处块的大小
从设备树节点获取 struct device
在编写 Zephyr 应用程序时,您通常希望获得与设备树节点对应的驱动程序级struct device。
例如,对于这个 devicetree 片段,您可能需要用于 Series@40002000的 struct device:
/ {
soc {
serial0: serial@40002000 {
status = "okay";
current-speed = <115200>;
/* ... */
};
};
aliases {
my-serial = &serial0;
};
chosen {
zephyr,console = &serial0;
};
};
有不同的方法可以做到这一点; 选择最适合您的需求的方法。下面是一些例子:
/* 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)
有了节点标识符之后,有两种方法可以继续。获得设备的一种方法是使用 DEVICE_DT_GET() :
const struct device *uart_dev = DEVICE_DT_GET(MY_SERIAL);
if (!device_is_ready(uart_dev)) {
/* Not ready, do not use */
return -ENODEV;
}
DEVICE_DT_GET()
有多种变体,例如 DEVICE_DT_GET_OR_NULL()
、 DEVICE_DT_GET_ONE()
或 DEVICE_DT_GET_ANY()
。这个习惯用法在构建时获取设备指针,这意味着没有运行时损失。如果要将设备指针存储为配置数据,则此方法非常有用。但是,由于设备可能没有被初始化,或者可能未能初始化,因此在将设备传递给任何 API 函数之前,必须验证设备是否已准备好可以使用。
在某些情况下,在构建时无法知道设备,例如,如果它依赖于用户输入,比如在 shell 应用程序中。在这种情况下,您可以通过将 DT_LABEL()
和 device_get_binding()
组合起来获得 struct 设备:
const struct device *uart_dev = device_get_binding(DT_LABEL(MY_SERIAL));