[001] [ESP32开发笔记] IDF工程创建与CMake配置

ESP32
开发笔记
工程创建
工程分析
项目CMakeLists文件
组件CMakeLists文件
组件依赖

芯片型号:ESP32-S2-MINI-1
开发环境:VS Code + ESP-IDF插件

1 工程创建

  • 在VS Code中点击查看 - 命令面板(或 Ctrl+Shift+P
    在这里插入图片描述

  • 输入show examples projects
    在这里插入图片描述

  • 然后点击Use current ESP-ID
    在这里插入图片描述

  • 选择一个例程(如blink),点击Create project using example blink
    在这里插入图片描述

  • 选择一个存放工程的文件夹(不能有中文路径和空格
    在这里插入图片描述

  • 以 SDK 中的blink为模板,自动创建一个新工程
    在这里插入图片描述

  • 修改工程名称,如my_project,然后修改blink_example_main.c修改,如app_main
    在这里插入图片描述

  • 修改项目CMakeLists.txt,项目名称改为my_project
    在这里插入图片描述

  • 修改main组件CMakeLists.txt,包含C文件名称修改为app_main(这个现在会自动修改)
    在这里插入图片描述

  • Makefile可不修改,为旧版GNU Make编译,Cmake由CMakeLists.txt控制
    在这里插入图片描述

  • 删除 example_test.pysdkconfig.cisdkconfig.defaults 不必要的文件
    在这里插入图片描述

  • 选择开发芯片型号,我的是ESP32-S2
    在这里插入图片描述

2 工程分析

以官方示例项目分析:

- 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/
  • CMakeLists.txt
    顶层项目CMakeLists.txt是 CMake 用于学习如何构建项目的主要文件,可以在这个文件中设置项目全局的 CMake 变量。顶层项目CMakeLists.txt文件会导入/tools/cmake/project.cmake文件,由它负责实现构建系统的其余部分。该文件最后会设置项目的名称,并定义该项目。
  • sdkconfig
    可以用mecuconfig配置该文件,该文件保存了项目所有组件(包括 ESP-IDF 本身)的配置信息,使用idf插件可以可视化配置,点击设置按钮即可打开:
    在这里插入图片描述
  • components
    可选组件,包含项目的部分自定义组件,如果项目源文件较多,不要全部放在main 中,可以放在该自定义组件中。该组件名称不可随意改变(文件夹名),起别名需要在顶层CMakeLists.txt中设置EXTRA_COMPONENT_DIRS变量以查找其他指定位置处的组件。
  • main
    main是一个特殊的组件,它包含项目本身的源代码。 main 是默认名称,CMake 变量COMPONENT_DIRS默认包含此组件,但可以修改此变量。(一般不改)
  • build
    该目录编译时会由idf自动创建,CMake 会配置项目,并在此目录下生成临时的构建文件。在主构建进程的运行期间,该目录还会保存临时目标文件、库文件以及最终输出的二进制文件。此目录通常不会添加到项目的源码管理系统中,也不会随项目源码一同发布。(即调试完可删除该目录发布项目)

注意

  • 每个组件目录都包含一个CMakeLists.txt文件,里面会定义一些变量以控制该组件的构建过程,以及其与整个项目的集成
  • 每个组件还可以包含一个 Kconfig 文件,它用于定义 menuconfig 时展示的 组件配置 选项。某些组件可能还会包含 Kconfig.projbuildproject_include.cmake 特殊文件,它们用于 覆盖项目的部分设置

3 项目CMakeLists 文件

每个项目都有一个顶层 CMakeLists.txt 文件,包含整个项目的构建设置。默认情况下,项目 CMakeLists 文件会非常小。

3.1 必要部分

在这里插入图片描述
最小项目CMakeLists 文件必须包含下面三行代码:

cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(myProject)
  • cmake_minimum_required(VERSION 3.5):必须放在 CMakeLists.txt 文件的第一行,它会告诉 CMake 构建该项目所需要的最小版本号。ESP-IDF 支持 CMake 3.5 或更高的版本。
  • include($ENV{IDF_PATH}/tools/cmake/project.cmake):会导入 CMake 的其余功能来完成配置项目、检索组件等任务。
  • project(myProject)myProject为工程名(如hello_world),该命令会创建项目本身,并指定项目名称。该名称会作为最终输出的二进制文件的名字,即 myProject.elfmyProject.bin。每个 CMakeLists 文件只能定义一个项目。

3.2 可选的项目变量

  • COMPONENT_DIRS:组件的搜索目录,默认为 IDF_PATH/componentsPROJECT_DIR/components、和 EXTRA_COMPONENT_DIRS。如果您不想在这些位置搜索组件,请覆盖此变量。
  • EXTRA_COMPONENT_DIRS:用于搜索组件的其它可选目录列表。
  • COMPONENTS:要构建进项目中的组件名称列表,默认为COMPONENT_DIRS 目录下检索到的所有组件。使用此变量可以“精简”项目以缩短构建时间。请注意,如果一个组件通过 COMPONENT_REQUIRES 指定了它依赖的另一个组件,则会自动将其添加到 COMPONENTS 中,所以 COMPONENTS 列表可能会非常短。

注意:

  • 以上变量中的路径可以是绝对路径,或者是相对于项目目录的相对路径。
  • 使用 cmake 中的 set 命令 来设置这些变量,如 set(VARIABLE "VALUE")。请注意,set() 命令需放在 include(...) 之前,cmake_minimum(...) 之后。如:
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/led_strip)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

3.3 覆盖默认的构建规范

构建系统设置了一些全局的构建规范(编译标志、定义等),这些规范可用于编译来自所有组件的所有源文件。

例如,其中一个默认的构建规范是编译选项 Wextra。假设一个用户想用 Wno-extra 来覆盖这个选项, 应在 project() 之后进行:

cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(myProject)

idf_build_set_property(COMPILE_OPTIONS "-Wno-error" APPEND)

4 组件CMakeLists 文件

组件 ComponentsCOMPONENT_DIRS 列表中包含 CMakeLists.txt 文件的任何目录。是模块化且独立的代码,会被编译成静态库(.a 文件)并链接到应用程序。每个项目都包含一个或多个组件,这些组件可以是 ESP-IDF 的一部分,可以是项目自身组件目录的一部分,也可以从自定义组件目录添加

最小组件CMakeLists文件

最小组件 CMakeLists.txt 文件通过使用 idf_component_register 将组件添加到构建系统中。

idf_component_register( SRCS "app_main.c"
                    	INCLUDE_DIRS "include"
                        REQUIRES mbedtls)
  • SRCS 是源文件列表(*.c*.cpp*.cc*.S),里面所有的源文件都将会编译进组件库中。
  • INCLUDE_DIRS 是目录列表,里面的路径会被添加到所有需要该组件的组件(包括 main 组件)全局 include 搜索路径中。(若只有一级目录,可以INCLUDE_DIRS "."或不填)
  • REQUIRES 实际上并不是必需的,但通常需要它来声明该组件需要使用哪些其它组件

也可以用CMake的set语法:

set(srcs 
"app_main.c" "src/oled.c")
idf_component_register(SRCS ${srcs}
                    INCLUDE_DIRS "inc")

4.1 同名组件

ESP-IDF 在搜索所有待构建的组件时,会按照COMPONENT_DIRS指定的顺序依次进行,即 ESP-IDF 内部组件(IDF_PATH/components)–> EXTRA_COMPONENT_DIRS 中的组件 --> 项目组件(PROJECT_DIR/components)。如果这些目录中的两个或者多个包含具有相同名字的组件,则使用搜索到的最后一个位置的组件。这就允许将组件复制到项目目录中再修改以覆盖 ESP-IDF 组件,如果使用这种方式,ESP-IDF 目录本身可以保持不变。

如果在现有项目中通过将组件移动到一个新位置来覆盖它,项目不会自动看到新组件的路径。请运行 idf.py reconfigure 命令后(或删除项目构建文件夹)再重新构建。

4.2 重命名 main 组件

构建系统会对 main 组件进行特殊处理。假如 main 组件位于预期的位置(即${PROJECT_PATH}/main),那么它会被自动添加到构建系统中。其他组件也会作为其依赖项被添加到构建系统中,这使用户免于处理依赖关系,并提供即时可用的构建功能。重命名 main 组件会减轻上述这些幕后工作量,但要求用户指定重命名后的组件位置,并手动为其添加依赖项。重命名 main 组件的步骤如下:

  1. 重命名 main 目录。
  2. 项目CMakeLists.txt文件中设置 EXTRA_COMPONENT_DIRS,并添加重命名后的 main 目录。
  3. 组件CMakeLists.txt文件中设置 COMPONENT_REQUIRESCOMPONENT_PRIV_REQUIRES 以指定依赖项。

4.3 预设的组件变量

以下专用于组件的变量可以在组件 CMakeLists 中使用,但不建议修改:

  • COMPONENT_DIR组件目录,即包含 CMakeLists.txt 文件的绝对路径,它与 CMAKE_CURRENT_SOURCE_DIR 变量一样,路径中不能包含空格。
  • COMPONENT_NAME组件名,与组件目录名相同。
  • COMPONENT_ALIAS库别名,由构建系统在内部为组件创建。
  • COMPONENT_LIB库名,由构建系统在内部为组件创建。

以下变量在项目级别中被设置,但可在组件 CMakeLists 中使用:

  • CONFIG_*:项目配置中的每个值在 cmake 中都对应一个以 CONFIG_ 开头的变量。(参考Kconfig
  • ESP_PLATFORM:ESP-IDF 构建系统处理 CMake 文件时,其值设为1。

4.4 构建/项目变量

以下是可作为构建属性的构建/项目变量,可通过组件 CMakeLists.txt 中的 idf_build_get_property 查询其变量值。

  • PROJECT_NAME项目名,在项目 CMakeLists.txt 文件中设置。
  • PROJECT_DIR:项目目录(包含项目 CMakeLists 文件)的绝对路径,与 CMAKE_SOURCE_DIR 变量相同。
  • COMPONENTS此次构建中包含的所有组件的名称,具体格式为用分号隔开的 CMake 列表。
  • IDF_VER:ESP-IDF 的 git 版本号,由 git describe 命令生成。
  • IDF_VERSION_MAJORIDF_VERSION_MINORIDF_VERSION_PATCH: ESP-IDF 的组件版本,可用于条件表达式。请注意这些信息的精确度不如 IDF_VER 变量,版本号 v4.0-dev-*v4.0-beta1v4.0-rc1v4.0 对应的 IDF_VERSION_* 变量值是相同的,但是 IDF_VER 的值是不同的。
  • IDF_TARGET:项目的硬件目标名称。
  • PROJECT_VER:项目版本号。

4.5 组件配置

每个组件都可以包含一个 Kconfig 文件,和 CMakeLists.txt 放在同一目录下。Kconfig 文件中包含要添加到该组件配置菜单中的一些配置设置信息。

5 组件依赖

每个组件都需要声明它所依赖的组件,即requires

idf_component_register(...
                       REQUIRES mbedtls
                       PRIV_REQUIRES console spiffs)
  • REQUIRES 需要包含所有在当前组件的公共头文件里 #include 的头文件所在的组件。(如a组件REQUIRES b b组件,若c组件包含了a组件的公共接口a.h,其可以递归包含b组件)
  • PRIV_REQUIRES 需要包含被当前组件的源文件#include 的头文件所在的组件(除非已经被设置在了 REQUIRES 中)。以及是当前组件正常工作必须要链接的组件。
  • REQUIRESPRIV_REQUIRES 的值不能依赖于任何配置选项 (CONFIG_xxx 宏)。这是因为在配置加载之前,依赖关系就已经被展开。其它组件变量(比如包含路径或源文件)可以依赖配置选择。
  • 如果当前组件除了通用组件依赖项中设置的通用组件(比如 RTOS、libc 等)外,并不依赖其它组件,那么对于上述两个 REQUIRES 变量,可以选择其中一个或是两个都不设置。

如果组件仅支持某些硬件目标(IDF_TARGET 的值),则可以在 idf_component_register 中指定 REQUIRED_IDF_TARGETS 来声明这个需求。在这种情况下,如果构建系统导入了不支持当前硬件目标的组件时就会报错。

在 CMake 中,REQUIRESPRIV_REQUIRES 是 CMake 函数 target_link_libraries(... PUBLIC ...)target_link_libraries(... PRIVATE ...) 的近似包装。

5.1 组件依赖示例

假设现在有一个 car 组件,它需要使用 engine 组件,而 engine 组件需要使用 spark_plug 组件:

- autoProject/
             - CMakeLists.txt
             - components/ - car/ - CMakeLists.txt
                                     - car.c
                                     - car.h
                           - engine/ - CMakeLists.txt
                                     - engine.c
                                     - include/ - engine.h
                           - spark_plug/  - CMakeLists.txt
                                          - plug.c
                                          - plug.h

5.1.1 Car 组件

car.h 头文件

car.h 头文件是 car 组件的公共接口。该头文件直接包含了 engine.h,这是因为它需要使用 engine.h 中的一些声明:

/* car.h */
#include "engine.h"

#ifdef ENGINE_IS_HYBRID
#define CAR_MODEL "Hybrid"
#endif

同时 car.c 也包含了 car.h:

/* car.c */
#include "car.h"

这代表文件 car/CMakeLists.txt 需要声明 car 需要 engine

idf_component_register(SRCS "car.c"
                  INCLUDE_DIRS "."
                  REQUIRES engine)
  • SRCS 提供 car 组件中源文件列表。
  • INCLUDE_DIRS 提供该组件公共头文件目录列表,由于 car.h 是公共接口,所以这里列出了所有包含了 car.h 的目录。
  • REQUIRES 给出该组件的公共接口所需的组件列表。由于 car.h 是一个公共头文件并且包含了来自 engine 的头文件,所以我们这里包含 engine。这样可以确保任何包含 car.h 的其他组件也能递归地包含所需的 engine.h

5.1.2 Engine 组件

engine 组件也有一个公共头文件 include/engine.h,但这个头文件更为简单:

/* engine.h */
#define ENGINE_IS_HYBRID

void engine_start(void);

engine.c 中执行:

/* engine.c */
#include "engine.h"
#include "spark_plug.h"
...

在该组件中,engine 依赖于 spark_plug,但这是私有依赖关系。编译 engine.c 需要 spark_plug.h 但不需要包含 engine.h

这代表文件 engine/CMakeLists.txt 可以使用 PRIV_REQUIRES

idf_component_register(SRCS "engine.c"
                  INCLUDE_DIRS "include"
                  PRIV_REQUIRES spark_plug)

因此,car 组件中的源文件不需要在编译器搜索路径中添加 spark_plug include 目录。这可以加快编译速度,避免编译器命令行过于的冗长。

5.1.3 Spark Plug 组件

spark_plug 组件没有依赖项,它有一个公共头文件 spark_plug.h,但不包含其他组件的头文件。

这代表 spark_plug/CMakeLists.txt 文件不需要任何 REQUIRESPRIV_REQUIRES

idf_component_register(SRCS "spark_plug.c"
                  INCLUDE_DIRS ".")

5.2 源文件 Include 目录

每个组件的源文件都是用这些 Include 路径目录编译的,这些路径在传递给 idf_component_register 的参数中指定:

idf_component_register(..
                       INCLUDE_DIRS "include"		# include文件夹下的所有.h文件
                       PRIV_INCLUDE_DIRS "other")	
  • 当前组件INCLUDE_DIRS指定include文件夹下的所有.h文件
  • 当前组件PRIV_INCLUDE_DIRS包含其他组件的.h文件
  • REQUIRESPRIV_REQUIRES 参数指定的所有其他组件(即当前组件的所有公共和私有依赖项)所设置的 INCLUDE_DIRS
  • 递归列出所有组件 REQUIRES 列表中 INCLUDE_DIRS 目录(如递归展开这个组件的所有公共依赖项)。

5.3 main组件依赖项

main 组件比较特别,因为它在构建过程中自动依赖所有其他组件。所以不需要向这个组件传递 REQUIRESPRIV_REQUIRES

5.4 通用组件依赖项

为避免重复性工作,各组件都用自动依赖一些“通用” IDF 组件,即使它们没有被明确提及。这些组件的头文件会一直包含在构建系统中。

通用组件包括:cxx、newlib、freertos、esp_hw_support、heap、log、lwip、soc、hal、esp_rom、esp_common、esp_system。

注意:要避免循环依赖,关于其定义和解决方法参考:循环依赖


参考:

  1. ESP-IDF编程指南-构建系统
  2. ESP32开发 2.添加.c.h并修改CMakeLists,来定制自己的工程
  3. ESPIDF添加自定义组件(创建C和H文件)
  4. ESP32学习笔记(21)——构建自己的工程和组件库

END

  • 17
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柯西的彷徨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值