设备树超强讲解(含大量示例)

📅 发布时间:2026/7/12 23:57:02 👁️ 浏览次数:
设备树超强讲解(含大量示例)
Linux设备树引言:为什么需要设备树?“中央集权” vs “地方割据”一、设备树基础1. 设备树结构2. 节点2.1 节点标签和节点名2.2 节点分类2.3 单元地址3. 属性3.1. 核心标识属性3.2 中断相关属性3.3 时钟和复位3.4 引脚控制(Pinctrl)3.5 设备类型3.6 通用和自定义属性二、不同节点讲解1. 设备节点2. 系统描述节点3. 容器节点4. 配置子节点引言:为什么需要设备树?在嵌入式Linux中,如何让一个内核镜像支持多种不同的硬件平台?传统的x86架构通过BIOS等标准接口自动探测硬件,但ARM等嵌入式平台缺乏此类标准。过去,开发者必须将硬件信息硬编码到内核源码中,导致内核越来越臃肿,增加了移植的困难程度。Linux内核从3.x开始引入设备树(Device Tree)的概念,解决了这一问题。​ 它的核心思想是:将硬件的结构性描述与内核代码分离。硬件的结构性描述包括系统的硬件组成和拓扑结构。在系统启动时,设备树作为一个单独的数据文件,由Bootloader传递给内核,内核据此来初始化相应的硬件和驱动。使用设备树后,如果想将Linux内核移植到其他硬件,或硬件发生改动,都可以直接通过修改设备树来实现,而不用修改驱动内核驱动代码。我们编辑的设备树文件格式为.dts(device tree source,设备树源文件),其中会通过#include “xxx.dtsi” 包含.dtsi文件(device tree source include,设备树头文件),.dtsi文件起到代码复用‌的作用,SoC或平台的通用硬件配置都被提取到.dtsi中,.dts文件直接包含.dtsi文件即可避免重复编写相同内容 。.dts文件根据硬件配置完成后,通过make dtbs 命令将其编译成二进制的.dtb(device tree binary,设备树二进制)文件供内核驱动使用。“中央集权” vs “地方割据”插播一个小知识,上面我们提到,传统的x86架构通过BIOS等标准接口可以自动探测硬件,但ARM等嵌入式平台缺乏此类标准。那ARM为什么不也搞一个标准接口自动探测硬件呢?其实ARM不是“不想搞”,而是其开放、碎片化的生态和多样化的应用场景,使得“搞一个像x86 BIOS那样的统一硬件探测标准”在技术上不必要、在商业上不可行、在历史上已错过最佳时机。而通过设备树,ARM也比BIOS更加轻便、更加灵活,这也是它区别于x86的特点。这么说来,其实x86有点像中央集权,而ARM有点像地方分权(本来想说地方割据的,但是AI说地方分权更准确)。x86是“中央集权”:由Intel/AMD(中央)制定严格的CPU、芯片组和接口标准(法律),所有硬件厂商(地方)必须严格遵守。操作系统(朝廷)通过BIOS/UEFI(钦差大臣)这套标准流程,就能掌控天下硬件。优点是秩序井然、兼容性强;缺点是创新慢、不够灵活。ARM是“地方分权”:ARM公司只提供CPU核心设计蓝图(基础律法)。高通、三星、NXP等数百家芯片厂商(诸侯)获得授权后,在自己的“封地”(SoC)上自由发挥,集成各种私有模块。结果是百花齐放、高度优化,但彼此互不兼容。Linux内核(共主)无法直接管理,需要每家诸侯提交一份详细的“户籍地图”(设备树)来了解实地情况。这个差异在技术上的具体体现:1. 设计流程:x86:先有标准(如新的PCIe规范),然后硬件厂商依标生产,最后操作系统适配标准。ARM:先有产品需求(例如一款智能手表),芯片厂商为此定制一颗SoC,然后软件工程师为这颗特定SoC编写/适配系统。2. 产品形态:x86:你可以买一块标准主板,自由插拔显卡、内存。硬件是模块化、可替换的。ARM:你的手机主板上的CPU、内存、传感器都是焊死的。硬件是一个不可分割的整体。3. 驱动问题:x86:显卡厂商必须提供符合标准接口(如DirectX、Vulkan)的通用驱动。ARM:设备厂商需要提供针对自己这款设备所有硬件的专属驱动包(或直接提交给内核主线)。这就是为什么安卓手机系统升级慢,因为需要各厂商“地方适配”。设备树扮演的角色:一部“地方自治基本法”在ARM的“分权”生态中,设备树就是那部让“共主”(Linux内核)能够有效管理所有“诸侯国”(SoC)的基本法。它规定:诸侯国必须用统一的格式(DTS语法)上报自己的资源(硬件信息)。共主按照统一的法律(OF API)来解读和使用这些资源。这样一来,内核无需知道诸侯国的内部细节,只要它们都遵守“上书”格式,内核就能统治天下。这正是在“地方分权”状态下实现高效治理的智慧。一、设备树基础1. 设备树结构设备树是一种树形结构的数据格式,由节点和属性组成。节点:是设备树的基本组织单元,它是一个包含属性和子节点(可选)的结构体,用于描述系统中的一个硬件设备、逻辑组件或配置实体。属性:节点的键值对,描述节点的特性、配置、资源或关系。下面是一个设备树的结构:#include "xxx.dtsi" /{ //根节点 node1{ //node1节点,是/的子节点 key=value; //node1节点的属性 ... node3{ //node3是node1的子节点 key=value; //node3的属性 ... } } node2{ //node2节点,是/的子节点 key=value; ... } }2. 节点2.1 节点标签和节点名节点标签:是节点的引用别名(相当于变量名),方便后续直接通过 “label” 引用,对节点信息进行补充或者使用。节点名:是节点的正式名称,格式是节点类型 [@单元地址],@单元地址可选(设备树规范允许同一个节点名称出现多次,只要它们的单元地址不同即可。因此,如果节点仅出现一次时,可以省略单元地址,而同一个节点名称需要出现多次时,可以通过指定不同的单元地址进行区分。)// 一个简单的节点示例 uart0: serial@10000000 { // 节点标签:uart0,节点名:serial@10000000 compatible = "ns16550a"; // 属性:驱动匹配关键字 reg = 0x10000000 0x1000; // 属性:寄存器地址和大小 status = "okay"; // 属性:设备状态 };节点标签和节点名不直接影响驱动匹配,驱动匹配主要看compatible属性。但节点名称应该具有描述性,方便开发者理解。2.2 节点分类按节点在设备树中的角色,分为以下几类:(1)根节点“ / ”:设备树的树根啦。在一个设备树.dts文件中,有且仅有一个根节点。(2)设备节点:与内核驱动进行匹配,初始化真实的硬件。特征是有compatible。(3)系统描述节点:这类节点为内核子系统提供组织结构信息,本身不对应具体硬件,用来描