嵌入式Linux系统裁剪与驱动移植:内核配置与裁剪、深度相机驱动移植(UVC、V4L2)、设备树修改与调试

各位同学,咱们今天聊点硬核的。嵌入式Linux系统裁剪与驱动移植,说白了就是给硬件“量体裁衣”。你想想看,一个深度相机模组,如果跑着完整的Ubuntu桌面系统,那得多浪费资源?我刚开始做这个方向时,也踩过不少坑——系统太大,启动要半分钟,帧率还上不去。后来才明白,内核裁剪和驱动移植,才是嵌入式深度相机部署的“基本功”。

内核配置与裁剪:给系统“瘦身”

嵌入式平台资源有限,内存、Flash、CPU都紧巴巴的。内核裁剪的目的,就是去掉那些你用不到的模块,只保留深度相机和必要外设的驱动。我个人习惯,先跑一遍 make menuconfig,把默认配置加载进来,然后一项一项地“砍”。

裁剪的核心原则:

  • 只留必需的:文件系统支持(ext4、squashfs)、网络协议栈(TCP/IP)、USB子系统、V4L2框架、I2C/SPI总线驱动。
  • 砍掉冗余的:声卡驱动、显卡驱动(除非你用GPU)、蓝牙、Wi-Fi(如果不用)、各种不常用的文件系统(NTFS、XFS)。
  • 模块化加载:把驱动编译成模块(.ko),而不是直接编进内核。这样启动快,调试也方便。

避坑指南:我曾经在裁剪时,把USB HID驱动给去掉了,结果深度相机的UVC协议死活不工作。折腾了两天才发现,UVC依赖HID来传输控制命令。所以,裁剪前最好先看看驱动的Kconfig依赖关系。

举个例子,我常用的裁剪命令:

# 进入内核源码目录
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

# 裁剪后,只保留以下关键选项:
# Device Drivers ---> Multimedia support ---> Media USB Adapters ---> USB Video Class (UVC)
# Device Drivers ---> Multimedia support ---> V4L2 sub-device userspace API
# Device Drivers ---> USB support ---> USB Gadget support (如果用到OTG)
# File systems ---> Pseudo filesystems ---> /proc file system, /sys file system

裁剪完成后,用 make zImage 编译。我习惯把生成的镜像大小控制在2MB以内,这样在SPI Flash上也能跑得动。

深度相机驱动移植:UVC与V4L2

深度相机大多遵循UVC(USB Video Class)协议,或者通过V4L2(Video for Linux 2)框架来驱动。说白了,UVC是标准协议,V4L2是Linux下的视频设备抽象层。你想想看,如果没有V4L2,每个相机都得写一套独立的驱动,那得多累?

UVC驱动移植步骤:

  1. 确认硬件支持:用 lsusb 查看相机的VID/PID,确保内核UVC驱动支持该设备。
  2. 配置内核:在 menuconfig 中启用 CONFIG_USB_VIDEO_CLASSCONFIG_MEDIA_USB_SUPPORT
  3. 编译模块make modules,生成 uvcvideo.ko
  4. 加载测试insmod uvcvideo.ko,然后用 v4l2-ctl --list-devices 查看设备节点。

个人经验:有些深度相机(比如Intel RealSense D435)除了UVC,还依赖额外的控制协议。我遇到过的情况是,UVC驱动加载后,图像流能出来,但深度数据是空的。后来发现,需要同时加载一个自定义的HID驱动来发送控制命令。嗯,这里要注意,别只盯着UVC。

V4L2框架下的调试技巧:

  • v4l2-ctl --all 查看当前设备的所有参数。
  • v4l2-ctl --set-fmt-video=width=640,height=480,pixelformat=YUYV 设置格式。
  • v4l2-ctl --stream-mmap --stream-count=100 抓取100帧数据,验证驱动是否稳定。

我建议,在移植驱动时,先在PC上验证UVC和V4L2是否正常工作,再移植到嵌入式平台。这样可以隔离问题——到底是驱动问题,还是硬件问题?

设备树修改与调试:让内核认识你的硬件

设备树(Device Tree)是嵌入式Linux的“硬件说明书”。内核通过它来知道:I2C总线接在哪里?GPIO怎么配置?SPI时钟频率是多少?说白了,没有设备树,内核就是个瞎子。

设备树修改的核心步骤:

  1. 找到对应dts文件:一般在 arch/arm/boot/dts/arch/arm64/boot/dts/ 下。
  2. 添加深度相机节点:如果是USB接口的相机,通常不需要修改设备树(USB是枚举的)。但如果是MIPI接口的相机,就需要在I2C总线下添加子节点。
  3. 配置引脚复用:用 pinctrl 设置GPIO功能,比如把某个引脚设为I2C的SCL/SDA。

举个例子,一个MIPI深度相机的设备树节点:

&i2c2 {
    status = "okay";
    clock-frequency = <400000>;

    depth_camera: depth@3d {
        compatible = "sony,imx219";  // 假设传感器型号
        reg = <0x3d>;
        clocks = <&clks IMX6QDL_CLK_CKO>;
        reset-gpios = <&gpio1 17 GPIO_ACTIVE_LOW>;
        port {
            depth_ep: endpoint {
                remote-endpoint = <&csi_ep>;
            };
        };
    };
};

注意:设备树中的 reg 地址必须与硬件手册一致。我曾经因为写错了I2C地址,导致内核一直报“No such device”,查了三天才发现是0x3d写成了0x3c。这种低级错误,真的让人抓狂。

调试设备树的常用方法:

  • dtc -I dtb -O dts 反编译dtb文件,检查节点是否正确。
  • 在uboot中,用 fdt list 查看设备树内容。
  • 内核启动时,加 earlyconignore_loglevel 参数,打印设备树解析日志。

我个人习惯,在修改设备树后,先用 make dtbs 编译,然后通过uboot的 tftp 命令加载测试。这样不用每次都烧写固件,调试效率高很多。

知识体系与核心逻辑

下面这张图,是我自己总结的嵌入式深度相机部署的“三板斧”。你仔细看看,内核裁剪、驱动移植、设备树修改,这三者其实是环环相扣的:

嵌入式深度相机部署核心逻辑 内核配置与裁剪 驱动移植(UVC/V4L2) 设备树修改与调试 调试反馈与迭代 • 移除无用模块 • 模块化加载 • 镜像大小控制 • 依赖关系检查 • UVC协议支持 • V4L2框架适配 • 控制命令调试 • 数据流验证 • 节点添加 • 引脚复用配置 • 地址核对 • 反编译验证 三者协同:裁剪为驱动腾空间,驱动依赖设备树描述

你看,内核裁剪是基础,它决定了系统能跑多快、占多少资源。驱动移植是核心,它让深度相机真正“开口说话”。设备树修改是桥梁,它告诉内核硬件长什么样。这三步走通了,你的深度相机在嵌入式平台上就能稳定工作了。

嗯,今天就先聊到这里。记住,做嵌入式开发,耐心比技术更重要。遇到问题别慌,先查硬件、再看驱动、最后怀疑设备树——这是我多年总结的“三板斧”。

专注资料整理