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

[Linux实战] Linux设备树原理与应用详解

Linux设备树原理与应用详解

一、设备树概述

1.1 什么是设备树

设备树(Device Tree,简称DT)是一种描述硬件资源的数据结构,它通过一种树状结构来描述系统硬件配置,包括CPU、内存、总线、外设等硬件信息。设备树最初在PowerPC架构中使用,后来被ARM等架构广泛采用,成为Linux内核中描述非可发现硬件(non-discoverable hardware)的标准方式。

1.2 设备树的发展历史

设备树的概念并非Linux首创,它起源于Open Firmware标准(IEEE 1275),主要用于PowerPC和SPARC架构。随着ARM架构在嵌入式领域的普及,Linux社区面临着大量ARM芯片和板级的支持需求。传统的"board file"方式导致内核中充斥着大量板级特定代码,维护困难。

2006年,设备树被引入到ARM架构的Linux支持中,并逐渐成为ARM Linux的标准硬件描述方式。设备树的采用解决了以下问题:

  • 减少了内核中板级特定代码的数量
  • 提高了内核的通用性
  • 使单个内核镜像能够支持多种硬件平台
  • 简化了硬件描述的维护工作

1.3 设备树的优势

与传统硬编码硬件信息的方式相比,设备树具有以下优势:

  1. 硬件描述与内核分离:硬件配置信息不再硬编码在内核中,而是通过外部文件描述
  2. 可移植性增强:同一内核可以支持不同硬件,只需加载不同的设备树文件
  3. 可维护性提高:硬件变更只需修改设备树文件,无需重新编译内核
  4. 可读性好:设备树源文件(dts)采用文本格式,易于理解和修改
  5. 层次化结构:可以复用公共部分,减少冗余描述

二、设备树原理

2.1 设备树的基本结构

设备树采用树状结构描述硬件,主要包含以下组成部分:

  1. 节点(Node):设备树的基本构建块,表示一个设备或总线
  2. 属性(Property):节点的特征描述,是键值对的形式
  3. 值(Value):属性的具体内容,可以是字符串、数字、数组或phandle等

一个简单的设备树示例:

/dts-v1/;/ {model = "My Board";compatible = "myvendor,myboard";cpus {#address-cells = <1>;#size-cells = <0>;cpu@0 {compatible = "arm,cortex-a9";reg = <0>;};};memory@80000000 {device_type = "memory";reg = <0x80000000 0x10000000>;};serial@101f0000 {compatible = "arm,pl011";reg = <0x101f0000 0x1000>;interrupts = <0 12 4>;};
};

2.2 设备树源文件格式

设备树源文件主要有两种格式:

  1. .dts (Device Tree Source):设备树源文件,人类可读的文本格式
  2. .dtsi (Device Tree Source Include):设备树包含文件,类似于C语言的头文件

这些源文件需要通过设备树编译器(DTC)编译成二进制格式:

  1. .dtb (Device Tree Blob):编译后的设备树二进制文件,由Bootloader加载并传递给内核

2.3 设备树编译流程

设备树的编译流程如下:

  1. 编写.dts和.dtsi文件
  2. 使用DTC编译器将.dts文件编译为.dtb文件
  3. Bootloader将.dtb文件和内核镜像一起加载到内存
  4. 内核启动时解析设备树,初始化描述的硬件

编译命令示例:

dtc -I dts -O dtb -o myboard.dtb myboard.dts

2.4 设备树绑定(Bindings)

设备树绑定是描述特定设备节点应包含哪些属性和值的规范文档。这些绑定文档通常位于内核源码的Documentation/devicetree/bindings目录下,为设备树编写者提供指导。

绑定文档通常包含:

  • 设备兼容性字符串(compatible)
  • 必需的属性
  • 可选的属性
  • 子节点的要求
  • 示例

三、设备树语法详解

3.1 设备树节点

设备树节点是描述硬件的基本单位,语法如下:

[label:] node-name[@unit-address] {[properties][child-nodes]
};
  • label:可选,节点的标签,用于在其他地方引用
  • node-name:节点名称,通常表示设备类型
  • unit-address:可选,设备的地址,通常与reg属性中的第一个地址相同

3.2 常用属性

  1. compatible:最重要的属性之一,用于匹配驱动程序

    compatible = "manufacturer,model", "generic-model";
    
  2. reg:描述设备寄存器或内存区域的地址和大小

    reg = <address1 length1 [address2 length2] ... >;
    
  3. #address-cells和#size-cells:描述子节点reg属性的地址和大小字段的单元格数量

    #address-cells = <1>;  // 地址用1个32位数表示
    #size-cells = <1>;     // 大小用1个32位数表示
    
  4. interrupts:描述设备的中断号

    interrupts = <IRQ_NUM TRIGGER_TYPE>;
    
  5. status:描述设备状态

    status = "okay";  // 或 "disabled", "fail", "fail-sss"
    

3.3 特殊节点

  1. 根节点:设备树的起点,用/表示
  2. aliases节点:为节点提供符号链接
    aliases {serial0 = &uart0;
    };
    
  3. chosen节点:描述运行时参数,如bootargs
    chosen {bootargs = "console=ttyS0,115200";
    };
    

3.4 设备树包含机制

类似于C语言的#include,设备树使用/include/#include指令包含其他文件:

#include "common.dtsi"

或者

/include/ "common.dtsi"

四、设备树在Linux中的应用

4.1 内核如何解析设备树

Linux内核在启动过程中解析设备树的流程:

  1. Bootloader将设备树二进制(.dtb)加载到内存,并将指针传递给内核
  2. 内核初始化阶段,OF(Open Firmware)子系统开始解析设备树
  3. 内核将设备树转换为设备节点的链表结构
  4. 驱动程序通过匹配compatible属性与设备节点绑定
  5. 驱动程序从设备节点中获取硬件配置信息

4.2 设备树与驱动程序的匹配

驱动程序通过of_match_table声明支持的设备树兼容性字符串:

static const struct of_device_id my_driver_ids[] = {{ .compatible = "vendor,device" },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_driver_ids);static struct platform_driver my_driver = {.probe = my_probe,.driver = {.name = "my-device",.of_match_table = my_driver_ids,},
};

当设备树节点的compatible属性与驱动程序的of_match_table中的条目匹配时,内核会调用驱动程序的probe函数。

4.3 从设备树获取硬件信息

驱动程序可以从设备树节点中获取各种硬件信息:

  1. 获取寄存器地址和大小:

    struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    void __iomem *base = devm_ioremap_resource(&pdev->dev, res);
    
  2. 获取中断号:

    int irq = platform_get_irq(pdev, 0);
    
  3. 获取属性值:

    u32 value;
    of_property_read_u32(np, "property-name", &value);
    
  4. 获取GPIO:

    int gpio = of_get_named_gpio(np, "gpio-name", 0);
    

4.4 设备树覆盖(Overlay)

设备树覆盖是一种动态修改设备树的技术,主要用于支持运行时硬件配置变化,常见于嵌入式Linux系统:

  1. 创建覆盖文件(.dtbo)
  2. 在运行时加载覆盖:
    echo overlay.dtbo > /sys/kernel/config/device-tree/overlays/0/path
    
  3. 卸载覆盖:
    echo 0 > /sys/kernel/config/device-tree/overlays/0/status
    

五、设备树调试技巧

5.1 查看设备树

  1. 查看/proc/device-tree:

    ls /proc/device-tree/
    
  2. 使用dtc反编译:

    dtc -I fs /proc/device-tree
    

5.2 调试工具

  1. dtc:设备树编译器,可用于反编译和验证

    dtc -I dtb -O dts -o myboard.dts myboard.dtb
    
  2. fdtdump:显示设备树二进制文件内容

    fdtdump myboard.dtb
    
  3. ofdump:内核工具,显示设备树信息

5.3 常见问题排查

  1. 设备未初始化:检查status属性是否为"okay"
  2. 驱动未加载:检查compatible属性是否匹配
  3. 资源冲突:检查reg、interrupts等属性是否正确
  4. 语法错误:使用dtc验证设备树源文件

六、设备树实践示例

6.1 添加一个GPIO设备

设备树描述:

gpio_keys {compatible = "gpio-keys";button {label = "User Button";gpios = <&gpio0 23 GPIO_ACTIVE_LOW>;linux,code = <KEY_ENTER>;};
};

驱动程序:

static int gpio_keys_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct gpio_keys_button *button;button = devm_kzalloc(dev, sizeof(*button), GFP_KERNEL);if (!button)return -ENOMEM;button->gpio = of_get_named_gpio(dev->of_node, "gpios", 0);button->code = KEY_ENTER;// 注册输入设备...return 0;
}

6.2 添加一个I2C设备

设备树描述:

&i2c1 {status = "okay";clock-frequency = <100000>;temperature-sensor@48 {compatible = "ti,tmp75";reg = <0x48>;};
};

驱动程序:

static int tmp75_probe(struct i2c_client *client,const struct i2c_device_id *id)
{struct device *dev = &client->dev;// 初始化温度传感器...return 0;
}static const struct of_device_id tmp75_of_match[] = {{ .compatible = "ti,tmp75" },{ }
};
MODULE_DEVICE_TABLE(of, tmp75_of_match);

七、设备树最佳实践

  1. 尽量复用:将公共部分提取到.dtsi文件中
  2. 遵循绑定:严格按照内核文档中的绑定规范编写
  3. 合理命名:节点和属性命名要清晰、一致
  4. 充分注释:复杂部分添加详细注释
  5. 验证修改:每次修改后都要验证功能
  6. 版本控制:将设备树文件纳入版本控制系统

八、总结

设备树作为现代Linux系统中描述硬件配置的标准方式,已经广泛应用于ARM、PowerPC等架构。它通过将硬件描述与内核分离,提高了系统的可移植性和可维护性。掌握设备树的原理和应用,对于嵌入式Linux开发者和内核驱动开发者来说是一项必备技能。

随着Linux内核和设备树标准的不断发展,设备树的功能也在不断增强,如动态设备树覆盖、设备树单元测试等新特性的加入,使得设备树能够更好地满足复杂嵌入式系统的需求。

相关文章:

[Linux实战] Linux设备树原理与应用详解

Linux设备树原理与应用详解 一、设备树概述 1.1 什么是设备树 设备树&#xff08;Device Tree&#xff0c;简称DT&#xff09;是一种描述硬件资源的数据结构&#xff0c;它通过一种树状结构来描述系统硬件配置&#xff0c;包括CPU、内存、总线、外设等硬件信息。设备树最初在…...

用Nginx实现负载均衡与高可用架构(整合Keepalived)

前言 在分布式架构中&#xff0c;负载均衡和高可用是保障系统稳定性的两大核心能力。本文将深入讲解如何通过Nginx实现七层负载均衡&#xff0c;并结合Keepalived构建无单点故障的高可用架构。文末附完整配置模板&#xff01; 一、Nginx负载均衡实现方案 1. 核心原理 Nginx通…...

SQLMesh调度系统深度解析:内置调度与Airflow集成实践

本文系统解析SQLMesh的两种核心调度方案&#xff1a;内置调度器与Apache Airflow集成。通过对比两者的适用场景、架构设计和操作流程&#xff0c;为企业构建可靠的数据分析流水线提供技术参考。重点内容包括&#xff1a; 内置调度器的轻量级部署与性能优化策略Airflow集成的端到…...

Excel 中 INDEX 和 VLOOKUP 的对比

INDEX 和 VLOOKUP 都是 Excel 中常用的查找函数&#xff0c;但它们的用途和灵活性有所不同。 1. 相同点 均可用于查找数据&#xff1a;都能根据某个条件返回目标值。 支持精确匹配&#xff1a;均可使用 0 或 FALSE 进行精确匹配。 2. 不同点 特性VLOOKUPINDEX MATCH查找方向…...

Multism TL494仿真异常

仿真模型如下&#xff1a;开关频率少了一半&#xff0c;而且带不动负载&#xff0c;有兄弟知道为什么吗 这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码…...

基于 Trae 的超轻量级前端架构设计与性能优化实践

一、技术背景与选型动因 在单页应用(SPA)复杂度指数级增长的今天&#xff0c;传统框架在千级列表渲染场景下普遍存在首屏延迟(>1.5s)、内存占用过高(>200MB)等问题。基于对 Webpack Bundle Analyzer 的长期观察&#xff0c;我们发现核心问题集中在&#xff1a; • 类组件…...

算法练习(队列)

队列 单向队列 1. 定义一个队列 Queue<Integer> q new LinkedList<>(); Queue<Character> q new LinkedList<>();2. 入队列 q.offer(1); q.offer(2); // 从队尾入队列 q.add();3. 出队列 q.poll() // 从队头出队列&#xff0c;并将删除的元素…...

HarmonyOS NEXT开发进阶(十五):日志打印 hilog 与 console.log 的区别

文章目录 一、前言二、两者区别对比三、HiLog 详解四、拓展阅读 一、前言 在日常开发阶段&#xff0c;日志打印是调试程序非常常用的操作&#xff0c;在鸿蒙的官方文档中介绍了hilog这种方式&#xff0c;前端转过来的开发者发现console.log也可以进行日志打印&#xff0c;而且…...

【差分隐私相关概念】差分隐私中的稀疏向量技术

差分隐私中的稀疏向量技术&#xff08;Sparse Vector Technique, SVT&#xff09; 稀疏向量技术&#xff08;SVT&#xff09;是差分隐私中的一种高效机制&#xff0c;专用于处理稀疏高影响查询的场景。其核心思想是&#xff1a;当面对大量查询时&#xff0c;仅对其中“显著超过…...

快速幂算法还有用吗?——从内置函数到高性能计算的深度解析

博主在学习过程中遇到了一个疑问&#xff0c;既然C语言中有内置函数pow&#xff0c;那为什么还需要算法思想中的快速幂算法呢&#xff1f;下面将会讲解快速幂算法在特定场景下依然非常有用&#xff0c;具体原因如下&#xff1a; 目录 1. 精度与整数运算 2. 性能对比 3. 应用场…...

开源测试用例管理平台

不可错过的10个开源测试用例管理平台&#xff1a; PingCode、TestLink、Kiwi TCMS、Squash TM、FitNesse、Tuleap、Robot Framework、SpecFlow、TestMaster、Nitrate。 开源测试用例管理工具提供了一种透明、灵活的解决方案&#xff0c;使团队能够在不受限的情况下适应具体的测…...

vue 权限应用

目录 一、系统菜单栏权限 二、系统页面按钮权限 在企业开发中&#xff0c;不同的用户所扮演的角色不一样&#xff0c;角色拥有权限&#xff0c;所以用户拥有角色&#xff0c;就会有角色对应的权限。例如&#xff0c;张三是系统管理员角色&#xff0c;登录后就拥有整个系统的…...

鸿蒙HarmonyOS NEXT设备升级应用数据迁移流程

数据迁移是什么 什么是数据迁移&#xff0c;对用户来讲就是本地数据的迁移&#xff0c;终端设备从HarmonyOS 3.1 Release API 9及之前版本&#xff08;单框架&#xff09;迁移到HarmonyOS NEXT&#xff08;双框架&#xff09;后保证本地数据不丢失。例如&#xff0c;我在某APP…...

利用 PCI-Express 交换机实现面向未来的推理服务器

在数据中心系统的历史上&#xff0c;没有比被 Nvidia 选为其 AI 系统的组件供应商更高的赞誉了。 这就是为什么新兴的互连芯片制造商 Astera Labs 感到十分高兴&#xff0c;因为该公司正在 PCI-Express 交换机、PCI-Express 重定时器和 CXL 内存控制器方面与 Broadcom 和 Marv…...

Python调用手机摄像头检测火焰烟雾的三种方法

方法1&#xff1a;使用IP摄像头应用 OpenCV 1. 在手机上安装IP摄像头应用&#xff08;如IP Webcam for Android&#xff09; 2. 配置应用并启动服务器 3. 在Python中使用OpenCV连接 import cv2 import numpy as np # 手机IP摄像头URL&#xff08;替换为你的手机IP和端口…...

Python if else while for 学习笔记

一.if&#xff0c;else if语句用于根据条件执行代码块 else语句可与if语句结合&#xff0c;当if判断为假时执行else语句 x10 if x>5:print("x大于5") y3 if y>5:print("y大于5") else:print("y小于等于5")结果&#xff1a; 二.while循环…...

正则化是什么?

正则化&#xff08;Regularization&#xff09;是机器学习中用于防止模型过拟合&#xff08;Overfitting&#xff09;的一种技术&#xff0c;通过在模型训练过程中引入额外的约束或惩罚项&#xff0c;降低模型的复杂度&#xff0c;从而提高其泛化能力&#xff08;即在未见数据上…...

搜索-BFS

马上蓝桥杯了&#xff0c;最近刷了广搜&#xff0c;感觉挺有意思的&#xff0c;广搜题类型都差不多&#xff0c;模板也一样&#xff0c;大家写的时候可以直接套模板 这里给大家讲一个比较经典的广搜题-迷宫 题目问问能否走到 (n,m) 位置&#xff0c;假设最后一个点是我们的&…...

《边缘计算风云录:FPGA与MCU的算力之争》

点击下面图片带您领略全新的嵌入式学习路线 &#x1f525;爆款热榜 88万阅读 1.6万收藏 文章目录 **第一章&#xff1a;边城烽烟——数据洪流压境****第二章&#xff1a;寒铁剑匣——FPGA的千机变****第三章&#xff1a;枯木禅杖——MCU的至简道****第四章&#xff1a;双生契…...

R-GCN-Modeling Relational Data with GraphConvolutional Networks(论文笔记)

CCF等级&#xff1a;B 发布时间&#xff1a;2018年6月 25年3月31日交 目录 一、简介 二、原理 1.整体 2.信息交换与更新 2.1基分解 2.2块对角矩阵 3.实体分类或链接预测 3.1实体分类 3.2链接预测 三、结论和未来工作 一、简介 RGCN通过允许不同关系类型之间的信息…...

蓝桥杯第十六届模拟赛——基础细节考频分析

文章目录 前言一、STL函数二、日期问题三、质数与约数四、基本常识总结 前言 一、STL函数 #include< cmath > 详解floor函数、ceil函数和round函数 1.floor() 功能&#xff1a;把一个小数向下取整如果数是2.2 &#xff0c;那向下取整的结果就为2.000000如果数是-2.2 &…...

【C++初阶】----模板初阶

1.泛型函数 泛型编程&#xff1a;编写与类型无关的通用代码&#xff0c;是代码复用的一种手段。模板是泛型编程的基础。 2.函数模板 2.1函数模板的概念 函数模板代表了一个函数家族&#xff0c;该函数模板与类型无关&#xff0c;在使用时被参数化&#xff0c;根据实参类型…...

PyCharm操作基础指南

一、安装与配置 1. 版本选择 专业版&#xff1a;支持 Web 开发&#xff08;Django/Flask&#xff09;、数据库工具、科学计算等&#xff08;需付费&#xff09;。 社区版&#xff1a;免费&#xff0c;适合纯 Python 开发。 2. 安装步骤 访问 JetBrains 官网 下载对应版本。…...

Pycharm(七):几个简单案例

一.剪刀石头布 需求&#xff1a;和电脑玩剪刀石头布游戏 考察点&#xff1a;1.随机数&#xff1b;2.判断语句 import random # numrandom.randint(1,3) # print(num) # print(**30) #1.录入玩家手势 playerint(input(请输入手势&#xff1a;&#xff08;1.剪刀 2.石头 3&…...

Android并发编程:线程池与协程的核心区别与最佳实践指南

1. 基本概念对比 特性 线程池 (ThreadPool) 协程 (Coroutine) 本质 Java线程管理机制 Kotlin轻量级并发框架 最小执行单元 线程(Thread) 协程(Coroutine) 创建开销 较高(需分配系统线程资源) 极低(用户态调度) 并发模型 基于线程的抢占式调度 基于协程的协作式调度 2. 核心差异…...

MySQL内存使用率高问题排查与解决方案:

目录标题 **一、问题现象****二、核心排查步骤****1. 参数检查****2. 内存使用分析****3. 存储过程/函数/视图检查****4. 操作系统级检查** **三、解决方案****1. 调整MySQL配置****2. 关闭透明大页&#xff08;THP&#xff09;****3. 优化查询与存储过程****4. 硬件与环境优化…...

gnvm切换node版本号

1. gnvm下载官网 GNVM - Node.js version manager on Windows by Go 2. 安装 2.1 不存在 Node.js 环境 下载并解压缩 gnvm.exe 保存到任意文件夹&#xff0c;并将此文件夹加入到环境变量 Path。 2.2 存在 Node.js 环境 下载并解压缩 gnvm.exe 保存到 Node.js 所在的文件夹。 2.…...

PyTorch 深度学习实战(29):目标检测与 YOLOv12 实战

在上一篇文章中&#xff0c;我们探讨了对比学习与自监督表示学习。本文将深入计算机视觉的核心任务之一——目标检测&#xff0c;重点介绍最新的 YOLOv12 (You Only Look Once v12) 算法。我们将使用 PyTorch 实现 YOLOv12 模型&#xff0c;并在 COCO 数据集上进行训练和评估。…...

Python爬虫:开启数据抓取的奇幻之旅(一)

目录 一、爬虫初印象&#xff1a;揭开神秘面纱​ 二、工欲善其事&#xff1a;前期准备​ &#xff08;一&#xff09;Python 环境搭建​ 1.下载 Python 安装包&#xff1a;​ 2.运行安装程序&#xff1a;​ 3.配置环境变量&#xff08;若自动添加失败&#xff09;&#x…...

python下载m3u8格式视频

一、安装 m3u8库 pip install requests pip install requests m3u8 二、编码实现 import os import re import requests import subprocess# 下载ts文件 def down_ts_file(base_url, m3u8_url, download_dir):# 从m3u8文件中获取所有ts的分片名称信息response requests.get…...