前言
博客里有讲过Zephyr的安装和后续的工作区搭建等等。但是设备树的内容由于篇幅缘故一笔带过了。因此这里重新开了一个帖子,专门用于记录Zephyr的设备树语法。
基本语法
Zephyr的写法基本与linux的一致,设备树由根节点开始:
/{
};
接下来的所有内容都是节点,节点又有子节点,同时又有自身的属性。他们的关系就和结构体一样,通过花括号包含:
/{
leds: leds {
compatible = "gpio-leds";
green_led: led_0 {
};
};
};
其中属性有很多类型。以下是属性的类型表格,摘自官网:
| 类型 | 格式说明 | 示例 |
|---|---|---|
| string 字符串 | 双引号括起 | A字符串 = "你好,世界!"; |
| int 整数 | 尖括号(< 和 >)括起 |
一个整数 = <1>; |
| boolean 布尔值 | true 无值;false 使用 /delete-property/ |
我的布尔值 = >真; |
| array 数组 | 尖括号(< 和 >)括起,元素用空格分隔 |
foo = <0xdeadbeef 1234 0>; |
| uint8-array 无符号8位整数数组 | 方括号([ 和 ])括起,十六进制格式,不带 0x 前缀 |
一个字节数组 = [00 01 ab]; |
| string-array 字符串数组 | 用逗号分隔 | 字符串数组 = "字符串一", "字符串二", "字符串三"; |
| phandle 句柄 | 尖括号(< 和 >)括起 |
一个句柄 = <&mynode>; |
| phandles 句柄列表 | 尖括号(< 和 >)括起,用空格分隔 |
句柄列表 = <&mynode0 &mynode1 &mynode2>; |
| phandle-array 句柄数组 | 尖括号(< 和 >)括起,用空格分隔 |
句柄数组 = <&mynode0 1 2>, <&mynode1 3 4>; |
拥有以上的功能,我们就可以组装出比较复杂的设备树:
/{
leds: leds {
compatible = "gpio-leds";
green_led: led_0 {
gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>;
label = "User LD1";
};
yellow_led: led_1 {
gpios = <&gpioe 1 GPIO_ACTIVE_HIGH>;
label = "User LD2";
};
red_led: led_2 {
gpios = <&gpiob 14 GPIO_ACTIVE_HIGH>;
label = "User LD3";
};
};
};
你可能会注意到red_led:led_2这样的语法。
其中,red_led是led_2的标签名。你可以通过标签或者这个节点的完整路径来访问它,或者使用句柄。
关于句柄,官方有这么一个案例:
foo: device@0 { };
device@1 {
sibling = <&foo 1 2>;
};
其中foo是标签,device@0是一个节点,device@1也是一个节点。 在这里,device@1有一个sibling属性,它包含foo的句柄(也就是device的句柄)和1,2两个常量值。
此外,这里有个compatible ,它是设备树中最重要的属性之一,它告诉操作系统:这个设备是什么,应该用哪个驱动来管理它。 他的语法一般是这样的: compatible = “厂商,设备名”, “通用设备名”;(这个应该在之前的文章里讲过了,但是这里有必要重新提出来) 这和binding息息相关,zephyr通过遍历binding找到对应的设备驱动。 在 Zephyr 源码中,设备树绑定文件通常在:zephyr/dts/bindings/ 每个子目录对应一种设备类型,里面有 .yaml 文件定义该类型的 compatible
注意:
- 关于布尔值(Boolean) 布尔属性的规则很简单:
存在 = true 不存在 = false 不要给它赋值 所以布尔属性不需要写 = true 或 = false,只要写了这个属性名就是真,完全不写就是假。
- 关于数组中的数值混合 一个数组(cell)中可以同时混用十六进制和十进制,完全没问题。因为 Zephyr 会把设备树转换成 C 源码,所以不需要担心字节序问题。
- 关于 64 位整数 设备树的每个 cell 是 32 位的。如果要表示一个 64 位的数,就需要用两个 cell 来表示,按大端序(高 32 位在前)排列。 在设备树(Devicetree)中,cell 就是一个 32 位(4 字节)的无符号整数,它是设备树中存储数据的基本单位。 ``` <123> // 这是一个 cell,里面装的值是 123
<0xdeadbeef 1234> // 这是两个 cell,分别装着 0xdeadbeef 和 1234
4. 关于字节数组
方括号 [] 里的是原始字节,每个值用十六进制表示(不带 0x 前缀),按顺序排列
`a-byte-array = [00 01 ab];
// 就是三个字节:0x00, 0x01, 0xab`
5. 关于运算
设备树中允许使用算术运算符(+, -, *, /)和位运算符(<<, >>, &, |),但必须用括号把整个表达式包起来。
```dts
bar = <(2 * (1 << 5))>;
// 计算过程:1 << 5 = 32, 2 * 32 = 64
// 结果:bar 的值是 64
如果表达式算出负数(如 <(-1)> 或 <(4 - 6)>),它会被保留为有符号值 如果是正数的字面量(包括 0xffffffff 这种看起来像负数的十六进制),它仍然是无符号的 简单说: 只有通过表达式显式计算出的负数才被视为有符号数。