【Linux驱动开发】第10天:设备树零基础入门——DTS/DTB/DTC全解+编译流程

目录

  1. 为什么需要设备树?传统驱动的终极痛点
  2. [DTS/DTB/DTC 大白话定义+核心区别](#DTS/DTB/DTC 大白话定义+核心区别)
  3. 三者关系+完整编译流程图
  4. 最简单的DTS示例+语法解析
  5. 设备树编译与反编译实操命令
  6. 内核如何加载和使用设备树
  7. 核心总结+面试必背考点

1. 为什么需要设备树?传统驱动的终极痛点

在设备树出现之前,Linux内核采用硬编码的方式描述硬件信息:

  • 所有寄存器地址、中断号、GPIO号都直接写死在驱动代码里
  • 换一个开发板,就要修改驱动代码重新编译
  • 一个ARM内核要支持几十上百个开发板,就要在代码里写几百个硬件信息的分支
  • 内核变得无比臃肿,维护成本极高

设备树的诞生,就是为了彻底解决这个问题

硬件描述信息从内核代码中抽离出来,用一个独立的文件来描述硬件。内核只需要一份通用的代码,通过读取这个文件来识别不同的硬件。

大白话类比

  • 传统驱动:厨师把所有客人的口味都写在菜谱里,来了一个客人就要改一次菜谱
  • 设备树:厨师用通用菜谱,客人自己带一张写着自己口味的纸条,厨师照着纸条做菜

2. DTS/DTB/DTC 大白话定义+核心区别

设备树体系由三个核心部分组成:DTS、DTB、DTC,三者分工明确,缺一不可。

2.1 三者大白话定义

缩写 全称 中文名称 大白话解释
DTS Device Tree Source 设备树源文件 人类可读的文本文件,用C语言风格的语法描述硬件信息,相当于Word文档
DTC Device Tree Compiler 设备树编译器 把人类可读的DTS文本文件,编译成内核能识别的二进制文件,相当于Word转PDF的转换器
DTB Device Tree Blob 设备树二进制文件 DTC编译后的二进制文件,内核启动时加载并解析,相当于PDF文件

2.2 核心区别对比表

对比项 DTS DTC DTB
文件类型 纯文本文件 可执行程序 二进制文件
可读性 人类可读 不可读 不可读
作用 描述硬件信息 编译DTS为DTB 内核加载解析
编辑方式 任何文本编辑器 无需编辑 不能直接编辑
后缀名 .dts.dtsi 无后缀 .dtb

2.3 补充:什么是.dtsi文件?

.dtsi是设备树头文件,相当于C语言的.h头文件,用于存放多个DTS文件共享的公共硬件信息。

  • 比如同一个芯片的所有开发板,芯片内部的外设信息是相同的,可以放在.dtsi文件中
  • 每个开发板自己的.dts文件只需要包含这个.dtsi,然后添加自己独有的硬件信息即可
  • 大大提高了代码复用性,减少了重复代码

3. 三者关系+完整编译流程图

3.1 三者核心关系

复制代码
DTS(源文件) --DTC编译--> DTB(二进制文件) --内核加载解析--> 识别硬件

3.2 完整编译与运行流程图

开发者编写DTS设备树源文件
包含公共的.dtsi头文件
DTC编译器编译DTS
生成DTB二进制设备树文件
将DTB烧录到开发板的指定分区
内核启动
bootloader将DTB地址传递给内核
内核解析DTB文件
根据DTB创建platform_device设备
platform总线匹配设备和驱动
调用驱动的probe函数初始化硬件
设备正常工作

3.3 关键流程说明

  1. bootloader的作用:bootloader(如U-Boot)负责在启动内核之前,将DTB文件加载到内存中,并将DTB的内存地址传递给内核
  2. 内核解析DTB:内核启动时,根据bootloader传递的地址,解析DTB文件,创建对应的platform_device设备
  3. 总线匹配:platform总线将创建的设备和已注册的驱动进行匹配,匹配成功后调用probe函数

4. 最简单的DTS示例+语法解析

下面是一个最小可运行的DTS文件,我会逐行解释每个部分的含义,让你快速掌握设备树的基本语法。

4.1 最简单的DTS示例

dts 复制代码
/dts-v1/;  // 设备树版本号,必须是第一行

/ {  // 根节点,所有设备节点都在根节点下面
    compatible = "myvendor,myboard";  // 板级兼容属性,内核用它来匹配板级支持包

    model = "My Custom Development Board";  // 开发板名称

    // 自定义LED设备节点
    led@12340000 {
        compatible = "myvendor,my-led";  // 设备兼容属性,和驱动的of_device_id匹配
        reg = <0x12340000 0x1000>;  // 寄存器基地址和长度
        status = "okay";  // 设备状态:okay表示启用,disabled表示禁用
    };

    // 自定义UART设备节点
    uart@12341000 {
        compatible = "myvendor,my-uart";
        reg = <0x12341000 0x1000>;
        interrupts = <5>;  // 中断号
        status = "okay";
    };
};

4.2 核心语法解析

1. 节点(Node)
  • 设备树用树形结构描述硬件,每个硬件对应一个节点
  • 节点格式:节点名@地址 { ... };
  • 节点名:描述设备类型,如leduartgpio
  • @地址:设备的寄存器基地址,用于区分同类型的不同设备
2. 属性(Property)
  • 每个节点包含多个属性,用于描述设备的具体信息
  • 属性格式:属性名 = 属性值;
  • 常见属性:
    • compatible:最重要的属性,用于和驱动匹配,格式为"厂商,设备名"
    • reg:描述设备的寄存器地址范围,格式为<基地址 长度>
    • interrupts:描述设备使用的中断号
    • status:描述设备状态,okay表示启用,disabled表示禁用
    • model:设备的人类可读名称
3. 根节点
  • 所有节点的父节点,用/表示
  • 根节点的compatible属性用于匹配板级支持包
  • 根节点的model属性用于描述开发板名称

5. 设备树编译与反编译实操命令

5.1 安装DTC编译器

Ubuntu系统下直接安装:

bash 复制代码
sudo apt-get install device-tree-compiler

5.2 编译DTS为DTB

bash 复制代码
# 基本语法:dtc -I dts -O dtb -o 输出文件.dtb 输入文件.dts
dtc -I dts -O dtb -o myboard.dtb myboard.dts

5.3 反编译DTB为DTS

这是调试设备树最常用的命令,可以把内核正在使用的DTB文件反编译成可读的DTS文件:

bash 复制代码
# 基本语法:dtc -I dtb -O dts -o 输出文件.dts 输入文件.dtb
dtc -I dtb -O dts -o myboard.dts myboard.dtb

5.4 查看开发板当前使用的设备树

在运行中的Linux系统中,可以通过/proc/device-tree目录查看内核解析后的设备树:

bash 复制代码
# 查看根节点的compatible属性
cat /proc/device-tree/compatible

# 查看所有设备节点
ls /proc/device-tree/

# 查看LED节点的reg属性
cat /proc/device-tree/led@12340000/reg | hexdump -C

6. 内核如何加载和使用设备树

6.1 两种加载方式

方式1:DTB编译进内核
  • 将DTB文件和内核镜像编译在一起,生成一个单一的镜像文件
  • 优点:简单,不需要单独管理DTB文件
  • 缺点:修改设备树需要重新编译内核
方式2:DTB单独加载(推荐)
  • DTB文件和内核镜像分开存储
  • bootloader在启动内核时,将DTB加载到内存中,并将地址传递给内核
  • 优点:修改设备树只需要重新编译DTB,不需要重新编译内核
  • 现代Linux系统全部采用这种方式

6.2 U-Boot传递DTB地址的命令

bash 复制代码
# 加载内核镜像到内存0x80800000地址
tftp 0x80800000 zImage

# 加载DTB文件到内存0x83000000地址
tftp 0x83000000 myboard.dtb

# 启动内核,传递DTB地址
bootz 0x80800000 - 0x83000000

6.3 内核解析DTB的过程

  1. 内核启动时,从bootloader获取DTB的内存地址
  2. 验证DTB的完整性和有效性
  3. 解析DTB的根节点和所有子节点
  4. 为每个设备节点创建对应的platform_device结构体
  5. platform_device注册到platform总线
  6. platform总线进行设备和驱动的匹配
  7. 匹配成功后调用驱动的probe函数

7. 核心总结+面试必背考点

核心总结

  1. 设备树的核心作用是将硬件描述信息从内核代码中抽离出来,解决传统驱动硬编码的问题
  2. DTS是人类可读的设备树源文件,DTB是内核能识别的二进制文件,DTC是编译两者的编译器
  3. 设备树采用树形结构描述硬件,每个硬件对应一个节点,每个节点包含多个属性
  4. compatible属性是设备和驱动匹配的唯一依据,格式为"厂商,设备名"
  5. 现代Linux系统采用bootloader单独加载DTB的方式,修改设备树不需要重新编译内核

面试必背考点

  1. 什么是设备树?它解决了什么问题?
  2. DTS、DTB、DTC分别是什么?三者的关系是什么?
  3. 什么是.dtsi文件?它的作用是什么?
  4. 设备树的基本语法结构是什么?
  5. compatible属性的作用是什么?格式是什么?
  6. 内核如何加载和解析设备树?
  7. bootloader在设备树启动过程中起到什么作用?
  8. 如何反编译DTB文件为DTS文件?
相关推荐
枳实-叶2 小时前
【Linux驱动开发】第11天:设备树(Device Tree)超详细全解:从诞生背景到工作原理
linux·运维·驱动开发
IceSugarJJ2 小时前
Windows下VSCode+ WSL项目启动流程
linux·windows·vscode·ubuntu·wsl
Bert.Cai2 小时前
Linux shift命令详解
linux·运维·服务器
叶~小兮2 小时前
Zabbix 7.0 深度原理与生产进阶运维学习笔记
运维·学习·zabbix
cui_ruicheng2 小时前
Linux网络编程(六):UDP聊天室与线程池
linux·服务器·网络·udp
Yeats_Liao2 小时前
物联网接入层技术剖析(一):从select到epoll
java·linux·后端·物联网·struts
XMAIPC_Robot3 小时前
深度无人机自动驾驶仪,中小型无人机硬件在环仿真飞行
运维·arm开发·人工智能·fpga开发·无人机·边缘计算
一只小逸白3 小时前
LeetCode Go 常用函数速查表
linux·leetcode·golang
文青小兵3 小时前
云计算Linux——数据库MySQL读写分离、数据库备份、恢复(十八)
linux·运维·服务器·数据库·mysql·云计算