esp32 基于 ESP-IDF 框架 GPIO 的详细使用介绍

基于 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);
    }
}

关键注意事项

  1. 结构体初始化:必须使用 C99 指定初始化器确保所有字段被正确初始化,避免未定义行为。
  2. 中断处理
    • 中断服务函数需标记为 IRAM_ATTR,确保其存放在 IRAM 中。
    • 避免在中断服务中进行耗时操作,建议使用任务通知或队列传递事件。
  3. 多设备测试:若涉及多设备通信(如 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值