在嵌入式Linux开发中,设备树(Device Tree)是连接硬件与内核的关键桥梁,尤其对于瑞芯微RK3288这类高性能ARM芯片而言,熟练掌握设备树的配置与修改,是实现硬件驱动适配、系统定制的核心能力。本文将从设备树的基本概念出发,聚焦RK3288平台,详解其设备树的结构、核心作用、常见修改场景及编译更新流程,帮助嵌入式开发者快速上手。
一、先搞懂:设备树是什么?
设备树(Device Tree,简称DTS)是一种采用ASCII文本格式描述硬件的数据结构,起源于OpenFirmware(OF)规范。在传统的Linux 2.6内核中,ARM架构的硬件细节(如CPU数量、内存地址、外设接口等)大多硬编码在内核源码的arch/arm/plat-xxx和arch/arm/mach-xxx目录下,导致内核冗余度高、适配不同开发板时需要大量修改内核代码。
引入设备树后,这一问题得到了根本性解决:硬件细节不再嵌入内核,而是通过独立的设备树文件描述,Bootloader启动时会将设备树传递给内核,内核根据设备树信息自动识别硬件、匹配驱动,实现了"内核与硬件解耦"。对于RK3288平台而言,设备树就是其硬件的"说明书",内核通过这份"说明书"就能清楚了解开发板的硬件配置。
二、RK3288设备树的核心文件与结构
RK3288的设备树文件主要位于Linux内核源码的arch/arm/boot/dts/目录下,核心文件分为两类:.dtsi(设备树头文件)和.dts(设备树源文件),二者的关系类似C语言的头文件与源文件。
- 核心文件分类
-
rk3288.dtsi:RK3288芯片级的通用设备树文件,包含芯片核心硬件的描述(如CPU、中断控制器、时钟控制器、GPIO控制器等),是所有基于RK3288的开发板设备树的基础,多个开发板可共用这份文件,避免重复编码。
-
rk3288-xxx.dts:针对具体RK3288开发板的设备树文件(如rk3288-firefly.dts对应萤火虫RK3288开发板),主要描述开发板级的硬件配置(如外设的引脚连接、外设型号等),通过#include "rk3288.dtsi"引入芯片级通用配置。
-
相关头文件:位于dt-bindings/目录下,如gpio/gpio.h(GPIO相关宏定义)、clock/rk3288-cru.h(时钟相关宏定义)等,提供设备树中常用的宏定义,简化配置编写。
- 核心结构解析
RK3288设备树采用"节点-属性"的树形结构,所有节点都以根节点/为起点,子节点嵌套在父节点中,属性则用于描述节点的具体信息(如地址、兼容型号、中断信息等)。以下是核心结构示例及说明:
// 1. 头文件引入
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "rk3288.dtsi"
// 2. 根节点
/ {
#address-cells = <2>; // 子节点地址字段的长度(2个32位值)
#size-cells = <2>; // 子节点地址长度字段的长度(2个32位值)
compatible = "rockchip,rk3288"; // 匹配内核中的机器描述,用于内核识别开发板
interrupt-parent = <&gic>; // 默认中断控制器(引用gic节点)
};
// 3. 别名节点(简化节点引用)
aliases {
serial0 = <&uart0>; // 将uart0节点别名设为serial0,后续可通过serial0快速引用
i2c0 = <&i2c0>;
spi0 = <&spi0>;
};
// 4. 子节点示例(以UART0为例)
uart0: serial@ff690000 {
compatible = "rockchip,rk3288-uart"; // 兼容型号,用于匹配UART驱动
reg = <0x0 0xff690000 0x0 0x1000>; // 寄存器地址与长度
interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>; // 中断信息
clocks = <&cru UART0_CLK>; // 时钟源
status = "okay"; // 使能该节点("disabled"为禁用)
};
关键属性说明:
-
compatible:最核心的属性,用于"驱动匹配",内核通过该属性查找对应的驱动程序(如UART节点的"rockchip,rk3288-uart"会匹配RK3288的UART驱动)。
-
reg:描述设备寄存器的物理地址和长度,内核通过该属性映射寄存器地址,实现对硬件的操作。
-
interrupts:中断信息,格式由中断控制器决定(RK3288使用GIC中断控制器,格式为<中断类型 中断号 触发方式>)。
-
status:控制节点启用/禁用,"okay"表示启用该外设,"disabled"表示禁用。
三、RK3288设备树的核心作用
对于RK3288开发而言,设备树的核心作用是"描述硬件、衔接内核与驱动",具体体现在以下3个场景:
- 内核识别硬件配置
设备树向内核传递的核心硬件信息包括:CPU数量与型号(RK3288为4核Cortex-A12)、内存基地址与大小(如2GB内存的地址范围)、外设的寄存器地址、中断号、GPIO引脚分配等。内核启动时解析设备树,自动识别这些硬件信息,无需硬编码。
- 驱动匹配与外设启用
内核中的驱动程序会预先定义支持的compatible属性值,当设备树节点的compatible属性与驱动的属性匹配时,驱动会自动加载并绑定到该设备。例如,若要启用RK3288的SPI0外设,只需在设备树中设置SPI0节点的status = "okay",并配置正确的compatible属性,内核就能自动匹配SPI驱动。
- 硬件配置定制化
不同RK3288开发板的外设配置可能不同(如A开发板使用SPI0连接TFT屏幕,B开发板使用SPI1连接温湿度传感器),通过修改设备树的节点配置(如引脚分配、compatible属性、时钟频率等),即可实现不同硬件配置的定制化,无需修改内核代码。例如,在RK3288上添加ILI9341 SPI屏幕时,只需在设备树的SPI节点下添加ILI9341的子节点,配置引脚、时钟频率等信息即可。
四、RK3288设备树的常见修改场景与示例
在RK3288开发中,设备树的修改是高频操作,以下是2个最常见的修改场景及示例:
场景1:启用/禁用外设(以UART0为例)
若开发板需要使用UART0外设,只需找到UART0节点,将status属性设为"okay";若不需要使用,设为"disabled"即可:
uart0: serial@ff690000 {
compatible = "rockchip,rk3288-uart";
reg = <0x0 0xff690000 0x0 0x1000>;
interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru UART0_CLK>;
status = "okay"; // 启用UART0(禁用改为"disabled")
};
场景2:添加SPI外设(以ILI9341 TFT屏幕为例)
若要在RK3288的SPI0上连接ILI9341 TFT屏幕,需在SPI0节点下添加ILI9341的子节点,配置引脚、时钟频率等信息:
&spi0 { // 引用SPI0节点(通过别名或节点路径)
status = "okay"; // 启用SPI0
ili9341@0 { // ILI9341子节点,@0表示SPI从设备地址为0
compatible = "ilitek,ili9341"; // 匹配ILI9341驱动
reg = <0>;
spi-max-frequency = <50000000>; // SPI最大频率50MHz
rotate = <270>; // 屏幕旋转270度
bgr; // 颜色格式为BGR
reset-gpios = <&gpio7 3 GPIO_ACTIVE_LOW>; // 复位引脚(GPIO7_3,低电平有效)
dc-gpios = <&gpio7 9 GPIO_ACTIVE_LOW>; // 数据/命令引脚(GPIO7_9,低电平有效)
status = "okay";
};
};
五、RK3288设备树的编译与更新流程
修改设备树后,无需重新编译整个内核,只需单独编译设备树,生成二进制文件.dtb,再更新到开发板的boot分区即可生效。具体步骤如下:
- 编译设备树
进入Linux内核源码根目录,执行以下命令编译设备树(需指定架构和交叉编译工具链):
# 编译所有RK3288相关的设备树(生成.dtb文件)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- dtbs
# 或单独编译指定开发板的设备树(如rk3288-firefly.dts)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- rk3288-firefly.dtb
编译完成后,生成的.dtb文件位于arch/arm/boot/dts/目录下(如rk3288-firefly.dtb)。
- 更新设备树到开发板
有两种常用方式更新设备树,可根据开发场景选择:
方式1:直接挂载boot分区替换(推荐,无需重新烧录镜像)
RK3288的boot分区通常为FAT32格式,可直接在开发板上挂载后替换.dtb文件:
# 1. 查看boot分区设备名(通常为/dev/mmcblk2p4,以实际为准)
fdisk -l
# 2. 创建挂载点并挂载boot分区
mkdir -p /mnt/boot
mount /dev/mmcblk2p4 /mnt/boot
# 3. 将新编译的.dtb文件复制到boot分区(替换原有文件)
cp arch/arm/boot/dts/rk3288-firefly.dtb /mnt/boot/
# 4. 卸载boot分区
umount /mnt/boot
# 5. 重启开发板,新设备树生效
reboot
方式2:重新打包boot.img烧录
若开发板的boot分区无法直接挂载,可将新的.dtb文件与内核镜像(zImage)重新打包为boot.img,再通过瑞芯微烧录工具(如RKDevTool、upgrade_tool)单独烧录boot分区:
# 以瑞芯微rockdev目录为例,执行打包脚本
cd rockdev
./mkbootimg.sh # 自动整合zImage和新的.dtb文件生成boot.img
注意:单独烧录boot.img不会影响rootfs分区(如/etc目录下的配置文件),安全性高。
六、常见问题与注意事项
-
修改后设备树不生效? 检查status属性是否设为"okay"、compatible属性是否正确、.dtb文件是否正确替换到boot分区,可通过dmesg | grep dtb查看设备树加载日志排查问题。
-
编译设备树报错"未定义的宏"? 检查是否引入了对应的头文件(如GPIO宏需引入dt-bindings/gpio/gpio.h)。
-
不要直接修改rk3288.dtsi:rk3288.dtsi是芯片级通用文件,修改后会影响所有基于RK3288的开发板,开发板级的修改应在对应的rk3288-xxx.dts文件中进行。
-
参考官方文档:设备树节点和属性的配置需遵循规范,可参考内核源码的Documentation/devicetree/bindings/目录下的文档(如UART对应serial/rockchip-uart.txt)。
七、总结
RK3288设备树是描述硬件的"结构化说明书",其核心价值在于实现"内核与硬件解耦",简化开发板适配流程。对于嵌入式开发者而言,掌握设备树的结构、核心属性及修改方法,是基于RK3288进行硬件定制和驱动开发的基础。