第四章:嵌入式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 - I2C:
I2C support -> I2C device interface(必选,否则无法控制镜头电机) - SPI:
SPI support -> BCM2835 SPI controller - UART:
Character 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检测不到设备,检查设备树和硬件连接。我曾经因为一根杜邦线接触不良,浪费了整整一个下午。
好了,这一章的内容就这些。从工具选择到内核配置,从根文件系统到启动验证,每一步我都踩过坑。你照着做,至少能省下两周的摸索时间。
下一章咱们要聊多传感器的时间同步问题——那才是真正考验系统设计能力的地方。