问题背景
熟悉stm32keil开发的都知道,我们在编写不同的外设时,通常都会单独编写一个app文件夹或者是user文件夹之类的来存放不同外设功能的源文件和头文件。
在前面一节2.构建第一个工程并烧录到ESP32开发板-CSDN博客中,我们是使用了一个乐鑫的官方所提供的一个hello world工程模板,这个工程里面我们表面上是用到了GPIO功能和USART功能,其他的一些功能我们暂时还没有用到,或者说暂时还没有包含其他功能模块。
那么如果我一开始使用了模板是generic_gpio这个模板工程,在项目设计的前期实现我只需要gpio功能就够了,所以我就直接使用的是generic_gpio模板,后来我有需要使用到定时器功能,可能generic_gpio模板里面并没有包含这个功能,我们难道再去打开一个timer外设的模板工程吗,显然是比较麻烦的。
虽然第二节我提到了我们一般会使用官方模板而不是自己新建一个空工程,但是那只是初期新建工程的时候是用官方模板,当我们新建工程完毕以后,我们需要对官方提供的模板进行修改,添加自己的文件目录以存放之后所使用的其他外设文件。因为官方的外设模板工程里面只有main文件夹下一个主函数的文件,我们总不可能把所有的外设实现函数统统写到这个主函数文件里面。
解决方案
事实上官方已经提前想到了这个问题,乐鑫官方对 components 路径的介绍:“可选的 “components” 目录中包含了项目的部分自定义组件,并不是每个项目都需要这种自定义组件,但它有助于构建可复用的代码或者导入第三方(不属于 ESP-IDF)的组件。或者,您也可以在顶层 CMakeLists.txt 中设置 EXTRA_COMPONENT_DIRS 变量以查找其他指定位置处的组件。有关详细信息,请参阅 重命名 main 组件。如果项目中源文件较多,建议将其归于组件中,而不是全部放在 “main” 中。
我们从官方的说明中可以获得两个方法来构建自己的工程文件
- 1 在顶层目录下的 components 路径下添加组件。
- 2 在顶层目录下的 CMakeLists.txt 中设置 EXTRA_COMPONENT_DIRS 变量以查找其他指定位置处的组件。
两种方法的实现原理:
我们观察顶层CMakeLists文件可以发现里面已有三行语句,其中第二行语句为include($ENV{IDF_PATH}/tools/cmake/project.cmake)
而配置项目、检索组件等任务都是 $ENV{IDF_PATH}/tools/cmake/project.cmake 决定的。
组件默认的搜索目录为 IDF_PATH/components、 PROJECT_DIR/components、和 EXTRA_COMPONENT_DIRS。
- ESP-IDF内部组件
IDF_PATH/components
- 扩展组件
EXTRA_COMPONENT_DIRS
- 项目组件
PROJECT_DIR/components
IDF_PATH/components 用于系统组件。我们一般不会将文件放在这里面。
PROJECT_DIR/components、和 EXTRA_COMPONENT_DIRS 对应上面两种添加组件的方法。
在 PROJECT_DIR/components 中添加组件
步骤:
- 首先新建 PROJECT_DIR/components 路径。
- 在PROJECT_DIR/components 中添加我们自定义的组件,比如我添加了 led_key
- 将组件相关的头文件和源文件放到 led_key中,并新建 CMakeLists.txt 文件。
- 在 CMakeLists.txt 中添加源文件与头文件的路径。格式为:
idf_component_register(SRCS "led_key.c"
INCLUDE_DIRS "."
REQUIRES driver
)
相信前两个SRC和INCLUDE_DIRS都很好理解,分别是包含源文件和头文件的路径,那么后一个QEQUIRES到底是干什么的,为啥要后面跟上driver?
这里我先解释第一个问题,QEQUIRES可以理解为添加依赖。在说第二个问题之前我们先来看看现在的文件目录是个什么情况:
然后看下源文件和头文件里面的内容:
led_key.c
#include "led_key.h"
void LED_Init(void)
{
gpio_config_t io_config;
io_config.intr_type = GPIO_INTR_DISABLE;
io_config.mode = GPIO_MODE_OUTPUT;
io_config.pin_bit_mask = 1<<2;
io_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_config.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_config);
// esp_rom_gpio_pad_select_gpio (GPIO_NUM_2);
// gpio_set_direction(GPIO_NUM_2,GPIO_MODE_OUTPUT);
}
void KEY_Init(void)
{
gpio_config_t io_config;
io_config.intr_type = GPIO_INTR_POSEDGE;
io_config.mode = GPIO_MODE_INPUT;
io_config.pin_bit_mask = 1<<0;
io_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_config.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_config);
}
void led_blink( void * pvParameters )
{
while(1)
{
gpio_set_level(GPIO_NUM_2,0);
vTaskDelay(1000 / portTICK_PERIOD_MS);//FreeRTOS延时函数--毫秒级
gpio_set_level(GPIO_NUM_2,1);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void gpio_isr_handler(void* arg)
{
static int flag = 0;
flag ^= 1;
gpio_set_level(GPIO_NUM_2,flag);
}
led_key.h
#ifndef __LED_KEY__
#define __LED_KEY__
#include "driver/gpio.h"
#define ESP_INTR_FLAG_DEFAULT 0
void LED_Init(void);
void KEY_Init(void);
void led_blink( void * pvParameters );
void gpio_isr_handler(void* arg);
#endif
看完文件结构和文件内容之后,我们大体可以明白为什么要添加一个driver依赖了,因为我们在led_key.h里面包含了driver/gpio.h,而如果我们不去进行依赖的包含,在编译的时候就会报出这样的错误:
fatal error: driver/gpio.h: No such file or directory
这就是由于依赖问题没有解决导致,我们编写的led_key.h文件没有指定依赖,那么编译器将不知道去哪里找这个driver/gpio.h文件。
在自定义的 EXTRA_COMPONENT_DIRS 中添加组件
有时候我们不想用官方所提供的存放组件集的组件目录components,我们仍然希望创建单独的组件目录,这时候就需要在最顶层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)
set(EXTRA_COMPONENT_DIRS "./src")
project(myProject)
比方说我们创建了一个叫做src的外部组件,就需要在最顶层CMakeLists文件添加set(EXTRA_COMPONENT_DIRS "./src"),
创建自己存放组件集的组件目录,之后在组件目录下创建组件,不能直接创建组件目录,什么意思呢,就是我们要创建一个led的程序,需要在src下面再创建一个led的文件夹作为组件目录,再在led组件目录里面新建led.c源文件。src是一个组件集的目录。
接下来的操作就和在PROJECT_DIR/components 中添加组件的操作一样了。其实这样做无非是吧官方提供的组件集文件名字替换为了自己起的组件集名字,然后由于顶层CMakeLists默认包含components组件集所以不需要我们添加,而我们自己创建的非components组件集属于外部组件,那么就需要在顶层说明一下外部组件集的路径了。