当前位置: 首页 > news >正文

【Linux驱动开发】第12天:Linux设备树核心:树形结构+节点+属性 完整全解

目录

  1. 设备树树形结构概述
  2. 节点(Node)全解:命名规范+标准节点+常用设备节点
  3. 属性(Property)全解:类型+核心属性+总线专用属性
  4. 标签与节点引用:设备树复用的核心
  5. 常见错误与注意事项
  6. 总结:驱动开发必背节点与属性

1. 设备树树形结构概述

设备树采用分层树形结构描述硬件,和计算机的文件系统目录结构几乎完全一致。它以根节点为起点,向下逐层展开,真实反映了计算机系统中硬件的物理连接关系。

1.1 树形结构的三大核心元素

设备树的所有内容都由这三个基本元素组成:

根节点(/) ├── 子节点1(代表一个硬件设备) │ ├── 属性1 = 值1(描述设备特性) │ ├── 属性2 = 值2 │ └── 孙子节点1(代表子设备) │ └── 属性3 = 值3 └── 子节点2 └── 属性4 = 值4
  • 根节点:所有节点的父节点,用/表示,每个设备树有且只有一个
  • 节点:每个硬件设备对应一个节点,节点可以包含子节点,形成分层结构
  • 属性:每个节点包含多个键值对形式的属性,用于描述设备的具体硬件信息

1.2 最简完整设备树示例

/dts-v1/; // 设备树版本号,必须是第一行 / { // 根节点 compatible = "myvendor,myboard"; model = "My Custom Development Board"; // 片上系统节点,包含所有内部外设 soc { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; ranges; // UART串口节点 uart@12340000 { compatible = "myvendor,my-uart"; reg = <0x12340000 0x1000>; interrupts = <5>; status = "okay"; }; // GPIO控制器节点 gpio@12341000 { compatible = "myvendor,my-gpio"; reg = <0x12341000 0x1000>; gpio-controller; #gpio-cells = <2>; status = "okay"; }; }; // 外接LED节点 leds { compatible = "gpio-leds"; blue-led { label = "blue:user"; gpios = <&gpio 13 GPIO_ACTIVE_HIGH>; }; }; };

2. 节点(Node)全解:命名规范+标准节点+常用设备节点

节点是设备树的基本单元,每个硬件设备对应一个节点。

2.1 节点命名规范(必须严格遵守)

不符合规范的节点会导致内核解析失败,驱动无法匹配。

标准命名格式
节点名@单元地址
各部分含义
  1. 节点名:描述设备的通用类型,如cpumemoryuarti2c

    • 只能使用小写字母、数字和连字符-
    • 不能使用下划线_、空格或其他特殊字符
    • 尽量使用内核通用的设备类型名,不要使用厂商自定义名称
  2. @单元地址:设备的唯一地址标识

    • 内存映射设备:寄存器基地址(十六进制)
    • I2C/SPI设备:从机地址/片选号
    • CPU:CPU编号
    • 作用:区分同类型的不同设备
正确与错误命名对比
正确命名错误命名错误原因
uart@12340000UART@12340000使用了大写字母
i2c@12341000i2c_controller@12341000节点名过长,应使用通用类型名
led@1led_1使用了下划线,缺少@和单元地址
cpu@0cpu0缺少@和单元地址

2.2 标准节点(每个设备树都必须包含)

这些节点不对应具体的外设,而是提供系统级的全局信息。

1. 根节点/
  • 作用:整个设备树的起点,包含系统全局信息
  • 必须属性
    / { compatible = "厂商,板级型号"; // 板级兼容属性,匹配内核板级支持包 model = "开发板全称"; // 人类可读的开发板名称 #address-cells = <1>; // 子节点reg属性中地址部分占几个32位整数 #size-cells = <1>; // 子节点reg属性中长度部分占几个32位整数 };
2./aliases别名节点
  • 作用:给节点起简短别名,方便内核和驱动引用
  • 示例
    aliases { serial0 = &uart4; // 给uart4节点起别名serial0 i2c1 = &i2c1; // 给i2c1节点起别名i2c1 led0 = &blue_led; // 给LED节点起别名led0 };
  • 用途:内核通过别名快速找到特定设备,如console=ttySTM0对应serial0别名
3./chosen选择节点
  • 作用:传递内核启动参数和运行时配置
  • 最重要属性bootargs(内核命令行参数)
  • 示例
    chosen { bootargs = "console=ttySTM0,115200 root=/dev/mmcblk0p2 rootwait rw"; stdout-path = &uart4; // 指定标准输出设备 };
4./memory内存节点
  • 作用:描述系统物理内存信息
  • 必须属性
    memory@c0000000 { device_type = "memory"; // 固定值,标识这是内存节点 reg = <0xc0000000 0x20000000>; // 基地址0xc0000000,长度0x20000000(512MB) };
5./cpusCPU节点
  • 作用:描述系统中的CPU核心信息
  • 结构:包含一个或多个cpu@x子节点
  • 示例
    cpus { #address-cells = <1>; #size-cells = <0>; cpu@0 { compatible = "arm,cortex-a7"; device_type = "cpu"; reg = <0>; // CPU编号 clock-frequency = <800000000>; // CPU主频800MHz }; cpu@1 { compatible = "arm,cortex-a7"; device_type = "cpu"; reg = <1>; clock-frequency = <800000000>; }; };
6./soc片上系统节点
  • 作用:包含所有片上外设(UART、I2C、SPI、GPIO等)
  • 必须属性
    soc { compatible = "simple-bus"; // 告诉内核这是一个简单总线 #address-cells = <1>; #size-cells = <1>; ranges; // 地址映射,空表示子节点地址与父节点地址相同 // 所有片上外设节点都放在这里 uart@40010000 { ... }; i2c@40012000 { ... }; gpio@50002000 { ... }; };

2.3 驱动开发最常用的通用设备节点

1. 总线类节点
(1)UART串口节点
uart4: serial@40010000 { compatible = "st,stm32mp157-uart"; reg = <0x40010000 0x400>; // 寄存器基地址和长度 interrupts = <52>; // 中断号 clocks = <&rcc UART4_CLK>; // 时钟 status = "okay"; // 启用设备 };
(2)I2C总线节点
i2c1: i2c@40012000 { compatible = "st,stm32mp157-i2c"; reg = <0x40012000 0x400>; interrupts = <31>; #address-cells = <1>; // 子节点reg占1个cell(I2C从地址) #size-cells = <0>; // 子节点reg没有长度部分 clock-frequency = <100000>; // I2C总线频率100kHz status = "okay"; // I2C从设备节点 eeprom@50 { compatible = "atmel,24c02"; reg = <0x50>; // I2C从机地址0x50 }; };
(3)SPI总线节点
spi1: spi@40013000 { compatible = "st,stm32mp157-spi"; reg = <0x40013000 0x400>; interrupts = <32>; #address-cells = <1>; // 子节点reg占1个cell(片选号) #size-cells = <0>; status = "okay"; // SPI从设备节点 flash@0 { compatible = "winbond,w25q64"; reg = <0>; // 片选号0 spi-max-frequency = <10000000>; // 最大SPI频率10MHz }; };
(4)GPIO控制器节点
gpioa: gpio@50002000 { compatible = "st,stm32mp157-gpio"; reg = <0x50002000 0x400>; gpio-controller; // 标识这是一个GPIO控制器 #gpio-cells = <2>; // GPIO引用需要2个参数:引脚号+极性 status = "okay"; };
2. 外设类节点
(1)LED节点
leds { compatible = "gpio-leds"; blue_led: led-blue { label = "blue:user"; // LED名称 gpios = <&gpioa 13 GPIO_ACTIVE_HIGH>; // 使用GPIOA13,高电平点亮 linux,default-trigger = "heartbeat"; // 默认心跳模式 }; };
(2)按键节点
keys { compatible = "gpio-keys"; key-user { label = "user-key"; gpios = <&gpioc 13 GPIO_ACTIVE_LOW>; // GPIOC13,低电平有效 linux,code = <KEY_ENTER>; // 按键码 }; };

3. 属性(Property)全解:类型+核心属性+总线专用属性

属性是节点的核心,用于描述设备的具体硬件特性,采用键值对形式。

3.1 属性的5种基本类型

类型语法示例用途
字符串类型双引号""包裹compatible = "st,stm32-uart";描述名称、兼容属性、状态
32位整数类型尖括号<>包裹reg = <0x12340000 0x1000>;描述地址、长度、中断号
字节数组类型方括号[]包裹mac-address = [00 11 22 33 44 55];描述MAC地址、二进制数据
布尔类型只有属性名,没有值gpio-controller;标识设备具有某种特性
字符串数组类型多个字符串用逗号分隔compatible = "st,stm32-uart", "arm,pl011-uart";多个兼容属性

3.2 核心属性(驱动开发每天都会用到)

这些属性是所有设备节点通用的,也是最重要的属性。

1.compatible(最重要,驱动匹配唯一依据)
  • 作用:设备和驱动之间的"配对暗号"
  • 格式"厂商,设备型号"
  • 匹配规则:内核从左到右依次匹配驱动的of_device_id表,匹配到第一个就停止
  • 示例
    compatible = "st,stm32mp157-uart", "arm,pl011-uart";
  • 注意:必须和驱动代码中的字符串完全一致,一个字符都不能错,否则驱动永远不会执行probe函数
2.reg(地址资源描述)
  • 作用:描述设备的寄存器地址范围或总线地址
  • 格式:由父节点的#address-cells#size-cells决定
    • 父节点#address-cells = <1>#size-cells = <1>:格式为<基地址 长度>
    • 父节点#address-cells = <2>#size-cells = <2>:格式为<addr_high addr_low len_high len_low>
  • 示例
    // 内存映射设备 reg = <0x40010000 0x400>; // 基地址0x40010000,长度0x400 // I2C设备(父节点#size-cells=0) reg = <0x50>; // I2C从机地址0x50 // SPI设备(父节点#size-cells=0) reg = <0>; // SPI片选号0
3.status(设备状态控制)
  • 作用:控制设备是否启用
  • 有效值
    含义
    okay设备正常启用
    disabled设备禁用,内核会忽略该节点
    fail设备存在但工作异常
  • 注意
    • 父节点status = "disabled"时,所有子节点都会被递归忽略
    • .dtsi文件中节点默认状态通常是disabled,在.dts文件中显式改为okay启用
4.interrupts(中断资源描述)
  • 作用:描述设备使用的中断号和触发方式
  • 格式<中断号 触发方式>
  • 常用触发方式
    • IRQ_TYPE_EDGE_RISING:上升沿触发
    • IRQ_TYPE_EDGE_FALLING:下降沿触发
    • IRQ_TYPE_LEVEL_HIGH:高电平触发
    • IRQ_TYPE_LEVEL_LOW:低电平触发
  • 示例
    interrupts = <52 IRQ_TYPE_LEVEL_HIGH>; // 中断号52,高电平触发
5.#address-cells#size-cells
  • 作用:定义子节点reg属性的格式
  • #address-cells:用来描述子节点reg属性的地址表中用来描述首地址的元素所用字(cell)的数量,而每个字(cell)是一个无符号32位整形。
  • #size-cells:用来描述子节点reg属性的地址表中用来描述地址范围大小的元素所用字(cell)的数量,每个字(cell)是一个无符号32位整形。
  • 示例
    soc { #address-cells = <1>; // 子节点地址占1个cell #size-cells = <1>; // 子节点长度占1个cell // 子节点reg格式:<addr len> uart@40010000 { reg = <0x40010000 0x400>; }; };
6.ranges(地址映射)
  • 作用:定义子总线地址到父总线地址的映射关系
  • 格式<子地址 父地址 长度>
  • 空值ranges;:表示子节点地址与父节点地址完全相同
  • 示例
    soc { ranges = <0 0x40000000 0x10000000>; // 子地址0映射到父地址0x40000000,长度0x10000000 };

3.3 常用总线专用属性

1. I2C总线专用
  • clock-frequency:I2C总线频率,单位Hz
  • reg:I2C从机地址
2. SPI总线专用
  • spi-max-frequency:SPI设备最大工作频率,单位Hz
  • reg:SPI片选号
  • spi-cpol:时钟极性,1表示空闲时时钟为高电平
  • spi-cpha:时钟相位,1表示在第二个时钟沿采样
3. GPIO专用
  • gpio-controller:标识这是一个GPIO控制器
  • #gpio-cells:GPIO引用需要的参数个数,通常是2(引脚号+极性)
  • gpios:引用GPIO引脚,格式为<&gpio_controller 引脚号 极性>
    • GPIO_ACTIVE_HIGH:高电平有效
    • GPIO_ACTIVE_LOW:低电平有效
4. 中断控制器专用
  • interrupt-controller:标识这是一个中断控制器
  • #interrupt-cells:中断引用需要的参数个数,通常是2(中断号+触发方式)
  • interrupt-parent:指定该设备的中断控制器

4. 标签与节点引用:设备树复用的核心

设备树通过标签引用实现代码复用,这是.dtsi头文件机制的基础。

4.1 标签(Label)

  • 在节点名前加标签名:,给节点起一个唯一的标识
  • 作用:方便在其他地方引用该节点
  • 示例
    uart4: serial@40010000 { ... };

4.2 节点引用

  • 使用&标签名引用已经定义的节点
  • 主要用途:在.dts文件中修改.dtsi文件中定义的节点属性
  • 示例
    // 在stm32mp157.dtsi中定义了uart4节点,默认状态是disabled uart4: serial@40010000 { compatible = "st,stm32mp157-uart"; reg = <0x40010000 0x400>; interrupts = <52>; status = "disabled"; }; // 在myboard.dts中引用uart4节点,修改为启用状态 &uart4 { status = "okay"; };

5. 常见错误与注意事项

  1. 节点名不规范:使用大写字母、下划线或特殊字符,导致内核解析失败
  2. 缺少@和单元地址:同类型的多个设备无法区分,驱动匹配混乱
  3. 属性值类型错误:应该用尖括号的用了双引号,应该用双引号的用了尖括号
  4. compatible属性不匹配:设备树和驱动的compatible字符串不一致,导致probe不执行
  5. 忘记以空结尾of_device_id表和数组类型的属性必须以空结尾
  6. 父节点禁用导致子节点失效:父节点status = "disabled"时,所有子节点都会被忽略

6. 总结:驱动开发必背节点与属性

必须掌握的节点

  • 标准节点://aliases/chosen/memory/cpus/soc
  • 常用设备节点:uarti2cspigpioledskeys

必须掌握的属性

  • 核心属性:compatibleregstatusinterrupts#address-cells#size-cells
  • 总线属性:rangesclock-frequencyspi-max-frequency
  • GPIO属性:gpiosgpio-controller#gpio-cells
http://www.jsqmd.com/news/873915/

相关文章:

  • 合肥市内10家防水补漏公司实战推荐 - 资讯纵览
  • AI正在重构工程师岗位:被替代的不是“人”,而是低维度能力
  • GPS测速仪SpeedView 3.2.0汉化版 精准速度 实时测速工具
  • 从 MacBook Air 到机器人:Caitlin Kalinowski 谈「硬件只有五次编译机会」
  • 第二周学习
  • 清远厂房搬家无缝攻略:费用明细 靠谱公司实测推荐 - 从来都是英雄出少年
  • pod创建
  • 永磁同步电机-叶片耦合激振系统数学建模
  • 从Java全栈开发到云原生:一次真实的面试对话与技术剖析
  • 2026高口碑木薯猫砂排行榜!兼顾安全与实用性,养猫党闭眼入 - 资讯纵览
  • C166 Class B硬件陷阱解析与调试实战
  • Shutter Encoder:构建高效媒体工作流的FFmpeg图形化解决方案
  • 【电机】基于matlab电机温度的BLDC冷却系统【含Matlab源码 15554期】
  • JDK常用类与工具(速览版)
  • 传统FPM项目怎么渐进式迁移到Swoole/Hyperf?
  • 清远搬厂公司推荐:实惠靠谱、无缝搬家全攻略2026 - 从来都是英雄出少年
  • MNBVC:重塑中文AI数据生态的突破性基础设施
  • 陈彪院士:一生奉献太阳物理,一心报国照亮苍穹
  • 企业部署文件加密系统后,员工嫌卡顿怎么办?我们这样优化策略
  • 最近调研了几套开源商城系统,聊聊真实二开体验
  • synapse-graph,图记忆skills——给全栈个体户的图拓扑工程记忆系统
  • Keil C166嵌入式开发中的宽字符实现与优化
  • 宣威龙泉汽修,宣威修车哪家好 - 资讯纵览
  • 为Hermes Agent配置自定义供应商接入Taotoken的完整流程
  • 2025大厂Java后端面试:RAG高频考点【干货】
  • 使用桥接模式的优点分析(一)
  • Agent大战,赢家暗自在哪下功夫?
  • 技术债务管理:平衡开发速度与代码质量
  • 号卡联盟官方邀请码应该填什么?实测填写16888注册一级代理全网佣金最高0抽成 - 流量卡代理招商
  • vscode+clangd打开头文件发现某些标识符不识别为“白色”语言模型识别为C++