正点原子2026开发板教程——从0开始配置Linux内核(5)——设备树在内核中的使用

news2026/3/24 9:57:37
正点原子2026开发板教程——从0开始配置Linux内核5——设备树在内核中的使用教程已经在Github上开源https://github.com/Awesome-Embedded-Learning-Studio/imx-forge欢迎尝试和围观为什么要谈内核中的设备树上一章我们讲了内核模块解决了代码如何进内核的问题。但还有一个更根本的问题内核怎么知道你的板子上有哪些硬件这个问题在设备树出现之前是通过硬编码解决的——每块板子都要写专门的板级初始化代码把硬件信息写死在 C 代码里。那种做法的痛苦程度我们在 U-Boot 教程里已经吐槽过了。有了设备树之后硬件描述从代码中分离出来内核只需要读懂设备树就知道该初始化哪些硬件、怎么初始化。但这个过程对驱动开发者来说是隐式的——内核默默完成了设备树的解析、设备创建、驱动匹配你只需要写好驱动代码在compatible字段里填上正确的值一切就能工作。但隐式不代表不需要理解。实际开发中你一定会遇到这些问题我的驱动为什么不加载设备树配置对了吗怎么调试设备树相关问题compatible 字段到底怎么匹配所以这一章我们深入内核的设备树处理机制。你会看到 DTB 是怎么被解析的、设备和驱动是怎么匹配的、常见的调试方法有哪些。理解了这些你写驱动的时候就能有的放矢而不是像以前那样——改改设备树编译烧录启动看运气。设备树在内核中的角色内核启动流程中的设备树Linux 内核启动时设备树的解析是一个关键环节。来看一下简化版的启动流程1. BootROM 运行 2. U-Boot 加载并运行 3. U-Boot 加载 DTB 和内核镜像到内存 4. U-Boot 跳转到内核入口传递 DTB 地址通过 r2 寄存器 5. 内核解压如果是压缩的 zImage 6. 内核初始化早期setup_arch() └── unflatten_device_tree()解析 DTB构建设备树数据结构 7. 内核初始化中期arch_init_sync() └── of_platform_populate()根据设备树创建 platform 设备 8. 内核初始化后期device_initcall └── 驱动初始化尝试匹配已注册的设备和驱动 9. 内核启动完成用户空间启动init 进程第 6 步的unflatten_device_tree()是关键。DTBDevice Tree Blob是一种紧凑的二进制格式不适合内核运行时频繁访问。所以内核会在启动早期把它展开unflatten转换成更容易访问的链表/树结构。第 7 步的of_platform_populate()负责创建设备。对于设备树中的每个节点如果它有compatible属性且不是特殊节点如chosen、aliases内核会创建对应的struct device并注册到相应的总线通常是 platform 总线。第 8 步是驱动匹配。当驱动模块加载时或静态编入内核初始化时驱动的probe函数会被调用如果匹配成功。设备树相关的内核数据结构内核用几个关键数据结构来表示设备树struct device_node表示设备树节点structdevice_node{constchar*name;// 节点名称 之前的部分constchar*type;// 设备类型device_type 属性phandle phandle;// 节点的 phandle用于引用constchar*full_name;// 完整路径名structfwnode_handlefwnode;// 固件节点接口structproperty*properties;// 属性链表structproperty*deadprops;// 已删除的属性structdevice_node*parent;// 父节点structdevice_node*child;// 子节点structdevice_node*sibling;// 兄弟节点structkobjectkobj;// sysfs 表示// ...};struct property表示设备树属性structproperty{char*name;// 属性名intlength;// 属性值长度void*value;// 属性值structproperty*next;// 下一个属性// ...};这些结构在内核运行时可以通过/sys/firmware/devicetree/base看到# 查看根节点ls-la/sys/firmware/devicetree/base/# 查看某个节点的属性ls-la/sys/firmware/devicetree/base/soc/aips-bus02000000/spba-bus02000000/uart02020000/# 查看某个属性的内容cat/sys/firmware/devicetree/base/soc/aips-bus02000000/spba-bus02000000/uart02020000/compatible输出类似fsl,imx6ull-uart\x00fsl,imx6q-uart注意\x00是 null 分隔符表示compatible属性有多个值。DTB 解析过程DTB 传递给内核U-Boot 启动内核时需要把 DTB 地址告诉内核。对于 ARM这是通过寄存器传递的r0CPU IDr1机器 ID设备树时代已经不用设为 0r2DTB 物理地址或 ATAG 列表地址在 U-Boot 中这是bootm命令自动处理的。如果你手动启动可以用bootz ${kernel_addr_r} - ${fdt_addr_r}-表示没有 initramfs${fdt_addr_r}是 DTB 地址。unflatten_device_tree()从 DTB 到 device_node内核的unflatten_device_tree()函数在drivers/of/fdt.c负责把 DTB 转换成device_node树验证 DTB 格式检查魔数0xd00dfeed、版本、大小扫描 DTB遍历所有节点和属性创建 device_node为每个节点创建struct device_node创建 property为每个属性创建struct property建立关系设置 parent/child/sibling 指针完成后你可以通过of_allnodes全局变量访问根节点。设备树调试接口内核提供了几个设备树调试接口/sys/firmware/devicetree/sysfs 接口只读# 浏览设备树cd/sys/firmware/devicetree/base/find.-namecompatible|head-20# 查看某个属性cat/proc/device-tree/modelcat/proc/device-tree/compatible/proc/device-tree是/sys/firmware/devicetree/base的符号链接。/sys/kernel/debug/debugfs调试接口# 挂载 debugfs如果没挂载sudomount-tdebugfs none /sys/kernel/debug/# 查看设备树ls/sys/kernel/debug/device-tree/# 查看某个节点的属性ls/sys/kernel/debug/device-tree/soc//lib/firmware/设备树文件存放有些系统会把编译好的 DTB 文件放在这里。设备和驱动匹配机制platform 总线和驱动匹配设备树中的大部分设备都注册为platform_device对应的驱动是platform_driver。匹配过程由内核的 driver core 负责。匹配条件优先级从高到低设备树的compatible属性vs 驱动的of_match_table设备名称vs 驱动名称ACPI 匹配x86 系统嵌入式不常见对于设备树关键是compatible属性uart1: serial02020000 { compatible fsl,imx6ull-uart, fsl,imx6q-uart; reg 0x02020000 0x4000; interrupts 26 2 0; // ... };对应的驱动代码staticconststructof_device_idimx_uart_dt_ids[]{{.compatiblefsl,imx6ull-uart,.dataimx6ull_uart_data},{.compatiblefsl,imx6q-uart,.dataimx6q_uart_data},{/* sentinel */}};MODULE_DEVICE_TABLE(of,imx_uart_dt_ids);staticstructplatform_driverimx_uart_driver{.driver{.nameimx-uart,.of_match_tableimx_uart_dt_ids,},.probeimx_uart_probe,.removeimx_uart_remove,};MODULE_DEVICE_TABLE 宏这个宏很重要它做什么在模块中创建一个特殊的 section.modinfomodinfo可以读取这个信息模块加载时udev 可以根据这个信息自动加载模块# 查看模块的设备表modinfo imx_uart# 输出类似# alias: of:N*T*Cfsl,imx6ull-uart*# alias: of:N*T*Cfsl,imx6q-uart*这些 alias 规则告诉 udev当设备树中出现compatible fsl,imx6ull-uart的设备时自动加载这个模块。踩坑提醒忘记MODULE_DEVICE_TABLE是新手常见的错误。结果是手动insmod模块能工作但系统启动时模块不会自动加载。probe 函数调用时机驱动匹配成功后驱动的probe函数会被调用。这是驱动的初始化函数在这里进行硬件初始化、资源申请、注册子设备等。staticintimx_uart_probe(structplatform_device*pdev){structdevice_node*nppdev-dev.of_node;structresource*res;void__iomem*base;intirq;// 获取设备树中的 reg 属性寄存器地址resplatform_get_resource(pdev,IORESOURCE_MEM,0);basedevm_ioremap_resource(pdev-dev,res);// 获取中断号irqplatform_get_irq(pdev,0);// 获取时钟ipg_clkdevm_clk_get(pdev-dev,ipg);// 获取其他可选属性of_property_read_u32(np,fsl,uart-has-rtscts,has_rtscts);// 初始化硬件...return0;}常用设备树属性在内核中的使用reg 属性物理地址映射reg属性描述设备的寄存器地址范围。内核通过platform_get_resource()获取uart1: serial02020000 { reg 0x02020000 0x4000; };structresource*res;void__iomem*base;// 获取 MEM 资源resplatform_get_resource(pdev,IORESOURCE_MEM,0);if(!res){dev_err(pdev-dev,No MEM resource\n);return-ENODEV;}// 映射到虚拟地址basedevm_ioremap_resource(pdev-dev,res);if(IS_ERR(base)){returnPTR_ERR(base);}// 现在可以通过 base 访问寄存器writel(0x1234,baseOFFSET);interrupts 属性中断获取uart1: serial02020000 { interrupts 26 2 0; };intirq;// 获取中断号irqplatform_get_irq(pdev,0);if(irq0){dev_err(pdev-dev,No IRQ resource\n);returnirq;}// 申请中断retdevm_request_irq(pdev-dev,irq,uart_isr,IRQF_SHARED,imx-uart,port);clocks 属性时钟获取uart1 { clocks clks IMX6UL_CLK_UART1_IPG, clks IMX6UL_CLK_UART1_SERIAL; clock-names ipg, per; };structclk*ipg_clk,*per_clk;ipg_clkdevm_clk_get(pdev-dev,ipg);if(IS_ERR(ipg_clk))returnPTR_ERR(ipg_clk);per_clkdevm_clk_get(pdev-dev,per);if(IS_ERR(per_clk))returnPTR_ERR(per_clk);// 使能时钟clk_prepare_enable(ipg_clk);clk_prepare_enable(per_clk);GPIO 属性GPIO 获取usdhc1 { pinctrl-names default; pinctrl-0 pinctrl_usdhc1; cd-gpios gpio1 19 GPIO_ACTIVE_LOW; wp-gpios gpio1 20 GPIO_ACTIVE_HIGH; };structcd_gpio;cd_gpiodevm_gpiod_get(pdev-dev,cd,GPIOD_IN);if(IS_ERR(cd_gpio))returnPTR_ERR(cd_gpio);// 读取 GPIO 状态if(gpiod_get_value(cd_gpio))pr_info(Card present\n);自定义属性of_property_read 系列设备树中可以定义任意自定义属性驱动通过of_property_read_*系列函数读取mydevice { vendor,id 0x1234; vendor,name my-awesome-device; vendor,config 0x01 0x02 0x03 0x04; vendor,flag; };structdevice_node*nppdev-dev.of_node;u32 id;constchar*name;u32 config[4];bool flag;// 读取 u32of_property_read_u32(np,vendor,id,id);// 读取字符串of_property_read_string(np,vendor,name,name);// 读取数组of_property_read_u32_array(np,vendor,config,config,4);// 检查布尔属性是否存在flagof_property_read_bool(np,vendor,flag);设备树调试方法方法 1检查 DTB 是否正确首先确保设备树被正确编译和加载# 1. 反编译 DTB检查内容dtc-Idtb-Odts /boot/imx6ull-14x14-evk.dtb/tmp/my.dtsless/tmp/my.dts# 2. 确认内核使用的 DTBcat/sys/firmware/devicetree/base/model# 3. 检查 compatiblecat/sys/firmware/devicetree/base/compatible方法 2检查设备是否创建设备树解析后对应的设备应该被创建# 查看 platform 设备ls-la/sys/devices/platform/# 查看特定设备ls-la/sys/devices/platform/*.uart/# 或根据实际名称# 查看设备的 uevent 文件包含设备信息cat/sys/devices/platform/xxx/uevent方法 3检查驱动是否加载# 查看已加载的模块lsmod|grepuart# 查看 platform 驱动ls-la/sys/bus/platform/drivers/# 查看特定驱动cat/sys/bus/platform/drivers/imx-uart/module方法 4检查绑定情况# 查看哪些设备绑定到哪个驱动ls-la/sys/bus/platform/devices/*/driver# 查看驱动绑定了哪些设备ls-la/sys/bus/platform/drivers/imx-uart/方法 5启用内核调试选项在内核配置中启用设备树调试Device Drivers --- -*- Device Tree and Open Firmware support --- [*] Runtime debugging of the device tree然后# 查看设备树解析日志dmesg|grepof_resolve# 查看设备创建日志dmesg|grepof_platform常见问题排查问题 1驱动没加载现象设备树有节点但设备没工作lsmod看不到驱动。排查# 1. 检查设备节点是否存在ls/sys/firmware/devicetree/base/soc/.../uart02020000/# 2. 检查 compatible 属性cat/sys/firmware/devicetree/base/soc/.../uart02020000/compatible# 3. 查找对应的驱动find/sys/module-nameuevent-execgrep-lcompatible{}\;# 4. 检查驱动模块是否在文件系统中modinfo-nname-of-module常见原因驱动模块没安装MODULE_DEVICE_TABLE缺失compatible 字符串拼写错误问题 2设备存在但驱动没绑定现象/sys/devices/platform/下有设备但driver符号链接指向空。排查# 检查设备的 modaliascat/sys/devices/platform/xxx/modalias# 手动加载驱动sudomodprobe driver-name# 查看内核日志dmesg|grep-iprobe常见原因驱动没加载of_match_table为空compatible 字符串不匹配问题 3probe 失败现象驱动已加载但dmesg显示 probe 失败。排查# 查看 probe 失败原因dmesg|grep-A5imx-uart# 检查资源获取dmesg|grepresource# 检查时钟dmesg|grepclock常见原因资源获取失败reg、irq、clock内存分配失败硬件初始化失败实战分析 i.MX6ULL 设备树让我们看看 i.MX6ULL 开发板的一个实际设备树节点。UART1 设备树节点# 查看设备树中的 UART1cat/sys/firmware/devicetree/base/soc/aips-bus02100000/spba-bus02200000/serial02020000/compatible输出fsl,imx6ull-uart\x00fsl,imx6q-uart对应的驱动代码在内核源码中UART 驱动位于drivers/tty/serial/imx.cstaticconststructof_device_idimx_uart_dt_ids[]{{.compatiblefsl,imx6ull-uart,},{.compatiblefsl,imx6q-uart,},{/* sentinel */}};MODULE_DEVICE_TABLE(of,imx_uart_dt_ids);验证绑定关系# 查找 UART 设备find/sys/devices-name*uart*|head-10# 查看设备信息ls-la/sys/devices/platform/serial02020000/# 查看绑定的驱动cat/sys/devices/platform/serial02020000/driver/modalias设备树叠加Overlay设备树叠加Device Tree Overlay是一种在运行时修改设备树的技术。常见用途加载可插拔设备的配置如 Cape、HAT调试时临时修改配置不重新编译 DTB 的情况下添加设备叠加文件格式/dts-v1/; /plugin/; uart1 { status okay; pinctrl-names default; pinctrl-0 pinctrl_uart1; };加载叠加# 编译叠加dtc --Idts-Odtb-o/tmp/uart1.dtbo uart1-overlay.dts# 加载叠加echo/tmp/uart1.dtbo/sys/kernel/config/device-tree/overlays/uart1/dtbo# 卸载叠加rmdir/sys/kernel/config/device-tree/overlays/uart1写在最后设备树在内核中的使用是一个从硬件描述到驱动匹配的完整链条。理解这个链条你就能知道问题出在哪一环节DTB 没正确加载内核启动就失败节点解析有问题设备创建不出来compatible 不匹配驱动绑不上probe 失败硬件初始化有问题每一环节都有对应的调试方法我们都在这一章里讲了。剩下的就是多实践——改改设备树看看效果用我们讲的调试方法验证一下。到这里你应该理解了设备树是如何被内核解析和使用的。但要让内核真正在板子上跑起来还需要解决一个实际问题如何快速迭代开发和测试下一章我们进入一个非常实战的话题WSL2 TFTP 网络启动。你会看到如何用 WSL2 搭建网络开发环境如何配置 TFTP 服务器如何解决 Windows 防火墙带来的各种坑。那是一个从理论到实践的完整过程而且踩坑记录非常详细。准备好了吗让我们继续。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2443514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…