节点标识符
若要获取有关特定设备树节点的属性信息,需要为其提供节点标识符。
以下是获取节点标识符的主要方法:
- 按路径: 从
根节点开始,在设备树中使用DT_PATH()以及节点的完整路径。 - 按节点标签: 使用
DT_NODELABEL()从节点标签中获取节点标识符。 - 按别名: 使用
DT_ALIAS()获取在/aliases节点下的特殊节点属性的节点标识符。 - 按实例编号:
实例编号是一种基于匹配兼容性引用单个节点的方法。使用DT_INST()获取这些内容。 - 按选择节点: 使用
DT_CHOSEN()获取在/chosen节点下的节点标识符。 - 按父/子节点: 使用
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)对于某个未知数字
设备树名称中的非字母数字字符将转换为下划线_。DTS 中的名称也会转换为小写。
基于实例的节点标识符
serial@40001000 {compatible = "vnd,serial";status = "okay";current-speed = <115200>;};
这里基于实例获取serial@40001000的节点标识符和实例属性,如下:
#define DT_DRV_COMPAT vnd_serialDT_DRV_INST(0) // node identifier for serial@40001000DT_INST_PROP(0, current_speed) // 115200
这里通过将DT_DRV_COMPAT定义为设备树中compatible属性的值,就代表我们要查vnd_serial的实例,由于设备树中只有一个compatible的属性为vnd,serial,因此只有一个实例也就是serial@40001000的节点标识符,之后我们用DT_DRV_INST来定义实例serial@40001000。之后就可以通过DT_INST_PROP来获取实例里的属性了。
节点标识符的引用
无法使用变量来存储一个节点标识符,例如以下的写法都会报错:
/* 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);
可以使用宏来标识一个节点标识符:
/* 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)
节点属性的访问
检查属性和值
可以使用DT_NODE_HAS_PROP()检查节点是否具有属性。
DT_NODE_HAS_PROP(DT_NODELABEL(i2c1), clock_frequency) /* expands to 1 */DT_NODE_HAS_PROP(DT_NODELABEL(i2c1), not_a_property) /* expands to 0 */
简单属性
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. */
字符串文本。
对于布尔值,宏将扩展为数字0或1。
不要将
DT_NODE_HAS_PROP()用于布尔属性。请改用DT_PROP()
对于数组类型的值,宏将扩展为数组。例如:
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 */
reg属性
给定一个node_id节点标识符,使用DT_NUM_REGS(node_id)可以获取reg节点属性中寄存器块的总数。reg属性是无法使用DT_PROP来获取值的。必须使用DT_REG_ADDR()或DT_REG_SIZE()。
如果节点只有一个寄存器块,则使用:
DT_REG_ADDR(node_id):给定节点的寄存器块地址DT_REG_SIZE(node_id):获取给定节点寄存器块地址的大小
如果节点有多个寄存器块,则使用:
DT_REG_ADDR_BY_IDX(node_id, idx):索引idx处寄存器块的地址DT_REG_SIZE_BY_IDX(node_id, idx):索引idx处的寄存器块的地址大小
其中索引必须是整数或者确定整数的宏,不能是变量或者表达式。以下的做法是错误的:
/* This will cause a compiler error. */for (size_t i = 0; i < DT_NUM_REGS(node_id); i++) {size_t addr = DT_REG_ADDR_BY_IDX(node_id, i);}
interrupts属性
给定一个node_id节点标识符,DT_NUM_IRQS(node_id)是节点属性中中断说明符的总数。
用于访问interrupts属性的API宏是DT_IRQ_BY_IDX():
DT_IRQ_BY_IDX(node_id, idx, val)
- node_id:代表节点标识符
- idx:代表此节点中
interrupts属性里数组的逻辑索引 - val: 代表选定的数组值中的
val名称
大多数Zephyr设备树绑定都有一个名为irq的单元,即中断数。可以使用DT_IRQN()作为获取此值。
处理包含节点的属性值
例如:
pwmleds {compatible = "pwm-leds";red_pwm_led {pwms = <&pwm3 4 15625000>;};green_pwm_led {pwms = <&pwm3 0 15625000>;};/* ... */};
在上面的示例中pwms就是包含节点的属性值,这里的节点就是pwm3。
可以使用DT_PHANDLE()、DT_PHANDLE_BY_IDX()或DT_PHANDLE_BY_NAME()将节点值转换为节点标识符,具体取决于所使用的属性类型。
另一个常见用例是访问节点数组中的说明符。用于此目的的通用API DT_PHA_BY_IDX()和DT_PHA()。还有特定于硬件的API,如DT_GPIO_CTLR_BY_IDX()、DT_GPIO_CTLR()、DT_GPIO_LABEL_BY_IDX()、DT_GPIO_LABEL()、DT_GPIO_PIN_BY_IDX()、DT_GPIO_PIN()、DT_GPIO_FLAGS_BY_IDX()和DT_GPIO_FLAGS()。
设备树其他API
上面我们介绍的都是一些设备树的基本API,还有一些其他的API,可以到Zephyr的设备API里查看。
