1. Zephyr与设备树概述:Zephyr RTOS简介、设备树的起源与作用、为什么Zephyr选择设备树

1.1 Zephyr RTOS简介

先聊聊Zephyr。这个RTOS,我接触它大概是在五年前。当时项目需要一个能跑在低功耗蓝牙芯片上的系统,Linux太重,FreeRTOS又太“裸”。Zephyr正好卡在中间——它给你一个完整的操作系统体验,但又足够轻量。

Zephyr是Linux基金会旗下的开源项目。说白了,它就是一个为资源受限设备打造的实时操作系统。你想想看,一个只有几十KB内存的MCU,也能跑多线程、有文件系统、支持网络协议栈。这在十年前几乎不敢想。

我个人习惯把Zephyr比作“嵌入式界的Linux”。为什么这么说?因为它借鉴了Linux的很多设计思想,尤其是设备驱动模型和构建系统。但Zephyr又比Linux更“嵌入式”——它没有MMU,没有复杂的进程调度,一切都是为了在单片机上高效运行。

Zephyr支持什么?我列几个关键点:

  • 多架构支持:ARM、RISC-V、x86、Xtensa……你能想到的主流架构它基本都覆盖了
  • 丰富的协议栈:BLE、Wi-Fi、Thread、Zigbee、甚至LTE-M
  • 模块化内核:你可以按需裁剪,不需要的功能直接砍掉
  • 设备树驱动模型:这就是我们今天要聊的重点

我记得第一次用Zephyr时,最让我头疼的就是它的配置方式。不是Kconfig,而是那个看起来像天书一样的.dts文件。但用顺手之后,我不得不承认——这玩意儿真香。

1.2 设备树的起源与作用

设备树(Device Tree)这个概念,最早是从PowerPC社区来的。嗯,这里要注意,它并不是Zephyr的原创。

故事是这样的:早期Linux内核里,每个板子都要写一堆C代码来描述硬件。比如“这个GPIO接在哪个控制器上”、“那个UART的中断号是多少”。这些代码散落在arch/arm/mach-xxx目录下,维护起来简直是噩梦。

后来大家想了个办法:为什么不把这些硬件描述信息单独拿出来,用一种结构化的文本格式来写?于是设备树就诞生了。

设备树的核心作用,说白了就是把硬件配置从代码中剥离出来。你不需要改C代码,只需要改一个文本文件,就能让同一个内核跑在不同的板子上。

举个例子:

/ {
    model = "STM32F429 Discovery Board";
    compatible = "st,stm32f429i-disco";

    chosen {
        zephyr,console = &usart1;
    };

    usart1: serial@40011000 {
        compatible = "st,stm32-usart";
        reg = <0x40011000 0x400>;
        interrupts = <37 0>;
        status = "okay";
    };
};

这段代码描述了什么?一个STM32F429开发板,UART1的基地址是0x40011000,中断号是37。如果你换一个板子,只需要改这个.dts文件,驱动代码完全不用动。

我在项目中遇到过一件事:客户临时换了一颗Flash芯片,引脚完全兼容但寄存器地址不同。如果是传统做法,我得改驱动代码、重新编译、测试。但用设备树,我只需要在.dts里改几行reg属性,十分钟搞定。

1.3 为什么Zephyr选择设备树

这个问题我思考过很久。Zephyr的竞争对手,比如FreeRTOS、RT-Thread,它们都不用设备树。为什么Zephyr偏偏要走这条路?

我个人认为有三个核心原因:

  1. 硬件抽象的需要:Zephyr支持的硬件平台太多了。从Cortex-M0到Cortex-A72,从几百KB的MCU到几百MB的MPU。如果没有统一的硬件描述方式,驱动代码会变得极其混乱。
  2. 与Linux生态对齐:Zephyr的很多开发者来自Linux社区。他们习惯设备树这套东西。而且,如果你在Zephyr上开发驱动,未来移植到Linux会非常顺畅——因为设备树语法几乎一样。
  3. 配置的灵活性:Kconfig负责“软件配置”(比如要不要开启蓝牙),设备树负责“硬件配置”(比如UART用哪个引脚)。两者分工明确,互不干扰。

核心观点:设备树让Zephyr的驱动模型变得“数据驱动”。你不需要写一堆if-else来判断板子型号,设备树自动帮你完成了硬件匹配。

我曾经踩过一个坑:在Zephyr 1.14版本时,有个驱动直接硬编码了GPIO引脚号。后来换了板子,代码跑不起来。查了半天才发现是引脚号不对。从那以后,我坚持所有硬件参数都走设备树,哪怕只是一个简单的LED。

个人建议:刚开始学Zephyr时,不要急着写驱动。先花点时间搞懂设备树怎么写。你后面会发现,90%的硬件适配问题,其实都是设备树写错了。

还有一个细节值得注意:Zephyr的设备树是编译期解析的。什么意思?就是.dts文件在编译时就被转换成二进制数据,嵌入到固件里。运行时不需要解析文本,效率很高。这一点和Linux不同——Linux是在启动时由bootloader加载设备树二进制(DTB)。

为什么Zephyr要这么做?因为MCU资源有限。你想想看,一个只有64KB Flash的芯片,哪来的空间放解析器?编译期搞定,运行时直接查表,这才是嵌入式该有的做法。

1.4 小结

这一章我们聊了三个东西:Zephyr是什么、设备树从哪来、为什么Zephyr要用它。

说白了,设备树就是Zephyr的“硬件身份证”。它让驱动代码和硬件配置解耦,让同一个固件可以跑在不同板子上。对于嵌入式开发来说,这简直是生产力工具。

下一章,我会带你手把手写一个设备树文件。到时候你会发现,这东西其实没那么神秘。

避坑提醒:千万不要在设备树里写死板级特定的逻辑。比如某个引脚的电平状态,应该由驱动根据设备树属性动态判断。我曾经见过有人把“上拉/下拉”直接写在设备树注释里,结果换板子时忘了改,排查了一整天。

嗯,今天就到这里。记住一句话:设备树是给硬件写的说明书,不是给代码写的注释