第四章:嵌入式Linux系统构建:基于Yocto/Buildroot构建最小系统

好,咱们进入正题。这一章要聊的,是嵌入式显微镜项目里最核心的一步——把Linux系统从零搭起来。说白了,就是给咱们的硬件平台装上"大脑"。

我个人习惯用Yocto和Buildroot这两个工具。它们各有千秋,但目标一致:生成一个能跑、够小、驱动齐全的嵌入式Linux系统。你想想看,显微镜要同时处理CSI摄像头数据、通过I2C控制镜头对焦、用SPI读取传感器、再通过UART输出调试信息——这四样缺一不可。

4.1 选择构建工具:Yocto vs Buildroot

先说说我的选择逻辑。我在项目中遇到过不少团队,上来就纠结选哪个。其实没那么复杂:

对比项 Yocto Buildroot
学习曲线 陡峭,需要理解bitbake语法 平缓,类似makefile风格
定制灵活性 极高,几乎可以改任何层 中等,适合标准场景
构建时间 第一次可能要4-6小时 通常30分钟到1小时
包管理 支持RPM/DEB/IPK 不支持运行时包管理
适合场景 产品级、需要长期维护 原型验证、快速迭代

嗯,这里要注意:如果你的显微镜项目要量产,我建议用Yocto。如果只是实验室验证,Buildroot完全够用。我自己做原型时常用Buildroot,但产品化后全切到了Yocto。

4.2 使用Buildroot构建最小系统

咱们先拿Buildroot开刀,因为它上手快。我直接说步骤:

# 1. 下载Buildroot
git clone git://git.buildroot.net/buildroot
cd buildroot

# 2. 配置目标平台(以树莓派4为例)
make raspberrypi4_64_defconfig

# 3. 进入菜单配置
make menuconfig

进入menuconfig后,你需要手动开启几个关键选项。我曾经因为漏掉一个CSI驱动,折腾了整整两天——摄像头死活不出图。

4.2.1 配置内核驱动

Kernel -> Linux Kernel -> Device Drivers路径下,找到以下选项:

  • CSI(摄像头串行接口)Multimedia support -> V4L platform devices -> Raspberry Pi Camera
  • I2CI2C support -> I2C device interface(必选,否则无法控制镜头电机)
  • SPISPI support -> BCM2835 SPI controller
  • UARTCharacter devices -> Serial drivers -> 8250 serial port support

避坑指南:我曾经在配置I2C时,只选了驱动没选设备接口。结果系统启动后,/dev/i2c-0 节点死活不出来。记住,驱动和接口层要同时勾选。

4.2.2 根文件系统定制

根文件系统决定了你的显微镜能跑什么软件。我建议的最小集:

# 在Target packages中勾选
Libraries -> libv4l          # 视频处理库
Libraries -> libi2c          # I2C操作库
Networking -> dropbear       # SSH远程调试
Shell and utilities -> bash  # 比sh好用太多
Development tools -> gdb     # 调试必备

你想想看,如果没有libv4l,你连摄像头数据都抓不到。没有libi2c,镜头对焦电机就是个摆设。

4.3 使用Yocto构建(进阶方案)

如果项目要量产,咱们得用Yocto。它的核心是"层"的概念。我一般这样组织:

meta-raspberrypi      # 硬件BSP层
meta-openembedded     # 通用软件层
meta-microscope       # 咱们自己的应用层

meta-microscope层里,我会创建一个自定义的image recipe:

# recipes-core/images/microscope-image.bb
inherit core-image

IMAGE_INSTALL = " \
    packagegroup-base \
    packagegroup-core-boot \
    linux-firmware-rpidistro-bcm43430 \
    v4l-utils \
    i2c-tools \
    spi-tools \
    python3-opencv \
    "

我的经验:Yocto第一次构建时,建议用bitbake -k参数。这样即使某个包编译失败,也不会中断整个流程。我见过太多新手因为一个依赖问题,重头编译6小时——太痛苦了。

4.4 内核配置实战:CSI与I2C的坑

这部分我得多说两句。CSI和I2C是显微镜的"眼睛"和"手指",配置不好整个项目就废了。

4.4.1 CSI摄像头驱动配置

以树莓派Camera Module 3为例,内核需要开启:

Device Drivers  --->
    Multimedia support  --->
        [*] V4L platform devices
        [*]   Raspberry Pi Camera (unicam)
        [*]   Video4Linux Linux drivers
            *** Media drivers ***
        [*]   Sony IMX708 sensor support

我记得第一次配置时,只开了unicam没开sensor驱动。结果v4l2-ctl --list-devices能识别到设备,但就是不出图像。后来查了三天资料才发现——sensor驱动才是真正干活的那个。

4.4.2 I2C设备树配置

I2C的坑主要在设备树。你需要确保dts文件里正确描述了I2C总线上的设备:

// arch/arm/boot/dts/bcm2711-rpi-4-b.dts
&i2c0 {
    pinctrl-names = "default";
    pinctrl-0 = <&i2c0_pins>;
    clock-frequency = <100000>;
    status = "okay";

    // 镜头对焦电机驱动
    focus_motor: ad5398@0c {
        compatible = "adi,ad5398";
        reg = <0x0c>;
    };
};

关键点:I2C地址一定要和硬件手册对上。我遇到过用0x0c还是0x0d的问题,最后拿示波器量了才确认。别信网上的默认值,以datasheet为准。

4.5 根文件系统瘦身技巧

嵌入式系统的存储空间通常很紧张。我总结了一套瘦身方法:

  • 删除文档和locale:/usr/share/doc、/usr/share/locale 直接砍掉,能省30-50MB
  • 使用busybox替代coreutils:一个busybox能顶几十个标准工具
  • 静态链接关键应用:比如v4l2-ctl,用静态编译避免依赖libc
  • 压缩内核模块:启用CONFIG_MODULE_COMPRESS,用zstd压缩

我做过一个极端案例:把根文件系统从120MB压到了18MB,还能正常跑OpenCV做图像处理。你想想看,省下来的空间可以存多少张显微镜图片?

4.6 启动验证与调试

系统构建完成后,别急着欢呼。先跑一轮验证:

# 检查CSI摄像头
v4l2-ctl --list-devices
v4l2-ctl --device=/dev/video0 --set-fmt-video=width=1920,height=1080
v4l2-ctl --device=/dev/video0 --stream-mmap --stream-count=30

# 检查I2C总线
i2cdetect -y 1
i2cget -y 1 0x0c 0x00

# 检查SPI设备
spi-test -d /dev/spidev0.0 -s 1000000

# 检查UART
stty -F /dev/ttyAMA0 115200
echo "Hello Microscope" > /dev/ttyAMA0

注意:如果CSI设备节点没出现,八成是内核配置漏了。如果I2C检测不到设备,检查设备树和硬件连接。我曾经因为一根杜邦线接触不良,浪费了整整一个下午。

好了,这一章的内容就这些。从工具选择到内核配置,从根文件系统到启动验证,每一步我都踩过坑。你照着做,至少能省下两周的摸索时间。

下一章咱们要聊多传感器的时间同步问题——那才是真正考验系统设计能力的地方。