跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C

PetaLinux 设备树配置深度剖析与驱动集成

PetaLinux 设备树通过 .dts 文件描述硬件拓扑结构,由内核编译为 .dtb 并加载。内核依据 compatible 字段匹配驱动,实现即插即用。管理上应使用 system-user.dtsi 进行增量修改而非直接编辑自动生成文件。驱动加载依赖 of_match_table 机制,调试时可利用 dmesg 和 status 属性排查问题。掌握分层设计与模块化开发能有效提升外设集成效率。

编程诗人发布于 2026/2/26更新于 2026/5/3126 浏览

PetaLinux 设备树配置深度剖析与驱动集成

在现代嵌入式开发中,面对 Xilinx Zynq 系列、Zynq UltraScale+ MPSoC 乃至 Versal ACAP 这类高度异构的平台,软硬件协同设计是工程落地的核心能力。PetaLinux 作为 Xilinx 官方推荐的嵌入式 Linux 构建工具链,承担着将 FPGA 逻辑与 ARM 处理器无缝融合的关键角色。而在这整套流程中,设备树(Device Tree)则是连接硬件定义与操作系统之间的桥梁——它决定了 Linux 是否能正确识别你的 AXI IP、I²C 传感器、GPIO 外设,甚至是你自己定制的加速模块。

设备树的本质:让 Linux'看见'你的硬件

传统嵌入式系统会把外设信息硬编码进内核源码,比如直接写死某个 UART 的基地址。这种方式对于固定板卡尚可接受,但在 Zynq 这种 PS+PL 动态重构的架构下,显然行不通。于是,设备树应运而生。

它到底是什么?

简单说,设备树就是一个描述硬件拓扑结构的数据文件,用文本格式(.dts)编写,最终被编译成二进制(.dtb),由 U-Boot 传给 Linux 内核。内核根据这份'地图',自动创建对应的设备实例,并尝试加载匹配的驱动。

在 Xilinx 平台上,这个文件通常叫 system-top.dts 或 zynq-7000.dtsi,由 PetaLinux 基于 HDF/XSA 自动生成初始版本,开发者在此基础上进行扩展或修改。

设备树是如何工作的?三步走通启动全流程

要真正掌握设备树,必须清楚它在整个启动过程中的生命周期。

第一步:硬件建模 —— .dts 文件结构解析

设备树采用树形结构组织节点,核心要素包括:

/ {
    model = "Xilinx Zynq";
    compatible = "xlnx,zynq-7000";
    cpus { ... };
    memory { ... };
    amba { // AMBA 总线容器,对应 PS 端外设
        uart0: serial@e0001000 {
            compatible = "xlnx,xuartps", "cdns,uart-r1p8";
            reg = <0xe0001000 0x1000>;
            interrupts = <0 31 4>;
            clocks = <&clkc 14>;
            status = "okay";
        };
    };
    amba_pl: amba_pl { // 可编程逻辑区域
        #address-cells = <1>;
        
        ranges
        custom_ip@43c00000 {
             = 
             = <x43c00000 x10000>
             = <  >
        }
    }
}
#size-cells = <1>;
;
compatible
"mycompany,accel-ip-1.0"
;
reg
0
0
;
interrupts
0
61
4
;
;
;
;

关键字段说明:

字段含义
compatible驱动匹配标识,最重要!
reg寄存器物理地址范围
interrupts中断号(GIC 编号)及触发类型
clocks所依赖的时钟源引用
statusokay 表示启用,disabled 表示关闭

其中 compatible 是灵魂字段——它告诉内核:'我是一个什么样的设备',从而决定加载哪个驱动。

第二步:编译与加载 —— .dts → .dtb 的转化之旅

设备树源文件不能直接运行,需要通过 Device Tree Compiler (dtc) 编译为二进制 Blob:

dtc -I dts -O dtb -o system.dtb system-top.dts

在 PetaLinux 中,这一步是全自动的。当你执行 petalinux-build 时,系统会:

  1. 解析 project-spec/dts/system-user.dtsi
  2. 合并自动生成的 system-conf.dtsi
  3. 输出最终的 image/linux/system.dtb

该文件会被打包进 BOOT.BIN 或放在 boot 分区,由 U-Boot 加载至内存并传递给内核。

第三步:内核解析 —— platform_device 的诞生

Linux 启动后,内核开始遍历 .dtb 中的每一个节点:

  • 对于 AMBA 总线下的设备(如 UART、SPI),生成 amba_device;
  • 对于普通总线设备(如 GPIO、I²C 设备),生成 platform_device;
  • 然后查找所有注册过的驱动,检查其 of_match_table 是否与当前节点的 compatible 匹配;
  • 若匹配成功,调用驱动的 .probe() 函数完成初始化。

整个过程无需任何硬编码,实现了真正的'即插即用'。

PetaLinux 怎么管理设备树?别再手动改 system-top 了!

很多初学者习惯直接编辑 system-top.dts,结果下次 petalinux-config --get-hw-description 一运行,所有改动全没了。

为什么?因为这是自动生成文件!

PetaLinux 有一套清晰的分层机制来避免这个问题。

推荐做法:使用 system-user.dtsi 做增量覆盖

正确的姿势是在 project-spec/dts/ 目录下维护两个文件:

  • system-top.dts:自动生成,不要动!
  • system-user.dtsi:用户自定义补丁,永远保留!

示例:添加一个 AXI I²C 控制器及其挂载的温度传感器 TMP102

// project-spec/dts/system-user.dtsi
/include/ "system-conf.dtsi"
/
amba_pl: amba_pl {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "simple-bus";
    ranges;
    i2c_axi@41600000 {
        compatible = "xlnx,xps-iic-2.00.a";
        reg = <0x41600000 0x10000>;
        interrupts = <0 29 4>;
        clocks = <&clkc 15>;
        #address-cells = <1>;
        #size-cells = <0>;
        tmp102@48 {
            compatible = "ti,tmp102";
            reg = <0x48>;
        };
    };
};

这样做的好处是: ✅ 修改独立于自动生成内容 ✅ 易于版本控制和迁移 ✅ 支持多人协作开发

每次重建项目时,只需重新导入 HDF/XSA + 复制 system-user.dtsi 即可快速恢复配置。

驱动是怎么被加载的?深入匹配机制

光有设备树还不够,还得有对应的驱动程序才行。那么,Linux 是怎么知道该用哪个驱动呢?

答案就在 of_match_table。

内核驱动的标准模板

以标准的 TMP102 温度传感器驱动为例(位于 drivers/hwmon/tmp102.c):

static const struct of_device_id tmp102_of_match[] = {
    { .compatible = "ti,tmp102", },
    { .compatible = "nxp,nct75", },
    { }
};
MODULE_DEVICE_TABLE(of, tmp102_of_match);

static struct i2c_driver tmp102_driver = {
    .driver = {
        .name = "tmp102",
        .of_match_table = of_match_ptr(tmp102_of_match),
    },
    .probe = tmp102_probe,
    .remove = tmp102_remove,
    .id_table = tmp102_ids,
};
module_i2c_driver(tmp102_driver);

注意这里的 .of_match_table 和 MODULE_DEVICE_TABLE(of, ...),它们共同构成了设备树匹配的基础。

当内核看到设备树中有这样一个节点:

tmp102@48 { compatible = "ti,tmp102"; reg = <0x48>; };

就会触发匹配流程,最终调用 tmp102_probe() 初始化设备。

如果没有现成驱动怎么办?

两种选择:

方案一:启用模块化支持,在运行时加载 ko
petalinux-config -c kernel

进入菜单:

Device Drivers ---> Hardware Monitoring support ---> <*> Texas Instruments TMP102

选择 <M> 编译为模块,则生成 tmp102.ko,可通过 insmod tmp102.ko 动态加载。

方案二:自己写 platform 驱动(适用于自定义 IP)

比如你有个 AXI GPIO LED 控制器,可以写一个简单的驱动:

// led-simple-gpio.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>

static int led_probe(struct platform_device *pdev)
{
    struct gpio_desc *desc;
    const char *label = "user-led";
    desc = devm_fwnode_gpiod_get(&pdev->dev, &pdev->dev.fwnode, "default", GPIOD_OUT_LOW, label);
    if (IS_ERR(desc)) {
        dev_err(&pdev->dev, "Failed to get GPIO\n");
        return PTR_ERR(desc);
    }
    gpiod_set_value_cansleep(desc, 1); // 点亮 LED 测试
    platform_set_drvdata(pdev, desc);
    dev_info(&pdev->dev, "Custom LED driver initialized\n");
    return 0;
}

static const struct of_device_id led_of_match[] = {
    { .compatible = "gpio-leds", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, led_of_match);

static struct platform_driver led_driver = {
    .probe = led_probe,
    .driver = {
        .name = "led-simple-gpio",
        .of_match_table = of_match_ptr(led_of_match),
    },
};
module_platform_driver(led_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple GPIO LED Driver for PetaLinux");

配合设备树片段:

leds {
    compatible = "gpio-leds";
    user_led {
        label = "user-led";
        gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
        default-state = "on";
    };
};

然后在 petalinux-config -c kernel 中确保选中:

Device Drivers ---> GPIO Support ---> <*> GPIO Class [*] /sys/class/gpio/... (sysfs interface)

编译后即可自动加载并点亮 LED。

实战技巧:那些年踩过的坑与应对策略

理论讲完,来点实战经验。以下是我在多个 Zynq 项目中总结出的高频问题和解决方案。

问题 1:设备树节点写了,但驱动没加载

排查步骤:

  1. 查看内核日志:

    dmesg | grep -i of_parse
    

    输出类似:

    of_parse_phandle_with_args: could not find phandle
    

    说明 clocks 或 interrupts 引用无效。

  2. 检查 compatible 是否拼错:

    dts compatible = "ti,tmp102"; // 正确
    dts compatible = "ti-tmp102"; // 错误!不能用连字符
    
  3. 确认驱动已编译进内核或模块存在:

    find . -name "*tmp102*"
    
秘籍 1:用 status = "disabled" 快速调试

不想永久删除某个接口?可以用 status 控制启停:

&spi0 { status = "disabled"; // 临时禁用 SPI0 };
&i2c1 { status = "okay"; };

特别适合在多外设冲突时逐个排查。

秘籍 2:合理利用 .dtsi 分离公共配置

如果你维护多个相似板型,建议提取共性部分:

project-spec/dts/
├── system-top.dts
├── system-user.dtsi
└── boards/
    ├── board_a.dtsi
    └── board_b.dtsi

在 system-user.dtsi 中 include 不同板型配置:

#include "boards/board_a.dtsi"

实现'一套代码,多板适配'。

秘籍 3:保留原始 HDF/XSA,便于重建

.hdf 或 .xsa 文件包含了完整的硬件连接信息,务必纳入版本管理(Git/LFS)。一旦需要升级 PetaLinux 版本或迁移到新环境,可以直接:

petalinux-create -t project -n new_proj --template zynq
petalinux-config --get-hw-description=../old_hw/design_1_wrapper.hdf

快速复现原有设备树结构。

总结:设备树不是负担,而是生产力工具

回顾全文,我们可以得出几个核心结论:

🔧 设备树的核心价值在于解耦 同一内核镜像,换一个 .dtb 就能在不同硬件上运行。这对产品多型号迭代意义重大。

⚡ PetaLinux 极大简化了设备树管理 自动化生成 + 用户覆盖机制,让我们既能享受灵活性,又不必陷入底层细节。

🎯 驱动匹配靠的是 compatible 字符串 只要名字对得上,内核就能自动加载标准驱动;否则就得自己写。

🧩 最佳实践 = 分层设计 + 模块化开发

  • 用 system-user.dtsi 做增量修改
  • 非关键驱动尽量编为模块
  • 保留 HDF/XSA 用于重建

掌握这套方法论后,你会发现,无论是接入一个新的 ADC 芯片,还是集成你自己写的 AXI DMA IP,都不再是令人头疼的任务。

更重要的是,这种基于设备树的开发模式,正是向 Versal ACAP、AI Engine 等更复杂平台演进的基础。今天的每一步实践,都在为未来的系统级创新铺路。

目录

  1. PetaLinux 设备树配置深度剖析与驱动集成
  2. 设备树的本质:让 Linux“看见”你的硬件
  3. 它到底是什么?
  4. 设备树是如何工作的?三步走通启动全流程
  5. 第一步:硬件建模 —— .dts 文件结构解析
  6. 第二步:编译与加载 —— .dts → .dtb 的转化之旅
  7. 第三步:内核解析 —— platform_device 的诞生
  8. PetaLinux 怎么管理设备树?别再手动改 system-top 了!
  9. 推荐做法:使用 system-user.dtsi 做增量覆盖
  10. 驱动是怎么被加载的?深入匹配机制
  11. 内核驱动的标准模板
  12. 如果没有现成驱动怎么办?
  13. 方案一:启用模块化支持,在运行时加载 ko
  14. 方案二:自己写 platform 驱动(适用于自定义 IP)
  15. 实战技巧:那些年踩过的坑与应对策略
  16. 问题 1:设备树节点写了,但驱动没加载
  17. 秘籍 1:用 status = "disabled" 快速调试
  18. 秘籍 2:合理利用 .dtsi 分离公共配置
  19. 秘籍 3:保留原始 HDF/XSA,便于重建
  20. 总结:设备树不是负担,而是生产力工具
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 预训练与微调的本质区别及实践指南
  • MC.JS WEBMC 在教育场景中的应用实践
  • Linux 进程控制详解
  • AI 学习路径:从 Python 到 OpenClaw 本地智能体部署
  • 利用 Kiro 与 AIClient-2-API 免费调用 Claude 顶级模型实战
  • OpenClaw 插件更新:支持配置 QQ 与飞书机器人
  • SVPWM 过调制算法原理及 Matlab 仿真
  • Komari 轻量级服务器监控探针部署指南
  • GitHub Codespaces 部署开源 AI 智能体 OpenClaw 指南
  • 国内外网络安全众测平台与 SRC 汇总指南
  • VS Code SSH 远程环境中 Copilot 无模型选项问题修复
  • 神经网络概率语言模型(NPLM)阅读笔记
  • 简单几步微调 Llama 3 大模型
  • Android Studio 集成 GitHub Copilot GPT-4o:AI 辅助开发实战与避坑指南
  • AI 与 SEO 结合:如何优化 AIGC 内容
  • KWDB 实战:SQL 构建物流车队轨迹监控与实时报警系统
  • 基于 SpringBoot 与 Vue 的高校实习生管理平台设计与实现
  • STM32 上运行 AI 大模型的四种方案及案例
  • 基于 MATLAB 的 A_Satr 算法多机器人分布式动态避障与领袖跟随策略(含 EKF)
  • C语言精准操控FPGA寄存器与通信协议底层机制

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online