6. 内核配置和设备树配置

内核配置和设备树配置是嵌入式Linux开发中两个关键但不同的概念,它们分别负责不同层面的硬件抽象与系统功能定义。以下是它们的核心区别:

6.1 基本定义与作用

维度

内核配置

设备树配置

定义

内核源码的编译选项,通过Kconfig系统生成.config文件

描述硬件拓扑结构的数据文件,通常以.dts/.dtb格式存在

作用

决定内核包含哪些功能模块(如文件系统、驱动框架、网络协议栈等)

描述硬件设备的物理属性(如地址、中断、GPIO等)和连接关系

使用者

内核开发者或系统集成商

硬件工程师或板级支持包(BSP)开发者

依赖关系

内核编译时必须存在

内核运行时动态解析,部分功能需内核配置支持(如设备树解析模块)

6.2 配置内容对比

6.2.1 内核配置(.config

  • 编译选项:通过make menuconfigmake defconfig生成,包含大量布尔值(如CONFIG_NET=y)或枚举值(如CONFIG_CPU_FREQ_GOV_ONDEMAND=y)。

  • 功能模块

    • 文件系统(如CONFIG_EXT4_FS

    • 网络协议(如CONFIG_TCP_CONG_CUBIC

    • 驱动框架(如CONFIG_USB_SUPPORT

    • 处理器架构特性(如CONFIG_ARM64

  • 示例

    CONFIG_USB=y
    CONFIG_USB_GADGET=y
    CONFIG_SND=y  # 启用音频子系统
    

6.2.2 设备树配置(.dts/.dtb

  • 硬件描述:以树形结构描述设备节点及其属性,如地址、中断、时钟等。

  • 典型节点

    • CPU/内存控制器

    • 外设接口(如UART、I2C、SPI)

    • 中断控制器

    • GPIO/电源管理单元

  • 示例

    uart0: serial@fe660000 {
        compatible = "rockchip,rk3588-uart";
        reg = <0x0 0xfe660000 0x0 0x1000>;
        interrupts = <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&cru SCLK_UART0>;
        status = "okay";
    };
    

6.3 工作流程与交互方式

流程

内核配置

设备树配置

配置方式

1. 选择基础配置(如make rockchip_linux_defconfig

1. 基于参考.dts修改(如rk3588.dtsi

2. 通过menuconfig图形界面调整选项

2. 定义板级特定设备(如rk3588-evb.dts

3. 编译内核(生成ImagezImage

3. 编译为.dtb(如dtc -I dts -O dtb rk3588-evb.dts

传递方式

直接编译到内核二进制文件中

由Bootloader(如U-Boot)传递给内核(如bootm zImage rk3588.dtb

加载时机

内核启动时立即生效

内核启动后解析(如/proc/device-tree目录可见)

6.4 核心差异总结

  1. 抽象层级

    • 内核配置:软件功能层(决定内核支持哪些特性)。

    • 设备树配置:硬件描述层(定义硬件长什么样、如何连接)。

  2. 修改影响

    • 修改内核配置需重新编译内核。

    • 修改设备树仅需重新编译.dtb,无需重编内核。

  3. 依赖关系

    • 内核配置是设备树生效的前提(需启用设备树解析模块)。

    • 设备树依赖内核提供的驱动框架(如compatible属性匹配驱动)。

  4. 常见场景

    • 内核配置:添加新文件系统、启用特定网络协议、裁剪内核大小。

    • 设备树配置:添加新外设、修改GPIO引脚定义、调整中断优先级。

6.5 实际应用中的配合

以RK3588平台为例:

  1. 内核配置

    # 启用设备树支持
    CONFIG_OF=y
    CONFIG_OF_FLATTREE=y
    # 启用I2C总线支持
    CONFIG_I2C=y
    CONFIG_I2C_ROCKCHIP=y
    
  2. 设备树配置

    i2c2: i2c@fe8a0000 {
        compatible = "rockchip,rk3588-i2c";
        reg = <0x0 0xfe8a0000 0x0 0x1000>;
        clocks = <&cru SCLK_I2C2>;
        status = "okay";
        
        #address-cells = <1>;
        #size-cells = <0>;
        
        // I2C从设备(如加速度计)
        mpu6050@68 {
            compatible = "invensense,mpu6050";
            reg = <0x68>;
            interrupt-parent = <&gpio3>;
            interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
        };
    };
    

6.6 常见误区

  1. 混淆概念:认为设备树可以替代内核配置(如误以为添加驱动只需修改设备树)。

    • 纠正:设备树仅描述硬件,驱动程序本身需通过内核配置编译进内核。

  2. 重复工作:在内核配置和设备树中重复定义硬件参数。

    • 最佳实践:硬件连接关系由设备树定义,驱动功能由内核配置控制。

通过合理分工,内核配置负责“软件能力”,设备树负责“硬件地图”,共同实现Linux系统的硬件抽象与驱动分离。