物联网专题10:ESP8266 GPIO 输出输入
GPIO输出
开发板原理图如下所示:
引脚功能配置:
在使用 stm32 时,引脚都有很多的复用功能,当我们想要使用引脚的某个功能(如I2C SPI)等,就需要配置引脚的功能。ESP6266也是如此,下面列出了8266引脚的复用功能:
如上图所示,这个引脚的功能1是N0RXD(串口0数据接收引脚),功能2是I2SO_DATA(I2S数据引脚)、功能4是普通的GPIO3、功能5是CLK_XTAL(时钟晶振引脚),然后这个功能配置寄存器的地址如下所示:
在技术参考手册中,也有配置功能的描述:
由上面的分析可知,如果我们希望使用功能4配置为GPIO3,则要往寄存器对应位写入3。在 eagle_soc.h 头文件中,有设置寄存器地址和功能的宏定义:
上图的地址和写入值,都跟我们的分析相一致,使用 PIN_FUNC_SELECT 语句来选择引脚功能。
我们来分析一下 PIN_FUNC_SELECT 这个语句:
可以看到,这个宏定义其实就是往选定的引脚配置寄存器中,写入想要配置的功能(GPIO I2C SPI等)。
配置GPIO输出的过程如下:
1 先选定一个GPIO管脚,用 PIN_FUNC_SELECT(PIN_NAME, FUNC) 函数。
比如我选定GPIO14这个管脚,则这样写:PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14);
这里的参数,在下面可以找到:
2 设置GPIO为输出模式,并设置电平;
如果你设置GPIO14这个管脚为高电平,则这样写:GPIO_OUTPUT_SET(GPIO_ID_PIN(14), 1);
GPIO_OUTPUT_SET 这个函数,第一个参数为GPIO引脚编号,定义如下所示:
这里,GPIO引脚编号用了一个宏定义来表示,实际上直接就是IO口的序号,GPIO理论部分结束。
点亮流水灯实验:
代码部分非常简单,如下所示:
编译程序并下载,LED灯确实在循环的闪烁,并在串口有调试信息输出:
GPIO输入
接下来使用GPIO输入来进行按键检测,首先看下原理图:
可以看到,BOOT引脚接到了GPIO0,默认被10K电阻上拉为高电平;当按键按下时,GPIO0变为低电平。
配置输入模式的步骤:
1 调用PIN_FUNC_SELECT函数,配置引脚功能为GPIO;
如配置GPIO0引脚为IO口,则使用语句PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0);
2 配置GPIO引脚为输入模式;
3 设置是否开启GPIO内部上拉;
GPIO输入 按键检测实验:
由于我手中的开发板,好像确实是有硬件问题,所以无论是自己写的或者是提供的例程,这个按键检测实验都失败了。
EXTI 外部中断
ESP8266的EXTI外部中断配置,其实很简单:
可以看到,前面的配置和基本的GPIO输入模式相同,接下来就是配置外部中断:
1 调用ETS_GPIO_INTR_DISABLE(函数位于ets_sys.h头文件),先关闭GPIO中断,以进行配置;
2 调用ETS_GPIO_INTR_ATTACH这个API,注册中断回调函数(中断函数要自己实现);
3 设置为下降沿中断;
其中,可以设置的中断源如下所示:
4 使能EXTI中断;
5 编写中断服务函数(主要部分);
下面是中断服务函数的主体,来进行逐句分析:
首先是两个宏定义,用于读取寄存器和写入寄存器,如下图所示:
GPIO_REG_READ用于读取寄存器值,GPIO_REG_WRITE向寄存器写入值。
接下来,看一下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(); // 喂狗,防止复位
}
}
上一篇: iOS 开发实训第五周周报
下一篇: iOS 开发实训第八周周报