树莓派刚刚泄露了 Pi Zero 3W 的信息,方式不是发布会,也不是产品页,而是一份描述 Pi Zero 硬件的设备树(DeviceTree)文件。正是从这份文件里,人们读出了它搭载 BCM2711(也就是 Pi 4 的 CPU)、用上了 PCIe(甚至可能已经引出)、带有一个板载电源键、多了一颗电源指示灯,还提供 eMMC 选项。
![]()
如果非要猜,这会是第一块双面布局的 Pi Zero 板;如果再抱点希望,它终于该用上 USB-C 了。不信的话,设备树文件就摆在那里,字里行间的信息其实非常好读。
设备树文件的作用,是告诉 Linux 内核或其他系统内核:该期待什么 CPU、该加载哪些驱动、板子上暴露了哪些接口,以及这些接口上接着什么设备。有时候,一份像样的设备树文件,就是让你手里那块被冷落的单板电脑跑上现代 Linux 系统的唯一必需品。
这类文件其实就在你身边。你的手机有,路由器有,ARM 笔记本有,各种单板电脑更不用说。有些设备树文件并不友善,只是 Linux 内核与专有驱动之间薄薄的一层;这类东西常见于某些厂商 CPU 的手机里。大多数设备树文件则只是老老实实做自己的事。
先要知道的事
设备树文件描述硬件,就像 HTML 描述网页元素。它不具备图灵完备性,所以单靠设备树是跑不了《毁灭战士》的——除非你碰上那种不友善的实现。设备树源文件(扩展名 .dts、.dtsi)会被编译成内核可读的二进制文件(.dtb、.dtbo)。就算手里没有源码,反编译并改动它也相对容易。
处理设备树文件并不难。编译与反编译命令简单到不能再简单,也很容易查到;那个编译器同时也是反编译器,一个小工具,一条 sudo apt install device-tree-compiler 就能装上。在为 Blepis PDA 干活时,团队写了一个脚本,为 Blepis 可能搭配的不同屏幕批量编译一堆 overlay,这大概是最直白不过的事情之一。
设备树文件分几类:include、基础文件和 overlay。include(.dtsi)通常存放 CPU 相关信息,多是些你永远不会去碰的无聊内容,比如时钟定义。基础文件完整描述一块板子,内核加载它,就知道该怎么对待这块板。overlay 一般是可选的,用来在基础设备树之上“添加”功能,既可以描述额外设备,也可以修改已经描述过的设备或接口。你在树莓派的 config.txt 里肯定见过 overlay 这个词。
想靠阅读来学设备树,可以去翻树莓派的 overlay 源码目录,从 README 开始。里面内容极多,大部分很无聊,但仔细找能找到金子。顺带一提,broadcom/ 目录下的基础设备树文件也值得一看,能让你了解不少树莓派硬件本来的样子。
从大量例子中学
多数时候你想写的是 overlay,而不是基础文件。写 overlay 要学的东西不多,因为可以学的地方实在太多。入门阶段,只需要掌握写出一个能用的 overlay 所需的最低限度知识;而 99% 的情况下,这意味着拿一份现成的 overlay 稍作修改。
树莓派有一份简单又格外啰嗦的 LED overlay,注释比代码还多,简直像是专门给人练手用的。它描述一个接在特定 GPIO 上的 LED、它的默认状态、标签,甚至还有覆盖项,让你可以随时替换这些参数。之后,在运行中的系统上用一条命令就能加载这个 overlay(这部分是树莓派独有),去拨弄那颗 LED,把它变成心跳灯或 CPU 活动信号,愿意的话还能写进 config.txt 永久加载。文件注释齐全,几乎解释了一切。
还有一份 overlay 描述接在 Pi 上的 I2C 触摸屏:把引脚编号改成对得上的,编译、加载,内核应该就会自动识别它。另一份 overlay 用来关闭 Pi 上的 WiFi——把两个变量设成“disabled”,看起来是不是超级简单?再有一份描述 SPI 以太网控制器,它定义了所用引脚,还特意把用到的那个 SPI 接口设为“disabled”,免得你从用户空间误写进去。
有时树莓派自家的 overlay 过于复杂,反而不利于学习,光看数量就知道。别担心,例子还有很多来源:整个 Linux 核心仓库里就不缺,此外还有 Radxa 的 overlay 仓库、Armbian 的 Rockchip 全家桶仓库、Armbian 的 sunxi(全志)overlay 仓库,尤其是它的 examples 目录。如果你需要某个特定功能或设备,很可能已经有人把它写成了 overlay 放在网上。做调研甚至走投无路时,你随时可以在 GitHub 上搜索包含关键词的 .dts 文件。
你需要的答案
设备树这类语言并不常见,overlay 又偏冷门,所以有些东西很少有人讲,但你应该知道。以下内容来自我目前尚属基础、但多少有些经验的理解。
首先,设备树 overlay 的注释是 C 风格,用 // 和 /* */。像 #size-cells 和 #address-cells 这类东西不是注释,而是关于如何读取属性的 overlay 元数据——要保留,它们很重要。
overlay 里的 & 字符是指向基础文件的指针,也就是指向你板子的 .dts(编译后成为 .dtb)。不同 CPU 家族、甚至不同板子或 CPU 的命名习惯都不一样:树莓派 .dts 里的 GPIO 指针叫 &gpio,别的板子上叫 &pio;如果 CPU 的 GPIO 多到一个控制器管不过来,指针还会指向不同的控制器(&gpio1、&gpio2 等)。拿不准时,打开你板子的基础 .dts,确认指针名字对得上;再拿不准,就对照同一块板子的其他 overlay,弄清该用哪个名字。
有的 overlay 写得啰嗦,有的写得简略,对比不同的版本能学到很多。比如把树莓派的 ENC28J60 overlay 和全志 CPU 的 ENC28J60 overlay 放在一起看:全志那份没有做 SPI 设备禁用,也没有提供覆盖项——因为 overlay 名字是写进 uBoot 配置文件的一行里的,而全志的 uBoot 大概没地方指定参数。它处理 GPIO 初始化的代码块也略有不同。稳妥起见,尽量贴合目标平台的惯例,不要因为别的平台有或没有某段内容就跟着加或删。是的,我本质上就是在劝你多复制粘贴。
如果你 overlay 写得够多、看得够多,会发现有些 overlay 用了简写形式,省略了 __overlay__、fragment0 之类的写法。我确实不知道这两种风格有什么区别,它们都能编译通过,看起来是等价的写法,StackOverflow 上的一个问题也指向这一点。我想进一步探究,却找不到这两种风格到底叫什么名字,所以目前只是提醒你留意,免得遇到时吃惊。
据我所知,如果你只是个普通人、直接跑 dtc 命令,overlay 里是不能用 #include 的。那需要预处理器,而朴素的 dtc 命令行不会替你跑预处理器。通常你会这样撞上问题:在网上找到一份带 #include 指令的 overlay,本地一编译就报错。这意味着你得把所有 #define 过的参数取消定义,在 dtc 报错的地方把名字换成数字。
比如一行很好读的 rockchip,pins = <...>;(来自又一份 ENC28J60 overlay 的例子),单独是编译不过的,你得调用预处理器,而那又是另一桩麻烦事。替代做法是查出 RK_PA7 和 RK_FUNC_GPIO 的数值(它们来自 dt-bindings/pinctrl/rockchip.h 这个 include),替换进去,让这一行变成数字形式。丑是丑了点,但不这么做 dtc 就不给你编译。顺带说一句,overlay 里另一个常见的 include 型 GPIO 数值来源是 dt-bindings/gpio/gpio.h。
据我所知,.dtbo 文件的编译方式和 .dtb 完全一样,.dtbo 只是树莓派引导程序期望 overlay 使用的扩展名。其他平台不用 .dtbo,而是用 .dtb,这同样是我见到的情况。另外,如果你用 Armbian,添加 overlay 就用 armbian-add-overlay 工具。
树莓派允许你用 dtoverlay 命令动态加载 overlay,但据我理解,其他单板电脑没有实现这个功能。所以在非 Pi 目标上测试 overlay 时,你会被困在“修改—重启—测试”的循环里;如果 overlay 写得太离谱,系统可能在启动时就卡住,你得拔下 SD 卡、把 overlay 注释掉才能重新开机。我怀疑当系统装在 eMMC 上时,你连这一步都做不到,所以务必确保启动失败时还有办法恢复。说实话,这对任何 eMMC 系统都适用,但你在折腾 overlay 时就是在动启动流程,值得被提醒一句。
准备下水
想学更多?ZephyrOS 有一份关于设备树 overlay 的入门材料,读起来很愉快。设备树的规范在描述性语言里也算出奇地好读,尤其是当你学会读字里行间的时候。它当然不是教程,但如果你要弄清设备树文件里某几行到底是什么意思,可能没有更好的兜底材料了。
最后一条建议:用大语言模型生成设备树要小心,尤其是你还没有足够经验判断哪个模型更擅长这件事的时候。我偶然翻到过一些 AI 生成的设备树“教程”,发现它们有时会凭空编造 overlay 属性;看来目前还没有什么能替代仔细阅读。
我眼下正在打磨一个设备树 overlay 模板,目标是做一个生成器,把同一份 overlay 一次性适配到大量不同的单板电脑上。下一期我肯定有东西可讲,虽然还不确定具体会讲什么。你呢?关于设备树,不管是不是 overlay,你希望接下来看到哪些内容?
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.