第四章 Linux内核移植基础:内核源码获取、Kconfig与设备树、内核配置裁剪
各位同学,欢迎来到内核移植实战的第一站。说实话,很多做ADAS的工程师一听到「移植内核」就觉得头大,觉得那是BSP团队的事。但我在做域控制器项目时发现,不懂内核裁剪,你连Flash都塞不下;不懂设备树,外设死活点不亮。今天我们就来把这层窗户纸捅破。
4.1 内核源码获取:从哪来,怎么选
先说说源码的事。我习惯直接从kernel.org拉主线代码,但做ADAS产品可不能这么干。为什么?因为车规级芯片的BSP,通常由芯片厂商提供定制分支。比如NXP的S32G系列,或者TI的TDA4系列,它们都有对应的官方内核仓库。
我的建议: 优先使用芯片厂商提供的LTS(长期支持)内核。比如NXP的Linux BSP,通常基于5.10或5.15 LTS。别追新,稳定第一。
获取方式无非三种:
- git clone:最常用,适合持续跟踪更新
- tar包下载:适合固定版本,比如量产锁定
- 厂商SDK:比如TI的Processor SDK,里面直接打包好了
嗯,这里要注意:拿到源码后第一件事,不是急着编译,而是先看README和Documentation/process/changes.rst。我吃过亏——有一次直接make,结果报了一堆工具链版本错误,折腾半天才发现是gcc太新了。
4.2 Kconfig:内核的「菜单系统」
Kconfig是什么?说白了就是内核的配置系统。你运行make menuconfig时看到的那个图形界面,背后就是Kconfig在驱动。
每个驱动目录下都有一个Kconfig文件,里面定义了配置项。比如:
config SENSOR_OV10640
tristate "OmniVision OV10640 camera sensor support"
depends on I2C
help
This driver supports OV10640 camera sensor used in ADAS systems.
这里tristate表示三种状态:编译进内核(y)、编译成模块(m)、不编译(n)。我在项目中遇到过,很多人把depends on写错,导致配置项死活不显示。你想想看,依赖链断了,菜单项就消失了,多坑。
小技巧: 用make menuconfig时,按/键可以搜索配置项。比如搜OV10640,直接定位到位置,省得一层层翻菜单。
4.3 设备树:硬件描述的「语言」
设备树(Device Tree)是ARM架构下替代ACPI的东西。它用文本文件描述硬件,内核启动时解析它来知道「我有哪些外设、它们怎么连接」。
一个典型的设备树节点长这样:
i2c@48000000 {
compatible = "ti,omap4-i2c";
reg = <0x48000000 0x100>;
interrupts = <56>;
clock-frequency = <400000>;
camera_sensor: sensor@30 {
compatible = "ovti,ov10640";
reg = <0x30>;
reset-gpios = <&gpio3 15 GPIO_ACTIVE_LOW>;
};
};
这里compatible是匹配驱动的关键。内核驱动通过of_match_table来匹配这个字符串。我曾经调试过一个摄像头驱动,死活不加载,最后发现是compatible字符串写错了一个字母——ovti写成了ovtI。这种坑,你查一天都不一定找得到。
注意: 设备树编译用dtc工具,生成.dtb文件。千万别直接修改.dtb,那是二进制文件。改.dts源文件,然后重新编译。
4.4 内核配置裁剪:给ADAS域控制器「瘦身」
ADAS域控制器的Flash通常不大,比如256MB或512MB。而标准内核动辄几十MB,不裁剪根本放不下。裁剪的核心思路就一句话:用不到的,统统关掉。
我一般按这个步骤来:
- 先做最小配置:只保留CPU架构、内存管理、中断控制器、定时器等基础项
- 加外设驱动:根据硬件原理图,逐个使能I2C、SPI、CAN、以太网、摄像头接口等
- 关掉调试选项:比如
CONFIG_DEBUG_KERNEL、CONFIG_PRINTK(量产时关掉) - 文件系统支持:只保留你用的文件系统,比如ext4或squashfs
举个例子,一个典型的ADAS域控制器配置裁剪前后对比:
| 配置项 | 裁剪前 | 裁剪后 |
|---|---|---|
| 内核镜像大小 | 28MB | 8.5MB |
| 驱动数量 | 1200+ | 180+ |
| 启动时间 | 8秒 | 2.3秒 |
你看,裁剪的效果非常明显。但要注意,别裁过头了。我曾经把CONFIG_HIGHMEM关掉,结果系统只能识别不到1GB内存,而我们的域控制器有4GB——嗯,那次排查花了我整整一个下午。
核心原则: 裁剪不是越少越好,而是「刚好够用」。保留必要的调试接口,比如串口打印,否则出了问题你连日志都看不到。
4.5 实战:从零开始配置一个ADAS内核
好了,理论讲完了,我们动手走一遍。假设你拿到了一个基于TI TDA4VM的域控制器板子:
# 1. 设置环境变量
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
# 2. 加载厂商默认配置
make defconfig
# 或者用厂商提供的配置
make tda4vm_evm_defconfig
# 3. 进入菜单配置
make menuconfig
# 4. 裁剪步骤(举例)
# 关闭蓝牙、WiFi、USB HID等不用的驱动
# 使能CAN、I2C、CSI摄像头、以太网AVB等
# 5. 编译内核
make -j8 Image dtbs
# 6. 查看生成的文件
ls arch/arm64/boot/Image
ls arch/arm64/boot/dts/ti/k3-tda4vm.dtb
这里有个坑:make defconfig生成的是通用配置,很多外设没开。我建议先找厂商要一份defconfig文件,在此基础上改。比如TI的SDK里就有现成的tda4vm_evm_defconfig,直接拿来用,省事不少。
保存配置: 改完后用make savedefconfig,会生成一个精简的defconfig文件。下次移植到新板子时,直接加载这个文件,再微调即可。
4.6 避坑指南:我踩过的那些雷
最后,分享几个我亲身经历过的坑,希望能帮你少走弯路:
- 设备树地址冲突:有一次两个外设用了相同的内存地址,内核启动时直接panic。排查方法:看
/proc/iomem,检查地址范围是否重叠。 - 中断号不对:设备树里写的中断号,和芯片手册对不上。我建议对照手册逐个核对,别偷懒。
- 时钟频率设置错误:I2C时钟设太快,传感器不响应。设太慢,带宽不够。这个只能试,没有捷径。
- 裁剪后功能异常:比如关了
CONFIG_PREEMPT,导致实时性变差。ADAS对延迟敏感,这个选项一般要保留。
嗯,今天就先讲到这里。下一章我们会深入设备树的具体编写,包括GPIO、中断、时钟的绑定规则,以及如何调试设备树。到时候我会拿一个真实的摄像头驱动来拆解,保证干货满满。