3. 设备树基础概念:DTS语法、节点与属性

设备树(Device Tree)是嵌入式Linux中描述硬件拓扑结构的核心机制。对于Camera Sensor驱动开发而言,设备树是驱动与硬件之间的“桥梁”——它告诉内核:Sensor挂接在哪个I2C总线上、使用哪个地址、需要哪些电源、中断引脚是什么。本章将系统讲解设备树的语法、节点与属性,重点剖析reg和compatible这两个驱动匹配的关键属性。

学习提示:设备树文件通常以.dts(Device Tree Source)为后缀,编译后生成.dtb(Device Tree Blob)供内核解析。驱动开发者主要阅读和编写.dts文件,无需手动处理二进制格式。

3.1 设备树语法基础

设备树采用树形结构描述硬件,每个硬件设备对应一个节点(node),节点内包含若干属性(property)。语法风格类似C语言的大括号块结构,以分号结尾。

3.1.1 节点定义

节点的基本格式如下:

[label:] node-name[@unit-address] {
    [property-list];
    [child-node-list];
};
  • label:可选标签,用于在其他位置引用该节点(如&i2c0)。
  • node-name:节点名称,应体现设备类型(如camera-sensor)。
  • unit-address:设备地址,通常与reg属性中的第一个地址一致(如I2C从设备地址)。
  • property-list:属性列表,描述设备特性。
  • child-node-list:子节点列表,形成层级结构。
Camera Sensor典型节点示例:
/* I2C控制器节点 */
&i2c0 {
    status = "okay";
    clock-frequency = <400000>;

    /* Camera Sensor子节点 */
    ov5640: camera-sensor@3c {
        compatible = "ovti,ov5640";
        reg = <0x3c>;
        clocks = <&clks IMX8MM_CLK_CLK_OUT1>;
        reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
        powerdown-gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
        port {
            ov5640_ep: endpoint {
                remote-endpoint = <&mipi_csi_ep>;
                data-lanes = <1 2>;
            };
        };
    };
};

3.1.2 属性类型与写法

属性类型 语法示例 说明
字符串 compatible = "ovti,ov5640"; 双引号包裹,用于驱动匹配
32位整数 reg = <0x3c>; 尖括号包裹,可包含多个值
64位整数 reg = <0x00000000 0x10000000>; 两个32位整数拼接表示64位
布尔值 status = "okay"; 通过字符串"okay"/"disabled"表示
字节数组 local-mac-address = [00 11 22 33 44 55]; 方括号包裹,十六进制字节
字符串列表 clock-names = "xvclk", "mclk"; 逗号分隔多个字符串
整数列表 data-lanes = <1 2>; 尖括号内空格分隔
混合结构 gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; 包含phandle+整数+宏

3.2 核心属性详解:compatible 与 reg

在设备树中,compatible和reg是驱动与设备匹配的关键属性。内核通过compatible找到对应的驱动,通过reg获取设备的硬件地址。

3.2.1 compatible 属性——驱动匹配的“身份证”

compatible属性是一个字符串列表,用于指定设备所兼容的驱动。内核在启动时,会遍历设备树中的所有节点,将节点的compatible值与驱动中of_match_table里的条目进行匹配。

命名规范:

  • 格式:"厂商,设备型号"
  • 厂商名使用小写字母,设备型号使用小写字母和数字
  • 例如:"ovti,ov5640"、"sony,imx219"、"omnivision,ov8865"

匹配优先级:内核按照compatible列表中字符串的从左到右顺序进行匹配,越靠前的字符串优先级越高。

常见错误:驱动中的of_match_table与设备树中的compatible字符串必须完全一致(包括大小写和标点)。例如驱动中写"ovti,ov5640",设备树中写"ovti,OV5640"会导致匹配失败。

3.2.2 reg 属性——设备地址的“坐标”

reg属性用于描述设备在总线上的地址范围。对于I2C设备,reg通常只包含一个从设备地址(7位或10位)。对于内存映射设备,reg包含起始地址和长度。

reg属性的通用格式:

reg = <address1 length1 [address2 length2] ...>;

I2C设备的reg使用:

  • 对于7位I2C地址,直接写入地址值(如0x3c)
  • 对于10位I2C地址,需要设置reg的高位标志(通常由I2C控制器驱动处理)
  • Camera Sensor常用的I2C地址范围:0x20 ~ 0x7f
设备类型 reg示例 含义
I2C Sensor reg = <0x3c>; I2C从设备地址为0x3c
SPI设备 reg = <0>; SPI片选号为0
内存映射设备 reg = <0x30000000 0x1000>; 基地址0x30000000,长度0x1000
多地址设备 reg = <0x40000000 0x1000 0x50000000 0x2000>; 两个地址区间

3.3 设备树层级结构与节点关系

设备树的层级结构反映了硬件的物理连接关系。对于Camera Sensor,典型的层级路径为:

/ {
    soc {
        i2c@30a20000 {          /* I2C控制器 */
            camera-sensor@3c {  /* Camera Sensor */
                port {          /* 端口子节点 */
                    endpoint {  /* 端点描述 */
                        remote-endpoint = <&csi_ep>;
                    };
                };
            };
        };
        mipi-csi@32e00000 {     /* MIPI CSI接收器 */
            port {
                csi_ep: endpoint {
                    remote-endpoint = <&sensor_ep>;
                };
            };
        };
    };
};

这种层级结构通过remote-endpoint属性建立跨节点连接,形成完整的数据通路。

Camera Sensor设备树层级结构 / (根节点) soc i2c@30a20000 (I2C控制器) camera-sensor@3c (OV5640) port endpoint (remote-endpoint) mipi-csi@32e00000 port endpoint (remote-endpoint) 跨节点连接:通过 remote-endpoint 属性建立 Sensor 输出端与 CSI 接收端的绑定关系 虚线箭头表示数据流方向:Camera Sensor → MIPI CSI 接收器

3.4 设备树中的地址编码与#address-cells/#size-cells

设备树使用#address-cells和#size-cells属性定义reg属性中地址和长度的字长(以32位为单位)。这两个属性在父节点中定义,影响所有子节点的reg解析。

属性 含义 常见值
#address-cells 地址字段占用的32位字数量 1(32位地址)、2(64位地址)
#size-cells 长度字段占用的32位字数量 0(无长度)、1(32位长度)

I2C总线场景:

/* I2C控制器节点 */
i2c@30a20000 {
    #address-cells = <1>;   /* 地址占1个32位字 */
    #size-cells = <0>;      /* 无长度字段(I2C设备只有地址) */
    
    camera-sensor@3c {
        reg = <0x3c>;        /* 解析为:地址=0x3c,无长度 */
    };
};

内存映射设备场景:

/* 内存映射设备 */
soc {
    #address-cells = <2>;   /* 64位地址 */
    #size-cells = <2>;      /* 64位长度 */
    
    display-controller@32e00000 {
        reg = <0x0 0x32e00000 0x0 0x1000>;
        /* 解析为:地址=0x32e00000,长度=0x1000 */
    };
};

3.5 设备树中的phandle与引用

设备树使用phandle(指针句柄)实现节点间的引用。常见的引用方式有两种:

  1. 显式phandle:使用phandle = <编号>属性定义句柄
  2. 隐式phandle:使用标签名: 节点名语法,编译器自动生成phandle

Camera Sensor中的典型引用:

/* 定义带标签的节点 */
ov5640: camera-sensor@3c {
    compatible = "ovti,ov5640";
    reg = <0x3c>;
    /* ... */
    port {
        ov5640_ep: endpoint {
            remote-endpoint = <&csi_ep>;  /* 引用CSI端点 */
        };
    };
};

/* 在另一个节点中引用Sensor */
csi@32e00000 {
    /* ... */
    port {
        csi_ep: endpoint {
            remote-endpoint = <&ov5640_ep>;  /* 引用Sensor端点 */
        };
    };
};
关键理解:&ov5640_ep中的&符号表示引用标签ov5640_ep对应的节点。编译器会将这种引用转换为实际的phandle值(一个32位整数),内核通过这个整数找到对应的设备树节点。

3.6 设备树编译与反编译

设备树源文件(.dts)需要编译为二进制格式(.dtb)才能被内核使用。常用的工具和命令如下:

操作 命令 说明
编译DTS→DTB dtc -I dts -O dtb -o output.dtb input.dts 将文本格式编译为二进制
反编译DTB→DTS dtc -I dtb -O dts -o output.dts input.dtb 将二进制反编译为文本(调试用)
查看DTB内容 dtc -I dtb -O dts input.dtb | less 管道输出到分页器查看
检查DTB语法 dtc -I dts -O dtb input.dts >/dev/null 只检查语法不输出文件
调试技巧:在开发Camera Sensor驱动时,经常需要反编译目标平台的.dtb文件,查看I2C节点和Sensor节点的实际配置。使用dtc -I dtb -O dts命令可以快速获取可读的设备树内容。

3.7 本章小结

设备树是嵌入式Linux驱动开发的基石。对于Camera Sensor驱动,需要重点掌握:

  • 节点结构:Sensor作为I2C控制器的子节点,通过compatible属性与驱动匹配
  • reg属性:指定I2C从设备地址,驱动通过该地址与Sensor通信
  • 层级关系:通过port和endpoint子节点描述数据通路
  • 跨节点引用:使用remote-endpoint和phandle建立Sensor与CSI接收器的连接