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 的菜单界面。

操作步骤很简单:

  1. 在源码根目录执行 make menuconfig。
  2. 用方向键导航,空格键选中/取消。
  3. 按 Y 编译进内核,按 N 排除,按 M 编译为模块(Bootloader 里一般没有模块概念)。
  4. 配置完成后选择 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 整体流程梳理

说了这么多,咱们把整个流程串起来。下面这张图展示了从创建目录到生成配置文件的完整链路:

板级配置完整流程 板级配置完整流程 创建板级目录 编写板级头文件 编写 Kconfig 菜单 make menuconfig 生成 .config 生成 autoconf.mk 关键点: • 目录命名规范 • 头文件按功能拆分 • Kconfig 必须写 输出产物: • .config(文本配置) • autoconf.mk(Make配置) • auto.conf(C头文件)

嗯,流程其实不复杂。但每个环节都有容易踩坑的地方。我总结一下:

  • 目录创建:注意路径不要有空格或特殊字符,否则 make 会报奇怪的错误。
  • 头文件:宏定义不要重复,用 #ifndef 保护。
  • Kconfig:依赖关系要写清楚,否则用户配置时会一脸懵。
  • menuconfig:保存后检查 .config 是否生成了你想要的配置。
  • autoconf.mk:不要手动改,不要手动改,不要手动改。

实战建议:第一次配置时,先用 make defconfig 生成一个默认配置,然后 make menuconfig 微调。这样比从头开始点菜单快得多。我一般会在 configs/ 目录下放一个 myboard_defconfig,里面只写跟默认配置不同的项,方便团队其他人快速上手。