Skip to content

ESP32项目配置

🚀 ESP32 项目配置 | 一步步掌握 Menuconfig 和 Kconfig 文件

首先我们先借助一个具体的例子来解释Menuconfig命令的使用和Kconfig文件的作用。

一、Menuconfig的使用

打开示例工程:esp-idf/examples/get-started/blink

这个历程是用来实现LED闪烁的程序,这个工程使用了一个叫led_strip的组件,所以代码稍显复杂,但这并不重要,我们的重点不再这里,只要知道这个项目可以点亮开发板上的一颗LED灯即可。

编译代码,烧录,发现并没有LED灯亮起(如果使用本教程推荐的开发板的话)。哈哈,这是由于我们并没有配置LED的引脚,示例程序里指定的LED引脚编号与我们实际开发板的引脚不一致。

那么该如何修改代码里对应控制LED的GPIO引脚号呢,不妨回到代码看看:

可以看到代码里LED对应的GPIO引脚的编号是由BLINK_GPIO这个宏充当的,而BLINK_GPIO这个宏又指向CONFIG_BLINK_GPIO这个宏,我们当然可以直接把CONFIG_BLINK_GPIO替换为我们实际的引脚号,来达到我们要点亮LED灯的目的,但这不是本节教程的主要矛盾。本节的关键是搞明白CONFIG_BLINK_GPIO这个宏是哪来的,为什么要用它,怎末用它,怎末修改它的值来点亮LED。

我们右键跳转到CONFIG_BLINK_GPIO宏定义的位置,可以发现:

CONFIG_BLINK_GPIO宏定义在 sdkconfig.h 文件里,而 sdkconfig.h 文件又位于build目录下,那我们可以直接修改这里的宏定义值吗,答案是不可以,build目录是构建编译项目产生的,每次编译项目里面的文件就会重新生成,修改了也会被覆盖掉。那 sdkconfig.h里的宏定义又是从何而来的呢?官方文档是这样描述的:

文档-->项目配置-->项目配置菜单 借助 sdkconfig,应用程序构建目标将在构建目录中生成 sdkconfig.h 文件,并使得 sdkconfig 选项可用于项目构建系统和源文件。

所以sdkconfig.h里的宏定义源自于sdkconfig文件,在sdkconfig文件中搜索CONFIG_BLINK_GPIO宏定义:

的确可以找到,那么可以通过修改这里的值来修改程序里的引脚定义吗,的确可以,但是不推荐,ESP-IDF框架为我们提供了更加优雅的配置方式,那就是Menuconfig配置菜单(终于引入正题):

下面使用idf.py menuconfig 命令打开 Menuconfig (配置菜单):

运行命令后,我们得到了一个位于终端的可视化配置菜单。下面显示了很多快捷键,也都有对应的文字说明,我这里就不详细解释了,看不明白文字就按着快捷键试试也就明白了。

这里我们选择 Example Configuration 选项:

会看到两个选项,修改Blink GPIO为自己开发板上的引脚,我这里是2号。

重新编译工程,烧录代码。

成功点亮LED(😊),相信通过这一番操作,我们对menuconfig有了基本的认识,说人话就是这玩意可以方便的帮助我们修改程序里的参数,在不修改代码的情况下。


补充:如果使用esp-idf插件打开menuconfig,只需要点击对应的快捷键(小齿轮)

也可以加载出menuconfig菜单,如下:

只是形式与命令行略有不同,但配置项内容都是一样的,特在此补充说明。

二、组件配置文件

那么我们刚才在menuconfig菜单里看到的关于LED配置的内容是那来的呢,通过官方文档,我们可以知道它来源于main组件下的 Kconfig.projbuild 文件。

查看main组件文件夹,我们可以发现这个工程里的 Kconfig.projbuild 文件:

文件内容如下:

menu "Example Configuration"

    orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"

    choice BLINK_LED
        prompt "Blink LED type"
        default BLINK_LED_GPIO
        help
            Select the LED type. A normal level controlled LED or an addressable LED strip.
            The default selection is based on the Espressif DevKit boards.
            You can change the default selection according to your board.

        config BLINK_LED_GPIO
            bool "GPIO"
        config BLINK_LED_STRIP
            bool "LED strip"
    endchoice

    choice BLINK_LED_STRIP_BACKEND
        depends on BLINK_LED_STRIP
        prompt "LED strip backend peripheral"
        default BLINK_LED_STRIP_BACKEND_RMT if SOC_RMT_SUPPORTED
        default BLINK_LED_STRIP_BACKEND_SPI
        help
            Select the backend peripheral to drive the LED strip.

        config BLINK_LED_STRIP_BACKEND_RMT
            depends on SOC_RMT_SUPPORTED
            bool "RMT"
        config BLINK_LED_STRIP_BACKEND_SPI
            bool "SPI"
    endchoice

    config BLINK_GPIO
        int "Blink GPIO number"
        range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
        default 8
        help
            GPIO number (IOxx) to blink on and off the LED.
            Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.

    config BLINK_PERIOD
        int "Blink period in ms"
        range 10 3600000
        default 1000
        help
            Define the blinking period in milliseconds.

endmenu

不难发现,下面这部分就和我们刚才见到的配置项有关:

    config BLINK_GPIO
        int "Blink GPIO number"
        range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
        default 5 if IDF_TARGET_ESP32
        default 18 if IDF_TARGET_ESP32S2
        default 48 if IDF_TARGET_ESP32S3
        default 8
        help
            GPIO number (IOxx) to blink on and off or the RMT signal for the addressable LED.
            Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.

    config BLINK_PERIOD
        int "Blink period in ms"
        range 10 3600000
        default 1000
        help
            Define the blinking period in milliseconds.

即使不了解 Kconfig 的具体语法,我们也能猜出这几行代码干了啥: 定义了两个配置项,BLINK_GPIOBLINK_PERIOD , 然后添加默认值,添加说明等,具体每行都是什么意思可以请教强大的AI,AI可以解释的很清楚,我这里就不作解释了。

通过官方文档我们可以知道:

文档-->项目配置 ESP-IDF 使用基于 kconfiglib 的 esp-idf-kconfig 包,而 kconfiglib 是 Kconfig 系统的 Python 扩展。Kconfig 提供了编译时的项目配置机制,以及多种类型的配置选项(如整数、字符串和布尔值等)。Kconfig 文件指定了选项之间的依赖关系、默认值、组合方式等。

这里我们只需要知道,这个 Kconfig.projbuild 文件就是用于灵活定义项目配置的文件,从官方文档里我们可以得知:这种配置文件会有两种格式。分别是KconfigKconfig.projbuild 文件,他们的区别简单来说就是作用域不同,一个用于定于项目全局配置,一个用于定义组件配置,具体的区别后面会讲,这里就不描述了。

这种Kconfig文件并不是ESP32独创的,Kconfig 文件的格式和配置模式在多个开源项目中都有使用,ESP32 使用的 Kconfig 只是其中的一种实现。在 Linux 内核中,Kconfig 用于配置内核选项,帮助开发者根据硬件和需求定制编译选项。

总结一下:ESP32 采用类似的配置方式来简化其开发者在配置固件时的工作。我们通过 Kconfig文件,去定义对应的宏,这些宏可以在我们的代码里调用,用于配置参数或者条件编译来启用或禁用各种功能模块,并且可以通过图形界面或命令行去定义修改这些自定义宏的值,避免了复杂的手动修改配置文件。

同时,ESP-IDF默认组件也使用了大量的Kconfig配置,通过这些配置,我们可以修改ESP-IDF的很多默认配置项,比如系统分区表、FreeRTOS 配置(任务调度器的行为、栈大小、系统 tick 时间)、驱动程序设置、日志输出级别等。

三、为什么要用组件配置(Menuconfig)

  1. 组件配置可以将相关配置常量移出代码,增强配置的可复用度。
  2. 组件配置生成的宏定义,配合C语言的预处理语句,可以针对不同的配置改变代码解结构。
  3. 组件配置变量也可以被CMake访问,可以通过手写逻辑实现根据配置执行不同的构建方案(如某个源文件要不要添加到构建系统)。参考:预设的组件变量

四、创建组件配置

下面我们试着自己写一个组件配置,体验一下全流程,首先复制blink历程到自己的目录,重命名为 blink_menuconfig

我们删除原有的 Kconfig.projbuild 文件,自己重新写一个,原来的Kconfig.projbuild里有三个主要的配置项:分别是:

  • BLINK_LED: 控制LED模式(选项)
  • BLINK_GPIO:LED连接的引脚
  • BLINK_PERIOD:LED闪烁的时间

关于Kconfig的具体格式要求,这里不展开描述,可以参考文档:Documentation of esp-idf-kconfig

创建框架(menu)

我们先创建个框架:

menu "点灯配置"

endmenu

这里菜单项可以使用中文,打开配置菜单可以看到:

创建单选选项(choice)

menu "点灯配置"
    choice BLINK_LED
        prompt "LED模式"
        default BLINK_LED_GPIO

        config BLINK_LED_GPIO
            bool "GPIO"
        config BLINK_LED_RMT
            bool "RMT - Addressable LED"
    endchoice
endmenu

效果:

创建菜单项目(config)

    config BLINK_GPIO
        int "LED引脚"
        default 2
        help
            This is an int BLINK_GPIO.
	
    config BLINK_PERIOD
        int "LED周期"
        default 1000
        help
            This is a int BLINK_GPIO.

效果:

最后我们只需要在对应的c文件里引用sdkconfig.h这个头文件,我们就可以使用我们自定义的这些宏,还需要说明的一点是这些宏会被默认加上CONFIG_前缀,所以我们定义的BLINK_GPIO在实际使用时应该写作:CONFIG_BLINK_GPIO

五、组件配置相关文件

最后总结一下,并附上一些细碎的知识点,不求大家记住,只要有个印象即可,具体要用到了再回来查。

在 ESP-IDF 构建系统中,有这么几种文件和组件配置相关。它们分别是:

  1. Kconfig文件
  2. Kconfig.projbuild文件
  3. sdkconfig文件
  4. sdkconfig.default文件
  5. sdkconfig.old文件
  6. sdkconfig.ci文件
  7. sdkconfig.old文件

其中KconfigKconfig.projbuild 文件用于定义菜单,其余各种“sdkconfig”文件用于保存我们配置的值。

先分别概括这几个“sdkconfig”文件的作用分别是什么:

文件名位置作用概述
Kconfig组件目录用于生成 menuconfig 中的组件菜单项菜单模板
Kconfig.projbuild组件目录用于生成 menuconfig 中的顶层菜单菜单模板
sdkconfig项目根目录用于保存 Kconfig 设置的键值对形式,不建议手动更改保存设置
sdkconfig.default项目根目录自定义 sdkconfig 的默认值,不会被构建系统更改,格式与 sdkconfig 相同,可以手动创建,也可以使用命令idf.py save-defconfig自动生成默认设置
sdkconfig.old项目根目录当保存旧的 menuconfig 设置时,自动备份原来的 sdkconfig 文件备份
sdkconfig.default. TARGET_NAME
项目根目录对于 esp32 这个目标芯片,sdkconfig 的默认值会首先 从 sdkconfig.defaults 获取,然后再从 sdkconfig.defaults.esp32 获取。

关于各种 sdkconfig 的区别和覆盖优先级,可以参考:文档:自定义 sdkconfig 的默认值

最后再讲一下KconfigKconfig.projbuild 文件有什么区别,可以参考官方文档:KConfig.projbuild

参考链接

  1. https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-reference/kconfig.html
  2. https://docs.espressif.com/projects/esp-idf/zh_CN/v5.3.2/esp32/api-guides/build-system.html#custom-sdkconfig-defaults