首页 > 技术文章 > 乐鑫Esp32学习之旅③ 认识并学习使用esp32的GPIO接口,开始点亮您的第一盏 LED和中断回调实现按键功能 。(附带Demo)

corehouse 2018-05-17 17:52 原文


  • 本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途。如有不对之处,请留言,本人及时更改。

1、 爬坑学习新旅程,虚拟机搭建esp32开发环境,打印 “Hellow World”。
2、 巧用eclipes编辑器,官方教程在在Windows下搭建esp32开发环境,打印 “Hellow World”。
3、 认识基本esp32的GPIO接口,开始点亮您的第一盏 LED和中断回调实现按键功能 。
4、体会esp32的强大的定时器功能, 实现定时2s闪烁一盏LED灯。
5、接触实践esp32的pwm宽度脉冲功能, 实现呼吸效果闪烁一盏LED灯。
6、smartConfig和微信airKiss在esp32的实现,一键配网轻松快捷连接路由器。
7、利用GPIO中断做一个按键的短按和长按的回调事件,再也无须担心触发源。
8、esp32上实现本地 UDP 客户端和服务端角色,在局域网内实现通讯。
9、esp32上实现本地 TCP 客户端和服务端角色,可断线重连原路返回数据。
10、乐鑫esp32 SDK编程利用rmt驱动ws2812七彩灯,实现彩虹渐变效果。
11、入门 乐鑫esp-adf 音频框架开发,esp32造一个蓝牙耳机,实现切换歌曲,获取歌曲信息等功能。
12、开源一个微信公众号airkiss配网esp32以及局域网发现功能的工程,分享一个airkiss配网小工具。
13、esp32 内置 dns 服务器,无需外网访问域名返回指定网页。
14、esp32 sdk编程实现门户强制认证,连接esp32热点之后,自动强制弹出指定的登录界面。
15、认识本地离线语音唤醒识别框架 esp-skainet ,实现较低成本的硬件语音本地识别控制。
16、学习本地语音唤醒离线识别框架 esp-skainet ,如何修改唤醒词? 如何自定义命令词?如何做意图动作?
17、全网首发,乐鑫esp32 sdk直连京东微联·小京鱼 · IoT开放平台,实现叮咚音响语音智能控制。

本篇博文目录:

一 、前言;

  • GPIO口一直是单片机的主要功能,今天小徐带来的是正是GPIO使用;本博文使用的是安信可的esp32s模组;
  • 本篇主要学习了怎么使用esp32的GPIO口,包括高低电平输入、高低电平输出和GPIO的中断使用;

二 、输出低电平,点亮一盏LED

  • 电路图接法如下图:

这里写图片描述


  • 第一种方法,较为简单:

gpio_pad_select_gpio(GPIO_NUM_16);//选择一个GPIO
gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT);//把这个GPIO作为输出
gpio_set_level(BLINK_GPIO, 0);//把这个GPIO输出低电平


  • 第二种方法,使用结构体来定义:
	gpio_config_t io_conf;
	//进制中断
	io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
	//选择为输出模式
	io_conf.mode = GPIO_MODE_OUTPUT;
	//配置GPIO_OUT寄存器
	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_set_level(BLINK_GPIO, 0); //第一个参数是GPIO,第二个是0或1

三 、获取某个GPIO的电平,并且打印出来;


  • 如果你要获取当前的电平状态,请把此配置io_conf.mode模式为GPIO_MODE_INPUT,表示为输入模式;见下面的源码可看到有多个输入输出模式,还有把此GPIO设置为不可用!可见esp32API丰富啊!

这里写图片描述

  • 下面的代码示范效果为: 每时隔 500ms读取GPIO16的输入电平状态,并且打印出来!
	//第一种方式配置
	//gpio_pad_select_gpio(BLINK_GPIO);
	//gpio_set_direction(BLINK_GPIO, GPIO_MODE_INPUT);

	//第二种方式配置
	gpio_config_t io_conf;
	//进制中断
	io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
	//选择为输出模式
	io_conf.mode = GPIO_MODE_INPUT;
	//配置GPIO_OUT寄存器
	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);

	// 挂起500ms
	const portTickType xDelay = 500 / portTICK_RATE_MS;

	while (1) {
		printf(" Current Gpio16 Level is : %d \r\n\r\n",
				gpio_get_level(BLINK_GPIO));
		vTaskDelay(xDelay);
	}

这里写图片描述


四 、配置某个GPIO的低/高电平触发事件,并且打印出来;


4.1:下降沿触发中断:

这里写图片描述


  • 下面的代码实现的效果的是:中断触发按键按下来,触发中断,执行回调函数;
  • 注意①:中断触发按键连接的是GNDGPIO14,因为是下降沿触发,所以必须一边接地!!
  • 注意②:代码中要设置输入模式,不下拉,内部上拉!原因在于内部上拉,就是高电平短脚,可以检测到下降沿。

    //GPIO口结构体定义
    gpio_config_t io_conf;
	//下降沿触发中断方式
	io_conf.intr_type = GPIO_INTR_NEGEDGE;
	//选择为输出模式
	io_conf.mode = GPIO_MODE_INPUT;
	//配置GPIO_OUT寄存器
	io_conf.pin_bit_mask = GPIO_SEL_4;
	//内部不下拉
	io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
	//内部上拉
	io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
	//最后配置使能
	gpio_config(&io_conf);

4.2:上升沿触发中断:

这里写图片描述


  • 下面的代码实现的效果的是:中断触发按键按下来,触发中断,执行回调函数;
  • 注意①:中断触发按键连接的是VCCGPIO14,因为是上升沿触发,所以必须一边接高电平!!
  • 注意②:代码中要设置输入模式,不上拉,内部下拉!原因在于内部下拉时候,就是低电平短脚输入,可以检测到上升沿。

	//GPIO口结构体定义
	gpio_config_t io_conf;
	//上升沿触发
	io_conf.intr_type = GPIO_INTR_POSEDGE;
	//选择为输出模式
	io_conf.mode = GPIO_MODE_INPUT;
	//配置GPIO_OUT寄存器
	io_conf.pin_bit_mask = GPIO_SEL_4;
	//内部下拉
	io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
	//禁止上拉
	io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
	//最后配置使能
	gpio_config(&io_conf);

  • 【其他】中断类型枚举:
GPIO_INTR_DISABLE //禁用GPIO中断
GPIO_INTR_POSEDGE //GPIO中断类型:上升沿
GPIO_INTR_NEGEDGE //下降沿
GPIO_INTR_ANYEDGE //上升沿和下降沿
GPIO_INTR_LOW_LEVEL //输入低电平触发
GPIO_INTR_HIGH_LEVEL //输入高电平触发

四 、配置某个GPIO的低/高电平触发事件,并且打印出来;


这里写图片描述


  • 嵌入式系统rtos的强大之处可以自行调度任务的优先级、任务的自由切换,最大程度的省下了MCU的空间资源。我也是初入rtos这趟深水,以上的图片是我个人的见解,如果有错,请留言,我立刻纠正!

过程如下:

  • ①:中断发生的中断,中断回调方法发送一个消息队列,消息队列包含了GPIO对应的端口号!
  • ②:另外一个任务不断从这个消息队列的句柄中获取消息并且可以做其他操作!
  • ③:上面可以看到,这是一个异步的操作,其中在串口中断的回调函数中,是加载在IRAM_ATTR中,注意连个简单的printf()函数调用都会报错。

  • 以下为完整的代码:
#include <stdio.h>
#include "esp_types.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"

//宏定义一个中断端口
#define GPIO_INPUT_IO_0     4
static xQueueHandle gpio_evt_queue = NULL; //定义一个队列返回变量

void IRAM_ATTR gpio_isr_handler(void* arg) {
	//把中断消息插入到队列的后面,将gpio的io参数传递到队列中
	uint32_t gpio_num = (uint32_t) arg;
	xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

//低电平触发的回调方法
void gpio_low_interrupt_callBack(void* arg) {
	printf(" \r\n into gpio_low_interrupt_callBack ...\r\n  ");
	uint32_t io_num;
	while (1) {
		//不断读取gpio队列,读取完后将删除队列
		if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
			printf("GPIO[%d] 中断触发, 当前的电压: %d\n", io_num,
					gpio_get_level(io_num));
		}
	}
}

void fun_set_gpio_low_interrupt() {

	//GPIO口结构体定义
	gpio_config_t io_conf;
	//下降沿触发
	io_conf.intr_type = GPIO_INTR_NEGEDGE;
	//选择为输出模式
	io_conf.mode = GPIO_MODE_INPUT;
	//配置GPIO_OUT寄存器
	io_conf.pin_bit_mask = GPIO_SEL_4;
	//设置下拉
	io_conf.pull_down_en = 0;
	//设置上拉
	io_conf.pull_up_en = 1;
	//最后配置使能
	gpio_config(&io_conf);

	//注册中断服务
	gpio_install_isr_service(1);
	//设置GPIO的中断回调函数
	gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler,
			(void*) GPIO_INPUT_IO_0);

	//创建一个消息队列,从中获取队列句柄
	gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));

	//新建队列的
	xTaskCreate(gpio_low_interrupt_callBack //任务函数
			, "gpio_task_example" //任务名字
			, 2048  //任务堆栈大小
			, NULL  //传递给任务函数的参数
			, 10   //任务优先级
			, NULL); //任務句柄

}

这里写图片描述


  • 本博文的代码工程下载:https://download.csdn.net/download/xh870189248/10423438

  • ESP8266学习之旅代码汇总,欢迎star:https://github.com/xuhongv/StudyInEsp8266

  • ESP32学习之旅代码汇总,欢迎star:https://github.com/xuhongv/StudyInEsp32

  • 小徐QQ交流群:434878850

推荐阅读