目录
前言
一、硬件准备
二、使用步骤
1.硬件连接
2. 代码实现
2.1 LED初始化
2.2 KEY初始化
2.3 main函数
2.4 中断处理与任务函数
三 完整代码
总结
参考资料
前言
ESP32芯片具有34个物理GPIO引脚,除了一些具有特殊用途的GPIO外,大部分GPIO都可以复用。本文就通过点亮一盏LED灯并通过一个按键控制其亮灭来简单学习如何控制ESP32芯片GPIO的输入与输出。本文仅供个人参考学习之用,不做其他任何用途。
一、硬件准备
1.一块ESP32模组/开发板
2.一个(若干)RGB三色LED灯(或其它LED灯)
3.一个(若干)按键模块
4.一根USB线
5.杜邦线若干
二、使用步骤
1.硬件连接
ESP32 | LED | KEY |
3V3(VCC) | VCC | VCC |
GPIO18 | R | |
GPIO19 | G | |
GPIO21 | B | |
GPIO4 | DATA | |
GND |
在此示例中,LED和KEY都是采用共阳极接法,即GPIO输出高电平时LED处于熄灭状态,输出低电平时点亮LED,GPIO4在按键按下时处于高电平,松开时处于低电平。
2. 代码实现
gpio的初始化有两种方式:结构体初始化(led);函数配置(key);包括设置GPIO的输入/输出模式、中断及上、下拉的使能/失能.
2.1 LED初始化
gpio的输出模式主要有:推挽输出、开漏输出、复用开漏输出、复用推挽输出
#define GPIO_OUTPUT_IO_0 18
#define GPIO_OUTPUT_IO_1 19
#define GPIO_OUTPUT_IO_2 21
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1)| (1ULL<<GPIO_OUTPUT_IO_2))
/*gpio输出设置*/
#define LED(x) do{\
gpio_set_level(GPIO_OUTPUT_IO_0,x);\
gpio_set_level(GPIO_OUTPUT_IO_1,x);\
gpio_set_level(GPIO_OUTPUT_IO_2,x);\
}while(0);
/*
函数功能:led初始化
GPIO:18,19,21
返回值:无
特征:低电平亮,高电平不亮
*/
static void led_init(void)
{
/*GPIO配置*/
gpio_config_t io_conf;
//禁用中断
io_conf.intr_type = GPIO_INTR_DISABLE;
//设置为输出模式
io_conf.mode = GPIO_MODE_OUTPUT;
//设置想要设置的GPIO口(如18,19,21)
io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
//禁用下拉模式
io_conf.pull_down_en = 0;
//禁用上拉模式
io_conf.pull_up_en = 0;
//使用给定的设置配置 GPIO
gpio_config(&io_conf);
//让led灯处于熄灭状态(设置输出电平为高电平)
LED(1);
}
2.2 KEY初始化
gpio的输入模式主要有:上拉输入、下拉输入、浮空输入、模拟输入(adc)
#define GPIO_INPUT_IO_0 4
/*
函数功能:key初始化
GPIO:4
返回值:无
特征:不按低电平,按下高电平
*/
static void key_init(void)
{
//设置GPIO为输入模式
gpio_set_direction(GPIO_INPUT_IO_0,GPIO_MODE_INPUT);
//设置中断类型:上升沿中断
gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_POSEDGE);
//下拉
gpio_set_pull_mode(GPIO_INPUT_IO_0,GPIO_PULLDOWN_ONLY);
}
2.3 main函数
main函数的主要工作是完成LED和KEY的初始化,并创建队列来处理GPIO中断产生的事件。
#define ESP_INTR_FLAG_DEFAULT 0
static xQueueHandle gpio_evt_queue = NULL;
static void led_init(void);
static void key_init(void);
void app_main(void)
{
led_init();
key_init();
//创建一个队列来处理来自isr的gpio事件
gpio_evt_queue = xQueueCreate(1, sizeof(uint32_t));
//开始 gpio 任务
xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);
//添加 gpio isr 服务
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
//添加相应的 GPIO 引脚的 ISR 处理程序。
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
//删除相应的 GPIO 引脚的 ISR 处理程序
//gpio_isr_handler_remove(GPIO_INPUT_IO_0);
}
2.4 中断处理与任务函数
当按键按下时触发上升沿中断,由中断处理函数收集信息并通过消息队列交由任务函数完成最终的事件处理。
/*gpio中断处理函数*/
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
uint32_t gpio_num = (uint32_t) arg;
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
/*gpio任务函数*/
static void gpio_task_example(void* arg)
{
uint32_t io_num;
uint32_t i=1;
for(;;) {
//收到消息队列的消息时执行以下操作
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY))
{
if(gpio_get_level(io_num))//获得输入电平
{
vTaskDelay(10 / portTICK_RATE_MS);//按键消抖(延时)
if(gpio_get_level(io_num))//获得输入电平,高电平代表按键按下
{
//printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
i=!i;
LED(i);//切换LED状态
}
}
}
}
}
三 完整代码
/* LED 示例
此示例基于乐鑫ESP-IDF环境进行编译
硬件配置:WT-ESP32-DevKitC V4 开发板(ESP32-WROOM-32D)
RGB三色LED灯
普通按键模块
杜邦线若干
效果展示:按键按下,点亮LED灯,再次按下,熄灭LED灯
*/
#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"
/**
* 重要:
* 此测试代码展示了如何配置 gpio 以及如何使用 gpio 中断。
*
* GPIO 状态:
* GPIO18: 输出
* GPIO19: 输出
* GPIO21: 输出
* GPIO4: 输入,上拉,上升沿中断
*
* 连接:
* GPIO18,GPIO19,GPIO21与LED灯连接
* GPIO4 与按键连接
*
*/
#define GPIO_OUTPUT_IO_0 18
#define GPIO_OUTPUT_IO_1 19
#define GPIO_OUTPUT_IO_2 21
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1)| (1ULL<<GPIO_OUTPUT_IO_2))
#define GPIO_INPUT_IO_0 4
#define ESP_INTR_FLAG_DEFAULT 0
/*gpio输出设置*/
#define LED(x) do{\
gpio_set_level(GPIO_OUTPUT_IO_0,x);\
gpio_set_level(GPIO_OUTPUT_IO_1,x);\
gpio_set_level(GPIO_OUTPUT_IO_2,x);\
}while(0);
static xQueueHandle gpio_evt_queue = NULL;
static void led_init(void);
static void key_init(void);
/*gpio中断函数*/
static void IRAM_ATTR gpio_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;
uint32_t i=1;
for(;;) {
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY))
{
if(gpio_get_level(io_num))
{
vTaskDelay(10 / portTICK_RATE_MS);//按键消抖(延时)
if(gpio_get_level(io_num))//获得输入电平,高电平代表按键按下
{
printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
i=!i;
LED(i);
}
}
}
}
}
/*
函数功能:led初始化
GPIO:18,19,21
返回值:无
特征:低电平亮,高电平不亮
*/
static void led_init(void)
{
/*GPIO配置*/
gpio_config_t io_conf;
//禁用中断
io_conf.intr_type = GPIO_INTR_DISABLE;
//设置为输出模式
io_conf.mode = GPIO_MODE_OUTPUT;
//设置想要设置的GPIO口(如18,19,21)
io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
//禁用下拉模式
io_conf.pull_down_en = 0;
//禁用上拉模式
io_conf.pull_up_en = 0;
//使用给定的设置配置 GPIO
gpio_config(&io_conf);
//让led灯处于熄灭状态(设置输出电平为高电平)
LED(1);
}
/*
函数功能:key初始化
GPIO:4
返回值:无
特征:不按低电平,按下高电平
*/
static void key_init(void)
{
//设置GPIO为输入模式
gpio_set_direction(GPIO_INPUT_IO_0,GPIO_MODE_INPUT);
//设置中断类型:上升沿中断
gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_POSEDGE);
//下拉
gpio_set_pull_mode(GPIO_INPUT_IO_0,GPIO_PULLDOWN_ONLY);
}
void app_main(void)
{
led_init();
key_init();
//创建一个队列来处理来自isr的gpio事件
gpio_evt_queue = xQueueCreate(1, sizeof(uint32_t));
//开始 gpio 任务
xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);
//添加 gpio isr 服务
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
//添加相应的 GPIO 引脚的 ISR 处理程序。
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
//删除相应的 GPIO 引脚的 ISR 处理程序
//gpio_isr_handler_remove(GPIO_INPUT_IO_0);
}
总结
上图来源于参考博客,较好的展现了GPIO的控制逻辑,因此借用一下进行参考学习。