ESP32-01 Hello World

概述

一个 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 组件的步骤如下:

  1. 重命名 main 目录。
  2. 在项目 CMakeLists.txt 文件中设置 EXTRA_COMPONENT_DIRS,并添加重命名后的 main 目录。
  3. 在组件的 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();
        }
    }
    

你可能感兴趣的:(ESP32,c++)