1、ESP32 GPIO介绍
在之前使用Arduino IDE 通过ESP32进行各模块单独测试的时候就已经接触过了,但是在Arduino IDE的环境下只需要进行一下pinMode,设置一下端口引脚是输入输出即可。在ESP-IDF中对于GPIO的设置就较为复杂了,但复杂带来的是更加全面的功能。
如果想理解ESP32所有GPIO的功能以及使用参数可以参考ESP32-GPIO引脚参考大全。
在这片文章中可以看到ESP32所有的GPIO信息以及相关保留或是特殊GPIO的解读,写的非常详细。
关于GPIO中断的介绍,其实和51单片机以及其他单片机相似,总的来说看起来会相当乏味,本篇文章主要记录如何使用GPIO的中断,至于理论性的东西,还请移步至乐鑫官方对GPIO中断的介绍,其中也包括了GPIO的参数介绍,毕竟官方文档写出来的肯定是最全面(最啰嗦 最看不懂 最看不下去)的。乐鑫官方GPIO说明
1. GPIO的配置
- 引入头文件
如果需要对GPIO进行配置,就需要引用driver/gpio的头文件。
#include "driver/ledc.h"
- 对GPIO进行配置
//方法1
gpio_config_t io_conf; //创建GPIO控制结构体
//对GPIO进行专项设置
io_conf.intr_type //设置GPIO中断类型
io_conf.mode //设置GPIO是输出还是输入模式
io_conf.pin_bit_mask //表明设置的是哪一个GPIO 通过 1ULL << GPIOx进行位计算告知
io_conf.pull_down_en //设置下拉模式 0:否 1:是
io_conf.pull_up_en //设置上拉模式 0:否 1:是
gpio_config(&io_conf); //将上述配置进行生效
//方法2 与方法1几乎一致,只是写法不同
gpio_config_t io_conf1 = { //创建GPIO控制结构体
.intr_type, //设置GPIO中断类型
.mode, //设置GPIO是输出还是输入模式
.pin_bit_mask, //表明设置的是哪一个GPIO 通过 1ULL << GPIOx进行位计算告知
.pull_down_en, //设置下拉模式 0:否 1:是
.pull_up_en, //设置上拉模式 0:否 1:是
};
gpio_config(&io_conf1); //将上述配置进行生效
//方法3 单个设置
gpio_reset_pin(gpio_num_t gpio_num);//重置某个GPIO的配置
gpio_set_direction(gpio_num_tgpio_num, gpio_mode_tmode);//设置GPIO是输出还是输入模式
gpio_set_level(gpio_num_t gpio_num, uint32_t level);//设置GPIO输出高低电平
gpio_get_level(gpio_num_t gpio_num);//读取GPIO获取到的高低电平
gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull);//设置GPIO上拉还是下拉
gpio_pullup_en();
gpio_pullup_dis();
gpio_pulldown_en();
gpio_pulldown_dis();
2. GPIO中断官方示例详解
GPIO的中断流程可以简单概括为以下7个步骤:
- 编写中断消息发出函数 gpio_isr_handler
作用:向消息队列中发送中断消息。 - 编写中断消息处理函数
作用:在接收到消息队列中的中断消息后,按照传递来的参数和编写的中断语句执行对应的中断任务。 - 通过GPIO设置是否开启中断,以及中断方式说明
- 创建一个队列用于发送中断信息
- 创建中断任务处理
- 安装中断服务驱动
- 给指定的GPIO绑定中断服务函数
一个简单的GPIO中断例程就用官方所给的例程来演示即可,规范例程注释都是英语,我听过机器翻译和自己浅薄的英语只是将其翻译成中文,相信看完这个例程,大家也都会对GPIO的中断有一个基本的理解。
/**
* 演示效果:
* 两个输出GPIO口不断输出高低电平
* 两个输入GPIO口任意上下沿会触发中断
* 为了对比GPIO5只支持上升沿中断,而GPIO4上下沿都可以触发
* 最终GPIO4 每秒触发一次中断 GPIO5 每2秒触发一次中断
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#define GPIO_OUTPUT_IO_0 18
#define GPIO_OUTPUT_IO_1 19
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1)) //表示此配置对 GPIO 0 和 GPIO 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)) //表示此配置对 GPIO 0 和 GPIO 1 生效,采用位运算
#define ESP_INTR_FLAG_DEFAULT 0
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(;;) {
/*只要接收到了消息就开是对应任务处理*/
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
/*这里的任务就是打出一行话*/
printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
}
}
}
void app_main(void)
{
gpio_config_t io_conf; //创建GPIO控制结构体
//对GPIO进行专项设置
io_conf.intr_type = GPIO_INTR_DISABLE; //禁用GPIO中断
io_conf.mode = GPIO_MODE_OUTPUT; //设置GPIO为输出模式
io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL; //表明设置的GPIO内容是谁 这里是对GPIO_OUTPUT_IO_0 18和GPIO_OUTPUT_IO_1 19起作用
io_conf.pull_down_en = 0; //不设置为下拉模式
io_conf.pull_up_en = 0; //不设置为上拉模式
gpio_config(&io_conf); //将上述配置进行生效
//开始下一项GPIO的配置
io_conf.intr_type = GPIO_INTR_POSEDGE; //设置GPIO上升沿中断
io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL; //表明设置的GPIO内容是谁 这里是对GPIO_INPUT_IO_0 4和GPIO_INPUT_IO_1 5起作用
io_conf.mode = GPIO_MODE_INPUT; //设置GPIO为输入模式
io_conf.pull_up_en = 1; //设置为上拉模式
gpio_config(&io_conf); //将上述配置生效
gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE); //配置中断类型(中断引脚: GPIO4, 中断类型:任意边沿中断)
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t)); //创建一个队列用于发送中断信息 xQueueCreate参数:队列中包含最大项目数量, 队列中每个项目所要的字节数
//开始中断任务处理 传入参数(具体处理中断任务的函数, 任务函数名, 任务栈大小, 任务参数, 任务优先级, 任务句柄)
xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); //安装中断服务驱动(默认为参数优先级)
//给指定的GPIO绑定中断服务函数 参数(需要设置的GPIO号, 对应GPIO端口的中断消息产生函数, 传递给中断消息产生函数需要的参数)
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);
//一下两行,官方例程纯属为了展示gpio_isr_handler_remove的用法
gpio_isr_handler_remove(GPIO_INPUT_IO_0); //删除对应GPIO号的中断服务函数
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0); //重新添加回来
int cnt = 0;
while(1) {
printf("cnt: %d\n", cnt++);
vTaskDelay(1000 / portTICK_RATE_MS);
gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2);
gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);
}
}