ESP32学习六-构建系统

一、简介

        如果想要新建一个ESP32项目,需要包含很多其他的文件夹和文件,如果对ESP32的勾线系统原理不理解,就会产生出很多编译不通过的问题。这里就对ESP-IDF构建系统的实现原理做一个简单的总结。

        测试环境:Ubuntu18.4

        ESP-IDF:V5.0

        官方参考链接:构建系统 - ESP32 - — ESP-IDF 编程指南 v5.0.1 文档

二、概念

        项目

        项目特指一个目录,其中包含了构建可执行应用程序所需的全部文件和配置,以及其他支持型文件,例如分区表、数据、文件系统分区和引导程序等等。通俗一点说,就是项目文件夹

        项目配置

        项目配置保存在项目根目录下的sdkconfig文件中。可以通过idf.py menuconfig指令进行修改,且一个项目只能包含一个项目配置

        应用程序

        应用程序是由ESP-IDF构建得到的可执行文件。一个项目通常会构建两个应用程序:项目应用程序(可执行的主文件,即用户自定义的固件)和引导程序(启动并初始化项目应用程序)。

        组件

        组件是模块化且独立的代码,会被编译成静态库(.a文件),并链接到应用程序。部分组件由ESP-IDF官方提供,其他组件则来源于其他开源项目。

        目标

        特指运行构建后应用程序的硬件设备。运行idf.py --list-targets可以查看当前ESP-IDF版本中支持目标的完整列表。

        注:以下部分不属于项目的组成部分

  • ESP-IDF。其并不是项目的一部分,它独立于项目,通过IDF-PATH环境变量(保存esp-idf目录的路径)链接到项目,从而将IDF架构与项目分离。
  • 交叉编译工具链。其应该被安装在系统PATH环境变量中。

        示例项目的项目树:

- DemoProject/
             - build
             - main/       
                - CMakeLists.txt
                - main.c
             - components/
                - CMakeLists.txt
                - test.c
             - CMakeLists.txt
             - sdkconfig

        项目包含了以下组成部分:

        build目录:该目录是存放构建输出的地方,如果没有此目录,idf.py会自动构建。CMake会配置项目,并在此目录下生成临时的构建文件。随后,在主构建进程的运行期间,该目录还会保存临时目标文件、库文件以及最终输出的二进制文件。次目录通常不会添加到项目的源码管理系统中,也不会随项目源码一同发布。

        main目录:该目录是一个特殊的组件,它包含项目本身的源代码。main是默认名称,CMake变量COMPONENT_DIRS默认包含此组件,可以自行修改此变量。有关详细信息,请参阅 重命名 main 组件。如果项目中源文件较多,建议将其归于组件中,而不是全部放在 “main” 中。

        components目录:该目录是可选的,其中包含了项目的部分自定义组件,并不是每个项目都需要这种自定义组件,但它有助于构建可复用的代码或者导入第三方(不属于ESP-IDF)的组件。或者,也可以在顶层CMakeLists.txt中设置EXTRA_COMPONENT_DIRS变量以查找其他指定位置处的组件

        顶层项目CMakeLists.txt文件:该文件是CMake用于学习如何构建项目的主要文件,可以在这个文件中设置项目全局的CMake变量。顶层项目CMakeLists.txt文件会导入esp-idf/tools/cmake/project.cmake文件,由它负责实现构建系统的其余部分。该文件最后会设置项目的名称,并定义该项目。

        sdkconfig文件:为项目配置文件,执行idf.py menuconfig时会创建或更新此文件,文件中保存了项目中所有组件(包括ESP-IDF本身)的配置信息。sdkconfig文件可能会也可能不会被添加到项目的源码管理系统中。

        注:每个组件目录都包含一个CMakeLists.txt文件,里面会定义一些变量以控制该组件的创建过程,以及其与整个项目的继承。

        项目CMakeLists文件

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

        最小CMakeLists文件示例

# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_project)

        必要部分

        每个项目都要按照上面显示的顺序添加上述三行代码:

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

        可选的项目变量

        以下这些变量都有默认值,用户可以覆盖这些变量值来自定义构建。更多实现细节,请参阅 /tools/cmake/project.cmake 文件。

  • 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 "./src")。请注意,set()命令需放在include(...)之前,cmake minimum(...)之后

cmake_minimum_required(VERSION 3.16)

set(EXTRA_COMPONENT_DIRS path)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_project)

        组件CMakeLists文件

        每个项目都包含一个或多个组件,这些组件可以是ESP-IDF的一部分,可以是项目自身组件目录的一部分,也可以从自定义组件目录添加。

        组件是COMPONENT_DIRS列表中包含CMakeLists.txt文件的任何目录。也就是说,组件目录下,必须包含CMakeLists.txt文件

        搜索组件

        搜索COMPONENTS_DIRS中的目录列表以查找项目的组件,此列表中的目录可以是组件自身(即包含CMakeList.txt文件的目录),也可以是子目录是组件顶级目录的目录。

        当CMake运行项目配置时,它会记录本次构建包含的组件列表,它可用于调试某些组件的天加/排除。

        同名组件

        ESP-IDF在搜索所有待构建的组件时,会按照COMPONENT_DIRS指定的顺序依次进行,这意味着在默认情况下,首先搜索ESP-IDF内部组件(IDF-PATH/components),然后是EXTRA_COMPONENT_DIRS中的组件最后是项目组件(PROJECT_DIR/components)。如果这些目录中的两个或多个包含具有相同名字的组件,则使用搜索到的最后一个位置的组件。这就允许将组件赋值到项目目录中再修改以覆盖ESP-IDF组件。如果使用这种方式,ESP-IDF目录本身可以保持不变。

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

        最小组件CMakeLists文件

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

idf_component_register(SRCS "test.c"
                    INCLUDE_DIRS "."
                    REQUIRES src)
  • SRCS:是源文件列表(*.c、*.cpp、*.cs、*.s),里面所有的源文件都将会编译进组件库中。
  • INCLUDEDIRS:是目录列表,里面的路径会被添加到所有需要该组件的组件(包括main组件)全局include搜索路径中。
  • REQUIRES:可选。通常需要它来声明该组件需要使用哪些其他组件。参考 组件依赖

        上述命令会构建生成与组件同名的库,并最终被链接到应用程序中。

         idf_component_register的其他参数可以参考here

         有关更完整的 CMakeLists.txt 示例,请参阅 组件依赖示例 和 组件 CMakeLists 示例

         

        组件依赖

        编译各个组件时,ESP-IDF系统会递归评估其依赖项。这意味着每个组件都需要声明它所依赖的组件,即"requires"。

        编写组件

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

       注:通俗一点说,如果头文件(.h)里边include了其他组件,需要使用REQUIRES包含被需要的组件。如果源文件(.c)里边include了其他组件,需要使用PRIV_REQUIRES包含被需要的组件

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

        注:在CMake中,REQUIRES和PRIV_REQUIRES是CMake函数target_link_libraries(...PUBLIC...)和target_link_libraries(...PRIVATE...)的近似包装。

        组件依赖示例

        假设现在有一个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
                                          - spark_plug.c
                                          - spark_plug.h

        Car组件

        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

        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依赖于spart_plug,但这是私有依赖关系。编译engine.c需要spart_plug.h但不需要包含engine.h。

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

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

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

        Spart Plug组件

        spart_plug组件没有依赖项,它有一个公共头文件spark_plug.h,但不包含其他组件的头文件。这代表spark_plug/CMakeLists.txt文件不要任何REQUIRES或PRIV_REQUIRES:

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

三、实战

        项目名修改

        我们创建一个简单的项目,先看项目树:

- DemoProject/
             - main/       
                - CMakeLists.txt
                - main.c
             - CMakeLists.txt
             - README.md

        项目结构很简单,只有一个main文件夹。这里为什么没有sdkconfig文件呢?是因为我们还没有配置。此时我们在终端中输入idf.py menuconfig指令。

cd demo_project/
idf.py menuconfig

         可以看到,当我们运行idf.py menuconfig指令后,系统就在项目目录下自动创建了sdkconfig

        此外,此时也还没有build文件夹。运行一下idf.py build.

        编译成功后,项目中就自动生成了build文件夹。我们打开该文件夹。 

         可以看到该文件夹中,已经输出了项目bin文件。名字为“main”。根据我们在上文中介绍的:

说明项目名是在顶层CMakeLists.txt文件中设定的。打开这个文件。     

        project(项目名) ,此时修改项目名为“demo_project”。并重新运行idf.py build指令。并查看Build文件夹。

         可以看到,这里已经生成了名为demo_project.bin的文件。但是main.bin还是存在的。这是因为每次编译,系统只会做增量的操作。如果我们不希望有上一个项目名的痕迹,可以运行如下指令:

idf.py fullclean     //清除所有编译

        运行结束后,可以看到build文件夹已经空了。

         重新运行idf,py build编译。

         此时,build文件夹中就只有修改后的项目文件了。

        添加components组件

        当需要添加components组件时,可以在项目中添加components文件夹,然后在该文件夹中添加自己的文件。

        比如现在添加test.c自定义文件。

        

         然后,我们在main.c中添加代码

#include "test.h"

        编译。

        系统报错,提示找不到test.h文件。奇怪,为什么呢? 查看概念:

         对了,忘了添加CMakeLists.txt文件了。

        再次提醒,组件目录下,必须包含CMakeLists.txt文件

        添加CMakeLists.txt文件,并修改为如下:

         这样应该就没问题了吧。再次编译。

         还报错!!!!为什么??目前来看,项目下各个文件应该都是正常的才对呀。为什么还是报错呢?再查概念。

         原来如此,因为新建了components文件夹,而Cmake在我们第一次idf.py build的时候链接了所有组件,并没有发现项目下有components组件,就没有链接进来。那运行一下idf.py reconfigure指令重新链接。        可以看到,重新链接了库,再编译。

         成功!!!

        这里解释一下,为什么我们在项目中添加components文件夹就直接能添加自定义文件。

         概念上有说,系统默认会去链接项目/comnponents文件夹。如果有的话,就自动添加到链接库中

        添加自定义组件

        如果我们不想用components组件,就想使用自定义组件要怎么操作呢?比如我们要添加一个src的组件。(我们把刚才的components文件夹改名为src).

         在main.c中还是#include "test.h"。

        方法一、修改顶层CMakeLists.txt 

        此时,我们编译一下,试试。

         报错!对了,是不是要重新链接一下呢?

         再编译。

         还是报错!!!查资料。

         原来是针对自定义的组件需要添加到顶层CMakeLists.txt中。好,修改文件。

        重新编译。

         成功!!

        注:在ESP-IDF5.0版本之后,在自定义组件中添加依赖组件,需要在组件的CMakeLists.txt文件中额外配置PRIV_REQUIRES/REQUIRES。而5.0以下的版本是不需要的。

idf_component_register(SRCS "test.c"
                    INCLUDE_DIRS "."
                    PRIV_REQUIRES driver)

        方法二、通过main组件链接

        因为main组件是系统默认链接的,那自然也就可以通过main组件来链接自定义组件。修改main组件的CMakeLists.txt文件。

         上述语句看不懂的请查看资料。

         编译。

         成功!!!

       

  • 13
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
ESP32-S3是一款高度集成的Wi-Fi和蓝牙LE(Smart)芯片,是ESP32系列产品的最新成员,具有更高的性能和更佳的功耗表现。在毕业设计中,你可以使用ESP32-S3来实现一些有趣的项目,以下是一些可能的想法: 1.智能家居控制器:使用ESP32-S3实现一个智能家居控制器,可以通过Wi-Fi和蓝牙连接到不同的智能设备,如灯光、空调、窗帘等。用户可以使用手机应用程序或Web界面来控制设备。 2.智能农业系统:使用ESP32-S3构建一个智能农业系统,可以通过Wi-Fi和蓝牙连接到各种传感器和执行器,如土壤湿度传感器、水泵、灌溉系统等。通过手机应用程序或Web界面,用户可以实时监测和控制农业系统。 3.智能健身设备:使用ESP32-S3实现一个智能健身设备,可以测量用户的心率、步数、卡路里消耗等健身数据。通过Wi-Fi或蓝牙,设备可以将数据上传到云端,用户可以使用手机应用程序来查看和分析健身数据。 4.远程监控系统:使用ESP32-S3构建一个远程监控系统,可以连接到多个摄像头和传感器,如温度传感器、湿度传感器等。通过Wi-Fi或蓝牙,用户可以远程监视房间、办公室、工厂等场所。 以上是一些可能的项目想法,你可以根据自己的兴趣和技能来选择一个适合自己的项目。在实现项目的过程中,你可以学习ESP32-S3的硬件和软件开发技术,同时也可以提高自己的项目管理和团队合作能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值