当前位置:首页>Linux>深入浅出Linux设备树:从原理到实战

深入浅出Linux设备树:从原理到实战

  • 2026-02-08 03:22:01
深入浅出Linux设备树:从原理到实战

早期Linux内核开发中,硬件信息被硬编码在源码(如arch/arm/mach-*目录),导致“一个板子,一个内核”的困境:

  • 每块电路板需定制独立内核,内核镜像泛滥;

  • 板级代码高度重复,维护成本极高;

  • 与Linux“一内核多平台”目标严重冲突(Linus曾直言ARM开发“令人崩溃”)。

设备树(Device Tree)的诞生解决了这一问题

  • 树状数据结构描述硬件资源(CPU、内存、外设等),实现硬件描述与内核代码分离

  • 内核启动时动态读取设备树信息,自动加载适配驱动,大幅提升可移植性与硬件兼容性

  • 成为Linux描述非可发现硬件的标准方案,支撑多平台通用内核。

设备树是驱动开发的核心基础,理解它,方能驾驭Linux硬件抽象的精髓。

一、 设备树基础

1.1 设备树的本质

设备树的本质是一种树状的数据结构,它以一种直观且结构化的方式,对硬件平台的各种信息进行了详细的描述。在设备树中,整个硬件平台被抽象为一个根节点 “/” ,就如同大树的根基,所有其他的硬件设备信息都以这个根节点为基础展开。

每个硬件设备在设备树中都对应着一个节点,这些节点可以包含多个属性,以键值对的形式来描述设备的各种特性。更为关键的是,节点之间支持嵌套,从而形成父子关系,这种层级关系能够非常清晰地描述硬件设备之间的连接和从属关系。例如,在一个包含多种外设的硬件系统中,总线节点可以作为父节点,而连接在该总线上的各种外设节点则作为子节点,通过这种方式,就能准确地展现出硬件系统的拓扑结构。

以一个简单的嵌入式系统为例,其设备树可能包含根节点 “/”,根节点下有 “cpus” 节点用于描述 CPU 相关信息,“memory” 节点描述内存信息,以及 “spi@12345678” 节点描述 SPI 控制器及其连接的设备 。“spi@12345678” 节点作为子节点,它的属性可以包括 “compatible”(用于指定设备的兼容性,表明该设备与哪些驱动兼容)、“reg”(描述设备寄存器的地址范围)等,这些属性详细地定义了 SPI 控制器的特性和地址信息,而 “spi@12345678” 节点下还可能包含其外设节点,如 “spi_device@0”,用于描述连接在该 SPI 总线上的具体设备,进一步丰富了对硬件系统的描述。

1.2 设备树价值

在这个树形结构中,根节点代表着整个系统,而其子节点则分别对应着系统中的各个硬件设备,如 CPU、内存、各类外设等。每个节点都包含了一系列属性,这些属性详细描述了对应硬件设备的各种特性和参数。

例如,对于一个包含 NXP i.MX6ULL 处理器的开发板,其设备树文件中可能会有如下描述:

/dts-v1/;/ {    model = "FriendlyARM i.MX6ULL MiniBoard";    compatible = "friendlyarm,imx6ull-mini""fsl,imx6ull";    memory {        device_type = "memory";        reg = <0x80000000 0x20000000>;  // 内存起始地址为0x80000000,大小为0x20000000 (512MB)    };    cpus {        cpu@0 {            compatible = "arm,cortex-a7";            clock-frequency = <792000000>;  // CPU时钟频率为792MHz        };    };    spi@02100000 {        compatible = "fsl,imx6ul-spi""arm,pl022";        reg = <0x02100000 0x4000>;        interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;        #address-cells = <1>;        #size-cells = <0>;        spidev@0 {            compatible = "spidev";            reg = <0>;            spi-max-frequency = <5000000>;  // SPI设备最大频率为5MHz        };    };};

在上述示例中,根节点的model属性描述了开发板的型号,compatible属性则指定了该开发板所兼容的设备类型。memory节点描述了内存的相关信息,包括设备类型和地址范围。cpus节点下的cpu@0子节点描述了 CPU 的类型和时钟频率。spi@02100000节点描述了 SPI 控制器的相关信息,包括其兼容的设备类型、寄存器地址、中断号,以及其子节点spidev@0描述了挂载在该 SPI 总线上的 SPI 设备的相关信息。

当 Linux 内核启动时,它会自动读取并解析设备树文件。通过对设备树中硬件信息的识别和分析,内核能够准确地了解系统中硬件设备的配置情况,并根据这些信息自动加载相应的驱动程序,完成硬件设备的初始化和配置工作。这一过程无需手动修改内核源码,极大地降低了驱动开发和平台移植的难度。例如,当需要将内核移植到另一款使用相同处理器但硬件配置略有不同的开发板上时,只需要修改设备树文件中相应的硬件描述信息,而无需对内核代码进行大规模的改动,大大提高了开发效率和内核的通用性。

1.3 三大核心文件:DTS/DTSI/DTB

在设备树的体系中,主要包含了三种核心文件格式,它们分别是 DTS、DTSI 和 DTB,这三种文件在设备树的描述和使用过程中相互协作,共同实现了对硬件信息的有效管理和利用。

DTS(Device Tree Source)DTS 文件是针对特定开发板的专属设备树描述文件,它就像是为每一款独特的开发板量身定制的一份详细的硬件说明书。每一款开发板都有其独一无二的硬件配置,而 DTS 文件的作用就是精确地描述这些硬件信息,包括开发板上的各种外设、接口、芯片等硬件设备的具体参数和连接关系。以一款基于瑞芯微 RK3399 处理器的开发板为例,其对应的 DTS 文件rk3399-myboard.dts可能会包含如下内容:

/dts-v1/;#include"rk3399.dtsi"/ {    model = "My RK3399 Board";    compatible = "mycompany,rk3399-board""rockchip,rk3399";    chosen {        bootargs = "console=ttyFIQ0,115200n8";    };    aliases {        ethernet0 = &eth0;    };    // 其他自定义硬件设备节点描述};

在这个 DTS 文件中,通过#include指令引用了rk3399.dtsi文件,该文件包含了 RK3399 处理器通用的硬件描述信息。而rk3399-myboard.dts文件主要描述了这款特定开发板独有的硬件信息,如开发板型号model、兼容属性compatible,以及一些自定义的硬件设备节点。

DTSI(Device Tree Source Include)DTSI 文件是一种类似于 C 语言头文件的设备树源包含文件,它主要存放的是同一 SOC(System on Chip,片上系统)系列下多款开发板的公共硬件信息。由于同一 SOC 系列的不同开发板通常会共享一些基本的硬件资源和配置,如处理器核心、内部总线结构、基本外设接口等,将这些公共信息提取出来放在 DTSI 文件中,可以有效地避免在多个 DTS 文件中重复编写相同的内容,提高代码的复用性和可维护性。例如,对于所有基于 NXP i.MX6 系列处理器的开发板,可能会有一个通用的imx6.dtsi文件,其中包含了如下公共硬件信息:

/dts-v1/;/ {    cpus {        cpu@0 {            compatible = "arm,cortex-a9";            clock-frequency = <792000000>;        };    };    memory {        device_type = "memory";        reg = <0x80000000 0x20000000>;    };    spi@02100000 {        compatible = "fsl,imx6q-spi""arm,pl022";        reg = <0x02100000 0x4000>;        interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;        #address-cells = <1>;        #size-cells = <0>;    };    // 其他公共硬件设备节点描述};

在这个imx6.dtsi文件中,定义了 i.MX6 系列处理器的 CPU 信息、内存信息以及 SPI 控制器等公共硬件设备的信息。不同的 i.MX6 开发板在其各自的 DTS 文件中,只需通过#include "imx6.dtsi"指令引用这个公共文件,就可以继承这些公共硬件描述,然后再根据自身的硬件特点,在 DTS 文件中添加或修改特定的硬件信息。

DTB(Device Tree Blob)DTB 文件是由 DTS 文件经过设备树编译器(Device Tree Compiler,简称 DTC)编译后生成的二进制文件。由于内核在启动时需要快速、高效地读取硬件信息,而二进制格式的 DTB 文件具有体积小、解析速度快的优点,因此更适合被内核直接解析和使用。在开发过程中,首先编写和修改 DTS 文件,然后使用 DTC 工具将 DTS 文件编译成 DTB 文件。

例如,使用以下命令可以将myboard.dts文件编译成myboard.dtb文件:

dtc -I dts -O dtb -o myboard.dtb myboard.dts

其中,-I dts表示输入文件格式为 DTS,-O dtb表示输出文件格式为 DTB,-o myboard.dtb指定输出文件名为myboard.dtbmyboard.dts是输入的 DTS 源文件。编译生成的 DTB 文件会在系统启动时,由引导加载程序(如 U-Boot)加载到内存中,并传递给内核。内核通过解析 DTB 文件,获取其中描述的硬件信息,从而完成对硬件设备的初始化和配置工作。

DTS、DTSI 和 DTB 这三种文件在设备树中分工明确,DTS 文件负责描述特定开发板的硬件信息,DTSI 文件用于存放公共硬件信息,而 DTB 文件则是供内核直接解析使用的二进制文件。它们之间的协作,实现了硬件信息的分层管理和高效利用,为 Linux 内核在不同硬件平台上的稳定运行提供了有力支持。

二、 设备树语法详解

2.1 核心组成

设备树采用树形结构来描述硬件信息,节点(Node)与属性(Property)是最基本的组成单元。

整个设备树以根节点 “/” 为起始,就如同大树的主干,所有其他节点都作为根节点的子节点,层层嵌套,形成一个完整的树状层级结构。每个节点都代表着系统中的一个硬件设备或者一组相关的硬件资源,例如,一个节点可以代表 CPU、内存、SPI 控制器等硬件设备。

节点的命名有着严格的规范,其格式为<name>[@<unit-address>]。其中,<name>是一个长度不超过 31 个字符的字符串,用于描述设备的类型或功能,比如 “uart” 代表串口设备,“i2c” 代表 I2C 总线设备;[@<unit-address>]是可选部分,只有当节点代表的设备具有可寻址能力时才会出现,<unit-address>表示设备在总线上的地址,这个地址格式会根据设备所挂载的总线类型而有所不同。以连接在 I2C 总线上的 EEPROM 芯片为例,假设其 I2C 地址为 0x50,那么对应的节点命名可能为 “eeprom@50” 。

属性则是依附于节点的键值对,用于描述节点所代表硬件设备的各种特性和参数。例如,“compatible” 属性是一个非常重要的属性,它的值是一个字符串列表,通常用于驱动匹配,其格式为 “compatible = "vendor,device", "model"”,内核在启动时会根据这个属性的值来查找与之匹配的驱动程序。

以一款基于 NXP i.MX6ULL 处理器的开发板上的 SPI 控制器为例,其设备树中 SPI 控制器节点的 “compatible” 属性如下:

spi@02100000 {    compatible = "fsl,imx6ul-spi""arm,pl022";    // 其他属性};

这里的 “compatible” 属性表明该 SPI 控制器既兼容飞思卡尔(Freescale,现恩智浦 NXP)i.MX6UL 处理器的 SPI 驱动,也兼容 ARM 公司的 PL022 SPI 控制器驱动。当内核启动时,会根据这个属性值去匹配相应的驱动程序,从而实现对 SPI 控制器的驱动和管理。

再比如 “reg” 属性,主要用于描述设备的寄存器地址和地址范围信息,它的值是一个由 32 位无符号整数组成的列表,具体格式取决于设备所在总线的地址描述规则。对于一些内存映射设备,“reg” 属性可以用来定义设备在内存中的映射地址和大小。例如,一个内存控制器节点的 “reg” 属性定义如下:

memory {    device_type = "memory";    reg = <0x80000000 0x20000000>;};

上述代码表示内存的起始地址为 0x80000000,大小为 0x20000000(即 512MB)。通过 “reg” 属性,内核可以准确地知道内存的位置和大小,从而进行正确的内存管理和访问。

“status” 属性也是一个常用属性,它的值通常为 “okay” 或 “disabled”,用于表示设备的当前状态。当设备树中某个设备节点的 “status” 属性值为 “okay” 时,表明该设备是可用的,内核会对其进行初始化和驱动;而当 “status” 属性值为 “disabled” 时,内核会忽略该设备,不会对其进行任何操作。例如,在设备树中可以通过修改某个 SPI 设备节点的 “status” 属性来控制该 SPI 设备的启用或禁用:

spi@02100000 {    compatible = "fsl,imx6ul-spi""arm,pl022";    reg = <0x02100000 0x4000>;    // 其他属性    status = "disabled"// 禁用该SPI设备};

通过这样的方式,可以灵活地控制设备的启用状态,满足不同的应用场景需求。节点与属性是设备树描述硬件信息的核心元素,它们之间相互配合,通过精确的定义和描述,为内核提供了全面、准确的硬件设备信息,使得内核能够高效地管理和驱动各种硬件设备。

2.2 数据类型

设备树中最常见的三种核心数据类型为字符串(String)、整数(Integer)和二进制数据(Binary Data)。

  • 字符串类型:字符串类型的属性在设备树中用于描述一些文本信息,如设备的型号、厂商名称、兼容类型等。其表达形式是使用双引号将字符串内容括起来,例如 “model = "FriendlyARM i.MX6ULL MiniBoard"”,这里的 “model” 属性就是字符串类型,它描述了开发板的型号为 “FriendlyARM i.MX6ULL MiniBoard”。需要注意的是,字符串属性在进行比较和匹配时是大小写敏感的,也就是说,“Model = "FriendlyARM i.MX6ULL MiniBoard"” 与 “model = "FriendlyARM i.MX6ULL MiniBoard"” 是两个不同的属性,因为属性名的大小写不同。

  • 整数类型:整数类型的属性在设备树中主要用于描述一些数值信息,如地址、中断号、时钟频率等。设备树中的整数通常是 32 位无符号整数,它可以用十进制或十六进制的形式来表示。在表示地址和大小等信息时,为了表达的简洁和直观,通常会使用十六进制表示法。例如,“reg = <0x101F2000 0x1000>”,这里的 “reg” 属性包含了两个 32 位无符号整数,分别表示设备的寄存器起始地址为 0x101F2000,地址范围大小为 0x1000;又如 “interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>”,其中的 “32” 就是一个 32 位无符号整数,表示中断号为 32 ,而 “GIC_SPI” 和 “IRQ_TYPE_LEVEL_HIGH” 则是用于描述中断类型和触发方式的常量。

  • 二进制数据类型:二进制数据类型的属性在设备树中用于描述一些需要以二进制形式表达的信息,如连续的内存地址范围、硬件设备的特定配置数据等。其表达形式是使用尖括号将二进制数据括起来,每个数据之间用空格分隔。例如,“reg = <0x100 0x200 0x300 0x400>”,这里的 “reg” 属性表示一段连续的二进制数据,包含了四个 32 位无符号整数,分别为 0x100、0x200、0x300 和 0x400 ,这些数据可能用于描述设备在内存中的多个地址范围或者其他与硬件相关的二进制配置信息。

在编写设备树文件时,必须严格遵循这些数据类型的表达规范,以确保设备树文件的正确性和可读性,从而保证内核能够正确地解析和使用设备树中描述的硬件信息。

2.3 便捷操作:别名(Alias)与节点引用

别名机制允许为设备树中的某个节点定义一个简短的别名,就像给一个人取了一个昵称一样,通过这个别名可以直接引用对应的节点,而无需书写完整的节点路径,提高设备树文件的可读性和维护性

别名的定义是在设备树的根节点下通过 “aliases” 节点来实现的。例如,在一个包含以太网设备和 GPIO 控制器的设备树中,可以这样定义别名:

/ {    aliases {        ethernet0 = &eth0;        gpio0 = &gpio1;    };    eth0: ethernet@10100000 {        compatible = "dm9000";        reg = <0x10100000 0x1000>;        // 其他属性    };    gpio1: gpio@0209c000 {        compatible = "fsl,imx6q-gpio""fsl,imx35-gpio";        // 其他属性    };};

在上述示例中,通过 “aliases” 节点定义了 “ethernet0” 作为 “eth0” 节点(即 “ethernet@10100000”)的别名,“gpio0” 作为 “gpio1” 节点(即 “gpio@0209c000”)的别名。这样,在驱动程序中或者其他需要引用这些节点的地方,就可以直接使用 “ethernet0” 和 “gpio0” 来代替完整的节点路径,使代码更加简洁和易读。比如,在驱动程序中获取以太网设备节点的信息时,可以通过别名 “ethernet0” 来引用,而不需要使用冗长的 “/ethernet@10100000” 路径。

除了别名机制,设备树还支持直接使用 “&” 符号加上节点标签的语法来引用已定义的节点。例如,在某个节点的属性中,如果需要引用另一个节点,可以使用这种方式。假设在一个音频设备节点中,需要引用 I2S 控制器节点和音频编解码器节点,可以这样编写:

sound {    compatible = "nvidia,harmony-sound";    i2s-controller = <&i2s1>;    i2s-codec = <&wm8903>;};i2s1: i2s@70002800 {    compatible = "nvidia,tegra20-i2s";    reg = <0x70002800 0x100>;    // 其他属性};wm8903: codec@1a {    compatible = "wlf,wm8903";    reg = <0x1a>;    // 其他属性};

在这个例子中,“sound” 节点的 “i2s-controller” 属性通过 “<&i2s1>” 引用了 “i2s1” 节点,“i2s-codec” 属性通过 “<&wm8903>” 引用了 “wm8903” 节点。这种节点引用方式使得设备树中的节点之间可以建立起清晰的关联关系,方便内核在解析设备树时,能够准确地理解各个硬件设备之间的连接和依赖关系,从而更好地进行设备的初始化和驱动配置。别名和节点引用机制极大地提高了设备树文件的灵活性和可读性,减少了因节点路径冗长而导致的错误和维护困难,是设备树语法中非常实用的特性。

2.4 关键属性:#address-cells 与 #size-cells

“#address-cells” 和 “#size-cells” 是设备树中非常关键的属性,它们主要用于总线节点,用于定义子节点 “reg” 属性的解析规则。简单来说,“#address-cells” 属性用于指定表示地址信息所占用的 cell 数量,而 “#size-cells” 属性用于指定表示长度信息所占用的 cell 数量。这里的 “cell” 通常是指 32 位无符号整数。

以常见的 SPI 总线节点为例,假设 SPI 总线节点的定义如下:

spi@02100000 {    compatible = "fsl,imx6ul-spi""arm,pl022";    reg = <0x02100000 0x4000>;    interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;    #address-cells = <1>;    #size-cells = <0>;    spidev@0 {        compatible = "spidev";        reg = <0>;        spi-max-frequency = <5000000>;    };};

在这个例子中,SPI 总线节点 “spi@02100000” 定义了 “#address-cells = <1>” 和 “#size-cells = <0>”。这意味着,对于该 SPI 总线下的子节点,如 “spidev@0”,其 “reg” 属性的解析规则如下:使用 1 个 32 位无符号整数(即 1 个 cell)来表示地址信息,并且不使用 cell 来表示长度信息(因为 “#size-cells = <0>”)。所以,“spidev@0” 节点的 “reg = <0>” 表示其地址为 0,由于 “#size-cells = <0>”,这里没有长度信息。

再看一个更复杂的例子,对于一个包含多个子设备的 amba 总线节点,其定义如下:

amba {    compatible = "arm,amba-bus";    #address-cells = <1>;    #size-cells = <1>;    spi@12345000 {        compatible = "spi-mem";        reg = <0x12345000 0x1000>;        status = "okay";    };    i2c@12346000 {        compatible = "i2c-gpio";        reg = <0x12346000 0x1000>;        status = "okay";    };};

在这个 amba 总线节点中,“#address-cells = <1>” 和 “#size-cells = <1>” 表示,对于其下的子节点,如 “spi@12345000” 和 “i2c@12346000”,它们的 “reg” 属性需要用 1 个 cell 表示地址信息,1 个 cell 表示长度信息。以 “spi@12345000” 节点为例,其 “reg = <0x12345000 0x1000>”,其中 “0x12345000” 是地址信息,占用 1 个 cell,“0x1000” 是长度信息,也占用 1 个 cell 。

“#address-cells” 和 “#size-cells” 属性的正确设置,能够确保内核在解析设备树时,准确地理解每个子节点的地址和长度信息,从而正确地进行硬件设备的初始化和资源分配。如果这两个属性设置错误,可能会导致内核无法正确识别设备的地址和资源范围,进而影响设备的正常驱动和使用。这两个属性是设备树语法中的关键部分,深入理解它们的作用和用法,对于编写正确、有效的设备树文件至关重要。

2.5 复用与扩展:#include 与 Overlay 动态机制

为了提高代码的复用性和可维护性,以及实现对硬件功能的动态扩展,引入了 “#include” 指令和设备树覆盖(Overlay)机制。

“#include” 指令的作用类似于 C 语言中的头文件包含,它允许在一个设备树文件中引入其他设备树文件或包含文件(通常是.dtsi 文件),从而实现硬件信息的复用。通过将一些通用的硬件描述信息提取出来,放在单独的.dtsi 文件中,不同的设备树文件(.dts)可以通过 “#include” 指令引用这些通用文件,避免了在多个.dts 文件中重复编写相同的硬件描述代码。

例如,对于同一 SOC 系列的不同开发板,它们可能共享一些基本的硬件资源和配置,如处理器核心、内部总线结构、基本外设接口等。可以将这些公共硬件信息放在一个名为 “soc_common.dtsi” 的文件中,然后在各个开发板的设备树文件中通过 “#include” 指令引用该文件。假设 “soc_common.dtsi” 文件中包含了如下内容:

/dts-v1/;/ {    cpus {        cpu@0 {            compatible = "arm,cortex-a9";            clock-frequency = <792000000>;        };    };    memory {        device_type = "memory";        reg = <0x80000000 0x20000000>;    };    spi@02100000 {        compatible = "fsl,imx6q-spi""arm,pl022";        reg = <0x02100000 0x4000>;        interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;        #address-cells = <1>;        #size-cells = <0>;    };};

在开发板的设备树文件 “myboard.dts” 中,可以这样引用 “soc_common.dtsi” 文件:

/dts-v1/;#include"soc_common.dtsi"/ {    model = "My Development Board";    compatible = "mycompany,myboard""soc-family";    // 其他针对本开发板的特定硬件节点描述};

通过这种方式,“myboard.dts” 文件继承了 “soc_common.dtsi” 文件中的所有硬件 

三、 设备树编译与加载

3.1 编译工具 DTC

DTC(Device Tree Compiler)是设备树编译的核心工具,负责将人类可读的设备树源文件(DTS)转换为内核可识别的二进制格式(DTB),实现硬件描述与内核代码的解耦。其源码集成于内核scripts/dtc目录,确保与内核编译深度协同。

  • 核心编译命令dtc -I dts -O dtb -o output.dtb input.dts

    • -I dts:输入格式为DTS

    • -O dtb:输出格式为DTB

    • -o output.dtb:指定输出文件名

  • 反向调试功能dtc -I dtb -O dts -o decompiled.dts input.dtb 将DTB还原为DTS,便于快速定位配置错误(如内核启动异常时的调试)。

DTC在转换过程中自动验证节点命名、属性定义及层次结构的规范性,确保DTB的正确性与兼容性。

3.2 两种编译方式

手动编译:手动编译方式具有很强的灵活性,特别适合在对单个设备树文件进行精细调试时使用。在这种方式下,可以直接调用 DTC 工具,通过执行编译命令dtc -I dts -O dtb -o myboard.dtb myboard.dts,将指定的myboard.dts设备树源文件编译成myboard.dtb二进制文件。这种方式能够对编译过程进行精准控制,随时根据调试需求修改编译参数,快速验证修改后的设备树文件是否符合预期。例如,在开发一款新的开发板时,可能需要频繁修改设备树文件,测试不同的硬件配置,此时手动编译方式就能满足快速迭代的需求。

内核集成编译:内核集成编译方式则更适用于量产等大规模开发场景。通过执行make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs命令,系统会自动批量编译arch/arm/boot/dts目录下的所有设备树文件。在编译过程中,系统会根据内核的配置和依赖关系,自动处理设备树文件之间的依赖和引用,确保所有设备树文件都能正确编译。编译完成后,生成的 DTB 文件会自动存放于对应架构的 boot 目录下,无需手动指定复杂的编译参数和输出路径。这种方式大大提高了编译效率,减少了人为错误,非常适合在产品量产阶段,需要大量编译设备树文件时使用。例如,在某款基于 ARM 架构的智能设备量产过程中,通过内核集成编译方式,可以快速编译出大量符合要求的设备树文件,为产品的快速上市提供了有力支持。

3.3 加载流程

整个加载流程主要分为两个关键步骤,分别由 U-Boot 和内核协同完成。

在系统启动的初期,U-Boot 承担着重要的任务。它首先从存储设备(如 NAND Flash、SD 卡等)中小心翼翼地将编译好的 DTB 文件加载到内存的特定地址。这一过程需要 U-Boot 准确地识别存储设备的格式和分区,找到 DTB 文件的存储位置,并将其完整地读取到内存中。加载完成后,U-Boot 会通过bootzbootm命令将 DTB 文件在内存中的地址传递给内核。以bootz命令为例,其格式通常为bootz [kernel_addr] [initrd_addr] [dtb_addr],其中dtb_addr就是 DTB 文件在内存中的地址。通过这一操作,U-Boot 为内核获取硬件信息搭建了桥梁。

内核在启动阶段,会敏锐地捕捉到 U-Boot 传递过来的 DTB 文件地址,并调用unflatten_device_tree函数,开始对二进制的 DTB 文件进行深度解析。在解析过程中,内核会将 DTB 文件中的数据结构逐步转换为内核内部的设备树树形结构,即device_node结构。每个device_node结构对应着设备树中的一个节点,通过这种方式,内核能够准确地获取设备树中描述的硬件信息,包括设备的类型、地址、属性等。根据这些信息,内核有条不紊地初始化系统中的各种硬件设备,为后续系统的稳定运行奠定坚实基础。例如,内核会根据设备树中描述的串口设备信息,初始化串口驱动,使得系统能够通过串口进行数据通信。

3.4 如何确认设备树加载成功

当设备树成功加载后,在根文件系统的/sys/firmware/devicetree/base目录下,会呈现出与设备树节点一一对应的文件和目录结构。每个设备树节点都会在该目录下对应一个子目录,而节点的属性则会以文件的形式存储在对应的子目录中。通过查看这些文件和目录,可以直观地了解设备树中各个节点和属性的信息,确认设备树是否按照预期被加载。比如,要查看 SPI 设备节点的信息,可以进入/sys/firmware/devicetree/base/spi@02100000目录,查看该目录下的属性文件,如compatiblereg等,确认 SPI 设备的属性是否与设备树文件中的定义一致。

在 U-Boot 环境中,可以使用md命令(如md 0x88000000,假设 DTB 文件被加载到 0x88000000 地址)来查看 DTB 文件在内存中的内容,通过分析这些内容,可以初步判断 DTB 文件是否被正确加载。同时,在内核启动过程中,会输出详细的内核日志,可以通过串口终端或其他日志查看工具,仔细检查内核日志中关于设备树解析的信息。如果设备树加载成功,内核日志中会显示各个硬件节点被正确识别的信息;反之,如果出现错误,内核日志中会提示相应的错误信息,快速定位问题所在。例如,当内核日志中出现 “Failed to parse device tree” 的错误提示时,就需要检查设备树文件的编译、加载过程是否存在问题。

四、 设备树实例

4.1 基础实例

为了更直观地理解设备树的结构和语法,让我们以一个简单的设备树文件为例,深入剖析其关键节点和属性的作用。假设我们有一个基于 ARM 架构的开发板,其设备树文件如下:

/dts-v1/;/ {    model = "My ARM Development Board";    compatible = "mycompany,arm-board""arm,generic-board";    memory {        device_type = "memory";        reg = <0x80000000 0x20000000>;    };    cpus {        cpu@0 {            compatible = "arm,cortex-a7";            clock-frequency = <792000000>;            enable-method = "psci";        };    };    serial@101F2000 {        compatible = "arm,pl011""arm,primecell";        reg = <0x101F2000 0x1000>;        interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;        clock-frequency = <100000000>;        status = "okay";    };};

根节点 “/” 是整个设备树的核心,它如同大树的主干,所有其他节点都从它这里衍生出来。在这个例子中,根节点包含了两个重要属性:“model” 和 “compatible”。“model” 属性用于描述开发板的型号,这里设置为 “My ARM Development Board”,它就像是开发板的一个身份标签,方便识别和区分不同的硬件平台。而 “compatible” 属性则更为关键,它是内核识别和匹配驱动程序的重要依据。这里的值 “mycompany,arm-board” 和 “arm,generic-board” 是一个字符串列表,内核在启动时会根据这个列表来查找与之匹配的驱动程序,确保硬件设备能够被正确驱动。

“cpus” 节点下的 “cpu@0” 子节点专门用于描述 CPU 的相关信息。“compatible” 属性表明该 CPU 采用的是 ARM Cortex-A7 架构,这是内核识别 CPU 类型的关键信息。“clock-frequency” 属性则明确指定了 CPU 的时钟频率为 792MHz,这个参数对于内核进行 CPU 的性能管理和任务调度至关重要。“enable-method” 属性设置为 “psci”,表示采用了电源状态协调接口(Power State Coordination Interface)来管理 CPU 的电源状态,这在现代低功耗嵌入式系统中是非常常见的配置方式。

“serial@101F2000” 节点用于描述串口设备,其中 “@101F2000” 表示该串口设备的寄存器基地址为 0x101F2000。“compatible” 属性的值 “arm,pl011” 和 “arm,primecell” 表明该串口设备兼容 ARM 公司的 PL011 串口控制器,以及 ARM PrimeCell 系列的外设,这使得内核能够准确地找到与之匹配的串口驱动程序。“reg” 属性指定了串口设备的寄存器地址范围,起始地址为 0x101F2000,大小为 0x1000,这确保了内核能够正确地访问串口设备的寄存器,进行数据的发送和接收操作。“interrupts” 属性定义了串口设备的中断信息,这里表示使用 GIC(通用中断控制器)的 SPI 中断,中断号为 32,触发方式为高电平触发,这对于串口设备在数据传输过程中及时通知内核进行处理非常重要。“clock-frequency” 属性指定了串口设备的时钟频率为 100MHz,这个频率决定了串口数据传输的速率。“status” 属性设置为 “okay”,表示该串口设备处于可用状态,内核会在启动时对其进行初始化和驱动配置,使其能够正常工作,实现与外部设备的串口通信功能。

4.2 实战案例 1:Pinctrl 子系统的设备树配置

在嵌入式系统开发中,Pinctrl 子系统在管理 GPIO 引脚的复用功能和电气属性方面起着关键作用。接下来,我们以 RK3568 开发板的 LED 引脚配置为例,深入剖析 Pinctrl 子系统在设备树中的配置方法和应用技巧。

Pinctrl 子系统的配置主要涉及 Client 端和 Service 端两个部分,它们相互协作,共同实现了 GPIO 引脚的精准配置。在 Client 端,主要通过 “pinctrl-names” 和 “pinctrl-0” 等属性来定义引脚的状态和引用 Service 端的节点。以 RK3568 开发板上控制 LED 的 GPIO 引脚配置为例,其 Client 端配置如下:

led {    compatible = "gpio-leds";    pinctrl-names = "default""sleep";    pinctrl-0 = <&led_pins_default>;    pinctrl-1 = <&led_pins_sleep>;    gpios = <&gpio3 RK_PA0 GPIO_ACTIVE_HIGH>;    default-state = "off";};

在这段配置中,“pinctrl-names” 属性定义了两个引脚状态,分别为 “default” 和 “sleep”,这两个状态对应着 LED 在不同工作模式下的引脚配置。“pinctrl-0” 属性通过 “<&led_pins_default>” 引用了 Service 端的 “led_pins_default” 节点,该节点定义了 LED 在默认状态下的引脚配置;“pinctrl-1” 属性通过 “<&led_pins_sleep>” 引用了 Service 端的 “led_pins_sleep” 节点,用于定义 LED 在睡眠状态下的引脚配置。“gpios” 属性指定了控制 LED 的 GPIO 引脚为 GPIO3 组的 PA0 引脚,且该引脚为高电平有效,即当该引脚输出高电平时,LED 点亮。

Service 端则根据不同的芯片平台厂商,有着不同的语法和配置规则。以 RK3568 开发板为例,其 Service 端的 LED 引脚配置如下:

&pinctrl {    led_pins_default: led_pins_default {        rockchip,pins = <3 RK_PA0 1 &pcfg_pull_up_drv_level_2>;    };    led_pins_sleep: led_pins_sleep {        rockchip,pins = <3 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;    };};

在 Service 端的配置中,“led_pins_default” 节点定义了 LED 在默认状态下的引脚配置。“rockchip,pins” 属性是 RK3568 平台特有的引脚配置属性,其格式为 “<bank pin mux & 配置 >”。在 “led_pins_default” 节点中,“<3 RK_PA0 1 &pcfg_pull_up_drv_level_2>” 表示将 GPIO3 组的 PA0 引脚复用为功能 1(通常是 GPIO 功能),并设置为上拉电阻,驱动强度为 2 级。这样的配置确保了在默认状态下,LED 引脚能够正常工作,并且具有合适的电气特性。

“led_pins_sleep” 节点则定义了 LED 在睡眠状态下的引脚配置。这里的 “<3 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>” 表示将 GPIO3 组的 PA0 引脚配置为 GPIO 功能,并且取消上下拉电阻,这是为了在睡眠状态下降低功耗,减少不必要的电流消耗。

通过以上 Client 端和 Service 端的配置,RK3568 开发板实现了对 LED 引脚在不同工作状态下的精准配置。这种配置方式不仅提高了硬件资源的利用率,还使得系统在不同工作模式下能够保持高效、稳定的运行。在实际开发中,可以根据具体的硬件需求和应用场景,灵活地调整 Pinctrl 子系统的配置,实现对 GPIO 引脚的多样化控制。

4.3 实战案例 2:i.MX6ULL 开发板设备树分层解析

i.MX6ULL 开发板在设备树的设计上采用了分层结构,这种巧妙的设计极大地提高了设备树的可维护性和复用性。下面,我们将对 i.MX6ULL 开发板的设备树分层结构进行深入剖析,探寻其背后的设计理念和实现方法。

i.MX6ULL 开发板的设备树主要由两部分组成,分别是 soc 通用文件 “imx6ull.dtsi” 和板级文件 “imx6ull-14x14-evk.dts”(以 14x14 尺寸的评估板为例)。“imx6ull.dtsi” 文件犹如一座坚实的基石,它存放着 i.MX6ULL SOC 的通用硬件信息,这些信息是整个设备树的基础,也是不同板级设计所共享的核心内容。在这个文件中,详细描述了 CPU 的架构、时钟频率、内存控制器、各类总线控制器以及其他片上外设的基本配置信息。例如,对于 CPU 的描述:

cpus {    cpu@0 {        compatible = "arm,cortex-a7";        clock-frequency = <792000000>;    };};

这段代码表明了 i.MX6ULL 采用的是 ARM Cortex-A7 架构的 CPU,时钟频率为 792MHz。这样的通用配置信息,使得不同的板级设计在使用相同 SOC 时,无需重复编写这些基础内容,大大提高了开发效率和代码的一致性。

而板级文件 “imx6ull-14x14-evk.dts” 则像是为特定开发板量身定制的一件外衣,它通过 “#include "imx6ull.dtsi"” 指令引入了 soc 通用文件,然后在此基础上,根据该开发板的独特需求,补充和定制了专属的外设信息。

以开发板上的 LED 和按键外设为例,配置如下:

#include"imx6ull.dtsi"/ {    model = "Freescale i.MX6ULL 14x14 EVK Board";    compatible = "fsl,imx6ull-14x14-evk""fsl,imx6ull";    led {        compatible = "gpio-leds";        pinctrl-names = "default";        pinctrl-0 = <&pinctrl_led>;        gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;        default-state = "off";    };    key {        compatible = "gpio-keys";        pinctrl-names = "default";        pinctrl-0 = <&pinctrl_key>;        key0 {            label = "key0";            gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;            debounce-interval = <20>;        };    };};&iomuxc {    pinctrl_led: ledgrp {        fsl,pins = <            MX6ULL_PAD_GPIO1_IO03__GPIO1_IO03 0x10b0        >;    };    pinctrl_key: keygrp {        fsl,pins = <            MX6ULL_PAD_GPIO1_IO04__GPIO1_IO04 0x10b0        >;    };};

在这段配置中,首先通过 “#include "imx6ull.dtsi"” 引入了 soc 通用文件,继承了其中的通用硬件信息。然后,在根节点下定义了 “led” 和 “key” 两个外设节点。“led” 节点配置了一个 GPIO 控制的 LED,通过 “pinctrl-0 = <&pinctrl_led>” 引用了 “iomuxc” 节点下的 “pinctrl_led” 子节点,该子节点定义了 LED 引脚的复用和电气属性。“key” 节点配置了一个 GPIO 按键,同样通过 “pinctrl-0 = <&pinctrl_key>” 引用了 “pinctrl_key” 子节点,设置了按键引脚的相关配置。

通过对 i.MX6ULL 开发板设备树分层结构的分析,我们可以清晰地看到这种设计模式的优势。它将通用硬件信息与板级专属信息进行了有效的分离,使得在开发不同的 i.MX6ULL 开发板时,只需在板级文件中进行少量的定制化修改,而无需对通用文件进行频繁的改动,大大提高了设备树的可维护性和复用性。这种分层设计思想,为嵌入式系统开发提供了一种高效、灵活的硬件描述和管理方式,是设备树应用中的一个重要实践范例。

五、 驱动与设备树的交互

5.1 匹配核心:compatible 属性与驱动绑定机制

在 Linux 设备驱动体系中,驱动与设备树之间的匹配核心在于 “compatible” 属性,它就像是一把精准的钥匙,能够开启硬件与驱动之间的通信大门,其背后的驱动绑定机制堪称整个系统正常运行的关键纽带。

设备树节点通过 “compatible” 属性明确声明自身所代表的硬件型号和兼容类型。通常遵循 “vendor,device” 的格式,其中 “vendor” 代表硬件设备的厂商,“device” 则表示设备的具体型号。例如,在一款基于 NXP i.MX6ULL 处理器的开发板中,SPI 控制器节点的 “compatible” 属性被定义为 “compatible = "fsl,imx6ul-spi", "arm,pl022";”。这表明该 SPI 控制器既与飞思卡尔(Freescale,现恩智浦 NXP)i.MX6UL 处理器的 SPI 驱动兼容,也与 ARM 公司的 PL022 SPI 控制器驱动相匹配,为内核在寻找合适驱动时提供了明确的线索。

而在驱动程序的代码世界里,“compatible” 属性同样扮演着至关重要的角色。驱动程序通过定义 “of_device_id” 结构体来构建一个支持的型号列表。以 SPI 控制器驱动为例,其代码中可能包含如下定义:

static const struct of_device_id spi_driver_of_match[] = {    {.compatible = "fsl,imx6ul-spi",.data = &imx6ul_spi_data },    {.compatible = "arm,pl022",.data = &pl022_spi_data },    {}};MODULE_DEVICE_TABLE(of, spi_driver_of_match);

在这个结构体数组中,每个元素都对应着一个支持的硬件设备,通过 “compatible” 字段与设备树节点的 “compatible” 属性进行精确匹配。“data” 字段则指向与该设备相关的特定数据,这些数据可能包含设备的初始化参数、寄存器映射信息等,为驱动与硬件设备的交互提供了必要的支持。

当内核启动的关键时刻,系统会有条不紊地遍历设备树中的每一个节点,并将其 “compatible” 属性与驱动程序中 “of_device_id” 结构体数组中的每一项进行仔细比对。这一匹配过程就像是在庞大的数据库中进行精准检索,只有当设备树节点的 “compatible” 属性与驱动程序中某一项的 “compatible” 字段字符串完全一致时,内核才会判定匹配成功。一旦匹配成功,内核就会立即调用驱动程序中的 probe 函数,这一函数是驱动与硬件设备建立联系的关键入口,它负责完成设备的初始化工作,包括分配内存、初始化寄存器、设置中断处理函数等,确保硬件设备能够在系统中正常运行。

5.2 OF 操作函数:驱动读取设备树信息的利器

为了助力驱动程序高效地读取设备树信息,Linux 内核精心打造了一套功能强大的 OF(Open Firmware)操作函数库。这些函数就像是驱动程序的得力助手,为驱动程序提供了便捷、高效的方式来解析设备树中的各种硬件参数,实现硬件设备的动态配置,使系统能够灵活适应不同的硬件环境。

在众多 OF 操作函数中,“of_find_node_by_path” 函数是查找节点的重要工具。它通过接收一个带有全路径的节点名作为参数,能够在设备树中准确找到对应的节点。例如,驱动程序想要获取 SPI 控制器节点的信息,可以使用以下代码:

struct device_node *spi_node;spi_node = of_find_node_by_path("/spi@02100000");if (spi_node) {    // 成功找到节点,进行后续操作else {    // 查找失败,处理错误}

这段代码中,“/spi@02100000” 是 SPI 控制器节点的全路径名,通过 “of_find_node_by_path” 函数,驱动程序能够快速定位到该节点,为后续获取节点属性和配置信息奠定基础。

“of_property_read_string” 函数则专门用于读取节点的字符串类型属性。比如,要获取 SPI 控制器节点的 “compatible” 属性值,可以这样编写代码:

const char *compatible_value;int ret = of_property_read_string(spi_node, "compatible", &compatible_value);if (ret == 0) {    printk(KERN_INFO "SPI controller compatible value: %s\n", compatible_value);else {    printk(KERN_ERR "Failed to read compatible property\n");}

通过上述代码,驱动程序能够轻松读取到 “compatible” 属性的值,并根据该值判断 SPI 控制器的兼容类型,从而选择合适的驱动和配置参数。

在处理 GPIO 相关的配置时,“of_get_named_gpio” 函数发挥着重要作用,它可以获取指定节点中指定名称的 GPIO 编号。假设设备树中定义了一个用于控制 LED 的 GPIO 引脚,驱动程序可以通过以下方式获取该 GPIO 的编号:

int led_gpio;led_gpio = of_get_named_gpio(spi_node, "led-gpio"0);if (led_gpio >= 0) {    // 成功获取GPIO编号,进行GPIO相关操作else {    printk(KERN_ERR "Failed to get led-gpio\n");}

这段代码中,“led-gpio” 是设备树中定义的 GPIO 属性名称,通过 “of_get_named_gpio” 函数,驱动程序能够准确获取到控制 LED 的 GPIO 编号,进而实现对 LED 的控制。

对于一些包含多个参数的属性,如 “reg” 属性,通常使用 “of_property_read_u32_array” 函数来读取。例如,要读取 SPI 控制器节点的 “reg” 属性值,代码如下:

u32 reg_values[2];int ret = of_property_read_u32_array(spi_node, "reg", reg_values, 2);if (ret == 0) {    printk(KERN_INFO "SPI controller reg values: 0x%x, 0x%x\n", reg_values[0], reg_values[1]);else {    printk(KERN_ERR "Failed to read reg property\n");}

在这个例子中,“reg” 属性通常包含设备的寄存器地址和地址范围信息,通过 “of_property_read_u32_array” 函数,驱动程序能够一次性读取到这些信息,为后续对 SPI 控制器寄存器的访问和操作提供准确的数据。

5.3 总线级交互

在 Linux 系统中,不同总线设备与设备树之间的匹配流程虽然在本质上都基于 “compatible” 属性,但由于总线特性和硬件连接方式的差异,具体的匹配实现过程存在一些微妙的区别。

platform 设备作为一种虚拟总线设备,在匹配过程中有着独特的流程。当系统启动时,内核会首先将设备树中的相关节点转换为 platform_device 结构体实例。这一转换过程涉及到对设备树节点属性的解析和提取,将设备树中描述的硬件信息转化为内核能够识别和管理的 platform_device 结构。在转换过程中,内核会根据设备树节点的 “compatible” 属性等关键信息,为 platform_device 结构体填充相应的字段,包括设备名称、资源信息等。

随后,platform_device 会与已注册的 platform_driver 进行匹配。platform_driver 通过定义 “of_match_table” 成员来指定其支持的设备树节点 “compatible” 属性列表。当 platform_device 与 platform_driver 的 “compatible” 属性匹配成功时,内核会调用 platform_driver 的 probe 函数,完成设备的初始化和驱动绑定工作。在 probe 函数中,驱动程序可以进一步解析设备树节点的其他属性,获取设备的详细配置信息,如寄存器地址、中断号等,并根据这些信息完成设备的初始化操作,使设备能够正常工作。

i2c 设备和 spi 设备作为实际存在的总线设备,其匹配流程也有各自的特点。在 i2c 总线设备中,内核在初始化 i2c 总线时,会将设备树中 i2c 控制器节点下的子节点转换为 i2c_client 结构体实例。这些 i2c_client 结构体代表着连接在 i2c 总线上的各个从设备。每个 i2c_client 结构体都会关联到对应的 i2c 控制器,形成一个完整的 i2c 设备层次结构。

i2c_client 通过 i2c_driver 的 “of_match_table” 与 i2c_driver 进行匹配。i2c_driver 在注册时,会将其支持的设备树节点 “compatible” 属性信息填充到 “of_match_table” 中。当 i2c_client 的 “compatible” 属性与 i2c_driver 的 “of_match_table” 中的某一项匹配成功时,内核会调用 i2c_driver 的 probe 函数,完成 i2c 设备的驱动绑定和初始化工作。在 probe 函数中,驱动程序可以根据设备树中提供的 i2c 设备属性信息,如设备地址、寄存器映射等,实现对 i2c 设备的控制和数据传输。

spi 设备的匹配流程与 i2c 设备类似。在系统启动时,内核会将设备树中 spi 控制器节点下的子节点转换为 spi_device 结构体实例,这些 spi_device 结构体代表着连接在 spi 总线上的各个设备。spi_device 通过 spi_driver 的 “of_match_table” 与 spi_driver 进行匹配。当匹配成功时,内核会调用 spi_driver 的 probe 函数,完成 spi 设备的驱动绑定和初始化工作。在 probe 函数中,驱动程序可以根据设备树中描述的 spi 设备属性,如设备片选信号、时钟频率等,实现对 spi 设备的高效控制和数据通信。

六、高级应用技巧

6.1 动态扩展:Device Tree Overlay 实战应用

在 Linux 设备树的高级应用领域,Device Tree Overlay(设备树覆盖)机制以其独特的动态扩展能力,为硬件功能的灵活配置和升级提供了强大支持。这一机制允许在系统运行时,无需重新编译内核与设备树,即可动态加载额外的设备树信息,实现硬件功能的动态扩展,尤其适用于外设热插拔等对硬件配置灵活性要求较高的场景。

以树莓派扩展传感器为例,假设我们需要在树莓派上动态添加一个温度传感器。首先,需要编写一个设备树覆盖文件(通常为.dtbo 格式),在这个文件中,定义新的传感器设备节点。以下是一个简单的设备树覆盖文件示例:

/dts-v1/;/plugin/;/ {    compatible = "brcm,bcm2835""brcm,bcm2708""brcm,bcm2709";    fragment@0 {        target = <&i2c1>;        __overlay__ {            sensor: temp_sensor@48 {                compatible = "max6675";                reg = <0x48>;                status = "okay";            };        };    };};

在这个示例中,compatible属性指定了该设备树覆盖文件适用的硬件平台,这里表示适用于树莓派系列(如 BCM2835、BCM2708、BCM2709 等芯片)。fragment@0节点定义了一个设备树片段,target = <&i2c1>表示该片段将被添加到设备树中i2c1节点下。在__overlay__子节点中,定义了一个新的温度传感器设备节点sensor: temp_sensor@48,其compatible属性表明该传感器兼容 MAX6675 型号,reg属性指定了传感器在 I2C 总线上的地址为 0x48,status = "okay"表示该设备处于可用状态。

编写好设备树覆盖文件后,通过dtc -I dts -O dtb -o temp_sensor.dtbo temp_sensor.dts命令将其编译成.dtbo 文件。然后,在树莓派系统中,使用echo "temp_sensor" | sudo tee -a /etc/modules-load.d/overlay.conf命令将该设备树覆盖文件添加到系统加载列表中,并通过modprobe overlay命令加载设备树覆盖模块,即可动态启用新添加的温度传感器硬件。

在使用 Device Tree Overlay 机制时,需要特别注意的是,Overlay 节点的属性设置需与主 DTB 节点的总线属性相匹配,避免出现地址冲突等问题。在上述示例中,新添加的温度传感器节点挂载在i2c1总线上,因此需要确保i2c1总线的地址范围和其他属性与传感器节点的配置一致,否则可能导致传感器无法正常工作。通过 Device Tree Overlay 机制,我们能够轻松实现硬件功能的动态扩展,为嵌入式系统的开发和应用带来了更高的灵活性和可扩展性。

6.2 进阶优化:减少冗余与提升兼容性的技巧

在设备树的开发过程中,优化设备树文件旨在减少设备树文件中的冗余信息,提升其与不同硬件平台和驱动程序的兼容性。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-08 06:17:49 HTTP/2.0 GET : https://f.mffb.com.cn/a/465946.html
  2. 运行时间 : 0.245344s [ 吞吐率:4.08req/s ] 内存消耗:4,835.72kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=144fca729fbba9ab99a4f048a5654729
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001038s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001508s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.015828s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000694s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001447s ]
  6. SELECT * FROM `set` [ RunTime:0.010806s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001595s ]
  8. SELECT * FROM `article` WHERE `id` = 465946 LIMIT 1 [ RunTime:0.001369s ]
  9. UPDATE `article` SET `lasttime` = 1770502669 WHERE `id` = 465946 [ RunTime:0.023081s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.003668s ]
  11. SELECT * FROM `article` WHERE `id` < 465946 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001926s ]
  12. SELECT * FROM `article` WHERE `id` > 465946 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001386s ]
  13. SELECT * FROM `article` WHERE `id` < 465946 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.008052s ]
  14. SELECT * FROM `article` WHERE `id` < 465946 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.004350s ]
  15. SELECT * FROM `article` WHERE `id` < 465946 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.002546s ]
0.249688s