1. Yocto与设备树概述

做嵌入式Linux开发这些年,我越来越觉得一个道理:不懂设备树,你连板子都跑不起来。Yocto呢,则是帮你把整个系统打包好的利器。这两样东西凑在一起,就是咱们这门课的核心。

先别急着敲代码。咱们花点时间,把基础概念理清楚。磨刀不误砍柴工嘛。

1.1 Yocto项目简介

Yocto是什么?说白了,它是一个开源协作项目,提供了一套模板、工具和方法,让你能定制自己的嵌入式Linux发行版。不是给你一个现成的系统,而是给你一套造系统的工具链。

我个人习惯把Yocto比作一个乐高工厂。你选好芯片、文件系统、应用软件这些"积木",Yocto帮你把它们拼成一个完整的、可启动的Linux镜像。

核心组件速览:

  • BitBake:任务调度引擎,负责解析配方、执行编译、打包。Yocto的心脏。
  • OpenEmbedded-Core (OE-Core):元数据层,包含基础配方、类和配置。
  • Poky:Yocto的参考发行版,也是你上手时最常用的构建系统。
  • Meta层:按功能划分的配方集合,比如meta-qt5、meta-openembedded。

我记得刚接触Yocto时,被它的层(Layer)概念绕晕过。其实你想想看,层就是模块化的体现。硬件相关放一层,软件包放一层,你自己的定制再放一层。互不干扰,维护起来特别清爽。

组件 作用 我常用的场景
BitBake 执行构建任务 解析.bb配方,调度编译
OE-Core 基础元数据 提供gcc、glibc等基础包配方
Poky 参考发行版 作为起点,再叠加自定义层
Meta层 功能模块 添加Wi-Fi、蓝牙、图形界面支持

我的建议:刚开始别贪多。先用Poky默认配置编译一个qemuarm64镜像跑起来,感受一下BitBake的工作流。等熟悉了,再往里面加自己的东西。

1.2 设备树(DTS)基本概念

设备树,英文叫Device Tree,缩写DTS(源文件)或DTB(二进制)。它是什么?一个描述硬件的数据结构。CPU有几个核?串口挂在哪根总线上?GPIO哪个引脚接了LED?这些信息,都写在设备树里。

为什么需要它?在ARM Linux早期,硬件描述代码散落在arch/arm/mach-xxx目录下,每个板子一个文件。内核维护者快疯了——每出一个新板子,就要改内核代码。设备树把硬件描述从内核源码里剥离出来,变成一份独立的数据文件。内核启动时解析它,就知道自己跑在什么硬件上了。

嗯,这里要注意:设备树不是万能的。它只描述不可枚举的硬件。像PCIe设备这种能自己报名字的,就不需要写进设备树。

我曾经踩过的坑:有一次我漏写了reg属性,结果驱动加载时内存映射全乱套。设备树里每个节点都有必须的属性,比如compatible、reg、interrupts。少一个,驱动就可能罢工。

一个最简单的设备树节点长这样:

/dts-v1/;

/ {
    model = "MyCustomBoard";
    compatible = "mycompany,myboard", "arm,vexpress";

    chosen {
        stdout-path = &uart0;
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x40000000>;  // 1GB RAM
    };

    uart0: serial@1c090000 {
        compatible = "snps,dw-apb-uart";
        reg = <0x1c090000 0x1000>;
        interrupts = <0 5 4>;
        clock-frequency = <24000000>;
        status = "okay";
    };
};

看到没?compatible是驱动匹配的关键字,reg告诉内核外设的地址和大小,interrupts指定中断号。这些信息,驱动通过设备树API就能拿到,不用再硬编码。

1.3 设备树在嵌入式Linux中的作用

设备树在嵌入式Linux里到底扮演什么角色?我总结了三个核心作用:

  1. 硬件描述标准化:同一份内核镜像,换一个设备树文件,就能跑在不同板子上。不用重新编译内核。
  2. 驱动与硬件解耦:驱动开发者只需要写通用的驱动逻辑,具体硬件参数从设备树读取。板子变了,改dts文件就行。
  3. 启动参数传递:内核启动时,设备树告诉它内存布局、根文件系统位置、早期控制台等关键信息。

你想想看,没有设备树会怎样?每换一个板子,就得改内核源码、重新编译、烧录。有了设备树,内核镜像不变,只换dtb文件。开发效率提升不止一个量级。

设备树在Yocto中的位置:

  • 设备树源文件(.dts/.dtsi)通常放在内核配方或板级支持包(BSP)层中。
  • Yocto构建时,BitBake会调用dtc(设备树编译器)将.dts编译为.dtb。
  • 生成的.dtb文件会被打包进内核镜像或单独部署到boot分区。

下面这张图,是我自己梳理的设备树在Yocto构建流程中的位置,你看一眼就明白了:

Yocto构建流程中的设备树 内核源码 linux-yocto_x.x.bb 设备树源文件 *.dts / *.dtsi BSP层配置 meta-myboard/conf/ BitBake 构建引擎 解析配方 → 执行任务 → 编译内核 → 编译设备树 内核镜像 zImage / Image 设备树二进制 *.dtb 部署到目标板:boot分区(内核 + dtb) U-Boot 加载内核和 dtb → 内核解析设备树 → 初始化硬件 💡 设备树在Yocto中作为独立构件存在,与内核镜像分离,便于板级复用

从这张图你能看到,设备树在Yocto里不是附属品,而是一等公民。它和内核源码、BSP配置一起,作为输入交给BitBake。最终产出的dtb文件,和内核镜像一起部署到目标板。

我个人觉得,理解这个流程是学好后面所有章节的基础。因为后续的设备树定制、覆盖、打包,全都是在这个框架里做文章。

一个小技巧:在Yocto构建目录下,编译完的设备树文件通常放在tmp/deploy/images/<MACHINE>/下。你可以用dtc -I dtb -O dts反编译看看,确认自己写的dts有没有被正确编译。

好了,这一章的概念就这些。记住三个关键词:Yocto是造系统的工具,设备树是描述硬件的语言,两者结合让你高效定制嵌入式Linux。后面每一章,我都会带着你一步步深入,从写dts到集成进Yocto,再到调试和优化。