4. U-Boot配置系统:menuconfig配置、defconfig文件、设备树(Device Tree)与配置的关系

U-Boot的配置系统,说实话,刚接触的时候容易让人一头雾水。menuconfig、defconfig、设备树,这三样东西到底怎么配合工作的?我当年第一次移植U-Boot时,就被这“三驾马车”绕晕过。今天咱们就把它们之间的关系彻底捋清楚。

4.1 配置系统的“三层架构”

我个人习惯把U-Boot的配置系统理解成一个三层结构:

  • 顶层:menuconfig(图形化配置界面)
  • 中间层:defconfig(默认配置文件)
  • 底层:Kconfig(配置规则定义) + 设备树(硬件描述)

这三层各司其职,又互相依赖。你想想看,menuconfig就像是一个超市,你可以自由挑选需要的功能;defconfig则是你的购物清单,告诉你哪些是默认要买的;而设备树,嗯,它更像是商品的说明书,告诉你这个硬件长什么样、怎么用。

核心观点:配置系统解决的是“软件要支持哪些功能”的问题,而设备树解决的是“硬件长什么样”的问题。两者通过配置选项(CONFIG_*)和编译脚本串联起来。

4.2 menuconfig:交互式配置工具

运行 make menuconfig 时,U-Boot会读取当前架构下的Kconfig文件,生成一个图形化的配置菜单。这里我提个醒:

我的经验:在第一次移植时,别急着用menuconfig改配置。先看看同系列开发板的defconfig文件,理解别人是怎么配的,再动手改。我曾经一上来就乱改,结果编译出来的U-Boot连串口都不打印信息,排查了半天才发现是调试串口配置错了。

menuconfig的操作很简单:

  • 按 Y 选中功能(编译进内核)
  • 按 N 排除功能
  • 按 M 编译为模块(U-Boot中较少用)
  • 按 ? 查看帮助信息

配置完成后,U-Boot会生成一个 .config 文件。这个文件就是当前配置的“快照”。

4.3 defconfig:你的配置“基准线”

defconfig文件,说白了就是一份“推荐配置”。它存放在 configs/ 目录下,命名规则通常是 <板子名称>_defconfig。

举个例子,我常用的 stm32mp157d_atk_defconfig 文件内容大致如下:

CONFIG_ARM=y
CONFIG_ARCH_STM32MP=y
CONFIG_SYS_TEXT_BASE=0xC0000000
CONFIG_SYS_MALLOC_F_LEN=0x2000
CONFIG_TARGET_STM32MP157D_ATK=y
CONFIG_ENV_SIZE=0x2000
CONFIG_DM=y
CONFIG_DM_GPIO=y
CONFIG_DM_SERIAL=y
CONFIG_DM_MMC=y
CONFIG_MMC=y
CONFIG_CMD_MMC=y
CONFIG_CMD_SF=y
CONFIG_CMD_SPI=y
CONFIG_SPI_FLASH=y
CONFIG_SPI_FLASH_MACRONIX=y

看到没?defconfig里只写“差异项”。那些默认值(比如大部分驱动默认关闭)不需要写进去。这样做的好处是:

  • 文件短小精悍,容易维护
  • 升级U-Boot版本时,只需要关注新增的配置项
  • 多人协作时,减少冲突

避坑指南:我曾经犯过一个错误——把整个.config文件直接复制成defconfig。结果里面包含了大量自动生成的配置项,下次编译时很多选项都变了,导致板子起不来。正确的做法是:用 make savedefconfig 命令,它会自动生成一个精简版的defconfig。

4.4 设备树(Device Tree):硬件的“身份证”

设备树,我更喜欢叫它“硬件的身份证”。它用文本文件(.dts)描述了一个板子上所有硬件资源:CPU、内存地址、外设寄存器、中断号、时钟频率……

U-Boot中的设备树文件通常放在 arch/arm/dts/ 目录下。一个典型的设备树节点长这样:

&uart4 {
    pinctrl-names = "default";
    pinctrl-0 = <&uart4_pins_a>;
    status = "okay";
};

这段代码的意思是:使能UART4外设,使用默认的引脚配置。嗯,就这么简单。

4.5 配置与设备树的关系

这里有个关键问题:配置系统和设备树是怎么配合的?

我画了一张图来说明它们的关系:

U-Boot配置系统与设备树关系图 defconfig 默认配置清单 configs/xxx_defconfig menuconfig 图形化配置界面 生成 .config 文件 设备树 (DT) 硬件描述文件 arch/arm/dts/xxx.dts 加载 生成 .config 编译过程 make 命令 编译为 dtb u-boot.bin + xxx.dtb 说明:defconfig提供默认配置,menuconfig允许定制,设备树描述硬件 三者最终在编译阶段合并,生成最终的U-Boot镜像

从这张图可以看出:

  • defconfig是起点,它告诉menuconfig“默认选什么”
  • menuconfig允许你在此基础上做调整
  • 设备树独立于配置系统,但在编译时被编译成dtb文件,最终和U-Boot镜像打包在一起

4.6 配置选项如何影响设备树

这里有个细节很多人会忽略:配置选项可以控制设备树中的节点是否生效。

比如,在Kconfig中定义:

config MY_BOARD_USB_SUPPORT
    bool "Enable USB support on my board"
    help
      Say Y here to enable USB support.

然后在设备树中,可以用条件编译:

#ifdef CONFIG_MY_BOARD_USB_SUPPORT
&usb1 {
    status = "okay";
};
#endif

这样,只有当你通过menuconfig选中了USB支持,设备树中的USB节点才会被使能。这种做法在需要支持多种硬件变体时特别有用。

我的建议:尽量少用这种条件编译。设备树应该保持“纯净”,只描述硬件本身。功能的开关应该通过U-Boot的驱动模型(Driver Model)在运行时控制。条件编译多了,设备树会变得难以维护。

4.7 实际操作流程

最后,我总结一下在实际移植中,这三者的操作顺序:

  1. 第一步:找一个相近的defconfig作为模板,复制一份
  2. 第二步:修改defconfig,添加你的板子特有的配置项(比如内存大小、外设驱动)
  3. 第三步:创建设备树文件,描述板子的硬件资源
  4. 第四步:运行 make xxx_defconfig 加载配置
  5. 第五步:运行 make menuconfig 微调配置
  6. 第六步:运行 make 编译,生成最终的U-Boot镜像

嗯,这套流程我用了很多年,基本没出过问题。你刚开始移植时,可以严格按照这个步骤来。等熟练了,就可以根据实际情况灵活调整了。

总结一下:配置系统(defconfig + menuconfig)决定了U-Boot“能做什么”,设备树决定了U-Boot“面对什么硬件”。两者通过CONFIG_*宏和编译脚本紧密配合。理解了这个关系,U-Boot移植就成功了一半。

可可精品资料库