设备树(Device Tree)说明

本文面向软件工程师,讲清三件事:本 SDK 的设备树分几层、怎么拼起来、怎么改。看完就能自己改引脚、增删节点、给新外设加设备树,不用再”猜”。


一、为什么要有设备树

Linux 内核不再把板级硬件信息写死在代码里,而是由一份 设备树(.dts/.dtb) 描述:有几个串口、每个引脚什么复用、挂的芯片地址是多少、屏幕时序多少。内核启动时读取它。

所以改硬件(换屏、改引脚、接新芯片)时,大多数情况只改设备树,不用动驱动代码。


二、本 SDK 的设备树分三层

层 文件 作用
基础层 device/config/chips/t153/configs/t153_nand_base/linux-5.10-origin/board-base.dtsi SoC 与板子的通用节点:pwm2、uart0/3/8、twi2、gmac0/1、csi、nand 等,默认使能状态在这里定义
型号层 BoardConfig/BoardType/ypt153evb-board.dts 本型号的差异:屏使能、触摸/网口 PHY/音频引脚、摄像头引脚、时钟,以及对基础层节点的覆盖
U-Boot 层 BoardConfig/uboot_dts/ypt153evb-board.dts U-Boot 自己的设备树(含 LT8912B HDMI 桥等),与内核 DT 分开编译

编译时,基础层 + 型号层会 merge 成最终 DTB(out/.../sun8iw22p1-*.dtb),U-Boot 用自己那份。

记忆口诀:通用能力在 board-base.dtsi,本板差异在 BoardType/xxx.dts,引导阶段在 uboot_dts/xxx.dts。


三、设备树语法速览(够用版)

&twi2 {                       // &节点标签:引用已存在的节点(追加/覆盖属性)
    clock-frequency = <100000>;
    status = "okay";          // 开关:okay 使能 / disabled 关闭

    es8388: es8388@11 {       // 子节点:@后面是 I2C 地址
        compatible = "everest,es8323";   // 匹配驱动的名字
        reg = <0x11>;                     // 寄存器地址(I2C 从机地址)
    };
};

四个高频概念:

概念 含义 例子
compatible 驱动匹配字符串 "everest,es8323"
reg 地址(I2C 从机地址 / 内存地址) <0x11>
status 使能开关 "okay" / "disabled"
pinctrl-0 绑定引脚复用配置 <&uart0_pins_active>
phandle 用 &标签 引用别的节点 <&pio PA 20 ...>

四、怎么看当前运行中的设备树

开发板上(无需源码):

# 内核已解析的实时设备树
ls /proc/device-tree/
cat /proc/device-tree/model
# 等价位置(新版内核)
ls /sys/firmware/devicetree/base

想看二分制 DTB 反编译成文本:

dtc -I dtb -O dts -o board.dts out/.../sun8iw22p1-ypt153evb.dtb   # SDK 主机上

五、怎么改引脚(pinctrl)——以串口为例

引脚复用写在 pinctrl 子节点里,uart0 的引脚定义(基础层):

uart0_pins_active: uart0@0 {
    pins = "PB9", "PB10";        // 引脚
    function = "uart0";          // 复用功能名
    drive-strength = <10>;
};

然后节点引用它:

&uart0 {
    pinctrl-0 = <&uart0_pins_active>;   // 使用上面这组引脚
    status = "okay";
};

改引脚三步:

  1. 找到/新建 xxx_pins_active 节点,改 pins 和 function;
  2. 让外设节点 pinctrl-0 指向它;
  3. 编译 + 上板 cat /proc/device-tree/... 验证。

六、怎么增 / 删 / 启用节点

  • 启用:把 status 改成 "okay"(如 &uart2 { status = "okay"; };)。
  • 关闭:status = "disabled";(板级 dts 里大量这么关掉不用的串口)。
  • 新增:在 &twi2 下加子节点(如上文的 es8388@11),写好 compatible/reg 即可。
  • 覆盖:在型号层写 &节点 { 属性 = 新值; };,后加载的覆盖先加载的。

七、本板各外设真实设备树节点(汇总)

1) PWM 与背光(board-base.dtsi)

backlight0: backlight0 {
    compatible = "pwm-backlight";
    status = "okay";
    default-brightness-level = <180>;
    enable-gpios = <&pio PD 22 GPIO_ACTIVE_HIGH>;   // 背光使能
    pwms = <&pwm2 7 25000 1>;                        // 通道7,40kHz
};
&pwm2 { status = "okay"; };
&pwm2_7 { pinctrl-0 = <&pwm2_7_pins_active>; status = "okay"; };
pwm2_7_pins_active: pwm2_7@0 { pins = "PD23"; function = "pwm2_7"; };

2) 串口(board-base.dtsi + 型号层)

&uart0 { pinctrl-0 = <&uart0_pins_active>; status = "okay"; };  // PB9/PB10 调试
&uart3 { status = "okay"; };                                   // PK6/PK9/PK10/PK11
&uart8 { status = "okay"; };                                   // PB4/PB5
// 型号层把 uart1/2/4/5/6/7/9 置 disabled

3) I2C2 上的三颗芯片(型号层 &twi2)

&twi2 {
    clock-frequency = <100000>;
    status = "okay";
    pcf8563: rtc@51   { compatible = "nxp,pcf8563";        reg = <0x51>; };
    gt9xx:  gt9xx@5d  { compatible = "goodix,gt9xx";       reg = <0x5d>;
                        reset-gpios = <&pio PA 21 GPIO_ACTIVE_LOW>;
                        irq-gpios   = <&pio PA 22 GPIO_ACTIVE_LOW>; };
    es8388: es8388@11 { compatible = "everest,es8323";     reg = <0x11>; };
};

4) 双网口(型号层)

&gmac0 { phy-mode = "rgmii"; phy-handle = <&gmac0_phy0>; tx-delay = <5>; rx-delay = <5>; status = "okay"; };
&gmac1 { phy-mode = "rgmii"; phy-handle = <&gmac1_phy0>; tx-delay = <5>; rx-delay = <5>; status = "okay"; };
&mdio0 { gmac0_phy0: ethernet-phy@0 { compatible = "ethernet-phy-ieee802.3-c22","jl2101b"; reg = <0x0>; reset-gpios = <&pio PG 5 GPIO_ACTIVE_LOW>; }; };
&mdio1 { gmac1_phy0: ethernet-phy@1 { compatible = "ethernet-phy-ieee802.3-c22","jl2101b"; reg = <0x1>; reset-gpios = <&pio PJ 6 GPIO_ACTIVE_LOW>; }; };

5) 双模显示(型号层)

&rgb_panel0 { status = "okay";  enable0-gpios = <&pio PA 20 GPIO_ACTIVE_LOW>;
              display-timings { /* 1024x600,clock 61470000 */ }; };
&panel_0    { status = "disabled"; display-timings { /* 1920x1080,clock 148500000 */ }; };
&dsi0       { status = "disabled"; };
// U-Boot 启动时读 env(display_mode),用 fdt set 覆盖,此处是默认值

6) 摄像头与 CAN(型号层 / 基础层)

mipi_csi1_pins_a: mipi_csi1@0 { pins = "PK0","PK1","PK2","PK3","PK4","PK5"; function = "mcsia"; };
ncsi_8bit_pins_a: ncsi_8bit@0 { pins = "PA7","PA4","PA5","PA8","PA9","PA10","PA11","PA12","PA13","PA14","PA15"; function = "ncsi"; };
can1 { status = "disabled"; };   // CAN 出厂关闭

八、改完怎么编译与验证

./yz_build.sh config          # 选 t153_nand_base
./yz_build.sh kernel          # 只编内核(含新 DTB)
./yz_build.sh pack            # 打包固件
# 或整包
./yz_build.sh all

验证:

  • 主机:dtc -I dtb -O dts out/.../xxx.dtb | less 看是否已改。
  • 板上:cat /proc/device-tree/.../status、ls /sys/class/pwm、ls /dev/ttyAS*、i2cdetect -l。

九、常见错误

现象 原因 / 处理
改完没生效 只编了 kernel 没重打 pack;或改错层(改到 U-Boot 层而内核不读)
引脚无输出 pinctrl-0 没指向新 pin 节点;或引脚被别的节点占用
I2C 设备找不到 reg 地址写错;status 不是 okay;&twi2 本身没 okay
编译报错 Phandle ... not found 引用的标签(&xxx)不存在或拼错
屏黑 enable0-gpios 极性与实际相反;display-timings 时钟算错

十、小结

  1. 通用能力在 board-base.dtsi,本板差异在 BoardType/ypt153evb-board.dts,引导在 uboot_dts/;
  2. 改引脚改 pinctrl,启用/关闭改 status,加芯片在总线节点下加子节点;
  3. 改完 ./yz_build.sh kernel && ./yz_build.sh pack,用 /proc/device-tree 或 dtc 验证。

具体每个外设怎么用,见「外设驱动」目录各篇;每篇末尾也都附了对应的真实设备树片段。

作者:Mai  创建时间:2026-10-20 16:46
最后编辑:Mai  更新时间:2026-11-05 14:56