GPIO,全称General-purpose input/output,通用输入输出,也就是芯片上的引脚。

GPIO的用途有很多:

  • 输出控制信号
  • 输入中断信号
  • 模拟I2C或SPI数据线
  • ADC电压采样
  • 输出PWM波形
  • 等等

可以参考这篇文章:硬件工程师必看:GPIO用法大汇总 - 知乎

GPIO默认为低电平

此测试代码演示如何配置 GPIO 以及如何在中断的情况下使用它。

由于本人近期不太可能用到矩阵键盘,因此矩阵键盘的例程就先放一放。

准备工作

准备两根跳线,连接GPIO18和GPIO4、GPIO19和GPIO5

在GPIO18/19产生脉冲,以在GPIO4/5上产生中断

GPIO 描述 配置
GPIO18 输出
GPIO19 输出
GPIO4 输入 pulled up, interrupt from rising edge and falling edge
GPIO5 输入 pulled up, interrupt from rising edge

宏定义与变量

#define GPIO_OUTPUT_IO_0    18
#define GPIO_OUTPUT_IO_1    19
//IO口的位掩码
#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))
#define GPIO_INPUT_IO_0     4
#define GPIO_INPUT_IO_1     5
#define GPIO_INPUT_PIN_SEL  ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))
#define ESP_INTR_FLAG_DEFAULT 0

//用于将信息从GPIO中断处理函数发送到GPIO任务函数的队列
static xQueueHandle gpio_evt_queue = NULL;

函数部分

//中断服务函数
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
    //将GPIO编号发送到队列末尾
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

static void gpio_task_example(void* arg)
{
    uint32_t io_num;
    for(;;) {
        //从队列接收GPIO编号
        if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
            //打印GPIO编号,并获取对应的电平
            printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
        }
    }
}

void app_main(void)
{
    //0初始化配置结构体
    gpio_config_t io_conf = {};
    //关中断
    io_conf.intr_type = GPIO_INTR_DISABLE;

/**********配置GPIO 18/19*************/
    //设置为输出模式
    io_conf.mode = GPIO_MODE_OUTPUT;
    //你想设置的GPIO的位掩码,e.g.GPIO18/19
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
    //禁用下拉
    io_conf.pull_down_en = 0;
    //禁用上拉
    io_conf.pull_up_en = 0;
    //使配置生效
    gpio_config(&io_conf);


/**********配置GPIO 4/5*************/
    //上升沿触发中断
    io_conf.intr_type = GPIO_INTR_POSEDGE;
    //你想设置的GPIO的位掩码, use GPIO4/5 here
    io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
    //set as input mode
    io_conf.mode = GPIO_MODE_INPUT;
    //设置为高电平
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);
    //将GPIO4设置为上升沿和下降沿都触发中断
    gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);

    //创建一个队列,用来处理来自中断服务函数的GPIO事件
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    //创建GPIO任务,分配2048字节给任务堆栈,优先级为10
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

    //安装GPIO中断服务函数
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    //为GPIO4/5添加中断服务函数句柄
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
    gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);

    //删除GPIO4的中断服务函数句柄
    gpio_isr_handler_remove(GPIO_INPUT_IO_0);
    //再次为GPIO4添加中断服务函数句柄
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);

    printf("Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size());

    int cnt = 0;
    while(1) {
        printf("cnt: %d\n", cnt++);
        vTaskDelay(1000 / portTICK_RATE_MS);
        //在GPIO18/19上产生脉冲
        gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2);
        gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);
    }
}
最后修改:2023 年 05 月 07 日
如果觉得我的文章对你有用,请随意赞赏