4. 板级配置实战:创建板级目录与头文件、配置CONFIG_xxx宏、使用menuconfig进行图形化配置、生成.config与autoconf.mk。
好,咱们进入真正的板级配置环节。前面几章我们把设备树、启动流程都理了一遍,现在该动手了。这一章,我会带你走一遍完整的板级配置流程——从创建目录、写头文件,到敲 menuconfig 生成配置,每一步我都会把坑点讲清楚。
本章核心目标:在 Bootloader 源码中为一块新板子建立独立的板级目录,配置必要的宏,并通过图形化界面生成最终的 .config 和 autoconf.mk。
4.1 创建板级目录与头文件
拿到一块新板子,第一件事不是改代码,而是建目录。我个人习惯在 board/ 下按厂商名+板子型号来命名,比如 board/mycompany/myboard。这样后续维护时一眼就能找到。
目录结构大概长这样:
board/mycompany/myboard/
├── Kconfig # 板级配置菜单
├── Makefile # 编译规则
├── myboard.c # 板级初始化代码
├── myboard.h # 板级头文件
├── lowlevel_init.S # 低层初始化汇编
└── configs/ # 默认配置文件
└── myboard_defconfig
这里重点说一下头文件。很多新手会把所有宏定义都塞进一个头文件,结果后期改一个引脚就要翻半天。我建议按功能拆分:
- myboard.h:板级专属的宏,比如 DDR 参数、时钟配置。
- myboard_pinmux.h:引脚复用配置,单独放,方便调试。
- myboard_power.h:电源管理相关的宏。
举个例子,我在 myboard.h 里通常会这样写:
#ifndef __MYBOARD_H
#define __MYBOARD_H
/* DDR 配置参数 */
#define CONFIG_SYS_SDRAM_BASE 0x80000000
#define CONFIG_SYS_SDRAM_SIZE (512 * 1024 * 1024) /* 512MB */
/* 串口调试配置 */
#define CONFIG_CONS_INDEX 1
#define CONFIG_BAUDRATE 115200
/* 环境变量存储位置 */
#define CONFIG_ENV_OFFSET 0x100000
#define CONFIG_ENV_SIZE 0x20000
#endif /* __MYBOARD_H */
小技巧:头文件里的宏尽量用 #define 而不是 enum,因为很多 Bootloader 的预处理阶段只认 #define。我曾经踩过这个坑,用 enum 定义地址,结果编译时死活找不到符号。
4.2 配置 CONFIG_xxx 宏
CONFIG_xxx 宏是 Bootloader 的灵魂。每个宏控制一个功能模块的开关。比如 CONFIG_CMD_NET 控制网络命令是否编译,CONFIG_DM_GPIO 控制 GPIO 驱动模型。
配置宏有三种方式:
| 方式 | 说明 | 适用场景 |
|---|---|---|
| 头文件直接定义 | 在 myboard.h 中用 #define 写死 |
板级固定参数,如 DDR 地址 |
| Kconfig 配置 | 通过 menuconfig 图形化选择 |
功能开关,如是否支持 USB |
| defconfig 默认配置 | 在 myboard_defconfig 中预设 |
批量部署时统一配置 |
我个人更推荐后两种方式。为什么?因为头文件写死的话,后期想改一个功能开关就得重新编译整个工程。用 Kconfig 的话,改配置只需要 make menuconfig 然后保存就行。
注意:有些宏是互斥的,比如 CONFIG_DM_GPIO 和 CONFIG_GPIO_LEGACY 不能同时开启。如果你在 menuconfig 里看到某个选项变灰了,说明它依赖的另一个选项没开。这时候按 ? 键可以查看依赖关系。
4.3 使用 menuconfig 进行图形化配置
配置宏最直观的方式就是 make menuconfig。这个命令会读取当前目录下的 Kconfig 文件,生成一个基于 ncurses 的菜单界面。
操作步骤很简单:
- 在源码根目录执行
make menuconfig。 - 用方向键导航,空格键选中/取消。
- 按
Y编译进内核,按N排除,按M编译为模块(Bootloader 里一般没有模块概念)。 - 配置完成后选择
Save,默认保存为.config。
这里有个细节:menuconfig 的菜单项来自 Kconfig 文件。如果你新建了板级目录,一定要在 board/mycompany/myboard/Kconfig 里添加菜单项,否则你的配置选项不会出现在菜单里。
举个例子,一个简单的 Kconfig 文件:
menu "MyBoard Configuration"
config MYBOARD_DDR_SPEED
int "DDR clock speed (MHz)"
default 800
help
Set the DDR clock speed in MHz.
config MYBOARD_USE_FAST_BOOT
bool "Enable fast boot"
default y
help
Skip some initialization to speed up boot.
endmenu
这样在 menuconfig 里就会出现 "MyBoard Configuration" 子菜单,里面有两个选项:DDR 频率和快速启动开关。
避坑指南:我曾经在配置 CONFIG_BOOTDELAY 时,不小心设成了 -1,结果板子启动时直接跳过倒计时,连按空格都进不了命令行。后来查了半天才发现是负数导致的。所以,数值类的宏最好设一个合理的默认值。
4.4 生成 .config 与 autoconf.mk
配置完成后,menuconfig 会生成两个关键文件:
- .config:文本格式的配置项列表,每行一个
CONFIG_xxx=y或CONFIG_xxx=值。 - autoconf.mk:Makefile 可包含的配置片段,供编译系统使用。
这两个文件的关系是这样的:
.config → scripts/basic/fixdep → include/config/auto.conf
→ scripts/kconfig/conf → include/autoconf.mk
说白了,.config 是给开发者看的,autoconf.mk 是给 Makefile 吃的。你平时改配置只需要改 .config,然后执行 make 就会自动更新 autoconf.mk。
看一下 .config 的内容长什么样:
#
# Automatically generated file; DO NOT EDIT.
# Bootloader Configuration
#
CONFIG_MYBOARD_DDR_SPEED=800
CONFIG_MYBOARD_USE_FAST_BOOT=y
CONFIG_CMD_NET=y
CONFIG_DM_GPIO=y
# CONFIG_GPIO_LEGACY is not set
注意看最后一行:# CONFIG_GPIO_LEGACY is not set 表示这个宏被显式关闭了。如果你在 .config 里直接删掉这一行,下次 make 时它会被自动补回来。所以,想关闭一个宏,必须写成 # CONFIG_xxx is not set 的格式。
重要提醒:永远不要手动编辑 autoconf.mk!这个文件是自动生成的,你改了也会被覆盖。要改配置,要么用 menuconfig,要么直接改 .config 然后重新 make。
4.5 整体流程梳理
说了这么多,咱们把整个流程串起来。下面这张图展示了从创建目录到生成配置文件的完整链路:
嗯,流程其实不复杂。但每个环节都有容易踩坑的地方。我总结一下:
- 目录创建:注意路径不要有空格或特殊字符,否则
make会报奇怪的错误。 - 头文件:宏定义不要重复,用
#ifndef保护。 - Kconfig:依赖关系要写清楚,否则用户配置时会一脸懵。
- menuconfig:保存后检查
.config是否生成了你想要的配置。 - autoconf.mk:不要手动改,不要手动改,不要手动改。
实战建议:第一次配置时,先用 make defconfig 生成一个默认配置,然后 make menuconfig 微调。这样比从头开始点菜单快得多。我一般会在 configs/ 目录下放一个 myboard_defconfig,里面只写跟默认配置不同的项,方便团队其他人快速上手。