1. 设备树基础概念

各位同学好,我是老李。做车载Linux驱动开发这些年,我最大的感触就是——设备树这东西,你绕不开。今天咱们就来聊聊设备树的基础概念。

1.1 什么是设备树

设备树,英文叫Device Tree,简称DT。说白了,它就是一个描述硬件信息的数据结构。你想想看,以前Linux内核里塞满了各种板级文件,每个板子一个board-xxx.c,改个引脚都得重新编译内核,多麻烦。

设备树就是来解决这个问题的。它用树形结构来描述硬件:

  • 根节点是/,代表整个系统
  • 子节点是各种设备,比如CPU、内存、I2C控制器、SPI控制器
  • 每个节点里放的是设备的属性,比如寄存器地址、中断号、时钟频率

我习惯把设备树想象成一张硬件配置表。内核启动时读这张表,就知道该怎么跟硬件打交道了。

核心要点:设备树是硬件描述语言,不是驱动代码。它告诉内核「有什么硬件」,而不是「怎么驱动硬件」。

1.2 为什么需要设备树

这个问题我经常被问到。其实原因很简单——硬件太多了,内核管不过来。

在设备树出现之前,Linux用的是什么方式?

  • 每个ARM平台都有自己的arch/arm/mach-xxx目录
  • 每个板子都要写一个board-xxx.c文件
  • 改一个GPIO引脚,就得重新编译整个内核

我在2012年刚入行时,就吃过这个亏。当时给一个车载信息娱乐系统换了个新屏幕,只是改了背光控制的GPIO号,结果编译内核花了快半小时。你说这效率能忍吗?

设备树带来的好处很明显:

  1. 硬件与驱动解耦:驱动代码不用改,改设备树就行
  2. 减少内核代码量:不再需要成百上千的板级文件
  3. 支持多平台:同一份内核镜像,换设备树就能跑不同硬件
  4. 启动参数灵活:内存大小、时钟频率等参数可以在设备树里配

我的经验:在车载项目中,设备树最大的价值就是「快速适配」。客户换了个传感器,或者改了引脚分配,我们只需要改dts文件,重新编译设备树,不用动内核。这在项目交付阶段特别重要。

1.3 设备树在车载Linux中的角色

车载系统和普通嵌入式系统不太一样。它有几个特点:

  • 硬件复杂:一个域控制器可能有几十个外设
  • 变种多:同一款车型,高配低配硬件不一样
  • 安全要求高:不能随便改内核,但又要适配不同硬件

设备树在车载系统里扮演什么角色?我总结了三点:

角色 说明 实际案例
硬件抽象层 把硬件细节从内核代码中抽离出来 不同车型的摄像头接口,通过设备树配置
配置中心 统一管理所有外设的参数 CAN总线波特率、I2C地址、GPIO复用
版本管理 不同硬件版本对应不同设备树 2023款和2024款车型,用不同dts文件

我记得有一次,客户要求在一周内适配三款不同配置的域控制器。要是没有设备树,我得改三份内核代码,测试三遍。有了设备树,我只需要写三个dts文件,内核镜像完全一样。嗯,这就是设备树的威力。

1.4 设备树文件格式

设备树源文件是.dts,编译后是.dtb。咱们先看看最基本的格式:

/dts-v1/;

/ {
    model = "TI AM5728 EVM";
    compatible = "ti,am5728-evm", "ti,am5728";

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

    chosen {
        stdout-path = &uart3;
    };

    &i2c1 {
        status = "okay";
        clock-frequency = <400000>;

        eeprom@50 {
            compatible = "atmel,24c02";
            reg = <0x50>;
        };
    };
};

这里有几个关键点:

  • /dts-v1/:版本声明,必须写在第一行
  • /:根节点,整个设备树的起点
  • modelcompatible:标识这个板子是谁家的
  • memory节点:告诉内核内存布局
  • chosen节点:启动参数,比如控制台用哪个串口
  • &i2c1:引用外部定义的节点,添加或覆盖属性

注意:设备树里的reg属性,第一个值是地址,第二个值是长度。很多新手搞反了,导致内存映射出错。我曾经调试过一个CAN控制器不工作的问题,查了两天才发现是reg写反了。

1.5 设备树在启动流程中的位置

搞懂设备树,还得知道它在系统启动时是怎么被用的。我画个简单的流程:

  1. Bootloader(U-Boot):加载dtb文件到内存
  2. Bootloader:把dtb地址传给内核(通过r2寄存器)
  3. 内核启动早期:解析dtb,建立设备树结构
  4. 内核驱动初始化:遍历设备树,匹配驱动
  5. 驱动probe:从设备树读取资源,初始化硬件

说白了,设备树就是内核和硬件之间的「翻译官」。没有它,内核不知道内存多大、串口在哪、中断号是多少。

我在调试一个车载摄像头驱动时,发现设备树里配的中断号是32,但芯片手册上写的是33。内核启动时中断注册失败,摄像头死活不出图。后来查了三天才发现是设备树写错了。嗯,从那以后我养成了一个习惯——拿到新板子,先对照芯片手册核对设备树里的所有中断号和地址。

1.6 小结

今天咱们聊了设备树的基础概念。记住三点:

  • 设备树是硬件描述,不是驱动代码
  • 它让硬件和驱动解耦,适配更灵活
  • 在车载系统里,它是硬件抽象和配置中心

下一章,我会带大家深入设备树的语法和常用属性。到时候咱们手把手写一个完整的设备树文件,把今天讲的东西用起来。

课后练习:找一块你手头的开发板,看看它的dts文件。找到memory节点、chosen节点、至少一个外设节点。试着理解每个属性的含义。下次课我会抽查哦。