Zephyr Devicetree详解 Devicetree语法基础

Zephyr遵守Devicetree spec,但Devicetree spec涵盖太广,这里列出理解和创建Zephyr Devicetree的重点内容,细节再去看spec既可。

Devicetree层次结构 链接到标题

Devicetree按照树状结构展开,节点之间的层级关系按下面原则安排:

  • 硬件的拓扑对应关系(主要参考)
    • 按照硬件从属关系
      • cpu0里有mpu,因此mpu的节点放到cpu0下
      • uart,gpio,i2c这些片上外设在soc的内部,因此这些节点放到soc下
      • sram在soc内部,因此放到soc下
    • 有外设设备总线从属关系的按总线从属关系
      • i2c总线上又挂了gt911,因此i2c下有gt911
    • 没有设备总线从属关系的设备放根节点下
      • led和button连接在gpio上,但gpio不是总线,因此led和button都放在/下面
      • sdram0是独立设备,但不是挂在外设设备总线上,因此也放到/下
  • 设备的初始化时机
    • 对于clock和cpu来说都要先于soc下其他外设进行初始化启动,因此放到/下面和soc同级 alt text

Devicetree语法基础 链接到标题

Devicetree基本语法结构 链接到标题

在Zephyr中有三种文件都是Devicetree:

  • .dts
  • .dtsi
  • .overlay

.dts会include, .dtsi, Zephyr的脚本会将overlay融合进dts,一个典型的dts文件格式如下

/dts-v1/

/{
a-node{
    a_node_label: a-sub-node {
        foo = <3>;
    };
    b-sub-node {
        foo = <3>;
        bar = <&a_node_label>;
    };	
};
};
  • /dts-v1/,指明DeviceTree的版本;Zephyr目前
  • Devicetree具有唯一的根节点/
  • 节点的名称写在大括号之前。如a-nodea-sub-nodeanother-sub-node
  • 节点的属性写在大括号内,是键值对(Key-Value Pair)的形式。例如foo = <3>;
  • 子节点写在父节点的大括号内,以表达树状的层次关系;
  • 通过节点路径来唯一标识一个节点/a-node/a-sub-node
  • 节点支持标签,例如a_node_label,标签与节点之间用冒号:连接。
  • 标签的作用:
    • 简化引用:可以用a_node_label替代,而不需要使用全路径/a-node/a-sub-node
    • 引用节点:标签允许在Devicetree的其他部分引用特定节点bar = <&a_node_label>
  • 一个节点可以有多个标签,例如 collapsed:: true 链接到标题

      ```
      label1: label2: label3: node-name {
          /* 节点属性 */
      };
      ```
      -
    

节点名 链接到标题

节点命名规则:name@address

  • name:必须以字母开头。长度在1~31字节。允许大小写字母、数字、英文逗号、小数点、加号、减号、下划线;
  • @address:称为「Unit Address」,描述当前节点在其父节点的地址空间中的地址。如果节点有reg属性,则address的值必须与reg描述的第一个寄存器地址相等,可以理解为该设备在总线上的基地址。如果节点没有reg属性,节点名中不能出现@address。address和reg都是16进制。address不需要写0x前缀,reg的16进制值需要写0x前缀。

示例:

#有address
sram0: memory@3ffb0000 {
        compatible = "mmio-sram";
        reg = < 0x3ffb0000 0x2c200 >;
    };
#无address
button0: button_0 {
        gpios = < &gpio0 0x0 0x11 >;
        label = "BOOT Button";
        zephyr,code = < 0xb >;
    };

属性 链接到标题

一个节点可以有多个属性,属性以键值对的形式存在。属性名可以含大小写字母、数字、逗号、小数点、下划线,加号、减号、问号、"#“号。属性的值有各种类型,Zephyr使用下面几种

类型 属性示例 说明
string a-string="hello world!"; 双引号表示字符串
string-array a-string-array = "string one", "string two", "string three"; 字符串数组,字符串之间用逗号隔开
int 10进制:an-int = <1>;16进制:an-int = <0xab>; 32bit整型,放在尖括号内
array foo = <0xdeadbeef 1234 0>; 数组,值的类型相同,值之间用空格隔开
uint8-array a-byte-array = [00 01 ab]; 字节数组,以十六进制表示,不带0x ,位于方括号之间,值之间以空格隔开
boolean my-true-boolean; 布尔型。没有值,属性存在时表示true,不存在则表示false
phandle a-phandle = <&mynode>; 节点引用,尖括号中放&节点标签)
phandles some-phandles = <&mynode0 &mynode1 &mynode2>; 节点引用数组,引用之间用空格隔开
phandle-array a-phandle-array = <&mynode0 1 2>, <&mynode1 3 4>; 含节点引用的混合数组,数组中值的类型不同
  • 64bit的整型用array来实现:64 位整数按大端顺序写入两个 32 bit单元。值 0xaaaa0000bbbb1111 将被写入 <0xaaaa0000 0xbbbb1111>
  • 允许使用算术表达式:bar = <(2 * (1 << 5))>; 注意:表达式必须用括号括起来
  • 数组,phandles, phandle-array的类型也可以再次作为数组的值,类似于string-array,例如:
    foo = <1 2>, <3 4>;                         // Okay for 'type: array'
    foo = <&label1 &label2>, <&label3 &label4>; // Okay for 'type: phandles'
    foo = <&label1 1 2>, <&label2 3 4>;         // Okay for 'type: phandle-array'
    

别名和选择节点 链接到标题

通过别名或选择节点也可以达到引用指定节点而不需要使用节点的全路径

/dts-v1/;

/ {
    chosen {
            zephyr,console = &uart0;
    };

    aliases {
            my-uart = &uart0;
    };

    soc {
            uart0: serial@12340000 {
                    ...
            };
    };
};
  • /下的/aliases 和 /chosen 节点并不表示实际的硬件设备。
  • aliases的子节点用于表示别名,标签为uart0的节点别名为my-uart
  • chosen的字节点用于表示选择节点,标签为uart0的节点被选作zephyr,console的设备
  • 在Zephyr中可以理解chosen是Zephyr OS内预设好的别名,而aliases可以由用户自己起名。

文件包含: 链接到标题

为了更好的结构化Devicetree源文件,将共用的部分抽离出来形成单个dtsi文件,通过include可以直接使用,例如对于不同的板级Devicetree: esp32c3_devkitm.dts和esp32c3_luatos_core.dts由于都使用了相同的芯片esp32c3,因此对esp32c3 soc的Devicetree描述被独立为一个文件esp32c3_mini_n4.dtsi,大家通过include这个文件就可以在Devicetree中得到esp32c3所有的硬件描述了

#include <espressif/esp32c3/esp32c3_mini_n4.dtsi>

此外为了让dts/dtsi更具可读性,Devicetree中属性的值也可以直接引用.h中的宏,例如在ili9xxxx.h中定义了#define ILI9XXX_PIXEL_FORMAT_RGB565 0U,通过include这个h文件就可以在Devicetree中引用该宏, 比起直接写pixel-format = <0>;要易读多了。

#include <zephyr/dt-bindings/display/ili9xxx.h>
ili9342c: ili9342c@0 {
        compatible = "ilitek,ili9342c";
        mipi-max-frequency = <30000000>;
        reg = <0>;
        vin-supply = <&lcd_bg>;
        pixel-format = <ILI9XXX_PIXEL_FORMAT_RGB565>;
        display-inversion;
        width = <320>;
        height = <240>;
        rotation = <0>;
    };

参考 链接到标题

https://docs.zephyrproject.org/latest/build/dts/intro-syntax-structure.html