鸿蒙的设备树DTS怎么修改?-【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
发布时间:2026/9/3 16:52:15 作者:尧图编辑部 阅读量:1,286

改硬件几乎都从设备树开始。今天讲为什么你改了源文件、编译也成功板上却没变化——开机往往没用你以为的那一份。hdmi { status okay; };写进板级 dts全量构建 exit 0boot_linux 刷进去了。板上# cat /proc/device-tree/hdmife0a0000/status disabled或者更气人status 已经是 okayuart0 仍然没有/dev/ttyS0手动 bind 才打出pin gpio0-17 already requested by vcc-camera-regulator。设备树「写了」和「内核拿到的 dtb 里是这个值」和「probe 时这根脚还在」是三件事。开源鸿蒙在这块 RK3568 上设备树还是 Linux 那套。多出来的痛苦在缓存GN 不跟踪板级 dtscheckpoint 不跟踪device/board/U-Boot 真正交给内核的是 resource 分区里的rk-kernel.dtb。三层任何一层没动你改的那份源码只是源码。产品名rk3568_evb板级device/board/rk/rk3568_evb/kernel/。官方语法设备树。下面按「为什么板上不是我写的」写。1. include 链你改的是最后那一环编译 dts 时cpp 先展开 include再交给dtc。RK3568 常见链rk3568.dtsi SoCCPU、总线、uart、i2c、hdmi、vop 的寄存器和时钟 └── rk3568-pinctrl.dtsi 引脚功能组 uart0-xfer、i2c1-xfer、... rk3568-evb.dtsi 公版 EVBregulator、HDMI、若干 always-on └── 板级 rk3568-evb-linux.dts 你的板okay/disabled、抢脚删除、扩展模块板级文件开头通常是/dts-v1/; #include rk3568.dtsi #include rk3568-pinctrl.dtsi #include rk3568-evb.dtsi / { model RK3568 EVB OpenHarmony; compatible rockchip,rk3568-evb, rockchip,rk3568; chosen { bootargs consolettyS2,1500000n8 androidboot.hardwarerk30board \ hardwarerk30board earlyconuart8250,mmio32,0xfe660000; }; };SoC 级节点默认经常是disabled。公版 EVB dtsi 打开它自己需要的HDMI、若干 regulator、camera 供电。板级再用label覆写。你不写不等于没有。公版已经okay的 HDMI会在你打开 LVDS 的同时去抢主显示。你不写不等于脚是空的——vcc_camera已经占用 GPIO0_C1。查最终展开不要只读板级文件# 在 src_tmp 工作树 cd out/kernel/src_tmp/linux-5.10 export ARCHarm64 # 预处理 clang -E -nostdinc -I include -I arch/arm64/boot/dts/rockchip \ -undef -D__DTS__ -x assembler-with-cpp \ arch/arm64/boot/dts/rockchip/rk3568-evb-linux.dts \ -o /tmp/evb.pre.dts # 或直接反编译产物 dtc -I dtb -O dts out/kernel/OBJ/linux-5.10/arch/arm64/boot/dts/rockchip/rk3568-evb-linux.dtb \ 2/dev/null /tmp/live.dts/tmp/live.dts里才是「内核将要看到的」。板级源码只是输入之一。文件名按你树里的实际 dts 为准。build_kernel.sh会把device/board/rk/rk3568_evb/kernel/*.dts拷到arch/arm64/boot/dts/rockchip/。权威在板级目录src_tmp 是副本。2.label覆写以及/delete-property/dtsi 里给节点起了 labeluart0: serialfdd50000 { compatible rockchip,dw-apb-uart; status disabled; pinctrl-names default; pinctrl-0 uart0_xfer; };板级不要把整个节点再抄一遍。引用 labeluart0 { status okay; };合并规则后出现的属性覆盖先出现的。status被改成 okay。没提到的pinctrl-0保留。要删掉公版加的属性用/delete-property/不要写成空值vcc_camera { /delete-property/ gpio; /delete-property/ pinctrl-names; /delete-property/ pinctrl-0; /delete-property/ enable-active-high; };写成gpio;或gpio ;经常仍留下一个属性pinctrl 子系统照样 claim 这根脚。/delete-node/删整节点regulator 还有别人引用时不要整段删只删抢脚的那些属性。/delete-property/必须出现在引用节点里对已经合并进 blob 的属性生效。写在新节点里删不掉旧节点的属性。覆盖 pinctrl 示例i2c1 { status okay; clock-frequency 100000; /* MLX90614 一类器件 400k 不稳 */ }; hdmi { status disabled; /* 要亮 LVDS / MIPI / RGB 时关掉它 */ };同一时刻只允许一路显示okay。公版 EVB 常把 HDMI 打开。你只加了 LVDS 节点、没关 HDMI桌面跑到 HDMI 口上本地屏只亮背光。显示那篇展开这里只要知道加 okay 之前先 grep 已经 okay 的同类节点。grep -n status okay /tmp/live.dts | grep -iE hdmi|dsi|lvds|rgb|edp3. 三层缓存源码对了dtb 还是旧的3.1 GN 不跟踪板级 dts内核 action 的sources指向//kernel/linux/linux-5.10。device/board/rk/rk3568_evb/kernel/*.dts不在里面。ninja 看内核 git 没变判定内核 target 不必跑build_kernel.sh整段跳过拷 dts 的那一步根本不执行。指纹out/kernel/src_tmp/.../rk3568-evb-linux.dts时间戳停在上次内容是旧的。3.2is_kernel_change也不跟踪 boardkernel_source_checker.sh用一组DEPS[]目录的 git log clang md5和checkpoint/last_build.info比。DEPS[]通常不含device/board/。只改 dts、只改build_kernel.sh、只改 khdf 子 hcs判定结果是没变。增量 make 用旧.config、旧 dtb。3.3 强制重跑rm -rf out/kernel/checkpoint rm -rf out/kernel/src_tmp/linux-5.10/boot_linux # 更狠 # rm -rf out/kernel ./build.sh --product-name rk3568_evb --ccache --build-target kernel --fast-rebuild编完先验时间戳和内容再刷stat out/kernel/OBJ/linux-5.10/arch/arm64/boot/dts/rockchip/*.dtb grep -A2 hdmi device/board/rk/rk3568_evb/kernel/*.dts grep -A2 hdmi out/kernel/src_tmp/linux-5.10/arch/arm64/boot/dts/rockchip/*.dts三处对不上不要刷。全清out/kernel能跑通但会换模块签名钥匙已经在板上的.ko会Key was rejected。只删 checkpoint 通常够。khdf 的 hcb 是另一份缓存依赖只盯顶层hdf.hcs。改 dts 同时改了 HCS两边都要清。HDF 那篇写 hcb。这里只提醒清了 checkpoint 不等于清了 hcb。4. 只改 dts单编 dtb仍要刷 resource全量 3040 分钟。只改 dts 时走这条一两分钟ROOT/path/to/OpenHarmony export PATH$ROOT/prebuilts/clang/ohos/linux-x86_64/llvm/bin:$PATH export KBUILD_OUTPUT$ROOT/out/kernel/OBJ/linux-5.10 SRC$ROOT/out/kernel/src_tmp/linux-5.10 BOARD$ROOT/device/board/rk/rk3568_evb/kernel # 权威 → 工作树脚本没跑时你得手拷 cp -f $BOARD/rk3568-evb-linux.dts \ $SRC/arch/arm64/boot/dts/rockchip/rk3568-evb-linux.dts cd $SRC make LLVM1 LLVM_IAS1 CROSS_COMPILEaarch64-linux-gnu- ARCHarm64 \ rockchip/rk3568-evb-linux.dtb -j$(nproc) DTB$KBUILD_OUTPUT/arch/arm64/boot/dts/rockchip/rk3568-evb-linux.dtb ls -l $DTB dtc -I dtb -O dts $DTB 2/dev/null | grep -A5 serialfdd50000把新 dtb 打进 boot_linux 的 ext2有的树叫 toybrick.dtbBOOT$SRC/boot_linux mkdir -p $BOOT/extlinux cp -f $DTB $BOOT/extlinux/toybrick.dtb # Image / ramdisk / extlinux.conf 已经在 BOOT 里的话不要动 genext2fs -B 4096 -b 16384 -d $BOOT -i 8192 -U $ROOT/out/rk3568_evb/packages/phone/images/boot_linux.img-b 16384是 64 MB。你原来的 boot_linux 多大对ls -l再算。genext2fs 的块数乘 4096 等于镜像大小。这一步只更新了 boot_linux 里那份 dtb。开机 U-Boot 交的是 resource 里的rk-kernel.dtb。所以还要# 用新 dtb 重打 resourcelogo 沿用旧的 cd /tmp cp $DTB ./rk-kernel.dtb cp $BOARD/logo.bmp $BOARD/logo_kernel.bmp . resource_tool --pack --image$ROOT/out/rk3568_evb/packages/phone/images/resource.img \ rk-kernel.dtb logo.bmp logo_kernel.bmp然后刷p4 p5eMMC 和 SD 都写。只刷 p5/proc/device-tree可以完全不动。这是「单编成功、板上没变」的第一号原因。5. dtc 反编译以 blob 为证不以源码为证三份 blob三份真相# 1) OBJ 里刚编的 dtc -I dtb -O dts $DTB 2/dev/null /tmp/obj.dts # 2) boot_linux 里那份开机不一定用 debugfs -R dump /extlinux/toybrick.dtb /tmp/toybrick.dtb \ $ROOT/out/rk3568_evb/packages/phone/images/boot_linux.img dtc -I dtb -O dts /tmp/toybrick.dtb 2/dev/null /tmp/boot.dts # 3) resource 里那份U-Boot 交给内核 resource_tool --unpack --image$ROOT/out/rk3568_evb/packages/phone/images/resource.img \ --output/tmp/res dtc -I dtb -O dts /tmp/res/rk-kernel.dtb 2/dev/null /tmp/res.dts三份都 grep 同一节点for f in /tmp/obj.dts /tmp/boot.dts /tmp/res.dts; do echo $f grep -A12 serialfdd50000 $f | head -15 done板上# cat /proc/device-tree/serialfdd50000/status # hexdump -C /proc/device-tree/serialfdd50000/pinctrl-0 # ls /proc/device-tree/serialfdd50000/proc/device-tree是当前内核拿到的树只读。它和/tmp/res.dts不一致U-Boot 取了另一块盘的 resource或你刷 p4 刷到假文件上了。它和 res.dts 一致、和板级源码不一致构建没用你的源。dtc 反编译会丢掉 label 名uart0变成完整路径。不要惊讶。status、reg、pinctrl phandle 还在。检查抢脚看同一 gpio 被几个节点引用。反编译后搜RK_PC1或 gpio 单元里的 pin 号。更直接的是上板# mount -t debugfs none /sys/kernel/debug # cat /sys/kernel/debug/gpio | grep -i gpio0gpio-17后面跟的请求者名字就是占用 uart0 TX 的那个设备。6. 抢脚之一公版 regulator 占 LED GPIO底板上四颗用户 LED设备树里用gpio-leds。公版 EVB dtsi 可能已经把其中两根脚给了别的 always-on regulator例如 VGA 供电、PCIe 3.3 V。表现# ls /sys/class/leds/ work ← 系统灯还在说明 leds 子系统活着 # ls /sys/class/leds/user-led* ls: ... No such filedmesgleds-gpio: probe of ... failed with error -16-16是 EBUSY。gpio-leds对列表里任意一根脚gpiod_get失败整节点 probe 失败四颗灯全灭。不是四颗灯的驱动都坏了是其中一根被别人先要了。处理把公版那两个没有消费者的 regulator 关掉。这块板若没有 VGA 座、PCIe 供电走别的节点禁用是安全的vcc3v3_vga { status disabled; }; pcie30_3v3 { status disabled; };label 名按你 dtsi 里的实际来。反编译搜 GPIO0_D4 / GPIO0_D5示例属于谁。加自己的 gpio 节点之前先在 live dts 里搜这根脚。公版 EVB 为了演示把很多脚占用了板级硬件并不接那些外设。静默抢脚节点看起来 okayprobe 失败。7. 抢脚之二uart0 TX 被 camera regulator 抢走/dev/ttyS0的两种假活着# ls -l /dev/ttyS0 crw-rw-rw- 1 root root 4, 64 ... ← 真的字符设备 # ls -l /dev/ttyS0 -rw-r--r-- 1 root root 5 ... ← 假的有人 echo 进不存在的路径假的删掉tcgetattr会报Not a tty。真的没有/proc/tty/driver/serial无0:行。uart2/3/7 都在只有 uart0 缺席。dtb 里 uart0 已经 okay时钟、pinctrl 齐全——源码没问题。运行时# ls -l /sys/bus/platform/devices/fdd50000.serial/driver ls: ... No such file ← 没绑定 # cat /sys/kernel/debug/devices_deferred # 空 ← 不是 defer是返回了不会重试的错误手动 bind把错误逼出来dmesg 被 audit 冲掉时尤其有用# readlink /sys/bus/platform/devices/fe660000.serial/driver ../../../../bus/platform/drivers/dw-apb-uart # echo fdd50000.serial /sys/bus/platform/drivers/dw-apb-uart/bind # dmesg | tail -20典型真因pin gpio0-17 already requested by vcc-camera-regulator; cannot claim for fdd50000.serial could not request pin 17 (gpio0-17) from group uart0-xfer probe failed with error -22uart0 的 TX 是 GPIO0_C1gpio0 pin 17。公版vcc_cameraregulatorgpio gpio0 RK_PC1regulator-always-on开机先把这根脚要走。板级相机供电若已经走vcc_avdd/vcc_dovdd/vcc_dvdd那些固定轨根本不引用vcc_camera。它是 EVB 遗留。uart0 { status okay; }; vcc_camera { /delete-property/ gpio; /delete-property/ pinctrl-names; /delete-property/ pinctrl-0; /delete-property/ enable-active-high; };保留 regulator 节点always-on 无 gpio 无害只腾脚。重编 dtb刷 resource boot_linux。验# ls -l /dev/ttyS0 crw-rw-rw- ... 4, 64 # cat /proc/tty/driver/serial | grep ^0: 0: uart:16550A mmio:0xFDD50000 irq:...同类PWM 风扇、MIPI 相机的复位脚公版 dtsi 里常有 always-on 先占用。probe -22 already requested by去 dtsi 搜那个请求者再决定 disable 还是 delete-property。不要先改驱动。8. 状态、别名、和运行时 overlaystatus只认okay或ok和disabled。写成Ok、enable、空着行为按版本可能当 disabled。手写时复制已有节点。别名让内核给 uart 编号aliases { serial0 uart0; serial2 uart2; serial7 uart7; };没有 aliasestty 编号按 probe 顺序uart0 不一定是 ttyS0。这块板约定 uart2 调试口 ttyS2cmdline 写死consolettyS2,1500000。别名乱改console 会跑到别的口串口没字。运行时不要靠自己 mount 一个 dtbo 当日常手段。开源鸿蒙这套启动不走发行版 Linux 那种 U-Boot overlay 菜单。改硬件就改 dts打进 resource。/delete-node/公版的触摸节点当 HDF 和内核 goodix 会抢同一地址gt911 { status disabled; };或按节点路径删。I2C 上 0x5d 只能有一个主人。触摸那两篇写 HDF 侧。dts 侧至少保证内核原生驱动不要 okay。9. 一份最小板级片段能直接抄结构/* device/board/rk/rk3568_evb/kernel/rk3568-evb-linux.dts 片段 */ uart0 { status okay; }; uart2 { status okay; /* console */ }; uart7 { status okay; }; i2c1 { status okay; clock-frequency 100000; }; i2c2 { status okay; }; hdmi { status disabled; }; vcc_camera { /delete-property/ gpio; /delete-property/ pinctrl-names; /delete-property/ pinctrl-0; /delete-property/ enable-active-high; }; vcc3v3_vga { status disabled; }; gpio_leds { status okay; };每打开一个节点同时问公版有没有同类已经 okay这根脚有没有 always-on regulator问完再编。10. 失败判断源码是 okay/proc/device-tree是 disabled。三层缓存或没刷 resource或 U-Boot 取了 eMMC 旧 p4。对时间戳解 resource 的 dtb。/proc/device-tree已 okay没有设备节点。probe 失败。devices_deferred、手动 bind、dmesg。抢脚优先于时钟、优先于 compatible 写错。-22 already requested。公版 regulator / pinctrl。delete-property 或 disabled 那个请求者。-16 EBUSY 在 gpio-leds。列表里有一根脚被占整组灯灭。不要先换 LED 极性。改 dts 全量很快结束。GN / checkpoint 没看见。删 checkpoint。只刷了 boot_linux。开机 dtb 不变。刷 p4。两盘都刷。dtc 报 duplicate label。板级又定义了一个和 dtsi 同名的 label。用引用不要重开节点。预处理找不到 rk3568.dtsi。-I路径没指到arch/arm64/boot/dts/rockchip。在 src_tmp 里编不要在板级目录直接 dtc 源码include 解不开。菜单里 HDMI 和 LVDS 同时亮不了。不是 dts 语法错是这套 compositor 只认一块主显示。关一路。设备树不生效时不要加更多节点。先让/proc/device-tree和你反编译的 resource dtb 变成同一份再处理 probe。源码、OBJ、resource、板上四份对齐这件事情才算改完。系列第 12 篇 · 芯片瑞芯微 RK3568 · OpenHarmony 4.1API 11 · Linux 5.10