第一章:设备树起源——为什么需要设备树?
各位同学,大家好。我是你们的嵌入式系统讲师。今天咱们来聊聊设备树的起源。
说实话,我刚入行那会儿,还没有设备树这东西。那时候做嵌入式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内核里到底扮演什么角色?
说白了,设备树就是内核的「硬件说明书」。内核启动时,会读取设备树文件,然后根据里面的描述来初始化硬件。
具体流程是这样的:
- Bootloader(如U-Boot)加载设备树二进制文件(.dtb)到内存
- Bootloader把设备树地址传给内核
- 内核解析设备树,获取硬件信息
- 内核根据设备树信息,注册平台设备、初始化驱动
我举个例子。假设你的板子上有个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驱动开始。