欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

物联网专题10:ESP8266 GPIO 输出输入

程序员文章站 2022-04-11 13:17:54
...

GPIO输出

开发板原理图如下所示:

物联网专题10:ESP8266 GPIO 输出输入

引脚功能配置:

在使用 stm32 时,引脚都有很多的复用功能,当我们想要使用引脚的某个功能(如I2C SPI)等,就需要配置引脚的功能。ESP6266也是如此,下面列出了8266引脚的复用功能:

物联网专题10:ESP8266 GPIO 输出输入

如上图所示,这个引脚的功能1是N0RXD(串口0数据接收引脚),功能2是I2SO_DATA(I2S数据引脚)、功能4是普通的GPIO3、功能5是CLK_XTAL(时钟晶振引脚),然后这个功能配置寄存器的地址如下所示:

物联网专题10:ESP8266 GPIO 输出输入

在技术参考手册中,也有配置功能的描述:

物联网专题10:ESP8266 GPIO 输出输入

由上面的分析可知,如果我们希望使用功能4配置为GPIO3,则要往寄存器对应位写入3。在 eagle_soc.h 头文件中,有设置寄存器地址和功能的宏定义:

物联网专题10:ESP8266 GPIO 输出输入

上图的地址和写入值,都跟我们的分析相一致,使用 PIN_FUNC_SELECT 语句来选择引脚功能。

我们来分析一下 PIN_FUNC_SELECT 这个语句:

物联网专题10:ESP8266 GPIO 输出输入

可以看到,这个宏定义其实就是往选定的引脚配置寄存器中,写入想要配置的功能(GPIO I2C SPI等)。

 

配置GPIO输出的过程如下:

1 先选定一个GPIO管脚,用 PIN_FUNC_SELECT(PIN_NAME, FUNC) 函数。

比如我选定GPIO14这个管脚,则这样写:PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14);

这里的参数,在下面可以找到:

物联网专题10:ESP8266 GPIO 输出输入

2 设置GPIO为输出模式,并设置电平;

物联网专题10:ESP8266 GPIO 输出输入

如果你设置GPIO14这个管脚为高电平,则这样写:GPIO_OUTPUT_SET(GPIO_ID_PIN(14), 1);

GPIO_OUTPUT_SET 这个函数,第一个参数为GPIO引脚编号,定义如下所示:

物联网专题10:ESP8266 GPIO 输出输入

这里,GPIO引脚编号用了一个宏定义来表示,实际上直接就是IO口的序号,GPIO理论部分结束。

 

点亮流水灯实验:

代码部分非常简单,如下所示:

物联网专题10:ESP8266 GPIO 输出输入

编译程序并下载,LED灯确实在循环的闪烁,并在串口有调试信息输出:

物联网专题10:ESP8266 GPIO 输出输入

 

GPIO输入

接下来使用GPIO输入来进行按键检测,首先看下原理图:

物联网专题10:ESP8266 GPIO 输出输入

物联网专题10:ESP8266 GPIO 输出输入

可以看到,BOOT引脚接到了GPIO0,默认被10K电阻上拉为高电平;当按键按下时,GPIO0变为低电平。

 

配置输入模式的步骤:

1 调用PIN_FUNC_SELECT函数,配置引脚功能为GPIO;

如配置GPIO0引脚为IO口,则使用语句PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0);

2 配置GPIO引脚为输入模式;

物联网专题10:ESP8266 GPIO 输出输入

3 设置是否开启GPIO内部上拉;

物联网专题10:ESP8266 GPIO 输出输入

 

GPIO输入 按键检测实验:

物联网专题10:ESP8266 GPIO 输出输入

由于我手中的开发板,好像确实是有硬件问题,所以无论是自己写的或者是提供的例程,这个按键检测实验都失败了。

 

EXTI 外部中断

ESP8266的EXTI外部中断配置,其实很简单:

物联网专题10:ESP8266 GPIO 输出输入

可以看到,前面的配置和基本的GPIO输入模式相同,接下来就是配置外部中断:

1 调用ETS_GPIO_INTR_DISABLE(函数位于ets_sys.h头文件),先关闭GPIO中断,以进行配置;

物联网专题10:ESP8266 GPIO 输出输入

2 调用ETS_GPIO_INTR_ATTACH这个API,注册中断回调函数(中断函数要自己实现);

物联网专题10:ESP8266 GPIO 输出输入

3 设置为下降沿中断;

物联网专题10:ESP8266 GPIO 输出输入

其中,可以设置的中断源如下所示:

物联网专题10:ESP8266 GPIO 输出输入

4 使能EXTI中断;

5 编写中断服务函数(主要部分);

下面是中断服务函数的主体,来进行逐句分析:

物联网专题10:ESP8266 GPIO 输出输入

首先是两个宏定义,用于读取寄存器和写入寄存器,如下图所示:

物联网专题10:ESP8266 GPIO 输出输入

GPIO_REG_READ用于读取寄存器值,GPIO_REG_WRITE向寄存器写入值。

接下来,看一下GPIO相关的寄存器描述:

物联网专题10:ESP8266 GPIO 输出输入

这就是以下代码的功能描述:

	S_GPIO_INT = GPIO_REG_READ(GPIO_STATUS_ADDRESS);	// 读取GPIO中断状态
	// 清除中断标志位,如果不清除标志位,则会持续进入中断
	GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, S_GPIO_INT);	// 向GPIO_OUT_W1TC写1会清除中断标志位

	F_GPIO_0_INT = S_GPIO_INT & (0X0001 << 0);	// 获取GPIO0的中断状态

接下来,判断是否有中断,然后翻转LED,如下所示:

// 未作按键消抖
if (F_GPIO_0_INT)	// GPIO0的下降沿中断
{
	F_LED = !F_LED;
	GPIO_OUTPUT_SET(GPIO_ID_PIN(4), F_LED);	// LED状态翻转
}

程序编译下载,能够正确执行。每次按键按下时,LED进行状态翻转,不会出现之前GPIO输入模式时的错误情况。

整个工程代码如下:

/*
 * ESPRESSIF MIT License
 *
 * Copyright (c) 2016 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
 *
 * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
 * it is free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

#include "ets_sys.h"
#include "osapi.h"

#include "user_interface.h"

#include "driver/uart.h"
#include "driver/uart_register.h"

/******************************************************************************
 * FunctionName : user_rf_cal_sector_set
 * Description  : SDK just reversed 4 sectors, used for rf init data and paramters.
 *                We add this function to force users to set rf cal sector, since
 *                we don't know which sector is free in user's application.
 *                sector map for last several sectors : ABCCC
 *                A : rf cal
 *                B : rf init data
 *                C : sdk parameters
 * Parameters   : none
 * Returns      : rf cal sector
*******************************************************************************/
uint32 ICACHE_FLASH_ATTR
user_rf_cal_sector_set(void)
{
    enum flash_size_map size_map = system_get_flash_size_map();
    uint32 rf_cal_sec = 0;

    switch (size_map) {
        case FLASH_SIZE_4M_MAP_256_256:
            rf_cal_sec = 128 - 5;
            break;
        case FLASH_SIZE_8M_MAP_512_512:
            rf_cal_sec = 256 - 5;
            break;
        case FLASH_SIZE_16M_MAP_512_512:
            rf_cal_sec = 512 - 5;
            break;
        case FLASH_SIZE_16M_MAP_1024_1024:
            rf_cal_sec = 512 - 5;
            break;
        case FLASH_SIZE_32M_MAP_512_512:
            rf_cal_sec = 1024 - 5;
            break;
        case FLASH_SIZE_32M_MAP_1024_1024:
            rf_cal_sec = 1024 - 5;
            break;
        case FLASH_SIZE_64M_MAP_1024_1024:
            rf_cal_sec = 2048 - 5;
            break;
        case FLASH_SIZE_128M_MAP_1024_1024:
            rf_cal_sec = 4096 - 5;
            break;
        default:
            rf_cal_sec = 0;
            break;
    }

    return rf_cal_sec;
}



void ICACHE_FLASH_ATTR
user_rf_pre_init(void)
{
}

uint8_t F_LED = 0;

#define		ProjectName		"GPIO_EXTI"		//工程名宏定义

// 自定义的毫秒延时函数(不要延时太久)
void ICACHE_FLASH_ATTR
bsp_delay_ms(uint16_t ms)
{
	for (; ms > 0; ms--)
	{
		os_delay_us(1000);
	}
}

// GPIO中断回调函数(注意:函数名前不要有ICACHE_FLASH_ATTR宏定义)
void GPIO_INTERRUPT(void)
{
	uint32_t S_GPIO_INT;			// 所有IO口的中断状态
	uint32_t F_GPIO_0_INT;			// GPIO0的中断状态

	S_GPIO_INT = GPIO_REG_READ(GPIO_STATUS_ADDRESS);	// 读取GPIO中断状态
	// 清除中断标志位,如果不清除标志位,则会持续进入中断
	GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, S_GPIO_INT);	// 向GPIO_OUT_W1TC写1会清除中断标志位

	F_GPIO_0_INT = S_GPIO_INT & (0X0001 << 0);	// 获取GPIO0的中断状态

	// 未作按键消抖
	if (F_GPIO_0_INT)	// GPIO0的下降沿中断
	{
		F_LED = !F_LED;
		GPIO_OUTPUT_SET(GPIO_ID_PIN(4), F_LED);	// LED状态翻转
	}

}


/******************************************************************************
 * FunctionName : user_init
 * Description  : entry of user application, init user function here
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR
user_init(void)
{
	uart_init(115200,115200);	// 初始化串口波特率
	os_delay_us(10000);			// 等待串口稳定
	os_printf("\r\n=================================================\r\n");
	os_printf("\t Project:\t%s\r\n", ProjectName);
	os_printf("\t SDK version:\t%s", system_get_sdk_version());
	os_printf("\r\n=================================================\r\n");

	// 初始化LED
	PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4); // 将GPIO4设为IO口
	GPIO_OUTPUT_SET(GPIO_ID_PIN(4), 1);					 // GPIO4设置为输出模式,高电平

	// 初始化按键 BOOT == GPIO0
	PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0); // 将GPIO0设为IO口
	GPIO_DIS_OUTPUT(GPIO_ID_PIN(0));		// 失能GPIO0输出(默认),设为输入模式
	//PIN_PULLUP_DIS(PERIPHS_IO_MUX_GPIO0_U); // 失能GPIO0内部上拉(默认),由硬件电路上拉

	// GPIO0中断配置
	ETS_GPIO_INTR_DISABLE();										// 先关闭GPIO中断
	ETS_GPIO_INTR_ATTACH((ets_isr_t)GPIO_INTERRUPT, NULL);			// 注册中断回调函数
	gpio_pin_intr_state_set(GPIO_ID_PIN(0), GPIO_PIN_INTR_NEGEDGE);	// GPIO0 下降沿中断
	ETS_GPIO_INTR_ENABLE();											// 使能GPIO中断


	while(1)
	{
		system_soft_wdt_feed();		// 喂狗,防止复位

	}
}