第一章:设备树起源——为什么需要设备树?

各位同学,大家好。我是你们的嵌入式系统讲师。今天咱们来聊聊设备树的起源。

说实话,我刚入行那会儿,还没有设备树这东西。那时候做嵌入式Linux开发,最头疼的就是板级文件。每次换一块新板子,都得改一堆代码。改完还得重新编译内核,那叫一个痛苦。

为什么会这样?咱们得从头说起。

1.1 板级文件的噩梦

在设备树出现之前,Linux内核是怎么知道硬件信息的?

答案很简单:硬编码。

每个板子都有一个对应的 board-xxx.c 文件。比如三星的S3C2440开发板,就有个 board-smdk2440.c。这个文件里写死了所有硬件信息:

  • 内存基地址和大小
  • GPIO的复用配置
  • 中断控制器的寄存器地址
  • I2C、SPI等外设的挂载信息
  • 时钟频率和分频系数

我当年在做一个项目时,客户要求从S3C2440换成S3C6410。你猜怎么着?整个 board-smdk2440.c 几乎要重写。因为两个芯片的内存映射完全不同,中断号也不一样。

核心问题:硬件描述和内核代码高度耦合。换硬件 = 改内核源码 = 重新编译 = 浪费时间。

更麻烦的是,这种板级文件在Linux内核源码里越堆越多。我记得Linux 2.6时代,arch/arm目录下光是板级文件就有上千个。每个文件对应一款开发板或产品。维护起来简直是噩梦。

1.2 从板级文件到设备树的演进

那怎么解决这个问题呢?

其实思路很简单:把硬件描述从内核代码里抽出来,单独放在一个文件里。这个文件就是设备树。

设备树(Device Tree)最早源于PowerPC架构。2005年左右,PowerPC社区就开始用设备树来描述硬件了。ARM架构直到2011年左右才开始大规模采用。

我记得当时Linus Torvalds还发过火,说ARM的板级文件太乱了,必须统一用设备树。从那以后,ARM社区才开始认真推进设备树的普及。

设备树的核心思想就一句话:

硬件描述和内核代码分离。设备树文件(.dts)描述硬件,内核代码(.c)处理逻辑。两者通过设备树编译器(dtc)生成的二进制文件(.dtb)连接起来。

这样一来,换硬件时只需要改设备树文件,不用动内核源码。编译也快多了——只编译设备树,不用重新编译整个内核。

1.3 设备树在Linux内核中的角色

设备树在Linux内核里到底扮演什么角色?

说白了,设备树就是内核的「硬件说明书」。内核启动时,会读取设备树文件,然后根据里面的描述来初始化硬件。

具体流程是这样的:

  1. Bootloader(如U-Boot)加载设备树二进制文件(.dtb)到内存
  2. Bootloader把设备树地址传给内核
  3. 内核解析设备树,获取硬件信息
  4. 内核根据设备树信息,注册平台设备、初始化驱动

我举个例子。假设你的板子上有个LED灯,接在GPIO1_3上。在板级文件时代,你得在 board-xxx.c 里写:

static struct gpio_led my_led = {
    .gpio = 32,  // GPIO1_3对应的编号
    .name = "my_led",
    .default_trigger = "heartbeat",
};

而在设备树时代,你只需要在 .dts 文件里写:

my_led {
    compatible = "gpio-leds";
    gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>;
    linux,default-trigger = "heartbeat";
};

内核的GPIO LED驱动会自动解析这个节点,然后注册LED设备。你根本不用改内核代码。

注意:设备树不是万能的。它只描述硬件,不处理硬件初始化逻辑。比如你要初始化一个I2C触摸屏,设备树只告诉内核「这个I2C设备地址是0x38」,但怎么初始化、怎么读取数据,还是得靠驱动代码。

1.4 设备树的优势总结

说了这么多,咱们总结一下设备树的优势:

对比项 板级文件时代 设备树时代
硬件描述方式 C语言硬编码 文本文件(.dts)
换硬件成本 改内核源码,重新编译 改设备树文件,重新编译
内核维护成本 高(上千个板级文件) 低(统一设备树接口)
硬件复用性 差(每个板子独立文件) 好(支持设备树包含和覆盖)
启动速度 慢(需编译大量代码) 快(只编译设备树)

我个人觉得,设备树最大的贡献不是技术上的,而是生态上的。它让硬件厂商和内核社区解耦了。硬件厂商只需要提供设备树文件,内核社区只需要维护驱动代码。两边各干各的,互不干扰。

嗯,这里要注意一点:设备树虽然好,但也不是没有缺点。比如调试起来比较麻烦,设备树语法错误可能导致内核启动失败,而且错误信息往往不够直观。我刚开始用设备树时,就经常因为少写一个分号或者括号不匹配,导致内核起不来。后来我养成了一个习惯:每次修改设备树后,先用 dtc -I dts -O dtb 检查语法,再烧录到板子上。

好了,第一章就讲到这里。下一章咱们会深入设备树的语法和结构,手把手教你写一个完整的设备树文件。

课后思考:如果你现在用的开发板没有设备树支持,你能手动添加一个设备树文件吗?试试看,从最简单的LED驱动开始。