基于 ESP-IDF 框架的 ESP32 引脚配置相关 API 的详细介绍及代码示例, ESP-IDF 的版本是 v5.2.5 。
一、GPIO 配置核心 API
1. GPIO 配置结构体 gpio_config_t
用于定义 GPIO 的工作模式、上下拉、中断类型等参数:
/**
* @brief Configuration parameters of GPIO pad for gpio_config function
*/
typedef struct {
uint64_t pin_bit_mask; /*!< GPIO pin: set with bit mask, each bit maps to a GPIO */
gpio_mode_t mode; /*!< GPIO mode: set input/output mode */
gpio_pullup_t pull_up_en; /*!< GPIO pull-up */
gpio_pulldown_t pull_down_en; /*!< GPIO pull-down */
gpio_int_type_t intr_type; /*!< GPIO interrupt type */
#if SOC_GPIO_SUPPORT_PIN_HYS_FILTER
gpio_hys_ctrl_mode_t hys_ctrl_mode; /*!< GPIO hysteresis: hysteresis filter on slope input */
#endif
} gpio_config_t;
2. GPIO 初始化函数 gpio_config()
应用配置结构体参数:
/**
* @brief GPIO common configuration
*
* Configure GPIO's Mode,pull-up,PullDown,IntrType
*
* @param pGPIOConfig Pointer to GPIO configure struct
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
*
*/
esp_err_t gpio_config(const gpio_config_t *pGPIOConfig);
- 返回值:
ESP_OK
表示成功,ESP_FAIL
表示失败。
3. 设置输出电平 gpio_set_level()
/**
* @brief GPIO set output level
*
* @note This function is allowed to be executed when Cache is disabled within ISR context, by enabling `CONFIG_GPIO_CTRL_FUNC_IN_IRAM`
*
* @param gpio_num GPIO number. If you want to set the output level of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16);
* @param level Output level. 0: low ; 1: high
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG GPIO number error
*
*/
esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level);
- 参数:
gpio_num
为引脚编号,level
为 0(低电平)或 1(高电平)。
4. 读取输入电平 gpio_get_level()
/**
* @brief GPIO get input level
*
* @warning If the pad is not configured for input (or input and output) the returned value is always 0.
*
* @param gpio_num GPIO number. If you want to get the logic level of e.g. pin GPIO16, gpio_num should be GPIO_NUM_16 (16);
*
* @return
* - 0 the GPIO input level is 0
* - 1 the GPIO input level is 1
*
*/
int gpio_get_level(gpio_num_t gpio_num);
5. 中断服务相关 API
-
安装中断服务:
/** * @brief Install the GPIO driver's ETS_GPIO_INTR_SOURCE ISR handler service, which allows per-pin GPIO interrupt handlers. * * This function is incompatible with gpio_isr_register() - if that function is used, a single global ISR is registered for all GPIO interrupts. If this function is used, the ISR service provides a global GPIO ISR and individual pin handlers are registered via the gpio_isr_handler_add() function. * * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. * * @return * - ESP_OK Success * - ESP_ERR_NO_MEM No memory to install this service * - ESP_ERR_INVALID_STATE ISR service already installed. * - ESP_ERR_NOT_FOUND No free interrupt found with the specified flags * - ESP_ERR_INVALID_ARG GPIO error */ esp_err_t gpio_install_isr_service(int intr_alloc_flags); 其中:@param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. 参数: /** @brief Interrupt allocation flags * * These flags can be used to specify which interrupt qualities the * code calling esp_intr_alloc* needs. * */ //Keep the LEVELx values as they are here; they match up with (1<<level) #define ESP_INTR_FLAG_LEVEL1 (1<<1) ///< Accept a Level 1 interrupt vector (lowest priority) #define ESP_INTR_FLAG_LEVEL2 (1<<2) ///< Accept a Level 2 interrupt vector #define ESP_INTR_FLAG_LEVEL3 (1<<3) ///< Accept a Level 3 interrupt vector #define ESP_INTR_FLAG_LEVEL4 (1<<4) ///< Accept a Level 4 interrupt vector #define ESP_INTR_FLAG_LEVEL5 (1<<5) ///< Accept a Level 5 interrupt vector #define ESP_INTR_FLAG_LEVEL6 (1<<6) ///< Accept a Level 6 interrupt vector #define ESP_INTR_FLAG_NMI (1<<7) ///< Accept a Level 7 interrupt vector (highest priority) #define ESP_INTR_FLAG_SHARED (1<<8) ///< Interrupt can be shared between ISRs #define ESP_INTR_FLAG_EDGE (1<<9) ///< Edge-triggered interrupt #define ESP_INTR_FLAG_IRAM (1<<10) ///< ISR can be called if cache is disabled #define ESP_INTR_FLAG_INTRDISABLED (1<<11) ///< Return with this interrupt disabled #define ESP_INTR_FLAG_LOWMED (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3) ///< Low and medium prio interrupts. These can be handled in C. #define ESP_INTR_FLAG_HIGH (ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6|ESP_INTR_FLAG_NMI) ///< High level interrupts. Need to be handled in assembly. /** Mask for all level flags */ #define ESP_INTR_FLAG_LEVELMASK (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3| \ ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6| \ ESP_INTR_FLAG_NMI)
-
注册中断处理函数:
/** * @brief Add ISR handler for the corresponding GPIO pin. * * Call this function after using gpio_install_isr_service() to * install the driver's GPIO ISR handler service. * * The pin ISR handlers no longer need to be declared with IRAM_ATTR, * unless you pass the ESP_INTR_FLAG_IRAM flag when allocating the * ISR in gpio_install_isr_service(). * * This ISR handler will be called from an ISR. So there is a stack * size limit (configurable as "ISR stack size" in menuconfig). This * limit is smaller compared to a global GPIO interrupt handler due * to the additional level of indirection. * * @param gpio_num GPIO number * @param isr_handler ISR handler function for the corresponding GPIO number. * @param args parameter for ISR handler. * * @return * - ESP_OK Success * - ESP_ERR_INVALID_STATE Wrong state, the ISR service has not been initialized. * - ESP_ERR_INVALID_ARG Parameter error */ esp_err_t gpio_isr_handler_add(gpio_num_t gpio_num, gpio_isr_t isr_handler, void *args);
-
开启外部中断函数:
/** * @brief Enable GPIO module interrupt signal * * @note ESP32: Please do not use the interrupt of GPIO36 and GPIO39 when using ADC or Wi-Fi and Bluetooth with sleep mode enabled. * Please refer to the comments of `adc1_get_raw`. * Please refer to Section 3.11 of <a href="https://espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf">ESP32 ECO and Workarounds for Bugs</a> for the description of this issue. * * @param gpio_num GPIO number. If you want to enable an interrupt on e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error * */ esp_err_t gpio_intr_enable(gpio_num_t gpio_num);
二、GPIO 模式与配置选项
1. 工作模式 gpio_mode_t
typedef enum {
GPIO_MODE_DISABLE, // 禁用输入输出
GPIO_MODE_INPUT, // 输入模式
GPIO_MODE_OUTPUT, // 输出模式
GPIO_MODE_OUTPUT_OD, // 开漏输出模式
GPIO_MODE_INPUT_OUTPUT_OD, // 输入输出开漏模式
GPIO_MODE_INPUT_OUTPUT // 输入输出模式
} gpio_mode_t;
/** @cond */
#define GPIO_MODE_DEF_DISABLE (0)
#define GPIO_MODE_DEF_INPUT (BIT0) ///< bit mask for input
#define GPIO_MODE_DEF_OUTPUT (BIT1) ///< bit mask for output
#define GPIO_MODE_DEF_OD (BIT2) ///< bit mask for OD mode
/** @endcond */
typedef enum {
GPIO_MODE_DISABLE = GPIO_MODE_DEF_DISABLE, /*!< GPIO mode : disable input and output */
GPIO_MODE_INPUT = GPIO_MODE_DEF_INPUT, /*!< GPIO mode : input only */
GPIO_MODE_OUTPUT = GPIO_MODE_DEF_OUTPUT, /*!< GPIO mode : output only mode */
GPIO_MODE_OUTPUT_OD = ((GPIO_MODE_DEF_OUTPUT) | (GPIO_MODE_DEF_OD)), /*!< GPIO mode : output only with open-drain mode */
GPIO_MODE_INPUT_OUTPUT_OD = ((GPIO_MODE_DEF_INPUT) | (GPIO_MODE_DEF_OUTPUT) | (GPIO_MODE_DEF_OD)), /*!< GPIO mode : output and input with open-drain mode*/
GPIO_MODE_INPUT_OUTPUT = ((GPIO_MODE_DEF_INPUT) | (GPIO_MODE_DEF_OUTPUT)), /*!< GPIO mode : output and input mode */
} gpio_mode_t;
2. 中断触发类型 gpio_int_type_t
typedef enum {
GPIO_INTR_DISABLE = 0, // 禁用中断
GPIO_INTR_POSEDGE, // 上升沿触发
GPIO_INTR_NEGEDGE, // 下降沿触发
GPIO_INTR_ANYEDGE, // 双边沿触发
GPIO_INTR_LOW_LEVEL, // 低电平触发
GPIO_INTR_HIGH_LEVEL // 高电平触发
} gpio_int_type_t;
typedef enum {
GPIO_INTR_DISABLE = 0, /*!< Disable GPIO interrupt */
GPIO_INTR_POSEDGE = 1, /*!< GPIO interrupt type : rising edge */
GPIO_INTR_NEGEDGE = 2, /*!< GPIO interrupt type : falling edge */
GPIO_INTR_ANYEDGE = 3, /*!< GPIO interrupt type : both rising and falling edge */
GPIO_INTR_LOW_LEVEL = 4, /*!< GPIO interrupt type : input low level trigger */
GPIO_INTR_HIGH_LEVEL = 5, /*!< GPIO interrupt type : input high level trigger */
GPIO_INTR_MAX,
} gpio_int_type_t;
三、测试代码示例
示例 1:基本输入输出配置( 按键检测)
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "driver/gpio.h"
#define BUTTON_PIN GPIO_NUM_45
void app_main(void)
{
// 配置按键引脚为输入,启用上拉
gpio_config_t btn_config = {
.pin_bit_mask = (1ULL << BUTTON_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&btn_config);
while (1)
{
// 读取按键状态
int btn_state = gpio_get_level(BUTTON_PIN);
printf("btn_state =%d\n",btn_state);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
示例 2:中断处理(按键触发)
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#define BUTTON_PIN GPIO_NUM_45
static const char *TAG = "GPIO_ISR";
static unsigned char isr_flage = 0;
// 中断处理函数
ISR处理程序不再需要用IRAM_ATTR来声明,除非你在gpio_install_isr_service()中分配ISR时传递了ESP_INTR_FLAG_IRAM标志。
static void IRAM_ATTR button_isr_handler(void *arg)
{
isr_flage =1;
}
void app_main(void)
{
// 配置按键引脚为输入,启用上拉,下降沿触发中断
gpio_config_t btn_config = {
.pin_bit_mask = (1ULL << BUTTON_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_NEGEDGE
};
gpio_config(&btn_config);
// 安装中断服务
gpio_install_isr_service(0);
gpio_isr_handler_add(BUTTON_PIN, button_isr_handler, NULL);
gpio_intr_enable(BUTTON_PIN);
while (1)
{
if(isr_flage )
printf("isr\n");
else
printf("main\n");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
或者
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "freertos/queue.h"
#define BUTTON_PIN GPIO_NUM_45
static const char *TAG = "GPIO_ISR";
static QueueHandle_t gpio_evt_queue = NULL;
// 中断处理函数
//static void IRAM_ATTR button_isr_handler(void *arg)
static void button_isr_handler(void *arg)
{
uint32_t gpio_num = (uint32_t) arg;
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
static void gpio_task_example(void* arg)
{
uint32_t io_num;
for(;;)
{
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY))
{
printf("GPIO[%"PRIu32"] intr, val: %d\n", io_num, gpio_get_level(io_num));
}
}
}
void app_main(void)
{
// 配置按键引脚为输入,启用上拉,下降沿触发中断
gpio_config_t btn_config = {
.pin_bit_mask = (1ULL << BUTTON_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_NEGEDGE
};
gpio_config(&btn_config);
//create a queue to handle gpio event from isr
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
//start gpio task
xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);
// 安装中断服务
gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1);
gpio_isr_handler_add(BUTTON_PIN, button_isr_handler, (void*) BUTTON_PIN);
//gpio_intr_enable(BUTTON_PIN);
while (1)
{
//printf("main \n");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
关键注意事项
- 结构体初始化:必须使用 C99 指定初始化器确保所有字段被正确初始化,避免未定义行为。
- 中断处理:
- 中断服务函数需标记为
IRAM_ATTR
,确保其存放在 IRAM 中。 - 避免在中断服务中进行耗时操作,建议使用任务通知或队列传递事件。
- 中断服务函数需标记为
- 多设备测试:若涉及多设备通信(如 GPIO 同步),可使用
TEST_CASE_MULTIPLE_DEVICES
宏编写测试用例。
四、其它
- 开漏模式:用于总线通信(如 I2C),需外接上拉电阻。
- 低功耗配置:在休眠模式下,GPIO 状态可通过
gpio_hold_en()
保持。
参考:
ESP-IDF GPIO 文档:
https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-reference/peripherals/gpio.html
esp-idf/ examples / peripherals / gpio :
https://gitee.com/EspressifSystems/esp-idf/tree/v5.2.5/examples/peripherals/gpio
ESP-IDF 编程指南 》 API参考》外设 API 》GPIO &RTC GPIO :
https://docs.espressif.com/projects/esp-idf/zh_CN/v5.2.5/esp32/api-reference/peripherals/gpio.html
https://duruofu.github.io/ESP32-Guide/docs/guide/%E7%9B%AE%E5%BD%95.html