本篇文章我们再回到基础篇,难道你还以为我会讲UDP?啊哈哈哈,UDP肯定是会讲的,但是应用场景不是很多,我们放到后面再讲,不过也是简单一讲,毕竟熟悉UDP协议的人来说,都知道UDP一种不可靠的传输协议,可以这样形容“我(Client)只管发,你(Server)爱收不收”,所以在一些实际应用场景中是不多见的,大多数时候我们还是要保证数据传输的可靠性,准确性!好了,不多扯了,开始今天的简单却很重要的知识学习~
我们先来了解一下什么是GPIO吧,虽然经常说GPIO,但是你有没有真正的去深入了解一下呢?随着知识的不断积累,有些时候去深入的了解某些东西,会让你有很多新的收获。
- 什么是GPIO
简单来说就是通用输入输出端口,英文全称:General Purpose Input Output。通俗地说,就是一些引脚,可以通过它们输出高低电平,或者通过它们读入引脚的状态是高电平或是低电平。GPIO是个比较重要的概念,用户可以通过GPIO口和硬件进行数据交互(如UART),控制硬件工作(如LED、蜂鸣器等),读取硬件的工作状态信号(如中断信号)等。
- GPIO的八种模式
这里说的八种模式并不是指ESP8266的八种模式,据我所知ESP8266的GPIO口并没有这么多模式,这里给大家说的是STM32 GPIO的八种模式,STM32单片机的八种模式相对来说是非常全的了,我们这里可以了解一下。
- 模拟输入(GPIO_Mode_AIN)
- 浮空输入(GPIO_Mode_IN_FLOATING)
- 下拉输入(GPIO_Mode_IPD)
- 上拉输入(GPIO_Mode_IPU)
- 开漏输出(GPIO_Mode_Out_OD)
- 推挽输出(GPIO_Mode_Out_PP)
- 复用开漏输出(GPIO_Mode_Out_PP)
- 复用推挽输出(GPIO_Mode_AF_PP)
这里就不再详细展开叙述了,因为要把这八种模式都搞明白,一两句话是说不完了,有时间在跟大家分享。
- GPIO可以用来做什么
我们去学一个东西,最重要是知道这个东西可以做什么,最终要落实到实际使用嘛,如果这个GPIO没有实际使用价值,那我们也肯定不会去好好琢磨它了,简单来说,实际使用场景非常多,比如我们常见的只有开关状态的电子器件都可以用GPIO控制,或者间接使用GPIO去控制,举个栗子!LED灯,纳尼?怎么老是说一些跟灯有关的东西,啊哈哈哈哈,因为我还是比较喜欢闪闪发光的小玩意,我们都知道灯只有两种状态,开或者关(不要跟我讲还有坏了这种状态!),开跟关的状态其实就是有无电流经过它,而有无电流经过LED,最重要的是LED两端要有一个电压差,所以我们可以让某一个管脚输出高电平,这样就会在LED两端形成一定的电压差,自然而然的灯就亮了,大家可以暂时这么理解(这里只是讲给不怎么懂电子的人的~)。又比如还可以控制继电器、还可以获取按键状态,复杂一点的还可以模拟I2C总线,总之GPIO是很通用,但是也是非常重要的!
- ESP8266GPIO接口函数
其实单就ESP8266的GPIO口操作来说,我起初是很不适应的,如果你之前接触的是STM32单片机开发的话,你会感到不适应,但是慢慢的熟悉一下,会发现其实很简单,所以学习最重要的是摒弃固有思维,要能够思维活跃一些。我们先看一下手册当中对GPIO接口的说明,只有这个读懂了,我们才能更好的去使用它。
我这里把几个常用的给圈出来了,基本上我们使用这几个就足够了,GPIO中断这块大家可以好好看一下,有时候我们会用到外部中断的,我们使用管脚去控制一个LED灯的基本流程就是先PIN_FUNC_SELECT(PIN_NAME, FUNC),然后就可以输出高低电平或者设置为输入,或者去读取管脚状态,但是有一点我们需要注意的就是gpio16这个管脚,在官方的技术参考手册中有说明,给大家截图看一下:
什么意思呢?就是gpio16这个管脚不是GPIO这一伙里,使用它人家有自己的接口函数,不跟其他管脚使用同样的接口函数,这里大家需要注意一些,那么为什么这个管脚这么特殊呢?其实主要时为了低功耗,要知道Wi-Fi的功耗是很大,但是我们如果拿ESP8266做一些便携的设备,靠电池供电是不足以支撑很久的,所以在它没有什么事情的时候,我们需要它进入睡眠模式,这时候整个芯片会关闭除RTC以外的所有模块,此时工作电流是在μA级别,可以说是非常省电的,下面我们看一下官方是如何介绍的低功耗模式:
可以看出在深度睡眠模式下功耗是极低的,所以我们是可以利用ESP8266做一些可穿戴设备的,但是戴着出去的时候,我们要去连接哪一个Wi-Fi呢?这是很值得思考的,很显然不能手机开热点,那样我们手机的功耗可就大大提高了,不过最近据说有家国内公司叫连尚网络的公司发布了自家的Wi-Fi卫星,据说明年将会搭载长征系列火箭升空,估计到那时候可穿戴Wi-Fi设备才有真正的应用场景,目前还是基于BLE的居多。详情戳卡片:
我们看一下三种模式睡眠模式的功耗对比吧!
不对,好像又有点扯多了?怎么睡眠模式又写了这么多,好吧,转回正题,大家对低功耗感兴趣的话可以留言,改天我单独整理一篇文章详细介绍一下,要是没有那就先不整理了,啊哈哈哈哈~
我们正式开始我们的点灯、按键点灯、定时器点灯等点灯之旅,不过还有个问题,我们这里如果是使用的NodeMCU(默认大家是使用的NodeMCU)话,我们还需要了解一下管脚定义,因为NodeMCU采用的管脚命名与ESP8266的默认管脚命名是不一样的,所以我们需要对应起来,直接给大家上个图片看一下,建议大家收藏一下,省的以后找起来麻烦。
话不多说?上代码?得嘞,客官您里边请~
/************************
* STATIC VARIABLES *
************************/
os_timer_t blue_led_timer;
os_timer_t key_read_timer;
/************************
* STATIC FUNCTIONS *
************************/
/**
* LED定时反转函数,1s状态反转一次
*/
static void ICACHE_FLASH_ATTR
blue_led_timer_toggle(void){
os_timer_disarm(&blue_led_timer);//取消定时器
uint32 status = GPIO_INPUT_GET(GPIO_ID_PIN(2));//获取蓝灯管脚状态
GPIO_OUTPUT_SET(GPIO_ID_PIN(2),!status);//取反实现蓝灯管脚电平反转,从而实现亮灭操作
os_timer_arm(&blue_led_timer, 1000, true);//使能定时器
}
/**
* 按键定时状态读取函数,10ms获取一次按键状态
*/
static void ICACHE_FLASH_ATTR
key_read_value(void){
os_timer_disarm(&key_read_timer);//取消定时器
if(GPIO_INPUT_GET(GPIO_ID_PIN(14)) == 0x00){//如果按键按下,低电平
os_delay_us(20000);//延时20ms消抖
if(GPIO_INPUT_GET(GPIO_ID_PIN(14)) == 0x00){//再次判断状态,防止误触
uint32 status = GPIO_INPUT_GET(GPIO_ID_PIN(4));//获取当前管脚状态
GPIO_OUTPUT_SET(GPIO_ID_PIN(4),!status);//取反实现管脚电平反转
while(GPIO_INPUT_GET(GPIO_ID_PIN(14)));//等待按键松开
}
}
os_timer_arm(&key_read_timer, 10, true);//使能定时器
}
/**
*按键中断服务函数,高电平触发
*/
static void ICACHE_FLASH_ATTR
key_intr_handler(void){
uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);//读取GPIO寄存器状态,获取中断信息
uint8 level = 0;
GPIO_REG_WRITE(GPIO_STATUS_ADDRESS,gpio_status);//清楚中断信息
if(gpio_status & (BIT(15))){//判断是否是gpio15
if(GPIO_INPUT_GET(15)){//如果是高电平
GPIO_OUTPUT_SET(GPIO_ID_PIN(5),0);//熄灭红灯
GPIO_OUTPUT_SET(GPIO_ID_PIN(12),1);//熄灭黄灯
GPIO_OUTPUT_SET(GPIO_ID_PIN(4),0);//熄灭绿灯
GPIO_OUTPUT_SET(GPIO_ID_PIN(2),0);//熄灭蓝灯
os_timer_disarm(&key_read_timer);//取消按键定时器
os_timer_disarm(&blue_led_timer);//取消蓝灯定时反转定时器
}else{//如果是低电平
//不做处理
}
}else{//如果不是gpio15
//不做处理
}
}
/************************
* GLOBAL FUNCTIONS *
************************/
void ICACHE_FLASH_ATTR
gpio_test(void){
gpio_init();//初始化GPIO
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U,FUNC_GPIO5);//红灯
GPIO_OUTPUT_SET(GPIO_ID_PIN(5),1);//高电平点亮,常亮
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U,FUNC_GPIO12);//黄灯
GPIO_OUTPUT_SET(GPIO_ID_PIN(12),0);//低电平点亮,常亮
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U,FUNC_GPIO2);//蓝灯
os_timer_disarm(&blue_led_timer);
os_timer_setfn(&blue_led_timer, (os_timer_func_t *) blue_led_timer_toggle,NULL);//定时回调函数
os_timer_arm(&blue_led_timer, 1000, true);//设置时间为1s
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U,FUNC_GPIO4);//绿灯,按键触发,反转
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U,FUNC_GPIO14);//按键
GPIO_DIS_OUTPUT(GPIO_ID_PIN(14));//按键设置为输入模式
PIN_PULLUP_EN(PERIPHS_IO_MUX_MTMS_U);//引脚上拉使能
os_timer_disarm(&key_read_timer);
os_timer_setfn(&key_read_timer, (os_timer_func_t *) key_read_value,NULL);//定时回调函数
os_timer_arm(&key_read_timer, 10, true);//设置时间为10ms
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U,FUNC_GPIO15);//中断按键
GPIO_DIS_OUTPUT(GPIO_ID_PIN(15));//设置为输入模式
PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDO_U);//下拉使能
ETS_GPIO_INTR_DISABLE();//禁止所有GPIO中断
ETS_GPIO_INTR_ATTACH((void *)key_intr_handler,NULL);//注册GPIO中断处理函数
gpio_pin_intr_state_set(GPIO_ID_PIN(15),GPIO_PIN_INTR_HILEVEL);//设置高电平触发中断
ETS_GPIO_INTR_ENABLE();//使能所有GPIO中断
}
代码不是很多,都有注释,大家可以自己看一下,最后我们来看一下实际运行效果,请原谅我这不标准的普通话,应该不用给大家加字幕吧?啊哈哈哈,请大家谅解~
视频还在找托管方~
再附上一张手画原理图,电路连接相对简单,就简单一画,应该很好看懂的。
源码可以在这里下载:
欢迎大家Star,您的鼓励是我最大的动力,有问题可以私信我,或者提交issues~
本系列文章在知乎同步更新,知乎搜索专栏:IAMLIUBO的神奇物联网之旅
视频可以去知乎上的文章查看~
QQ交流群:592587184