找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4812

积分

0

好友

620

主题
发表于 1 小时前 | 查看: 3| 回复: 0

设备树(Device Tree)是嵌入式 Linux 开发中绕不开的一环。它用树形结构描述硬件平台上的设备信息,由节点(Node)和属性(Property)两种基本元素组成。设备树源文件以 .dts 为扩展名,通过 DTC(Device Tree Compiler)编译为 .dtb 二进制文件供内核使用。

本文梳理了设备树的核心语法、常用属性与节点写法,并结合 i.MX6ULL 平台风格给出 USART、I2C、SPI、CAN 等外设的完整配置示例,云栈社区希望这份指南能帮你少踩几个坑。

01 设备树的格式

设备树源文件以版本声明开头,根节点用 / 表示:

/dts-v1/;

/ {
    /* 根节点内容 */
    node1 {
        /* 子节点 */
    };

    node2 {
        /* 子节点 */
    };
};

注意:设备树有且仅有一个根节点,所有硬件信息都挂载在根节点之下。

1.1 基本结构

节点的基本定义格式为:

[label:] node-name[@unit-address] {
    [properties definitions]
    [child nodes]
};
  • node-name:节点名称,最长 31 个字符,应清晰描述设备功能
  • @unit-address:单元地址,通常是设备的寄存器基地址或总线地址
  • label:标签(可选),用于在其他地方引用该节点
  • properties definitions:节点的属性定义
  • child nodes:子节点定义

注意:节点内可包含属性定义和子节点,属性必须放在子节点之前。

一个串口节点的示例:

uart1: serial@02020000 {
    compatible = "fsl,imx6ul-uart", "fsl,imx-uart";
    reg = <0x02020000 0x4000>;
    interrupts = <0 26 4>;
    status = "disabled";
};

其中 uart1 是标签,serial 是节点名,@02020000 是单元地址。

1.2 属性值的数据类型

设备树属性支持多种数据类型:

类型 语法 示例
32 位整数数组(cells) < > 包围,空格分隔 reg = <0x02020000 0x4000>;
字符串 双引号包围 compatible = "fsl,imx-uart";
字符串列表 逗号分隔多个字符串 compatible = "v1", "v2", "v3";
二进制数据 [ ] 包围,十六进制字节 local-mac-address = [00 11 22 33 44 55];
空属性 仅有属性名,无值 status;
混合类型 逗号分隔不同部分 example = <0xf00f0000 19>, "a string";

注意:
① 64 位值可用两个 32 位 cell 表示:clock-frequency = <0x00000001 0x00000000>;
② 可用 /bits/ 前缀指定 8、16、64 位元素:interrupts = /bits/ 8 <17 0xc>;
③ 属性值支持算术、位运算、逻辑运算和条件表达式。

1.3 注释风格

设备树沿用了 C 语言的注释风格:

  • 单行注释:// 注释内容
  • 多行注释:/* 注释内容 */

1.4 节点删除与属性删除

在 .dts 文件中可以删除先前在 .dtsi 中定义的节点或属性:

/delete-node/ &can1;           // 删除节点
/delete-property/ ethernet0;   // 删除属性

02 dts 文件如何包含 dtsi 文件

2.1 文件类型说明

文件类型 全称 作用
.dts Device Tree Source 板级入口文件,每种主板一份
.dtsi Device Tree Source Include 可复用的公共部分,类似 C 语言的 .h 文件
.dtb Device Tree Blob 编译后的二进制文件,内核直接使用

2.2 包含语法

设备树使用 C 预处理器语法进行文件包含:

#include "rk3576.dtsi"
#include "rk3576-rk806.dtsi"
#include <dt-bindings/gpio/gpio.h>

关键点:

① 可使用 #include 引用 .dtsi 文件,也可以引用 .h 头文件

② 被包含的文件名应以 .dtsi 结尾

③ 包含文件可以递归包含其他文件

④ 所有 #include 的内容在编译时会被递归合并到最终的 .dtb 中

2.3 分层设计思想

设备树采用分层设计实现硬件描述的模块化和可重用:

  • SoC 级(.dtsi):描述芯片公共资源,如 CPU、中断控制器、片上外设控制器等
  • 板级(.dts):描述具体开发板的差异信息,如电源、外设、引脚复用等

以 Rockchip 平台为例的包含层次:

tspi-3m-rk3576.dts(主板入口)
    ├── #include "rk3576.dtsi"(SoC 公共资源)
    └── #include "rk3576-rk806.dtsi"(板级电源配置)

03 嵌入式开发中常用到的属性

3.1 compatible — 兼容性属性

用于设备与驱动的匹配,内核通过匹配 compatible 值来识别设备并绑定对应驱动。推荐格式为 "manufacturer,model":

compatible = "fsl,imx6ul-uart", "fsl,imx-uart";

内核会按顺序匹配,直到找到匹配的驱动。

3.2 reg — 寄存器地址属性

描述设备的寄存器地址空间,格式为 <基地址 地址长度>:

reg = <0x02020000 0x4000>;

对于总线上的设备,reg 也可表示设备地址。

3.3 #address-cells 和 #size-cells — 地址/大小单元格数

定义子节点中 reg 属性的解析格式:

#address-cells = <1>;  // reg 中地址占 1 个 cell
#size-cells = <1>;     // reg 中大小占 1 个 cell

如果 #address-cells=<1>、#size-cells=<1>,则子节点 reg = <0x0 0x100> 表示地址 0x0、大小 0x100。

3.4 interrupts — 中断属性

描述设备的中断信息:

interrupts = <0 26 4>;

格式取决于中断控制器,通常包含中断号、触发类型等。

3.5 clocks — 时钟属性

描述设备使用的时钟:

clocks = <&clk1>, <&clk2>;

通过 & 引用其他节点(phandle)来指定时钟源。

3.6 status — 设备状态属性

描述设备的当前状态:

值 含义
"okay" 设备正常工作,内核会初始化该设备
"disabled" 设备禁用,内核不会初始化

3.7 phandle 与标签引用性

phandle 是节点的唯一标识符,用于节点间相互引用。更常用的方式是使用标签(label):

// 定义标签
uart1: serial@02020000 {
    compatible = "fsl,imx-uart";
};

// 引用标签(编译时自动展开为 phandle)
some-device {
    uart = <&uart1>;
};

&uart1 会被编译为 uart1 节点的 phandle 值。

3.8 其他常用属性

属性 用途
model 设备型号描述,根节点用于传递板级型号
device_type 设备类型,如 "cpu"、"memory"
pinctrl-* 引脚控制配置
dma-names / dmas DMA 通道配置
clock-frequency 时钟频率

04 嵌入式开发中常用到的节点(node)

4.1 根节点 — /

所有设备树的顶层节点,有且仅有一个:

/ {
    model = "My Board";
    compatible = "myvendor,myboard";
    #address-cells = <1>;
    #size-cells = <1>;
};

4.2 /cpus — CPU 节点

描述系统的 CPU 信息,包含一个或多个 cpu 子节点:

cpus {
    #address-cells = <1>;
    #size-cells = <0>;

    cpu0: cpu@0 {
        device_type = "cpu";
        compatible = "arm,cortex-a7";
        reg = <0>;
        clock-frequency = <996000000>;
    };
};

4.3 /memory — 内存节点

描述系统的物理内存布局,所有设备树必须包含:

memory@80000000 {
    device_type = "memory";
    reg = <0x80000000 0x20000000>;  // 512MB 内存
};

4.4 /chosen — 选择节点

用于传递内核启动参数等配置信息,不描述硬件:

chosen {
    bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait";
    stdout-path = &uart1;
};

4.5 /aliases — 别名节点

为设备节点定义别名,便于内核和用户空间引用:

aliases {
    serial0 = &uart1;
    serial1 = &uart2;
    i2c0 = &i2c1;
    spi0 = &ecspi1;
    can0 = &flexcan1;
};

4.6 /soc — 片上系统节点

通常作为 SoC 所有片上外设的容器节点:

soc {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "simple-bus";
    ranges;

    /* 各种外设控制器节点 */
    uart1: serial@02020000 { ... };
    i2c1: i2c@021a0000 { ... };
};

4.7 外设控制器节点

各类接口控制器节点,包括:

  • UART/USART:serial@ 或 uart@
  • I2C:i2c@
  • SPI:spi@
  • CAN:can@ 或 flexcan@
  • GPIO:gpio@
  • Pinctrl:pinctrl@

05 设备树示例(包含 USART、I2C、SPI、CAN)

以下是一个综合示例,展示了嵌入式开发板中常用的 USART、I2C、SPI、CAN 等接口的设备树配置(基于 i.MX6ULL 平台风格):

/dts-v1/;

#include "imx6ull.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>

/ {
    model = "My Embedded Board";
    compatible = "myvendor,myboard", "fsl,imx6ull";

    /* 内存配置 */
    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x20000000>;  /* 512MB DDR */
    };

    /* 启动参数 */
    chosen {
        bootargs = "console=ttymxc0,115200 root=/dev/mmcblk0p2 rootwait rw";
        stdout-path = &uart1;
    };

    /* 别名 */
    aliases {
        serial0 = &uart1;
        serial1 = &uart2;
        serial2 = &uart3;
        i2c0 = &i2c1;
        i2c1 = &i2c2;
        spi0 = &ecspi1;
        can0 = &flexcan1;
    };
};

&uart1 {  /* USART1 — 调试串口 */
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_uart1>;
    status = "okay";
};

&uart2 {  /* USART2 — RS232 通信 */
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_uart2>;
    fsl,uart-has-rtscts;
    status = "okay";
};

&uart3 {  /* USART3 — RS485 模式 */
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_uart3>;
    fsl,uart-has-rtscts;
    linux,rs485-enabled-at-boot-time;
    status = "okay";
};

&i2c1 {  /* I2C1 — 挂载传感器和 RTC */
    clock-frequency = <100000>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c1>;
    status = "okay";

    /* I2C 温湿度传感器 */
    sensor@44 {
        compatible = "ti,hdc1080";
        reg = <0x44>;
        status = "okay";
    };

    /* I2C RTC 时钟芯片 */
    rtc@68 {
        compatible = "nxp,pcf8563";
        reg = <0x68>;
        status = "okay";
    };
};

&i2c2 {  /* I2C2 — 挂载 EEPROM */
    clock-frequency = <400000>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c2>;
    status = "okay";

    eeprom@50 {
        compatible = "atmel,24c02";
        reg = <0x50>;
        pagesize = <16>;
        status = "okay";
    };
};

&ecspi1 {  /* SPI1 — 挂载 ADC 芯片 */
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_ecspi1>;
    cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW>;
    status = "okay";

    adc@0 {
        compatible = "ti,ads7950";
        reg = <0>;                    /* SPI 片选 0 */
        spi-max-frequency = <1000000>;
        status = "okay";
    };
};

&ecspi2 {  /* SPI2 — 挂载 Flash */
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_ecspi2>;
    cs-gpios = <&gpio4 24 GPIO_ACTIVE_LOW>;
    status = "okay";

    flash@0 {
        compatible = "winbond,w25q128";
        reg = <0>;
        spi-max-frequency = <50000000>;
        status = "okay";
    };
};

&flexcan1 {  /* CAN1 */
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_flexcan1>;
    status = "okay";
};

&flexcan2 {  /* CAN2 */
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_flexcan2>;
    status = "okay";
};

示例解析

文件头:/dts-v1/; 声明版本,通过 #include 引入 SoC 的公共 dtsi 文件和头文件。

根节点:定义板级 model 和 compatible,以及 memory、chosen、aliases 等标准节点。

USART 配置:&uart1 使用 & 引用 SoC dtsi 中定义的节点;pinctrl-* 指定引脚复用配置;status = "okay" 使能设备;RS485 相关属性用于 RS485 模式。

I2C 配置:clock-frequency 设置总线速率;I2C 子节点用 reg 指定从设备地址;每个从设备有自己的 compatible 和 status。

SPI 配置:cs-gpios 指定片选引脚;子节点用 reg 指定片选编号;spi-max-frequency 指定最大 SPI 时钟频率。

CAN 配置:使用 &flexcan1、&flexcan2 引用 CAN 控制器节点,配置 pinctrl 后使能。




上一篇:4万次请求实测:GPT-6 Astra开xhigh反而更省Token?
下一篇:Claude Code 2.1.277 终于读 AGENTS.md 了:内置 mod 插件机制首次亮相
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-9-30 08:13 , Processed in 1.394873 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表