本篇为devicetree.h部分常用的宏定义功能及用法。
文件路径为:ncs\zephyr\include\devicetree.h
关于gpio的PIN及Flag说明:
* gpio1: gpio@... {
* compatible = "vnd,gpio";
* #gpio-cells = <2>;
* };
*
* gpio2: gpio@... {
* compatible = "vnd,gpio";
* #gpio-cells = <2>;
* };
*
* n: node {
* gpios = <&gpio1 10 GPIO_ACTIVE_LOW>,
* <&gpio2 30 GPIO_ACTIVE_HIGH>;
* };
这里用到了#gpio-cells
,介绍下#gpio-cells
的概念
gpio可以有两个参数,分别是PIN和Flag,PIN脚说明了该gpio的引脚位置,flag则描述该引脚是高电平还是低电平有效。#gpio-cells
的值决定了gpio的参数的个数,以上面的dts文件为例,这里面#gpio-cells
的值为2,所以后面<&gpio1 10 GPIO_ACTIVE_LOW>
会有PIN和flag两个参数,也就是<&gpio1 10 GPIO_ACTIVE_LOW>
里面的pin脚是10,GPIO_ACTIVE_LOW
这两个参数,如果#gpio-cells
的值为1,那么这里只需要写成<&gpio1 10>
即可。
0,DT_PATH(…)
根据传入路径获取node id。
* / {
* soc {
* serial1: serial@40001000 {
* status = "okay";
* current-speed = <115200>;
* ...
* };
* };
* };
dts文件如上图时,调用 DT_PATH(soc,serial_40001000)可以获取serial1对应的node id。
1,DT_NODELABEL(label)
根据devicetree 的 node label获取node id,注意,node label和label属性不是一种概念。
* serial1: serial@40001000 {
* label = "UART_0";
* status = "okay";
* current-speed = <115200>;
* ...
* };
如上图所示,node label 为 serial1,而不是 “UART_0”,可以通过 DT_NODELABEL(serial1)来获取serial@40001000 node的node id.
2,DT_ALIAS(alias)
根据devicetree的别名获取node id,参数是节点的别名
*
* / {
* aliases {
* my-serial = &serial1;
* };
*
* soc {
* serial1: serial@40001000 {
* status = "okay";
* current-speed = <115200>;
* ...
* };
* };
* };
如图所示:可以通过 DT_ALIAS(my_serial) 来获取到serial@40001000 node的nodeid
3,DT_INST(inst, compat)
通过一个兼容性Instance number来获取node id。参数分别是instance number和兼容性组件名字。
具有相同兼容性的属性值会被分配一个从0开始的Instance number,通过 DT_INST()可以根据Instance number来获取到对应的node id。
Instance number 具有如下属性:
- 对于任意的兼容性组件,Instance number从0开始并持续增长
- 每一个兼容性组件节点,只分配一个Instance number,包括disabled状态的节点
- 状态为 “okay”或者未定义状态的节点,从0开始分配Instance number,状态为disabled的节点的Instance number比其他的要大
* serial1: serial@40001000 {
* compatible = "vnd,soc-serial";
* status = "disabled";
* current-speed = <9600>;
* ...
* };
*
* serial2: serial@40002000 {
* compatible = "vnd,soc-serial";
* status = "okay";
* current-speed = <57600>;
* ...
* };
*
* serial3: serial@40003000 {
* compatible = "vnd,soc-serial";
* current-speed = <115200>;
* ...
* };
如上图所示,假设没有其他"vnd,soc-serial"节点,那么instance numbers 被分配为0,1,2
由于serial2和serial3的状态是“okay”和缺省,所以他们两个的instance number被分配为0和1,但是这两个哪个是0哪个是1是没有规则限制的。
由于serial1的状态时disable,所以他的instance number被分配为2
所以如果使用 DT_INST(0,vnd_soc_serial),则获取到的可能是serial2或者serial3
DT_INST(1,vnd_soc_serial),则获取到的可能是serial2或者serial3
DT_INST(2,vnd_soc_serial),则获取到的一定是serial1
备注:在dts文件中,存在,
或者-
等符号,如上文的vnd,soc-serial
和current-speed = <115200>
,这些符号,在作为参数调用的时候,需要变成下划线,也就是_
,如上面的vnd,soc-serial
和current-speed = <115200>
变成vnd_soc_serial
和current_speed
.
4,DT_PARENT(node_id)
获取某个节点的父节点的node_id,参数是子节点的node id
* parent: parent-node {
* child: child-node {
* ...
* };
* };
如上图所示,通过调用DT_PARENT(DT_NODELABEL(child))可以获取到parent节点的node_id。同理,根据宏定义1的用法,通过DT_NODELABEL(parent)通样可以获取到parent节点的node id,他们两个是等价的
5,DT_CHILD(node_id, child)
获取节点的子节点的node id,参数分别是父节点的node id和子节点的节点名字
* / {
* soc-label: soc {
* serial1: serial@40001000 {
* status = "okay";
* current-speed = <115200>;
* ...
* };
* };
* };
如上图所示,可以通过:
* #define SOC_NODE DT_NODELABEL(soc_label)
* DT_CHILD(SOC_NODE, serial_40001000)
来获取serial1的node id,这里SOC_NODE是父节点的node id,serial_40001000是子节点的节点名字
这里不能用serial1作为“child”参数,因为它是一个node label,而不是node name
可以通过DT_FOREACH_CHILD()来遍历一个节点的所有子节点。
6,DT_PROP(node_id,prop)
获取节点的属性值,参数分别为节点的node id和要获取属性值的属性名字。
* soc {
* serial1: serial@40001000 {
* status = "okay";
* current-speed = <115200>;
* ...
* };
* };
* };
如上图的dts文件,可以通过DT_PROP(DT_NODELABEL(serial1),status)来获取到"okay"这个属性值。
7,DT_PROP_HAS_IDX(node_id, prop, idx)
检查节点的属性是否有idx的索引,参数分别是node_id,要检查的属性以及索引值。当属性具有idx的索引是,宏返回 1,当没有这个索引是,返回0.
当prop是reg property时,用DT_REG_HAS_IDX(node_id, idx)
当prop是interrupts property时,用DT_IRQ_HAS_IDX(node_id, idx)代替,用法和上面描述一致。
test_phandles: phandle-holder-0 {
compatible = "vnd,phandle-holder";
gpios = <&test_gpio_1 10 20>, <&test_gpio_2 30 40>;
};
当dts文件如上图所示时,通过调用如下参数
#define TEST_PH DT_NODELABEL(test_phandles)
DT_PROP_HAS_IDX(TEST_PH, gpios, 0),由于gpios具有2个值,因此返回值为1
同理,DT_PROP_HAS_IDX(TEST_PH, gpios, 1)通用返回1
DT_PROP_HAS_IDX(TEST_PH, gpios,2)由于gpios不具有第三个值,因此返回0
8,DT_PROP_BY_IDX(node_id, prop, idx)
通过节点属性的索引获取属性的描述值,参数分别是节点的node id,属性名字和索引值。
当参数prop是reg property时,用DT_REG_ADDR_BY_IDX() 或 DT_REG_SIZE_BY_IDX()代替
当参数prop是interrupts property时,用DT_IRQ_BY_IDX()代替
test_arrays: array-holder {
/* vnd,undefined-compat is for DT_NODE_HAS_COMPAT_STATUS(..,okay) */
compatible = "vnd,array-holder", "vnd,undefined-compat";
a = <1000 2000 3000>;
b = [aa bb cc dd];
c = "bar", "baz";
};
#define TEST_ARRAYS DT_NODELABEL(test_arrays)
当dts文件如上图所示时,调用DT_PROP_BY_IDX(TEST_ARRAYS, a, 0),返回值为0x1000,调用DT_PROP_BY_IDX(TEST_ARRAYS, a, 1)时,返回值是0x2000
9, DT_PROP_OR(node_id, prop, default_value)
获取节点的属性值,如果值不存在,则使用default_value作为属性值。用法类似于DT_PROP().
test_reg: reg-holder@9999aaaa {
compatible = "vnd,reg-holder";
reg = < 0x9999aaaa 0x1000 0xbbbbcccc 0x3f >;
status = "okay";
reg-names = "first", "second";
misc-prop = <1234>;
};
#define TEST_REG DT_NODELABEL(test_reg)
dts文件如上图所示:
调用DT_PROP_OR(TEST_REG, misc_prop, X)返回misc-prop属性值“1234”
调用DT_PROP_OR(TEST_REG, not_a_property, -1),由于test_reg没有“not_a_propety”的属性,所以给这个属性赋值为-1的默认值。
10,DT_LABEL(node_id)
获取节点的label值,用法等同于DT_PROP_LABEL(node_id,label),需要注意,node label和label不是一个概念。
test_i2c_1: i2c@77778888 {
label = "TEST_I2C_CTLR_1";
};
dts文件如上图所示时,node label是test_i2c_1,而test_i2c_1的label是"TEST_I2C_CTLR_1"
当调用DT_LABEL(DT_NODELABEL(test_i2c_1))时,返回"TEST_I2C_CTLR_1“
11,DT_PROP_BY_PHANDLE_IDX(node_id, phs, idx, prop)
获取某个节点的具有phandle类型的的属性的idx项属性值,等同于:DT_PROP(DT_PHANDLE_BY_IDX(node_id, phs, idx), prop)
这里prop是指获取一个node id
具有phandle
类型的属性的属性的值,而不是一个node id
的属性值。
* n1: node-1 {
* foo = <&n2 &n3>;
* };
*
* n2: node-2 {
* bar = <42>;
* };
*
* n3: node-3 {
* baz = <43>;
* };
* #define N1 DT_NODELABEL(n1)
*
* DT_PROP_BY_PHANDLE_IDX(N1, foo, 0, bar) // 42
* DT_PROP_BY_PHANDLE_IDX(N1, foo, 1, baz) // 43
如上图dts文件,使用上面的指令,分别可以获取到n2
的bar
值42 和n3
的baz
值 53。这里的node id为n1
的node id,n1里面的foo
属性具有phandle属性,phandle属性可以简单理解为指针,将n2
和n3
引用进来。
然后这里idx
参数分别是0,1,对应的就是foo
值的n2
和n3
,prop
属性分别是bar
和baz
对应的分别是n2
的bar
属性和n3
的baz
属性。
12,DT_PROP_BY_PHANDLE(node_id, ph, prop)
等同于DT_PROP_BY_PHANDLE_IDX(node_id, ph, 0, prop)
,用法参考第11条宏定义。
13,DT_PHA_BY_IDX(node_id, pha, idx, cell)
通过node id
的具有phandle array
属性的描述符的idx
值获取对应的cell
值。
* gpio0: gpio@... {
* #gpio-cells = <2>;
* };
*
* gpio1: gpio@... {
* #gpio-cells = <2>;
* };
*
* led: led_0 {
* gpios = <&gpio0 17 0x1>, <&gpio1 5 0x3>;
* };
当dts文件如上图所示时,通过下面指令:分别可以获取到led
的gpios
第0项也就是gpio0
的pin项,也就是0x1,和gpio1
的flag
项,也就是0x3.
* #define LED DT_NODELABEL(led)
*
* DT_PHA_BY_IDX(LED, gpios, 0, pin) // 17
* DT_PHA_BY_IDX(LED, gpios, 1, flags) // 0x3
14,DT_PHA_BY_IDX_OR(node_id, pha, idx, cell, default_value)
用法类似于第13条,区别是在如果要查询的项不存在的话,使用default_value
代替。
15,DT_PHA(node_id, pha, cell)
等同于DT_PHA_BY_IDX(node_id, pha, 0, cell)
,用法介绍见第13条。
16,DT_PHA_OR(node_id, pha, cell, default_value)
等同于DT_PHA_BY_IDX_OR(node_id, pha, 0, cell, default_value)
,用法参考第14条。
17,DT_BUS(node_id)
根据node id获取它的总线控制器
* i2c@deadbeef {
* label = "I2C_CTLR";
* status = "okay";
* clock-frequency = < 100000 >;
*
* i2c_device: accelerometer@12 {
* ...
* };
* };
dts文件如上图所示:
#define TEST_LABEL DT_NODELABEL(i2c_device)
获取到“i2c_device”的node id,然后
DT_BUS(TEST_LABEL )
获取i2c_device的总线控制器 i2c@deadbeef.然后就可以通过
DT_PROP(DT_BUS(TEST_LABEL ),clock_frequency)
来获取i2c@deadbeef的clock_frequency属性的值
18,DT_DRV_COMPAT
这个宏定义用于指定一个compatile的名字,可以用于DT_DRV_INST(inst)
,这里等同于DT_INST(inst, DT_DRV_COMPAT)
,compatible
项默认以DT_DRV_COMPAT
代替。