ESP32学习六-构建系统

news2024/11/25 15:21:04

一、简介

        如果想要新建一个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中。好,修改文件。

        重新编译。

         成功!!

        方法二、通过main组件链接

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

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

         编译。

         成功!!!

       

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/456640.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Observability:添加免费和开放的 Elastic APM 作为 Elastic 可观察性部署的一部分 - 8.x

作者:David Hope 在最近的一篇博文中,我们向你展示了如何开始使用 Elastic 可观察性的免费开放层。 下面,我们将介绍你需要做些什么来扩展你的部署,这样你就可以开始免费从应用程序性能监控(APM)或跟踪集群…

我国直播电商行业市场增速下降 核心竞争力发生转变 新一轮洗牌变革步伐将加快

1、直播电商概念及其产业链图解 直播电商是属于网络直播的一个分支,属于一种泛娱乐类直播,电商店铺的店家或模特主播在直播间通过借助视频录制工具,将店铺所售的商品展示给用户,并为用户答疑解惑,提供实时的客服服务&…

【JavaEE初阶】多线程(二)线程状态以及多线程安全问题

摄影分享~~ 文章目录 线程的状态多线程带来的风险线程安全线程安全的原因解决线程不安全问题(加锁)synchronized关键字-监视器锁monitor locksynchronized的特性 java中的死锁问题死锁死锁的三个典型情况死锁的四个必要条件如何避免死锁? J…

家用洗地机怎么选?2023高性价比家用洗地机推荐

相信大家和我一样是妥妥的“懒人一族”了,不喜欢做家务、不喜欢碰脏水、不喜欢花费过多的时间在家务上,但是却想有一个整洁干净的家居环境。而作为家务清洁中面积最大、耗时耗力最多的就是扫地拖地了。传统的地面清洁方式,要先用扫把扫一遍&a…

Git上传本地代码到Github

参考 https://zhuanlan.zhihu.com/p/138305054 文章目录 上传本地代码到github仓库(1)创建一个空文件夹,在该文件夹下右键,Git bash here(2)初始化仓库(3) 建立本地与github上项目的…

ChatGPT 速通手册——让 ChatGPT 来写正则表达式

regex 生成 正则表达式可谓是一门让广大程序员们又爱又恨的技术。它易学难精,而且可维护性又差,别说交接给其他同事,同一个人写的正则表达式,三个月后回头再看,也可能完全不知所云。 因此,让 ChatGPT 来写…

【计网 从头自己构建协议】一、libpcap 介绍 手撕以太网帧

上一篇:IndexError: list index out of range 下一篇:[【计网 从头自己构建协议】二、收发 ARP 请求帧与响应帧] 介绍 理论的学习总是枯燥的,想要加深对理论的理解,最好的方法就是自己实践一遍。 想要亲手实现各种协议&#xf…

通讯方式连接成功,其他原因导致的连接失败解决方案

一、电脑中有其他品牌visa导致的冲突(以tekvisa为例) 1、删除tekvisa 2、下载一个NI Package Manager,卸载里面所有NI的东西 (https://www.ni.com/zh-cn/support/downloads/software-products/download.package-manager.html#32…

❀五一劳动节来啦❀

今年“五一”,4月29日至5月3日放假调休,共5天。 如果你在5月4日到5月6日请假3天,加上5月7日周日,就可以形成9天的假期。 一,五一劳动节的由来⭐ 国际劳动节又称“五一国际劳动节”“国际示威游行日”(英语…

人生中最好的等待叫做来日可期,社科院与杜兰大学金融管理硕士等你惊艳岁月

有句话说:“去日不可追,来日犹可期”。过去的已经过去,不管好的、坏的都已成为我们的回忆。人生中最好的等待就是未来可期。别辜负现在的好时光,努力做想做的事。社科院与杜兰大学金融管理硕士项目等你惊艳时光。 所有出众者的背…

【计算机网络:自顶向下方法】(三) 运输层 (TCP | UDP | 复用 | 传输原理rdt)

【计算机网络:自顶向下方法】 3.1 概述 传输层协议是在端系统中实现的传输层将发送的应用程序进程接受到的报文转换成传输层分组 (运输层报文段)实现的方法/过程 : 将应用报文划分为较小的块,并为每块加上传输层首部以生成传输层报文段ff。I…

【C++】类和对象(中篇)—— 默认成员函数,const成员函数,运算符重载

前言 类和对象没有技巧,只有多加练习,多多尝试自己完成代码,例如各种运算符的重载,或是实现一个自己的日期类 目录 一、类的六个默认成员函数 二、构造函数 2.1 概念 2.2 特点 2.3 默认无参的构造函数 三、析构函数 3.1 概…

ORB305与CISCO路由器构建L2TP over IPSec VPN操作手册

1、网络拓扑在思科路由器与ORB305之间建立一个安全隧道,对客户路由器端设备子网,与思科路由器端服务器子网之间的数据流进行安全保护,组网拓扑图如图所示。 2、思科路由器端配置指导(此处以多数客户使用专线上网形式为例)Cisco(AR…

90年三本程序员,8年5跳,年薪4万变92万……

很多时候,虽然跳槽可能带来降薪的结果,但依然有很多人认为跳槽可以涨薪。近日,看到一则帖子。 发帖的楼主表示,自己8年5跳,年薪4万到92万,现在环沪上海各一套房,再干5年码农,就可以…

2022年NOC大赛创客智慧编程赛道图形化scratch初赛题,包含答案解析

目录 一、单选题 二、多选题 三、判断题 下载打印文档做题: 一、单选题

项目干系人是什么?如何有效管理项目干系人?

项目干系人是指对项目具有利益关系或影响力的个人、团体或组织。他们可能会对项目的目标、范围、进度、成本、质量等方面产生影响,因此,有效地管理项目干系人是项目管理成功的关键之一。 一、干系人识别和分类 项目经理应该首先识别和分类所有与项目有关…

STM32模数转换器(ADC)

1.ADC的简要 我们首先说一下ADC的转换过程,然后说一下原理,当然如果嫌啰嗦可以直接跳过。 ADC是英文Analog-to-Digital Converter缩写,翻译过来就是模数转换器,是指将连续变化的模拟信号转换为离散的数字信号的器件。A/D转换的作…

DiffusionDet源码阅读(1)

本文仅仅适用于已经通读过全文的小伙伴 本文代码节选自 mmdet 中的 DiffusionDet 代码,目前该代码还处于 Development 阶段,所以我博客里写的代码和之后的稳定版本可能稍有不同,不过不用担心,我们只看最关键的部分 DDPM中扩散部…

mybatis中大数据量foreach插入效率对比

1.controller代码 RequestMapping("/testInsert")public String testInsert(Integer sum){testService.testInsert(sum);return "发送成功";}2.service代码 Overridepublic void testInsert(Integer sum) {long start System.currentTimeMillis();List<…

LightGBM面试题

1.偏差 vs 方差? 偏差是指由有所采样得到的大小为m的训练数据集&#xff0c;训练出的所有模型的输出的平均值和真实模型输出之间的偏差。 通常是由对学习算法做了错误的假设导致的描述模型输出结果的期望与样本真实结果的差距。分类器表达能力有限导致的系统性错误&#xff0c…