ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RK3588 U-Boot设备树定制指南:改动到生效完整流程

RK3588 U-Boot设备树定制指南:改动到生效完整流程 1. 为什么偏偏要改U-Boot的设备树拿到一块RK3588开发板上电、跑demo、看log大多数人第一步都是在内核里折腾。但玩过几个项目之后你会发现真正卡你一个星期的地方往往不在内核而在更早的U-Boot阶段。尤其是当你需要换调试串口、调显示分辨率、控一组GPIO去点灯或者拉风扇甚至只是想让某个外设在开机logo阶段就完成初始化时改内核设备树根本不顶用——U-Boot阶段根本还没走到那一步。这时候U-Boot自己的设备树文件就成了绕不开的关卡。设备树Device Tree本质上是一份描述硬件资源的数据结构从U-Boot到内核它一路传递硬件信息。RK3588这套方案里U-Boot里也有一份完整的设备树用来在启动最早期初始化DDR、时钟、串口、显示、电源域等关键资源。简单说内核设备树管的是Linux起来之后的设备而U-Boot设备树管的是Linux起来之前的那些硬件。很多使用RK3588做AI视觉、机器人、工业控制项目的朋友都会卡在“设备树明明改了但U-Boot阶段没反应”这种问题上本质上就是没分清楚自己在改哪一份。这篇文章适合正在做RK3588相关嵌入式开发、接触到U-Boot阶段定制需求、或者被启动早期外设问题折磨的开发者。我会尽量把从源码定位、设备树语法、编译打包到烧录验证的完整链路讲清楚并且把我在实际项目里踩过的坑、排查过的疑难杂症一并整理出来。不同基础的读者都能从这里找到可以直接抄的配置方法。2. 动手前的准备工作源码定位、工具链与备份2.1 从SDK里找到U-Boot设备树文件RK3588的SDK通常叫rk3588_linux_sdk解压之后U-Boot代码在u-boot目录下设备树源码则统一放在u-boot/arch/arm/dts/。你打开这个目录会发现里面有大量文件名带rk3588的dts/dtsi文件比如rk3588-evb.dts、rk3588-evb1-lp4-v10.dts、rk3588s.dtsi等等。这里有个特别容易看花眼的地方RK3588的U-Boot设备树并不是只有板级文件而是由多个dtsi一层层包含起来的。最常见的是下面这个继承链rk3588-evb.dts ├── rk3588s.dtsi # SoC级通用定义包含所有外设控制器节点 │ ├── rk3588s-pinctrl.dtsi # 所有引脚复用定义 │ ├── rk3588s-clk.dtsi # 时钟树定义 │ └── rk3588s-gpu.dtsi # GPU相关节点 ├── rk3588-evb.dtsi # 板级通用外设差异定义 └── rk3588-linux.dtsi # Linux内核启动相关定义命名规则上rk3588s代表的是RK3588S芯片版本少了一些接口的裁剪版evb是官方评估板的缩写后面带lp4、v10、v11的一般是具体硬件版本和内存颗粒类型。如果你用的是第三方核心板板级文件往往是从官方的rk3588-evb.dts复制过去改的。那么问题来了你第一件事就需要确认自己的开发板到底对应哪个dts文件然后在自己的板级dts而不是dtsi里做修改。这一点极其重要改到dtsi里虽然也可能生效但多人协作、SDK升级时很容易冲突而且后期维护会非常痛苦。2.2 编译工具链与U-Boot编译流程RK3588 U-Boot的编译不像普通U-Boot那样用make rk3588_evb_defconfig就完了瑞芯微把整个流程封了一层官方推荐在SDK顶层目录下直接编译。如果只想编译U-BootSDK里也更推荐在u-boot目录用自带的make.sh大致流程是cd u-boot ./make.sh rk3588 -j$(nproc)执行完以后会生成uboot.img、trust.img、rk3588_loader_all.bin这些固件。注意uboot.img并不是单纯的U-Boot ELF而是打包了SPL、TPL、DDR初始化代码和U-Boot主程序的镜像。这也是瑞芯微方案的特殊之处——它的DDR初始化、Trusted Firmware都不在U-Boot主程序里而是由trust.img等镜像管理。所以这里有个非常容易踩的认知误区你只改了设备树但烧录时没把uboot.img重新编出来并下载到开发板上那一切修改都不会生效。我见过不止一个同事改了设备树源码只跑了一下dtc生成了dtb然后直接拿去烧结果上电发现U-Boot仍然用旧配置。原因很简单U-Boot的设备树会被打包进uboot.img而不是像内核那样单独分成一个boot.img里的dtb分区。还有一点需要提醒编译前确保SDK里已经设置好交叉编译工具链路径。官方SDK一般自带或通过source envsetup.sh加载环境变量。如果你是自己搭的环境至少需要aarch64的裸机或Linux交叉工具链建议直接用预编译好的工具链别花时间自己编译。2.3 烧录前的备份策略不要等变砖了再后悔在开始改任何文件之前先把当前开发板上能跑固件完整备份出来。瑞芯微方案的烧录和备份方式相对成熟一般有两种一种是用Windows下的RKDevTool在“高级功能”里可以按分区读取固件把当前eMMC里的uboot、boot、misc、vendor等分区全部备份出来。另一种是Linux下用upgrade_tool命令大致是upgrade_tool rd # 读取设备信息 upgrade_tool rl # 备份整个eMMC镜像如果你用的开发板支持SD卡启动那备份更容易把当前eMMC上跑的系统做成SD卡启动镜像或者至少确保手里有出厂官方的完整固件包。备份的意义我在项目里体会极深改U-Boot设备树不是每次都能一次过一旦改错导致U-Boot起不来如果手里有原始固件用Maskrom模式几分钟就能救回来如果没有备份可能就得返厂烧写了周期少则两三天项目直接卡住。提示操作前请务必确认开发板的烧录模式。RK3588一般按住BOOT键再上电进入Loader模式如果U-Boot已经被你改挂则进入Maskrom模式。不同开发板操作方法会有细微差异建议先查自己板子的手册。3. 核心实操常见需求下设备树改法与重编译3.1 修改调试串口让log从另外一路UART出来很多RK3588项目为了调试方便会把调试串口从默认的UART2换到其他串口或者主板设计上明明用了UART3做log却想通过U-Boot设备树改过来。U-Boot阶段的串口输出由两部分决定一是chosen节点里的stdout-path二是在dtsi里定义好的串口控制器以及pinctrl。在U-Boot设备树里常见的串口节点定义是这种风格uart2 { status okay; pinctrl-names default; pinctrl-0 uart2m0_xfer; }; sdmmc { status okay; };要把默认log换到UART3最稳妥的做法是修改板级dts里的chosen节点和对应的uart节点类似/ { chosen { stdout-path uart3:1500000n8; }; }; uart3 { status okay; pinctrl-names default; pinctrl-0 uart3m1_xfer; };注意这里的uart3m1_xfer不是随便写的它表示UART3使用m1组引脚复用。具体用哪一组引脚取决于你的电路板上UART3接到了哪几个物理引脚。引脚复用组在rk3588s-pinctrl.dtsi里都有定义建议先搜索确认。这里有个容易被忽略的点修改串口时如果只改了chosen节点而对应的UART3没有配置status okayU-Boot会打印不出来任何log而如果UART3的pinctrl配置错了比如针脚被复用成了GPIO或者I2Clog会直接消失。而且这两类问题在过程中都很难通过看屏幕发现因为屏幕压根没输出。我的排查经验是改用示波器或者逻辑分析仪抓串口引脚上的波形或者对照原厂的硬件原理图确认引脚复用的组号正确。3.2 显示分辨率和U-Boot logo调整项目里使用RK3588做AI视觉或边缘计算网关时经常需要接HDMI或者LVDS屏幕在U-Boot阶段就要显示logo或调试信息。瑞芯微方案里U-Boot的显示逻辑由设备树中的显示相关节点决定常见有display-timings、route_hdmi、route_dsi、route_edp等。如果只是改分辨率核心是确保display-timings里设置的时序参数与屏体的规格一致。常见配置大概长这样route_hdmi { status okay; connect hdmi; }; hdmi { status okay; pinctrl-names default; pinctrl-0 hdmim1_tx0_cec hdmim1_tx0_sda hdmim1_tx0_scl hdmim1_tx1_cec hdmim1_tx1_sda hdmim1_tx1_scl; }; hdmi_in_vp0 { status okay; }; video_phy0 { status okay; }; display_timing { clock-frequency 148500000; // 1080p hactive 1920; vactive 1080; hfront-porch 88; hsync-len 44; hback-porch 148; vfront-porch 4; vsync-len 5; vback-porch 36; hsync-active 0; vsync-active 0; de-active 0; pixelclk-active 0; };注意RK3588的显示链路很复杂包含视频端口、video_phy、HDMI控制器、路由选择等多个环节光改display-timings不一定能生效还得保证route和对应的video_port节点处于okay状态。更麻烦的是瑞芯微不同版本的SDK在显示设备树结构上并不完全一致有的版本用hdmi、video_phy0有的版本用hdmi_phy之类命名所以建议先在dtsi里把现有节点结构摸清楚再动手。另外一个隐藏很深的问题logo图片资源其实不在U-Boot设备树里而是打包在resource.img里对应的是内核的logo_kernel.bmp、logo.bmp这些图片。U-Boot设备树只决定显示接口和时序logo图片本身则决定显示内容。如果你改了分辨率但logo花屏或者比例不对多半是图片分辨率与显示分辨率不匹配得重新生成resource.img而不是继续改设备树。3.3 在U-Boot阶段控制GPIO点亮LED或拉起风扇RK3588做工业产品时经常有这种需求开机瞬间就要点亮一个电源指示灯或者U-Boot阶段就把散热风扇转起来避免后面内核起来太慢导致温度过高。这些功能都需要在U-Boot设备树里做好GPIO的初始化。设备树层面需要确认GPIO引脚没有被其他外设复用并且在pinctrl中正确配置。在实际操作中我很少只依赖设备树去完成U-Boot阶段的GPIO控制因为U-Boot的设备树主要管“初始状态”真正动态控制靠的是U-Boot命令行或代码。例如你可以在U-Boot命令行下用# 查看GPIO分组状态 gpio status # 将GPIO4_B3设置为输出并拉高 gpio set 4-3-1这个4-3-1的含义是bank4、groupB、index3、输出高电平。要解释一下RK3588的GPIO分组是GPIO0到GPIO4每个bank有A/B/C/D四组每组有8个引脚。如果你在设备树里看到的某个引脚是RK_GPIO4 | RK_PB3 | GPIO_ACTIVE_HIGH那在U-Boot命令行里对应的编号就是4-3-1。更大的坑是引脚复用冲突。RK3588很多引脚默认会被dtsi里的某个外设节点占用比如I2C、UART、PWM。如果你直接用gpio set去操作一个已经被复用的引脚往往没有任何反应。排查时优先使用gpio status确认引脚当前是否被复用为GPIO。如果发现被其他外设占用就要在板级dts里把那个外设节点置为disabled例如i2c3 { status disabled; };然后再确认引脚组是否被设定为GPIO功能。你可以在pinctrl相关节点里查看是否有对应的gpio配置。大多数情况下一个引脚只要没被其他外设占用默认就是GPIO功能。另外调试时建议把GPIO的初始状态放在U-Boot环境变量里通过bootcmd设置而不是每次手工敲。例如在U-Boot的默认环境变量中加入一行让风扇在启动时立刻打开会稳定很多。这个控制需要在board代码里实现但设备树的正确配置是前提别跳过。3.4 修改启动顺序与分区相关配置RK3588从eMMC、SD卡还是U盘启动虽然主要由U-Boot环境变量和启动源决定但设备树里也有些间接影响。比如mmc节点、sdmmc节点和eMMC节点的状态决定了U-Boot阶段能不能正确识别这些存储介质。我在实际项目里遇到过这样一种情况把SD卡启动功能打开后U-Boot死活不识别我的SD卡。查了半天最后发现是SD卡接口的pinctrl配置在板级dts里被屏蔽了。补上之后sdmmc { status okay; pinctrl-names default; pinctrl-0 sdmmc_clk sdmmc_cmd sdmmc_bus4; bus-width 4; max-frequency 150000000; };U-Boot就能正常识别SD卡了。另一个容易忽略的问题是电压域配置。如果你的板子SD卡供电是3.3V但设备树里配了一个外部io-domain节点并且电压域不匹配会导致识别不稳定甚至烧卡。RK3588的电源域管理比较细致改存储相关节点时务必对照原理图检查io-domain的配置。启动顺序方面用fw_setenv或直接在/etc/u-boot/env部分SDK里修改boot_targets环境变量会直接得多比如把SD卡启动优先级提前boot_targetsmmc1 mmc0 usb0 pxe dhcp这里的mmc1对应SD卡mmc0对应eMMC。设备树主要负责“硬件能不能被正确初始化”启动顺序则是另一个维度两件事别混在一起排查。4. 踩坑实录我改坏过的那些地方4.1 设备树编译报错语法问题、节点重复与标签未定义改设备树最常见的报错其实跟硬件关系不大而是语法和参考问题。拿我自己的经历来说刚接触设备树时最常犯的错就是漏掉分号、注释嵌套错误、还有节点属性拼写错误。设备树语法本身不算复杂但RK3588的dtsi文件很大嵌套层级深一旦少一个符号编译报错信息往往指向一个很靠后的位置排查起来非常恼火。比较典型的两类编译错误Error: arch/arm/dts/rk3588-evb.dts:123.1-3 syntax error FATAL ERROR: Syntax error parsing input tree这种一般是少了分号或者括号不匹配。另一个常见的是Error: arch/arm/dts/rk3588-evb.dts:87.3-6 Label or path not found说明你引用的某个标签如uart2在dtsi里不存在。原因可能是这个SoC版本没有该外设也可能是你拼写错了。比如RK3588的UART3有m0、m1、m2等多组引脚复用标签可能是uart3m0_xfer、uart3m1_xfer、uart3m2_xfer少写一个m就找不到了。排查这类问题时别急着猜。用搜索工具在整个arch/arm/dts目录下搜一下标签定义确认存在再写。也可以先用SDK自带的编译脚本对单独的dts文件做语法检查不需要整包编译。比如make dtbs单独编译dts会快很多能大大缩短调试周期。4.2 U-Boot编译通过了但改动完全没生效这个坑是我见过最多的情况而且非常诡异。明明在板级dts里改了GPIO状态、串口配置./make.sh rk3588也确实重新跑完了烧录后U-Boot启动log却和之前一模一样。排除烧录不正确之后最可能的原因就是U-Boot编译时默认使用了多个设备树而你改的那个dts根本没被编译进去。RK3588的make.sh默认会为多种板型生成对应的dtb并根据实际运行的板型自动匹配。如果你在rk3588-evb.dts里改了内容但你的开发板在U-Boot启动时实际匹配到的是rk3588-evb1-lp4-v10.dtb那就等于白改。确认当前板子用的是哪份dtb可以在U-Boot log里搜索FDT或DTB相关输出也可以烧录后在U-Boot命令行执行fdt addr然后看打印出的内存地址对应哪个dtb文件再回到源码里核对板级dts。很多时候官方出厂固件默认匹配的板型和你以为的板型并不是同一个尤其是第三方核心板移植出来的DTS修改需要找到真正生效的那个文件。一个我再三强调的经验修改前先把config文件里默认的DEFAULT_DTB或CONFIG_DEFAULT_DEVICE_TREE确认清楚。瑞芯微某些SDK里还会用CONFIG_SYS_CONFIG_NAME来决定编译哪个板级文件搞错了就是改了个寂寞。4.3 pinctrl冲突和GPIO复用改了个寂寞RK3588的引脚复用是出了名的复杂一个引脚可能是UART、I2C、SPI、PWM、GPIO等好几种功能。设备树里如果两个节点同时引用同一个引脚编译不一定报错但实际运行时只有一个功能生效另一个则静默失效。我实际排查过的一个案例想在U-Boot阶段通过GPIO0_B5控制一个外部复位芯片但每次拉高之后很快又变成低电平。查来查去发现这个引脚在dtsi里被默认配置为I2C2的SDA。虽然板级dts里没有显式打开I2C2但I2C控制器的pinctrl配置还在U-Boot初始化I2C时把这个引脚复用了。解决办法是在板级dts里把I2C2节点显式设为disabledi2c2 { status disabled; pinctrl-names default; pinctrl-0 i2c2m1_xfer; };注意这里还要同时把pinctrl子项注释掉或者改为空。因为有的U-Boot版本即使节点disabledpinctrl解析还是会执行。更稳妥的做法是把这个引脚在pinctrl里显式配置成GPIO功能比如pinctrl { gpio0_b5 { rockchip,pins 0 RK_PB5 0 pcfg_pull_none; }; };这里0 RK_PB5 0的含义是bank0、B组5号引脚、功能mux为0即GPIO功能。这种三层括号的写法初看很劝退但理解了含义之后就清楚多了第一层是bank第二层是引脚位置第三层是复用功能号。排查引脚冲突时最实用的方法是检索dtsi里同一个引脚被引用了几次grep -R RK_PB5 arch/arm/dts/看输出结果凡是带1、2这些非零功能号的引用都可能和你的GPIO配置存在竞争关系。4.4 regulator和电源域配置错启动直接卡在DDR初始化RK3588的电源管理非常“敏感”设备树里的regulator节点如果配置不对轻则某个外设不工作重则卡死在DDR初始化或TF-A阶段。这个阶段屏幕通常还没有任何输出排查起来非常痛苦。我经历过一次改错vdd_cpu_big的regulator节点把输出电压范围写得太低结果U-Boot反复重启。log里最后能看到的输出往往是“DDR Version”一行之后就没下文了。这类问题和设备树语法无关纯粹是电源参数被改坏了。所以我的建议是在需要改regulator相关配置时除非有明确的原理图依据否则千万不要动电压范围、ramp rate这些参数。如果你只是想给某个外设断电或者调整供电策略优先用regulator-boot-on、regulator-always-on这些标志位而不要动电压数值。例如vcc5v0_otg { regulator-boot-on; regulator-always-on; };这样既能达到“上电就供电”的目的又不会引入电压错误的风险。如果一定要改电压值务必对照硬件原理图和官方datasheet双重确认。另外电源域节点和io-domain节点配置错误也可能导致eMMC、SD卡、USB等外设在U-Boot阶段工作不稳定。这类问题很难直接定位往往表现为“有时候启动正常有时候启动卡死”。遇到这种情况先别急着怀疑硬件回头检查自己的设备树有没有动过io_domain相关的配置。5. 调试设备树的几个实用工具与心得5.1 用dtc反编译确认编译结果和预期一致编译生成的dtb是二进制格式不方便直接查看。拿到U-Boot生成的uboot.img后如果你想知道里面真正打包进去的设备树内容是啥最可靠的办法是反编译。瑞芯微的SDK里通常自带dtc工具如果没有可以用系统自带的dtc -I dtb -O dts -o uboot_release.dts uboot_release.dtb反编译后重点检查你修改的节点是否真的存在比如chosen、uart3、gpio0_b5这些。这一步能帮你快速排除“改了但没编译进去”的问题。我习惯在每次烧录之前都做一次反编译确认尤其是多人协作的项目避免别人在你之后重新编译覆盖了你的修改。5.2 在U-Boot命令行里用fdt命令即时验证如果不方便反复烧录U-Boot命令行本身也提供了fdt操作工具可以在运行阶段实时查看和修改设备树内容。进入U-Boot命令行后# 将设备树加载到内存假设之前在fdt_addr fdt addr $fdt_addr # 打印某个节点 fdt print /chosen # 修改某个属性比如把stdout-path改成uart3 fdt set /chosen stdout-path uart3:1500000n8fdt set修改的是当前内存里的设备树重启后会丢失但用于验证思路非常方便。比如你怀疑串口配置有问题可以在U-Boot里先用fdt set把chosen改掉再执行boot启动内核看log是否从目标串口输出。如果有效再回到源码里修改并重新编译固件。这套流程能省下大量反复烧录的时间。还有个小技巧fdt list、fdt get value都是很好用的查询命令。建议在改动前先把当前设备树的关键节点内容全部打印出来存档这样对比起来一目了然。5.3 维护一份自己板子的差异补丁RK3588的官方SDK更新频率不低第三方板卡厂商也会不定期升级SDK。如果你在板级dts上做了零零散散的修改升级时很容易遗漏。我个人的习惯是所有设备树改动都集中在一个单独的文件里或者在板级dts里加上清晰的分区注释并且用git管理。每次升级SDK前先把自己的改动打成一个patch升级后再重新apply。举个例子我在U-Boot设备树里加的配置会统一放在板级dts末尾用以下格式分隔/* ---------- custom: fan led ---------- */ / { fan_gpio { compatible gpio-leds; status okay; }; };虽然这个语法不一定在所有SDK版本通用但关键是把改动视觉隔离出来后续维护不用到处找。别小看这个习惯RK3588的SDK结构复杂几个项目并行的时候没有这个习惯早晚要在版本合并上翻车。另外RK3588这款芯片在AI视觉、SLAM类项目里应用特别广很多人会同时改内核设备树摄像头、ISP相关节点和U-Boot设备树显示、存储、调试口。我的建议是内核和U-Boot两份设备树分开管理各自个的分支或标签。毕竟它们用的文件路径、编译方式、打包脚本完全不一样混在一起会让问题边界变得非常模糊。6. 回顾一段真实调试经历最后简单分享一个让我印象深刻的排查过程。某个RK3588项目里客户反馈设备经常在低温环境下启动失败概率性卡死在U-Boot阶段。一开始以为是硬件问题查了电源、晶振、DDR都没找到明确原因。后来反复对比设备树发现板级dts里有一个SD卡接口的电源控制引脚被配置成了普通GPIO输出但这个引脚的pinctrl同时被默认的pinctrl配置复用为其他功能。低温环境下电源轨上升斜率变缓SD卡检测引脚的状态不稳定最终导致U-Boot在初始化SD卡时卡死。解决办法其实很简单在板级dts里把这个引脚强制配置为GPIO功能并设置正确的上下拉pinctrl { sdmmc_pwr_en { rockchip,pins 4 RK_PA5 0 pcfg_pull_none; }; };问题的根源完全在设备树的pinctrl配置而不是硬件。这次调试让我深刻意识到U-Boot设备树不是“随便改改就能行”的东西它直接决定了RK3588上电后最早的几百毫秒里硬件以什么状态运行。这几百毫秒虽然短却往往是整个系统稳定性的基石。如果你现在正在被RK3588的U-Boot设备树问题卡住不妨按照这篇文章的顺序从头捋一遍先确认自己改的dtb文件真的被编译进uboot.img再确认pinctrl没有冲突最后再深入排查电源域和regulator。这三步走完大部分疑难杂症都能找到方向。尤其要记住改U-Boot设备树和改内核设备树是两套完全不同的方法论千万别用后者的思维去调试前者。
返回列表