跳到主要内容

示例:GPIO 控制数字输出

重要提示:关于开发板的兼容性

本教程的核心逻辑适用于所有 ESP32 开发板,但所有操作步骤均以 微雪 ESP32-S3-Zero 迷你开发板 为例进行讲解。如果您使用其他型号的开发板,请根据实际情况修改相应设置。

本教程将介绍如何使用乐鑫 ESP-IDF 框架,通过 GPIO 控制数字输出,从而点亮和关闭 LED。

1. GPIO

在 ESP-IDF 中使用 GPIO 控制数字输出,通用步骤如下:

  1. 包含头文件

    首先需要包含 GPIO 驱动的头文件:

    #include "driver/gpio.h"

    并在 CMakeLists.txt 中添加依赖(如 REQUIRES esp_driver_gpio)。

  2. 配置 GPIO

    配置 GPIO 为输出模式。可以通过 gpio_config_t 结构体进行初始化,例如:

    gpio_config_t io_conf = {
    .pin_bit_mask = (1ULL << GPIO_NUM_7), // 选择要配置的 GPIO
    .mode = GPIO_MODE_OUTPUT, // 设置为输出模式
    .pull_up_en = GPIO_PULLUP_DISABLE, // 禁用上拉
    .pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用下拉
    .intr_type = GPIO_INTR_DISABLE // 禁用中断
    };
    gpio_config(&io_conf);
  3. 设置输出电平

    使用 gpio_set_level 控制 GPIO 输出高低电平:

    gpio_set_level(GPIO_NUM_7, 1); // 输出高电平
    gpio_set_level(GPIO_NUM_7, 0); // 输出低电平
  4. (可选)重置引脚

    可用 gpio_reset_pin(GPIO_NUM_7); 将引脚恢复到默认状态(例如高阻态)。这在需要动态改变引脚功能或进入低功耗模式前非常有用。

2. 示例项目

本示例将演示如何配置一个 GPIO 引脚为数字输出模式,并控制其循环输出高低电平,以实现 LED 灯的周期性闪烁。

2.1 电路

需要使用的器件有:

按照下面接线图连接电路:

ESP32-S3-Zero 引脚图

ESP32-S3-Zero-Pinout

接线图

2.2 创建项目

  1. 创建一个项目。如果不清楚如何操作,请参考 从模板创建项目

  2. 查看 GPIO API 参考。根据文档中的指引完成以下步骤。

    首先在 main.c 中包含头文件:

    #include "driver/gpio.h"

    然后在 main/CMakeLists.txt 中声明 esp_driver_gpio 组件:

    idf_component_register(SRCS "main.c"
    INCLUDE_DIRS "."
    REQUIRES esp_driver_gpio)

2.3 示例代码

#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

static const char *TAG = "example";

static const gpio_num_t GPIO_OUTPUT_LED = GPIO_NUM_7;

void app_main(void)
{
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << GPIO_OUTPUT_LED), // 选择要配置的 GPIO
.mode = GPIO_MODE_OUTPUT, // 设置为输出模式
.pull_up_en = GPIO_PULLUP_DISABLE, // 禁用上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用下拉
.intr_type = GPIO_INTR_DISABLE // 禁用中断
};
gpio_config(&io_conf);

while (1) {
// 设置 GPIO 高电平
ESP_LOGI(TAG, "Turn the LED on");
gpio_set_level(GPIO_OUTPUT_LED, 1);
vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1 秒

// 设置 GPIO 低电平
ESP_LOGI(TAG, "Turn the LED off");
gpio_set_level(GPIO_OUTPUT_LED, 0);
vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1 秒
}
}

2.4 构建并烧录

  1. 配置烧录选项

    首先,在构建和烧录之前,请务必检查并设置正确的目标设备、串口和烧录方式。参考 第2节 运行示例 - 1.3 配置项目

    VS Code 工具栏

  2. 点击 VS Code 一键构建烧录监视图标 一键自动依次执行构建、烧录和监视这三个步骤。

  3. 烧录完成后,可以看到开发板上的 LED 开始闪烁。同时,串口监视器会启动并输出如下日志信息:

    I (256) main_task: Started on CPU0
    I (266) main_task: Calling app_main()
    I (266) example: Turn the LED on
    I (1266) example: Turn the LED off
    I (2266) example: Turn the LED on
    I (3266) example: Turn the LED off
    I (4266) example: Turn the LED on
    I (5266) example: Turn the LED off
    ...

2.5 代码解析

本示例代码的核心是配置一个 GPIO 引脚为输出模式,并在一个无限循环中交替设置其电平为高和低,从而实现 LED 的闪烁效果。

  • 包含头文件

    #include "driver/gpio.h"
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    #include "esp_log.h"
    • driver/gpio.h:ESP-IDF GPIO 驱动的头文件,提供了配置和操作 GPIO 所需的所有函数和类型定义,如 gpio_config()gpio_set_level()
    • freertos/FreeRTOS.hfreertos/task.h:FreeRTOS 的核心头文件。这里主要用于调用 vTaskDelay() 函数,以实现精确且高效的延时,这在实时操作系统中是标准做法。
    • esp_log.h:ESP-IDF 的日志库,用于在串口监视器中打印信息,方便调试和观察程序运行状态。
  • 定义 GPIO 引脚

    static const gpio_num_t GPIO_OUTPUT_LED = GPIO_NUM_7;
    • 这行代码定义了一个常量 GPIO_OUTPUT_LED 来表示 LED 所连接的 GPIO 引脚号。
    • gpio_num_t 是 ESP-IDF 中用于表示 GPIO 编号的枚举类型。
    • GPIO_NUM_7 是该枚举中的一个取值,表示编号为 7 的通用 IO 引脚(GPIO7)。从可读性角度,建议使用 GPIO_NUM_7 而不是直接写字面值 7
    备注

    不同芯片对 GPIO7 的可用性与限制不同,请检查你所用的开发板的引脚定义。

  • 配置 GPIO

    gpio_config_t io_conf = {
    .pin_bit_mask = (1ULL << GPIO_OUTPUT_LED), // 选择要配置的 GPIO
    .mode = GPIO_MODE_OUTPUT, // 设置为输出模式
    .pull_up_en = GPIO_PULLUP_DISABLE, // 禁用上拉
    .pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用下拉
    .intr_type = GPIO_INTR_DISABLE // 禁用中断
    };
    gpio_config(&io_conf);
    • 我们首先创建并初始化一个 gpio_config_t 结构体 io_conf,它像一个“配置包”,包含了所有需要的设置。
    • .pin_bit_mask:这是一个位掩码,用于指定要配置哪些 GPIO 引脚。每一位 (bit) 对应一个 GPIO 编号。
      • 1ULL:表示一个 64 位无符号长整型(unsigned long long)的数字 1。在二进制中,它是 0x00...0001
      • <<:是 C 语言中的左移运算符。1ULL << N 的意思就是将这个数字 1 的二进制表示向左移动 N 位。
      • 在本例中,GPIO_OUTPUT_LED 的值是 7,所以 1ULL << 7 会将 0x00...0001 左移 7 位,得到 0x00...10000000(即第 7 位为 1,其余为 0)。 这个结果就是“掩码”。gpio_config() 函数会检查这个掩码的每一位,如果某一位是 1,就将该配置应用到对应的 GPIO 引脚上。
    • .mode = GPIO_MODE_OUTPUT:将引脚模式设置为数字输出。
    • .pull_up_en.pull_down_en:禁用内部上拉和下拉电阻。对于驱动 LED 这种简单的推挽输出场景,通常不需要它们。
    • .intr_type = GPIO_INTR_DISABLE:禁用中断功能,因为我们只进行输出操作,不需要响应外部信号。
    • 最后,调用 gpio_config(&io_conf) 函数,将这个配置应用到指定的 GPIO 引脚上。

    提示

    配置 GPIO 有两种主要方式:

    1. 使用 gpio_config_t 结构体(推荐): 这是最常用、最高效的方法。您可以将引脚掩码、I/O 模式、上下拉等所有参数打包到一个 gpio_config_t 结构体中,然后只需调用一次 gpio_config() 函数,就能同时为一个或多个 GPIO 完成配置。

    2. 使用独立函数: ESP-IDF 也提供了一系列独立的 API 函数,如 gpio_set_direction()gpio_set_pull_mode() 等,用于单独设置某个引脚的特定属性。这种方式更灵活,适合在运行时动态修改或进行简单的单项设置。

      对于本教程的示例,如果采用独立函数进行配置,代码如下:

      gpio_reset_pin(GPIO_OUTPUT_LED);                       // 重置引脚
      gpio_set_direction(GPIO_OUTPUT_LED, GPIO_MODE_OUTPUT); // 设置引脚为输出模式
  • 主循环:控制 LED 闪烁

    while (1) {
    // 设置 GPIO 高电平
    ESP_LOGI(TAG, "Turn the LED on");
    gpio_set_level(GPIO_OUTPUT_LED, 1);
    vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1 秒

    // 设置 GPIO 低电平
    ESP_LOGI(TAG, "Turn the LED off");
    gpio_set_level(GPIO_OUTPUT_LED, 0);
    vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1 秒
    }
    • while (1) 创建了一个无限循环,确保 app_main 函数不会退出,并使其中的代码持续运行。
    • gpio_set_level(GPIO_OUTPUT_LED, 1):调用此函数将 GPIO 7 的电平设置为高电平(通常是 3.3V)。根据电路连接,这会点亮 LED。
    • gpio_set_level(GPIO_OUTPUT_LED, 0):将 GPIO 7 的电平设置为低电平(0V),从而熄灭 LED。
    • vTaskDelay(pdMS_TO_TICKS(1000)):这是 FreeRTOS 提供的延时函数。它会使当前任务(主任务)暂停指定的时长。与简单的忙等待(如 for 循环)不同,vTaskDelay 会让出 CPU 使用权,允许其他任务运行,是一种非常高效的延时方式。pdMS_TO_TICKS(1000) 是一个宏,用于将 1000 毫秒(1 秒)转换为 FreeRTOS 调度器所使用的“系统节拍数 (ticks)”。

3. 参考链接

ESP-IDF 编程指南 - ESP32-S3 GPIO API 参考