概述
一个 ESP-IDF 项目可以看作是多个不同组件的集合,ESP-IDF可以显式地指定和配置每个组件。在构建项目的时候,构建系统会前往** ESP-IDF 目录、项目目录和用户自定义组件目录(可选)**中查找所有组件,允许用户通过文本菜单系统配置 ESP-IDF 项目中用到的每个组件。在所有组件配置结束后,构建系统开始编译整个项目。
概念
项目
: 特指一个目录,其中包含了构建可执行应用程序所需的全部文件和配置,以及其他支持型文件,例如分区表、数据/文件系统分区和引导程序。项目配置
: 保存在项目根目录下名为 sdkconfig 的文件中,可以通过idf.py menuconfig
进行修改,且一个项目只能包含一个项目配置。应用程序
: 是由 ESP-IDF 构建得到的可执行文件。一个项目通常会构建两个应用程序:项目应用程序(可执行的主文件,即用户自定义的固件)和引导程序(启动并初始化项目应用程序)。组件
: 是模块化且独立的代码,会被编译成静态库(.a 文件)并链接到应用程序。部分组件由 ESP-IDF 官方提供,其他组件则来源于其它开源项目。目标
: 特指运行构建后应用程序的硬件设备。运行idf.py –list-targets
可以查看当前 ESP-IDF 版本中支持目标的完整列表。
示例项目
- myProject
- CMakeLists.txt
- sdkconfig
- components
- component1
- CMakeLists.txt
- Kconfig
- src1.c
- component2
- CMakeLists.txt
- Kconfig
- src1.c
- include
- component2.h
- main
- CMakeLists.txt
- src1.c
- src2.c
- build
该示例项目 “myProject” 包含以下组成部分:
- 顶层项目 CMakeLists.txt 文件,这是 CMake 用于学习如何构建项目的主要文件,可以在这个文件中设置项目全局的 CMake 变量。顶层项目 CMakeLists.txt 文件会导入 /tools/cmake/project.cmake 文件,由它负责实现构建系统的其余部分。该文件最后会设置项目的名称,并定义该项目。
- sdkconfig 项目配置文件,执行
idf.py menuconfig
时会创建或更新此文件,文件中保存了项目中所有组件(包括 ESP-IDF 本身)的配置信息。 sdkconfig 文件可能会也可能不会被添加到项目的源码管理系统中。 - 可选的 components 目录中包含了项目的部分自定义组件,并不是每个项目都需要这种自定义组件,但它有助于构建可复用的代码或者导入第三方(不属于 ESP-IDF)的组件。或者,您也可以在顶层 CMakeLists.txt 中设置 EXTRA_COMPONENT_DIRS 变量以查找其他指定位置处的组件。
- main 目录是一个特殊的组件,它包含项目本身的源代码。”main” 是默认名称,CMake 变量 COMPONENT_DIRS 默认包含此组件,但您可以修改此变量。有关详细信息,请参阅 重命名 main 组件。如果项目中源文件较多,建议将其归于组件中,而不是全部放在 “main” 中。
- build 目录是存放构建输出的地方,如果没有此目录,idf.py 会自动创建。CMake 会配置项目,并在此目录下生成临时的构建文件。随后,在主构建进程的运行期间,该目录还会保存临时目标文件、库文件以及最终输出的二进制文件。此目录通常不会添加到项目的源码管理系统中,也不会随项目源码一同发布。
每个组件目录都包含一个 CMakeLists.txt 文件,里面会定义一些变量以控制该组件的构建过程,以及其与整个项目的集成。更多详细信息请参阅 组件 CMakeLists 文件.
每个组件还可以包含一个 Kconfig 文件,它用于定义 menuconfig 时展示的 组件配置 选项。某些组件可能还会包含 Kconfig.projbuild 和 project_include.cmake 特殊文件,它们用于 覆盖项目的部分设置
项目 CMakeLists 文件
每个项目都有一个顶层 CMakeLists.txt 文件,包含整个项目的构建设置。默认情况下,项目 CMakeLists 文件会非常小。
最小 CMakeLists 文件示例
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp-wroom-32)
必要部分
每个项目都要按照上面显示的顺序添加上述三行代码:
- **cmake_minimum_required(VERSION 3.16) **必须放在 CMakeLists.txt 文件的第一行,它会告诉 CMake 构建该项目所需要的最小版本号。ESP-IDF 支持 CMake 3.16 或更高的版本。
- include($ENV{IDF_PATH}/tools/cmake/project.cmake) 会导入 CMake 的其余功能来完成配置项目、检索组件等任务。
- **project(esp-wroom-32) ** 会创建项目本身,并指定项目名称。该名称会作为最终输出的二进制文件的名字,即 esp-wroom-32.elf 和 esp-wroom-32.bin。每个 CMakeLists 文件只能定义一个项目。
可选的项目变量
以下这些变量都有默认值,用户可以覆盖这些变量值以自定义构建行为。
- COMPONENT_DIRS:组件的搜索目录,默认为* IDF_PATH/components、 PROJECT_DIR/components、和 EXTRA_COMPONENT_DIRS*。如果您不想在这些位置搜索组件,请覆盖此变量。
- EXTRA_COMPONENT_DIRS:用于搜索组件的其它可选目录列表。路径可以是相对于项目目录的相对路径,也可以是绝对路径。
- COMPONENTS:要构建进项目中的组件名称列表,默认为 COMPONENT_DIRS 目录下检索到的所有组件。使用此变量可以“精简”项目以缩短构建时间。请注意,如果一个组件通过 COMPONENT_REQUIRES 指定了它依赖的另一个组件,则会自动将其添加到 COMPONENTS 中,所以 COMPONENTS 列表可能会非常短。
以上变量中的路径可以是绝对路径,或者是相对于项目目录的相对路径。请使用 cmake 中的 set 命令 来设置这些变量,如 set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/core)。请注意,set() 命令需放在 include(…) 之前,cmake_minimum(…) 之后。
重命名 main 组件
构建系统会对 main 组件进行特殊处理。假如 main 组件位于预期的位置(即 ${PROJECT_PATH}/main),那么它会被自动添加到构建系统中。其他组件也会作为其依赖项被添加到构建系统中,这使用户免于处理依赖关系,并提供即时可用的构建功能。重命名 main 组件会减轻上述这些幕后工作量,但要求用户指定重命名后的组件位置,并手动为其添加依赖项。重命名 main 组件的步骤如下:
- 重命名 main 目录。
- 在项目 CMakeLists.txt 文件中设置 EXTRA_COMPONENT_DIRS,并添加重命名后的 main 目录。
- 在组件的 CMakeLists.txt 文件中设置 COMPONENT_REQUIRES 或 COMPONENT_PRIV_REQUIRES 以指定依赖项。
组件 CMakeLists 文件
每个项目都包含一个或多个组件,这些组件可以是 ESP-IDF 的一部分,可以是项目自身组件目录的一部分,也可以从自定义组件目录添加。组件是 COMPONENT_DIRS 列表中包含 CMakeLists.txt 文件的任何目录。
搜索组件
搜索 COMPONENT_DIRS 中的目录列表以查找项目的组件,此列表中的目录可以是组件自身(即包含 CMakeLists.txt 文件的目录),也可以是子目录为组件的顶级目录。当 CMake 运行项目配置时,它会记录本次构建包含的组件列表,它可用于调试某些组件的添加/排除。
同名组件
ESP-IDF 在搜索所有待构建的组件时,会按照 COMPONENT_DIRS 指定的顺序依次进行,这意味着在默认情况下,首先搜索 ESP-IDF 内部组件(IDF_PATH/components),然后是 EXTRA_COMPONENT_DIRS 中的组件,最后是项目组件(PROJECT_DIR/components)。如果这些目录中的两个或者多个包含具有相同名字的组件,则使用搜索到的最后一个位置的组件。这就允许将组件复制到项目目录中再修改以覆盖 ESP-IDF 组件,如果使用这种方式,ESP-IDF 目录本身可以保持不变。
最小组件 CMakeLists 文件
最小组件 CMakeLists.txt 文件通过使用 idf_component_register 将组件添加到构建系统中。
set(SRC_LISTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
idf_component_register(
SRCS ${SRC_LISTS}
INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}
REQUIRES freertos esp_common
)
- SRCS 是源文件列表(.c、.cpp、.cc、.S),里面所有的源文件都将会编译进组件库中。
- INCLUDE_DIRS 是目录列表,里面的路径会被添加到所有需要该组件的组件(包括 main 组件)全局 include 搜索路径中。
- REQUIRES 实际上并不是必需的,但通常需要它来声明该组件需要使用哪些其它组件。
上述命令会构建生成与组件同名的库,并最终被链接到应用程序中。上述目录通常设置为相对于 CMakeLists.txt 文件的相对路径,当然也可以设置为绝对路径。
预设的组件变量
以下专用于组件的变量可以在组件 CMakeLists 中使用,但不建议修改:
- COMPONENT_DIR:组件目录,即包含 CMakeLists.txt 文件的绝对路径,它与 CMAKE_CURRENT_SOURCE_DIR 变量一样,路径中不能包含空格。
- COMPONENT_NAME:组件名,与组件目录名相同。
- COMPONENT_ALIAS:库别名,由构建系统在内部为组件创建。
- COMPONENT_LIB:库名,由构建系统在内部为组件创建。
组件配置
每个组件都可以包含一个 Kconfig 文件,和 CMakeLists.txt 放在同一目录下。Kconfig 文件中包含要添加到该组件配置菜单中的一些配置设置信息。运行 menuconfig 时,可以在 Component Settings 菜单栏下找到这些设置。创建一个组件的 Kconfig 文件,最简单的方法就是使用 ESP-IDF 中现有的 Kconfig 文件作为模板,在这基础上进行修改。
Hello Word实现
项目结构组织
├── CMakeLists.txt
├── README.md
├── build.sh
├── core
│ ├── CMakeLists.txt
│ ├── main.cpp
│ └── main.h
├── docs
└── sdkconfig
- CMakeLists.txt
cmake_minimum_required(VERSION 3.16) set(CMAKE_CXX_STANDARD 17) # search for components in these places set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/core ) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(esp-wroom-32)
- core/CMakeLists.txt
set(SRC_LISTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
idf_component_register(
SRCS ${SRC_LISTS}
INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}
REQUIRES freertos esp_common
)
- core/main.h
#pragma once #include "freertos/FreeRTOS.h" #include "freertos/task.h" class Main final { public: [[nodiscard]] esp_err_t setup(void); void loop(void); };
- core/main.cpp
#include "main.h" #define LOG_LEVEL_LOCAL ESP_LOG_VERBOSE #include "esp_log.h" #define LOG_TAG "MAIN" static Main my_main; [[nodiscard]] esp_err_t Main::setup(void) { auto status{ESP_OK}; ESP_LOGI(LOG_TAG, "app main setup done"); return status; } void Main::loop(void) { ESP_LOGI(LOG_TAG, "Hello world!"); vTaskDelay(pdMS_TO_TICKS(1000)); } extern "C" void app_main(void) { ESP_ERROR_CHECK(my_main.setup()); while (true) { my_main.loop(); } }