保姆级教程:在Ubuntu 18.04上为AM40盒子编译和替换Firefly RK3399的DTB文件
深度定制Firefly RK3399设备树:从DTS反编译到boot.img打包全流程
在嵌入式开发领域,设备树(Device Tree)作为硬件描述的标准方式,已经成为Linux内核不可或缺的组成部分。对于使用Firefly RK3399开发板的开发者而言,掌握设备树的定制技术意味着能够精准控制硬件资源分配、优化外设驱动配置,甚至修复官方镜像中的兼容性问题。本文将带领你从零开始,完成从现有固件中提取DTB、反编译为可编辑的DTS、进行定制修改,最终重新打包为可刷写boot.img的完整流程。
1. 环境准备与工具链配置
在开始设备树定制之前,我们需要准备一个稳定的开发环境。Ubuntu 18.04 LTS作为长期支持版本,提供了完善的软件包支持和稳定的开发体验,是进行嵌入式开发的理想选择。
首先安装必要的工具链:
sudo apt update sudo apt install -y device-tree-compiler build-essential libssl-dev \ flex bison bc u-boot-tools android-tools-fsutils对于RK3399平台的开发,还需要配置特定的交叉编译工具链。虽然本文不涉及内核编译,但安装合适的工具链有助于后续可能的扩展开发:
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz -C /opt/验证工具链是否安装成功:
/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc --version2. 提取与反编译设备树
2.1 从现有固件中提取DTB文件
大多数RK3399设备的固件采用Android boot镜像格式,我们可以使用以下步骤提取其中的设备树:
# 解包boot.img获取原始资源 unpackbootimg -i boot.img -o unpacked_boot解包后会得到多个文件,其中unpacked_boot/second通常就是包含设备树的resource.img。进一步解压resource.img:
resource_tool --unpack --image=unpacked_boot/second --output=resource_unpacked2.2 DTB到DTS的反编译过程
获得原始DTB文件后,使用dtc工具进行反编译:
dtc -I dtb -O dts -o extracted.dts rk3399-smart-am40.dtb对于复杂的设备树(包含多个dtsi引用的情况),建议使用内核源码中的预处理脚本:
cpp -nostdinc -Iinclude -undef -x assembler-with-cpp \ -o preprocessed.dts arch/arm64/boot/dts/rockchip/rk3399-smart-am40.dts dtc -I dts -O dts -o full_dts.dts preprocessed.dts常见问题处理:
- 遇到"undefined reference"错误时,检查内核头文件路径是否正确
- 警告信息如
Warning (unit_address_vs_reg)通常可以忽略,不影响功能 - 反编译后的文件若缺少节点,可能是预处理阶段遗漏了必要的dtsi包含路径
3. 设备树修改实践
3.1 关键节点解析
在RK3399设备树中,以下几个节点最常需要修改:
| 节点路径 | 功能描述 | 典型修改内容 |
|---|---|---|
| /memory | 内存配置 | 内存容量、bank分布 |
| /cpus | CPU配置 | 频率调节、电压控制 |
| &i2c1 | I2C总线 | 外设地址、时钟频率 |
| &pwm0 | PWM控制器 | 占空比、极性设置 |
| /gpio | GPIO控制 | 引脚复用、驱动能力 |
3.2 典型修改案例
案例1:调整HDMI输出参数
&hdmi { status = "okay"; ddc-i2c-bus = <&i2c3>; rockchip,phy-table = <92812500 0x8009 0x0000>, <165000000 0x800b 0x0000>; };案例2:修改GPIO按键功能
&gpio_keys { button@1 { gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>; label = "Custom Button"; linux,code = <KEY_POWER>; }; };案例3:禁用未使用的外设
&spi1 { status = "disabled"; }; &uart4 { status = "disabled"; };修改完成后,建议使用dtc进行语法检查:
dtc -I dts -O dtb -o /dev/null test.dts4. 重新打包与刷写
4.1 生成新的resource.img
将修改后的DTB与可选资源文件打包:
resource_tool --pack --image=new_resource.img \ --dtb=custom.dtb --logo=logo.bmp --logo_kernel=logo_kernel.bmp4.2 构建完整boot.img
使用mkbootimg工具组合内核镜像和设备树:
mkbootimg --kernel zImage --second new_resource.img \ --output new_boot.img --pagesize 4096 --base 0x00200000关键参数说明:
--pagesize必须与目标设备匹配(通常4096)--base指定内核加载地址(RK3399通常为0x00200000)- 可添加
--cmdline参数传递内核启动参数
4.3 刷写验证流程
- 进入设备的Loader模式(通常按住Recovery键上电)
- 使用RKDevTool选择新生成的boot.img
- 仅勾选"boot"分区进行刷写,保留其他分区不变
- 刷写完成后重启设备,通过
dmesg | grep -i dts验证修改
调试技巧:
- 刷写前备份原始镜像
- 首次测试建议保持串口调试连接
- 若启动失败,可尝试仅更新resource.img保留原内核
- 完全无法启动时,通过Maskrom模式恢复
5. 高级技巧与问题排查
5.1 设备树调试方法
实时调试技巧:
# 查看已加载的设备树 cat /proc/device-tree/model ls /proc/device-tree/ # 检查特定节点属性 dtc -I fs -O dts /sys/firmware/devicetree/base内核日志分析:
dmesg | grep -iE 'dts|dtb|fdt'5.2 常见问题解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无法启动 | DTB地址错误 | 检查boot.img基地址参数 |
| 外设不工作 | 时钟未启用 | 验证相关clk节点状态 |
| 内核崩溃 | 内存配置错误 | 核对memory节点与硬件规格 |
| 驱动加载失败 | 兼容字符串不匹配 | 检查compatible属性 |
5.3 性能优化建议
电源管理优化:在CPU节点中添加合适的opp表
cpu_opp_table: opp-table-0 { opp-1416000000 { opp-hz = /bits/ 64 <1416000000>; opp-microvolt = <1150000>; }; };IO调度优化:调整MMC控制器参数
&sdhci { max-frequency = <150000000>; supports-emmc; non-removable; };中断响应优化:合理分配中断号
interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
在实际项目中,设备树的修改往往需要结合硬件原理图和内核驱动源码进行分析。建议每次只修改一个参数,验证效果后再进行其他调整,这样可以快速定位问题所在。
