图10.3.1.1 LED实验程序流程图
10.3.2 GPIO函数解析
ESP-IDF提供了丰富的GPIO操作函数,开发者可以在esp-idf-v5.1.2\components\driver\gpio路径下找到相关的gpio.c和gpio.h文件。在gpio.h头文件中,你可以找到ESP32-S3的所有GPIO函数定义。接下来,作者将介绍一些常用的GPIO函数,这些函数的描述及其作用如下:
1,GPIO配置函数
该函数用来配置GPIO的模式、上下拉等功能,其函数原型如下所示:
esp_err_t gpio_config(const gpio_config_t *pGPIOConfig)
该函数的形参描述如下表所示:
表10.3.2.1 gpio_config函数形参描述
返回值:ESP_OK表示配置成功,ESP_FAIL表示配置失败。
pGPIOConfig为GPIO配置结构体指针,下面来看一下gpio_config_t结构体中的变量。
/* GPIO配置参数 */
typedef struct {
uint64_t pin_bit_mask; /* 配置引脚位 */
gpio_mode_t mode; /* 设置引脚模式 */
gpio_pullup_t pull_up_en; /* 设置上拉 */
gpio_pulldown_t pull_down_en; /* 设置下拉 */
gpio_int_type_t intr_type; /* 中断配置 */
} gpio_config_t;
关于各个参数有哪一些看下表说明:
| 类型说明 | | |
| | (1 << x)其中x为ESP32S3中可用GPIO | 设置的引脚位,比如本实验用到的IO1引脚,则写为: (1ull << GPIO_NUM_1) |
| | | |
| |
| |
| |
GPIO_MODE_INPUT_OUTPUT_OD | |
| |
| | | |
| |
| | | |
| |
| | |
| |
| |
| |
| | | |
| |
| |
| |
| |
| |
| |
| |
表10.3.2.2 gpio_config_t结构体的参数描述
在上表中,可填参数均可在gpio_types.h文件中找到。这些参数通常是通过枚举类型(enum)定义的,它们为特定的GPIO模式或配置提供了预定义的数值。当我们需要为结构体变量(如gpio_mode_t)设置参数时,我们可以查阅gpio_types.h文件,找到对应的枚举类型,并从中选择适当的数值。这样,我们可以确保为GPIO接口设置的模式或配置是准确和有效的。
2,设置管脚输出电平
该函数用于配置某个管脚输出电平,该函数原型如下所示:
esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level);
该函数的形参描述如下表所示:
参数 | 描述 |
gpio_num | GPIO引脚号。(在gpio_types.h文件中枚举gpio_num_t有定义) |
level | GPIO引脚输出电平。0:低电平,1:高电平 |
表10.3.2.3 gpio_set_level函数形参描述
返回值:ESP_OK表示设置成功,ESP_FAIL表示设置失败。
3,获取管脚电平
该函数用于获取某个管脚的电平,该函数原型如下所示:
esp_err_t gpio_get_level(gpio_num_t gpio_num);
该函数的形参描述如下表所示:
参数 | 描述 |
gpio_num | GPIO引脚号。(在gpio_types.h文件中枚举gpio_num_t有定义) |
表10.3.2.4 gpio_get_level函数形参描述
返回值:ESP_OK表示获取成功,ESP_FAIL表示获取失败。
上述函数,便是本实验所需的核心GPIO函数。对于其他未提及的GPIO函数,我们用到了再去了解。
10.3.3 LED驱动解析
在IDF版的01_led例程中,作者在01_led\components\BSP路径下新增了一个LED文件夹,用于存放led.c和led.h这两个文件。其中,led.h文件负责声明LED相关的函数和变量,而led.c文件则实现了LED的驱动代码。下面,我们将详细解析这两个文件的实现内容。
1,led.h文件
/* 引脚定义 */
#define LED_GPIO_PIN GPIO_NUM_1 /* LED连接的GPIO端口 */
/* 引脚的输出的电平状态 */
enum GPIO_OUTPUT_STATE
{
PIN_RESET,
PIN_SET
};
/* LED端口定义 */
#define LED(x) do { x ? \
gpio_set_level(LED_GPIO_PIN, PIN_SET) : \
gpio_set_level(LED_GPIO_PIN, PIN_RESET); \
} while(0) /* LED翻转 */
/* LED取反定义 */
#define LED_TOGGLE() do {
gpio_set_level(LED_GPIO_PIN, !gpio_get_level(LED_GPIO_PIN));
} while(0) /* LED翻转 */
/* 函数声明*/
void led_init(void); /* 初始化LED */
作者巧妙地编写了LED(x)宏,用于控制IO1管脚的电平状态。当x为1时,该宏会设置IO1管脚输出高电平;反之,则输出低电平。此外,作者还定义了LED_TOGGLE()宏,它能够实现IO1管脚电平状态的快速翻转。这些宏的实现均基于之前小节所介绍的函数,使得对LED的控制变得简洁而高效。
2,led.c文件
/**
* @retval 无
*/
void led_init(void)
{
gpio_config_t gpio_init_struct = {0};
gpio_init_struct.intr_type = GPIO_INTR_DISABLE; /* 失能引脚中断 */
gpio_init_struct.mode = GPIO_MODE_INPUT_OUTPUT; /* 输入输出模式 */
gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE; /* 使能上拉 */
gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE; /* 失能下拉 */
gpio_init_struct.pin_bit_mask = 1ull << LED_GPIO_PIN; /* 设置的引脚的位掩码*/
gpio_config(&gpio_init_struct); /* 配置GPIO */
LED(1); /* 关闭LED */
}
在.c文件中,作者首先对gpio_init_struct结构体变量进行了参数配置。接着,调用gpio_config函数,利用该配置变量完成了GPIO的初始化工作。最后,通过调用LED(x)宏定义,实现了LED灯的关闭操作,即输出了高电平信号。整个流程清晰、简洁,有效地实现了对LED灯的控制。
10.3.4 CMakeLists.txt文件
打开本章节的实验(01_LED),我们惊奇地发现,在components/BSP路径下定义了CMakeLists.txt文件(该文件的作用,作者已经在第四章中阐述过)。此文件的作用是将BSP文件夹下的驱动程序添加到构建系统中,确保在编译项目工程时能够调用这些驱动程序。下面展示了该驱动CMakeLists.txt文件的具体内容。
①源文件路径,指本目录下的所有代码驱动
set(src_dirs
LED)
②头文件路径,指本目录下的所有代码驱动
set(include_dirs
LED)
③设置依赖库
set(requires
driver)
idf_component_register(SRC_DIRS ${src_dirs}
INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})
component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
从上述描述中,我们了解到①和②处均需要添加相应的驱动程序。以本章节实验为例,在BSP文件夹下仅有一个LED驱动,因此我们需要在①和②的位置引入该LED驱动。而③处所提到的,是LED驱动所依赖的ESP-IDF中的具体驱动库。本实验中的LED驱动调用io.c和gpio.h文件下的函数实现的,这些文件均位于esp-idf-v5.1.2\components\driver文件夹中,因此我们可以确定LED驱动所依赖的是driver库。通过这样的依赖关系,我们可以确保LED驱动的正确集成和调用。
10.3.5 实验应用代码
打开main/main.c文件,该文件定义了工程入口函数,名为app_main。该函数代码如下。
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
esp_err_t ret;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES
|| ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
led_init(); /* 初始化LED */
while(1)
{
LED_TOGGLE();
vTaskDelay(500); /* 延时500ms */
}
}
上述应用代码中,作者首先通过调用nvs_flash_init函数来初始化NVS。若初始化时遇到没有足够空闲页面或检测到新版本的情况,代码会先擦除整个NVS分区,并随后重新进行初始化。这种处理方式旨在确保NVS在特定错误条件下能够被重置并重新使用。紧接着,代码调用led_init函数来初始化LED。在随后的while循环中,利用LED_TOGGLE()宏定义来定期翻转LED的电平状态,每次翻转间隔为500毫秒,从而实现了LED的闪烁效果。
10.4 下载验证
下载完之后,可以看到 LED以每次500ms闪烁。