在上一篇里,我们已经把高通边缘硬件平台的基本脉络梳理了一遍:QRB5165、IQ-9075、IQ-9100 这类平台并不是“买一块板子、烧个 Ubuntu”那么简单,它们背后通常包含 SoC、PMIC、电源域、摄像头子系统、DSP/NPU、安全岛、Bootloader、BSP、固件分区等一整套工程体系。

从这一篇开始,我们进入真正的嵌入式 Linux 开发主线。

本篇聚焦三个问题:

  • 如何理解高通平台上的 Linux BSP 与开源内核?

  • 如何编译、裁剪适合工业场景的 Linux Kernel?

  • 如何修改 Device Tree,并把 kernel、dtb、dtbo、modules 打包成可启动固件?

面向的典型场景包括:

  • 工业机器人控制器

  • 智能相机和机器视觉盒子

  • ROS2 边缘计算节点

  • 长时间运行的工业 Linux 网关

  • 基于 QRB5165 / IQ 系列平台的整机产品开发

1. 高通嵌入式 Linux 的 BSP 结构

在 x86 PC 上,Linux Kernel 往往可以直接从 kernel.org 拉下来编译运行。但在高通嵌入式平台上,情况会复杂很多。

原因是高通 SoC 内部并不只有 CPU,还包括:

  • Kryo / ARM Cortex-A 系列应用处理器

  • Adreno GPU

  • Hexagon DSP

  • ISP / Camera Subsystem

  • NPU / AI Engine

  • Sensor Hub / Always-on 子系统

  • PMIC 电源管理芯片

  • PCIe / USB / UFS / eMMC / SDIO 控制器

  • MIPI CSI / DSI 接口

  • 安全启动、安全岛、看门狗、调试认证模块

这些硬件资源通常需要配套的:

  • Linux Kernel patch

  • Device Tree

  • firmware blob

  • bootloader 配置

  • 用户态 HAL / daemon

  • Yocto 或 Ubuntu rootfs 集成脚本

  • 分区镜像打包工具

所以在高通平台上,我们讨论“编译 Linux 内核”,实际上通常是在 BSP 体系中编译一个和硬件、固件、启动链强绑定的 kernel。

一个典型 BSP 目录可能类似下面这样:

bsp/
├── bootloader/
│   ├── xbl/
│   ├── abl/
│   └── tools/
├── kernel/
│   └── linux-msm/
├── device-tree/
│   └── qcom/
├── firmware/
│   ├── adsp.mbn
│   ├── cdsp.mbn
│   ├── modem.mbn
│   └── ...
├── yocto/
│   ├── meta-qti/
│   ├── meta-openembedded/
│   └── build/
├── rootfs/
├── out/
└── flash_tools/

不同厂商发布的 BSP 目录结构会有差异。比如 Qualcomm 官方、Thundercomm、Lantronix、Axiomtek 或工业板卡厂商给出的包名和路径可能不同,但核心组成大体一致:

组成

作用

linux-msm / kernel

高通平台 Linux 内核源码

arch/arm64/boot/dts/qcom/

SoC 和开发板 Device Tree

firmware/

DSP、NPU、Camera、Wi-Fi、BT 等固件

meta-qti / Yocto layer

BSP 构建规则

boot.img / vendor_boot.img

启动镜像

dtbo.img

Device Tree Overlay 镜像

rootfs.img

根文件系统

flash.xml / rawprogram XML

分区刷写描述

实际开发时,不建议一上来就脱离 BSP 单独编 kernel。更推荐先跑通厂商 BSP 的完整编译链路,再逐步拆出 kernel、dtb、modules 的独立编译流程。

2. 高通开源内核与 BSP 内核的关系

高通平台常见的内核来源大致有三类。

2.1 Qualcomm BSP 内核

这是开发板厂商或高通官方 SDK 中附带的 kernel,通常已经包含:

  • SoC bring-up patch

  • PMIC 驱动

  • Camera 驱动框架

  • DSP 通信驱动

  • IOMMU / SMMU 配置

  • NPU / GPU 相关接口

  • 板级 dts/dtsi

  • 分区和启动相关配置

工业产品开发中,通常以 BSP 内核作为基线,因为它最接近板卡实际硬件。

2.2 CodeLinaro / Qualcomm 开源内核

高通开源代码历史上曾大量发布在 Code Aurora Forum,后来逐步迁移到 CodeLinaro 等平台。开发者可以在公开仓库中找到部分 Qualcomm Linux kernel、Android kernel、firmware loader、device tree 相关内容。

这类代码适合用于:

  • 查看驱动实现

  • 对比 upstream Linux 差异

  • 跟踪高通平台补丁

  • 分析某个外设驱动的演进历史

但它不一定能直接替代开发板厂商 BSP,因为工业板卡往往还有额外的 carrier board、接口扩展、电源控制、摄像头模组、CAN、RS485、隔离 IO 等私有修改。

2.3 Mainline Linux

主线 Linux 对 Qualcomm SoC 的支持越来越完善,很多 Snapdragon / QRB 平台的基础外设已经进入 upstream。

但对于工业机器人和智能相机来说,mainline 仍可能缺少:

  • 完整 Camera pipeline

  • Hexagon / DSP 低功耗链路

  • 商用 NPU 用户态栈

  • 板厂自定义接口

  • 安全岛相关驱动

  • 厂商验证过的功耗和稳定性配置

所以实际产品中常见策略是:

量产用 BSP 内核,研发时参考开源内核,长期维护时逐步吸收 mainline patch。

3. 准备交叉编译环境

高通 QRB / IQ 系列平台通常是 ARM64 架构,因此 kernel 编译需要 AArch64 交叉工具链。

常见工具链包括:

  • aarch64-linux-gnu-

  • aarch64-none-linux-gnu-

  • Yocto SDK 中导出的交叉编译器

  • Android Clang 工具链,部分 BSP 会要求使用 Clang

如果是普通 Linux kernel 构建,典型环境变量如下:

export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
export KERNEL_SRC=$PWD/kernel/linux-msm
export OUT=$PWD/out/kernel

如果 BSP 要求使用 Clang,可能类似:

export ARCH=arm64
export LLVM=1
export LLVM_IAS=1
export CLANG_TRIPLE=aarch64-linux-gnu-

建议优先阅读 BSP 中的编译脚本,例如:

build.sh
build_kernel.sh
setup_env.sh
qti_build.sh
yocto/build.sh

不要一开始就手写完整命令。因为很多 Qualcomm BSP 会额外传入:

  • KCFLAGS

  • DTC_FLAGS

  • vendor config fragment

  • dtbo build 参数

  • board variant

  • kernel module install path

  • boot image packing 参数

4. 编译高通 Linux Kernel

一个最小化的 kernel 编译流程通常包括:

cd kernel/linux-msm

make O=$OUT qcom_defconfig

make O=$OUT -j$(nproc) Image.gz
make O=$OUT -j$(nproc) dtbs
make O=$OUT -j$(nproc) modules

实际 BSP 中,defconfig 名称可能不是 qcom_defconfig,而是类似:

vendor/qrb5165_defconfig
vendor/sa8155p_defconfig
vendor/qcom_defconfig
qti_defconfig

可以在源码里查看:

ls arch/arm64/configs/

编译完成后,常见产物包括:

out/kernel/arch/arm64/boot/Image
out/kernel/arch/arm64/boot/Image.gz
out/kernel/arch/arm64/boot/dts/qcom/*.dtb
out/kernel/drivers/**/*.ko
out/kernel/vmlinux
out/kernel/System.map

其中最重要的是:

文件

说明

Image / Image.gz

ARM64 Linux kernel 镜像

*.dtb

编译后的 Device Tree Blob

*.dtbo

Device Tree Overlay

*.ko

内核模块

vmlinux

未压缩带符号 kernel,调试用

System.map

符号地址表,排查 crash 用

工业项目里,建议每次发布内核时保留:

Image.gz
dtb/dtbo
所有 .ko
vmlinux
System.map
.config
commit id
编译脚本

否则后续遇到 kernel panic、Oops、watchdog reset 时,很难精确还原问题版本。

5. Yocto BSP 下的 kernel 编译

很多高通嵌入式 Linux BSP 使用 Yocto / OpenEmbedded 构建完整系统。此时不建议直接在 kernel 目录下 make,而是通过 Yocto recipe 编译。

典型流程类似:

source setup-environment build-qti
bitbake virtual/kernel

如果需要进入 kernel 配置界面:

bitbake virtual/kernel -c menuconfig

保存配置后,可以导出 config fragment:

bitbake virtual/kernel -c savedefconfig

或者使用 BSP 提供的方式将 .config 转换成 fragment,并放入对应 layer:

meta-custom/
└── recipes-kernel/
    └── linux/
        ├── linux-qcom_%.bbappend
        └── files/
            └── industrial_robot.cfg

industrial_robot.cfg 示例:

CONFIG_CAN=y
CONFIG_CAN_RAW=y
CONFIG_CAN_DEV=y
CONFIG_CAN_MCP251X=m
CONFIG_SPI=y
CONFIG_I2C_CHARDEV=y
CONFIG_PREEMPT=y
CONFIG_CGROUPS=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y

然后在 .bbappend 中追加:

FILESEXTRAPATHS:prepend := "${THISDIR}/files:"

SRC_URI += "file://industrial_robot.cfg"

这种方式比直接修改 .config 更适合长期维护,因为它可以被 git 管理,也方便在不同 BSP 版本之间迁移。

6. 内核裁剪的基本原则

工业嵌入式 Linux 裁剪内核,不是盲目追求“越小越好”,而是在以下目标之间平衡:

  • 启动速度

  • 内存占用

  • 稳定性

  • 可维护性

  • 外设支持

  • 现场调试能力

  • 安全性

  • 长时间运行可靠性

6.1 可以优先裁剪的部分

如果产品是固定硬件、固定用途,可以考虑关闭:

不使用的文件系统
不使用的网络协议
不使用的 USB Gadget 功能
不使用的 HID / 输入设备
不使用的无线协议
不使用的调试接口
不使用的虚拟化功能
不使用的桌面图形驱动

例如智能相机系统中,如果 rootfs 固定使用 ext4,可以考虑不启用大量冷门文件系统:

CONFIG_BTRFS_FS=n
CONFIG_F2FS_FS=n
CONFIG_XFS_FS=n
CONFIG_JFS_FS=n
CONFIG_REISERFS_FS=n

如果设备不需要蓝牙:

CONFIG_BT=n

如果设备没有 Wi-Fi:

CONFIG_CFG80211=n
CONFIG_MAC80211=n

如果没有 USB 外设模式:

  CONFIG_USB_GADGET=n

6.2 不建议过早关闭的部分

早期开发阶段,不建议急着关闭:

CONFIG_PRINTK
CONFIG_KALLSYMS
CONFIG_DEBUG_FS
CONFIG_PROC_FS
CONFIG_SYSFS
CONFIG_DEVTMPFS
CONFIG_MODULES
CONFIG_DYNAMIC_DEBUG
CONFIG_FTRACE

原因很简单:产品初期最重要的是可调试性。

很多现场问题,例如:

  • 摄像头偶发打不开

  • CAN 总线通信异常

  • SPI 设备 probe 失败

  • 系统运行 72 小时后内存下降

  • 某个中断风暴导致 CPU 占用异常

  • watchdog reset 后无法定位原因

都需要依赖这些调试能力。

更合理的做法是分阶段裁剪:

阶段策略
Bring-up 阶段保留调试能力,优先跑通硬件
EVT 阶段裁剪明显不用的驱动和协议
DVT 阶段关闭高风险 debug 接口,保留诊断手段
PVT / 量产固化配置,记录 config、符号表和镜像版本

6.3 模块还是 built-in?

在工业机器人和智能相机系统中,驱动是编进 kernel 还是做成 module,需要结合启动链路判断。

适合 built-in 的内容:

启动存储驱动
根文件系统驱动
early console
PMIC / regulator
pinctrl
clock controller
关键总线驱动
watchdog
安全相关驱动

适合 module 的内容:

可选外设
不同型号传感器
调试驱动
非启动必需的工业接口
部分 camera sensor driver

例如,如果 rootfs 在 UFS 上,UFS、SCSI、block layer 相关驱动必须在 rootfs 挂载前可用,否则系统无法启动。

如果某个 MIPI 摄像头模组存在多个型号,做成 .ko 可以让同一套 rootfs 支持不同 SKU。

7. Device Tree 在高通平台中的作用

Device Tree 是 ARM Linux 平台描述硬件资源的核心机制。

在高通平台上,Device Tree 通常描述:

  • SoC 外设控制器

  • GPIO / pinctrl

  • I2C / SPI / UART / CAN 控制器

  • regulator 电源

  • clock

  • interrupt

  • reserved memory

  • remoteproc / DSP

  • camera sensor

  • display panel

  • PCIe / USB

  • thermal zone

  • board variant

内核源码中常见路径:

arch/arm64/boot/dts/qcom/

通常会看到三类文件:

soc.dtsi
platform.dtsi
board.dts

可以简单理解为:

文件作用
*.dtsi可复用的 SoC 或板级片段
*.dts最终板卡入口文件
*.dtbo可叠加的板级 overlay

示例结构:

#include "qrb5165.dtsi"
#include "qrb5165-pmic.dtsi"
#include "qrb5165-industrial-carrier.dtsi"

/ {
    model = "QRB5165 Industrial Vision Board";
    compatible = "vendor,qrb5165-industrial", "qcom,qrb5165";
};

8. 修改 Device Tree:启用一个 SPI 外设

假设我们在 carrier board 上挂了一个 SPI 设备,例如工业 IO 扩展芯片或外部 ADC,需要在 Device Tree 中启用对应 SPI 控制器。

一个简化示例如下:

&spi12 {
    status = "okay";

    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&spi12_default>;
    pinctrl-1 = <&spi12_sleep>;

    industrial_adc@0 {
        compatible = "vendor,industrial-adc";
        reg = <0>;

        spi-max-frequency = <10000000>;

        interrupt-parent = <&tlmm>;
        interrupts = <42 IRQ_TYPE_LEVEL_LOW>;

        vdd-supply = <&vreg_l11a_1p8>;
    };
};

同时需要配置 pinctrl:

&tlmm {
    spi12_default: spi12-default-state {
        pins = "gpio110", "gpio111", "gpio112", "gpio113";
        function = "qup12";
        drive-strength = <8>;
        bias-disable;
    };

    spi12_sleep: spi12-sleep-state {
        pins = "gpio110", "gpio111", "gpio112", "gpio113";
        function = "gpio";
        drive-strength = <2>;
        bias-pull-down;
    };
};

这里有几个关键点:

  • status = "okay" 表示启用该控制器

  • reg = <0> 表示 SPI chip select 0

  • spi-max-frequency 要结合外设手册、走线长度、EMI 要求设置

  • interrupts 要和硬件原理图中的 GPIO 中断脚一致

  • vdd-supply 要绑定正确 regulator,否则驱动 probe 时可能上电失败

  • pinctrl 的 default 和 sleep 状态会影响功耗表现

在工业场景中,pinctrl 的 sleep 状态很重要。很多板子待机功耗异常,最后发现不是 CPU 没睡下去,而是 GPIO、SPI、I2C、MIPI reset 脚在 suspend 状态下仍然保持高驱动或错误上拉。

9. 修改 Device Tree:启用 CAN-FD

工业机器人和运动控制场景中,CAN-FD 很常见。高通 SoC 本身是否集成 CAN 控制器,要看具体型号和板级设计;很多项目会通过 SPI 外挂 CAN-FD 控制器,例如 MCP2518FD 一类芯片。

Device Tree 示例:

&spi8 {
    status = "okay";

    canfd0: can@0 {
        compatible = "microchip,mcp251xfd";
        reg = <0>;

        spi-max-frequency = <20000000>;

        interrupt-parent = <&tlmm>;
        interrupts = <36 IRQ_TYPE_LEVEL_LOW>;

        clocks = <&canfd0_osc>;
    };
};

canfd0_osc: canfd0-osc {
    compatible = "fixed-clock";
    #clock-cells = <0>;
    clock-frequency = <40000000>;
};

内核配置需要启用:

CONFIG_CAN=y
CONFIG_CAN_RAW=y
CONFIG_CAN_DEV=y
CONFIG_CAN_MCP251XFD=m

系统启动后可以检查:

dmesg | grep -i can
ip link show

配置 CAN-FD:

ip link set can0 down
ip link set can0 type can bitrate 500000 dbitrate 2000000 fd on
ip link set can0 up

测试收发:

candump can0
cansend can0 123##1122334455667788

如果 dmesg 中出现 probe 失败,优先检查:

  • SPI 控制器是否启用

  • compatible 是否匹配驱动

  • 中断 GPIO 是否正确

  • 外部晶振频率是否正确

  • 电源 regulator 是否上电

  • SPI mode 和 max frequency 是否符合芯片要求

10. 修改 Device Tree:MIPI 摄像头的特殊性

智能相机是高通边缘平台的重要方向,但 MIPI 摄像头不是简单加一个 I2C 设备就能工作。

一个摄像头模组通常涉及:

  • I2C 控制通道

  • MIPI CSI-2 data lane

  • MCLK

  • reset GPIO

  • powerdown GPIO

  • AVDD / DVDD / IOVDD 电源

  • regulator 上电时序

  • sensor driver

  • camera clock

  • IOMMU / reserved memory

  • ISP / CSID / VFE / ICP pipeline

  • 用户态 camera service 或 GStreamer plugin

简化的 Device Tree 片段可能类似:

&i2c10 {
    status = "okay";

    camera_sensor0: camera@1a {
        compatible = "vendor,imxxxx";
        reg = <0x1a>;

        reset-gpios = <&tlmm 78 GPIO_ACTIVE_LOW>;
        pwdn-gpios = <&tlmm 79 GPIO_ACTIVE_HIGH>;

        avdd-supply = <&vreg_l7a_2p8>;
        dvdd-supply = <&vreg_l3a_1p1>;
        iovdd-supply = <&vreg_l5a_1p8>;

        clocks = <&camcc CAM_CC_MCLK0_CLK>;
        clock-names = "mclk";

        port {
            camera0_ep: endpoint {
                data-lanes = <0 1 2 3>;
                clock-lanes = <4>;
                remote-endpoint = <&csiphy0_ep>;
            };
        };
    };
};

实际 Qualcomm Camera Subsystem 的 dts 结构通常更复杂,不同 BSP 差异也比较大。开发时不要只看 sensor 节点,还要同时检查:

  • camcc

  • csiphy

  • csid

  • vfe

  • ife

  • eeprom

  • actuator

  • ois

  • flash

  • camera overlay

  • 用户态 camera 配置 XML / JSON

工业智能相机项目里,一个常见做法是:

Device Tree 只描述硬件连接,sensor mode、曝光、帧率、ISP tuning、AI pipeline 放到用户态配置中管理。

这样同一个 kernel 可以支持多个相机 SKU,避免每换一个模组就重刷 kernel。

11. 编译 dtb 和 dtbo

修改 .dts 或 .dtsi 后,需要重新编译 Device Tree。

如果走 kernel make:

make O=$OUT ARCH=arm64 dtbs -j$(nproc)

产物通常在:

out/kernel/arch/arm64/boot/dts/qcom/

如果使用 overlay,需要生成 .dtbo,并进一步打包为 dtbo.img。

常见流程:

make O=$OUT ARCH=arm64 dtbs

然后使用 BSP 提供的工具打包:

mkdtimg create dtbo.img --page_size=4096 board-camera.dtbo board-canfd.dtbo

或通过厂商脚本:

./build_dtbo.sh

不同平台对 dtb/dtbo 的加载方式不同:

方式特点
dtb 直接附加到 kernel早期或简单启动链常见
boot.img 内包含 dtbAndroid boot image 常见
独立 dtbo 分区支持不同板型 overlay
FIT imageU-Boot 场景常见
extlinux.conf 指定 dtb通用嵌入式 Linux 常见

高通平台上,尤其是 Android-derived BSP,经常会有独立 dtbo 分区。工业 Linux BSP 则可能根据厂商启动链定制。

12. 固件打包:从 kernel 到 boot.img

编译出 Image.gz 和 dtb 后,还不能直接启动。还需要打包成 bootloader 能识别的格式。

在 Qualcomm 平台上,启动链大致可以简化理解为:

PBL -> XBL -> ABL / U-Boot -> Linux Kernel

其中:

  • PBL 是 SoC ROM 内部启动代码

  • XBL 负责早期硬件初始化

  • ABL 或 U-Boot 负责加载 kernel

  • kernel 根据 dtb 初始化硬件并挂载 rootfs

如果平台使用 Android boot image 格式,常见打包产物包括:

boot.img
vendor_boot.img
dtbo.img
rootfs.img

boot.img 通常包含:

kernel
ramdisk
boot header
cmdline
可选 dtb

Android 11 之后的部分启动格式中,vendor ramdisk 和部分硬件相关内容可能被放入:

vendor_boot.img

一个简化打包命令可能类似:

mkbootimg \
  --kernel Image.gz \
  --ramdisk initramfs.cpio.gz \
  --cmdline "console=ttyMSM0,115200n8 root=/dev/sda2 rw" \
  --base 0x00000000 \
  --pagesize 4096 \
  --output boot.img

实际项目中不要照抄这个参数。base、pagesize、header_version、cmdline、dtb、vendor_boot 的设置必须以 BSP 脚本为准。

更安全的方式是复用原 BSP 的打包脚本,例如:

./pack_bootimg.sh \
  --kernel out/kernel/arch/arm64/boot/Image.gz \
  --dtb out/kernel/arch/arm64/boot/dts/qcom/board.dtb \
  --ramdisk out/rootfs/initramfs.cpio.gz \
  --output out/images/boot.img

如果 BSP 使用 U-Boot 和 FIT image,流程可能是:

mkimage -f kernel.its fitImage

kernel.its 中会描述:

kernel image
fdt
ramdisk
load address
entry address
hash
signature

如果使用 extlinux,启动分区可能有:

/boot/extlinux/extlinux.conf
/boot/Image.gz
/boot/qcom/board.dtb

extlinux.conf 示例:

LABEL primary
    MENU LABEL primary kernel
    LINUX /Image.gz
    FDT /qcom/board.dtb
    APPEND console=ttyMSM0,115200n8 root=/dev/mmcblk0p12 rw rootwait

13. 内核模块安装与 rootfs 集成

编译 kernel modules 后,需要安装到 rootfs 中:

make O=$OUT ARCH=arm64 modules_install INSTALL_MOD_PATH=$ROOTFS

安装后会生成类似目录:

/lib/modules/5.15.XX-qcom/
├── kernel/
├── modules.dep
├── modules.alias
├── modules.symbols
└── ...

如果 rootfs 是单独镜像,需要重新打包:

sudo mount rootfs.img mnt
sudo cp -a lib/modules/5.15.XX-qcom mnt/lib/modules/
sudo umount mnt

或者在 Yocto 中通过 recipe 自动集成。

需要特别注意:kernel image 和 /lib/modules/$(uname -r) 必须匹配。

如果设备启动后出现:

module: version magic '...' should be '...'

说明 .ko 和当前运行 kernel 不是同一次构建产物。

可以在设备上检查:

uname -a
ls /lib/modules/
modinfo your_driver.ko

14. 刷写与验证

如果平台支持 fastboot,常见刷写方式是:

fastboot flash boot boot.img
fastboot flash dtbo dtbo.img
fastboot reboot

如果是 Yocto / 工业 BSP,可能使用厂商刷机工具:

./flash.sh

或者通过 EDL / QDL 模式刷写:

rawprogram.xml
patch.xml
prog_firehose_ddr.elf

刷写后,首先确认内核版本:

uname -a
cat /proc/version

确认 cmdline:

cat /proc/cmdline

确认 Device Tree 是否生效:

ls /proc/device-tree/
cat /proc/device-tree/model

检查某个节点:

ls /proc/device-tree/soc/

检查驱动 probe:

dmesg | grep -i spi
dmesg | grep -i i2c
dmesg | grep -i can
dmesg | grep -i camera

检查模块:

lsmod
modprobe your_driver

检查中断:

cat /proc/interrupts

检查 regulator:

ls /sys/class/regulator/

检查 clock,部分 BSP 需要 debugfs:

mount -t debugfs none /sys/kernel/debug
cat /sys/kernel/debug/clk/clk_summary

15. 工业场景下的裁剪建议

针对工业机器人、智能相机、ROS2 边缘计算这类场景,可以按功能域裁剪。

15.1 工业机器人控制器

建议保留或启用:

CONFIG_PREEMPT
CONFIG_HIGH_RES_TIMERS
CONFIG_CAN
CONFIG_CAN_RAW
CONFIG_NET_SCHED
CONFIG_CGROUPS
CONFIG_CPUSETS
CONFIG_WATCHDOG
CONFIG_HW_RANDOM
CONFIG_PSTORE
CONFIG_PSTORE_RAM

重点关注:

  • 调度延迟

  • CAN / EtherCAT / TSN 通信

  • watchdog 恢复

  • 电源异常后的日志保留

  • CPU isolation

  • ROS2 节点资源隔离

15.2 智能相机

建议保留或启用:

CONFIG_MEDIA_SUPPORT
CONFIG_VIDEO_V4L2
CONFIG_IOMMU_SUPPORT
CONFIG_DMA_SHARED_BUFFER
CONFIG_DMABUF_HEAPS
CONFIG_CMA
CONFIG_REMOTEPROC
CONFIG_RPMSG

重点关注:

  • MIPI CSI 稳定性

  • DMA buffer 零拷贝

  • ISP / NPU pipeline

  • 长时间视频流内存泄漏

  • thermal throttling

  • 摄像头异常断流恢复

15.3 ROS2 边缘节点

建议保留或启用:

CONFIG_NAMESPACES
CONFIG_CGROUPS
CONFIG_CGROUP_CPUACCT
CONFIG_CGROUP_DEVICE
CONFIG_MEMCG
CONFIG_OVERLAY_FS
CONFIG_BRIDGE
CONFIG_VETH
CONFIG_NETFILTER

如果后续要跑 Docker 或 containerd,这些配置非常关键。

ROS2 对网络和时间同步也比较敏感,建议关注:

CONFIG_PTP_1588_CLOCK
CONFIG_NETWORK_PHY_TIMESTAMPING
CONFIG_POSIX_TIMERS
CONFIG_HIGH_RES_TIMERS

16. 常见问题排查

16.1 修改 dts 后没有生效

优先检查:

cat /proc/device-tree/model
cat /proc/cmdline

可能原因:

  • 刷错分区

  • bootloader 加载的是另一个 dtb

  • dtbo.img 没更新

  • overlay 优先级覆盖了你的修改

  • 编译的是 A 板 dts,实际启动的是 B 板 dts

  • secure boot / verified boot 阻止了镜像加载

16.2 驱动没有 probe

检查顺序:

dmesg | grep -i your_driver
ls /sys/bus/platform/devices/
ls /sys/bus/i2c/devices/
ls /sys/bus/spi/devices/

常见原因:

  • compatible 字符串和驱动 of_match_table 不一致

  • status 仍然是 disabled

  • 父节点 I2C/SPI 控制器未启用

  • regulator 获取失败

  • clock 获取失败

  • reset GPIO 配错

  • 中断号错误

  • pinctrl 状态错误

16.3 kernel 可以启动,但模块加载失败

检查:

uname -r
ls /lib/modules/$(uname -r)
modinfo driver.ko
dmesg | tail

常见原因:

  • modules 和 kernel 不是同一版本

  • CONFIG_MODVERSIONS 不一致

  • 依赖模块未安装

  • rootfs 中 modules.dep 未更新

  • 使用了错误 toolchain 编译外部模块

16.4 系统启动卡在 rootfs 挂载

检查 bootargs:

cat /proc/cmdline

重点关注:

root=
rootwait
rootfstype=
init=
console=

常见原因:

  • UFS/eMMC 驱动未 built-in

  • ext4 驱动被编成 module

  • root 分区号变化

  • initramfs 缺少必要驱动

  • 分区表和 bootargs 不一致

17. 推荐的版本管理方式

工业项目中,kernel 和 BSP 的版本管理非常关键。建议建立这样的目录或仓库策略:

product-bsp/
├── kernel/
├── device-tree/
├── config/
│   ├── base_defconfig
│   ├── robot.cfg
│   ├── camera.cfg
│   └── safety.cfg
├── patches/
│   ├── 0001-enable-canfd.patch
│   ├── 0002-add-camera-sensor.patch
│   └── 0003-tune-cpu-freq.patch
├── scripts/
│   ├── build_kernel.sh
│   ├── pack_boot.sh
│   └── flash_kernel.sh
└── release/
    └── 2026.07/
        ├── boot.img
        ├── dtbo.img
        ├── modules.tar.gz
        ├── vmlinux
        ├── System.map
        └── manifest.txt

manifest.txt 建议记录:

Product: IQ9100 Vision System
BSP Version: xxx
Kernel Commit: xxxxxxxx
DTS Commit: xxxxxxxx
Toolchain: gcc/clang version
Build Time: 2026-07-09
Defconfig: vendor_xxx_defconfig + industrial_camera.cfg
Artifacts:
  - boot.img
  - dtbo.img
  - modules.tar.gz
  - rootfs.img

这样做的意义是:现场任何一台设备出现问题,都能追溯到明确的 kernel、dtb、modules 和 rootfs 组合。

18. 一个推荐的开发闭环

实际项目中,可以按下面这个闭环推进:

1. 获取官方 BSP
2. 原样编译并刷写
3. 确认开发板能正常启动
4. 固化原始构建脚本
5. 修改 kernel config
6. 修改 Device Tree
7. 编译 Image.gz / dtb / dtbo / modules
8. 打包 boot.img / dtbo.img
9. 刷写验证
10. dmesg / sysfs / debugfs 检查
11. 压测外设和功耗
12. 记录版本和产物

19. 小结

高通嵌入式 Linux 开发的难点,不在于单独执行一次 make Image.gz,而在于理解 kernel、Device Tree、firmware、boot image、rootfs、bootloader 之间的关系。

本篇我们完成了几个关键点:

  • 高通 BSP 内核通常比 mainline 更适合量产开发

  • kernel 编译要优先复用 BSP 脚本和 toolchain

  • Device Tree 是板级硬件适配的核心

  • SPI、CAN-FD、MIPI 摄像头的 dts 修改重点不同

  • dtb、dtbo、boot.img、modules 必须成套发布

  • 工业项目裁剪内核时,要在体积、稳定性和可调试性之间平衡

  • 每次发布都要保留 .config、vmlinux、System.map 和 manifest

对工业机器人、智能相机、ROS2 边缘计算来说,内核裁剪不是“删功能”,而是把系统变成一个可控制、可验证、可长期维护的平台。

下一篇我们将进入更具体的外设驱动开发:I2C、SPI、CAN-FD 以及 MIPI 摄像头驱动如何在高通平台上落地,并进一步讨论 Hexagon 侧低功耗外设方案。

更多推荐