GPIO的输入有很多的类型,当初51单片机还得加上拉和下拉电阻,现在大部分都可以直接用代码就可以配置了,这次主要记录一下ESP8266的三种输入模式分别是:上拉输入、下拉输入和中断函数。
再介绍一下我自己画的板子,输出我用了一个三色灯,而对于输入我只有启动按钮和复位按钮两种,没有其他输入按钮,好在启动按钮在启动后就不再需要了,我们就可以配置为输入按钮了,但是我再启动按钮上面增加了上拉电阻,在这里还得提醒一句,非常重要:
数字引脚0-15可设置为INPUT、OUTPUT、INPUT_PULLUP模式(输入、输出、上拉输入);数字引脚16可设置为INPUT、OUTPUT、INPUT_PULLDOWN_16模式(输入、输出、下拉输入);启动时,这些引脚默认配置为INPUT模式;
所以说,在ESP8266中除了特殊的需求外,该接上拉电阻还是下拉电阻都是需要接入的。其实,在其他的芯片中也理当如此,方便接入还是需要接入的。
无中断输入
再次进行修改预定义
#define GPIO_INPUT_IO 0
#define GPIO_INPUT_PIN_SEL (1ULL << GPIO_INPUT_IO)
配置输入模式:
gpio_config_t io_input_conf;
io_input_conf.intr_type = GPIO_INTR_DISABLE;
io_input_conf.mode = GPIO_MODE_INPUT;
io_input_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
io_input_conf.pull_down_en = 0;
io_input_conf.pull_up_en = 1;
gpio_config(&io_input_conf);
完整代码在下面:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#define GPIO_INPUT_IO 0
#define GPIO_INPUT_PIN_SEL (1ULL << GPIO_INPUT_IO)
#define GPIO_OUTPUT_IO_0 2
#define GPIO_OUTPUT_IO_1 4
#define GPIO_OUTPUT_IO_2 5
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1) | (1ULL<<GPIO_OUTPUT_IO_2))
void app_main()
{
uart_set_baudrate(0, 74880);
printf("Esp8266 Hello world!\n");
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
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_config_t io_input_conf;
io_input_conf.intr_type = GPIO_INTR_DISABLE;
io_input_conf.mode = GPIO_MODE_INPUT;
io_input_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
io_input_conf.pull_down_en = 0;
io_input_conf.pull_up_en = 1;
gpio_config(&io_input_conf);
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
printf("This is ESP8266 chip with %d CPU cores, WiFi, ",
chip_info.cores);
printf("silicon revision %d, ", chip_info.revision);
printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
while(true) {
printf("gpio %d.\n", gpio_get_level(GPIO_INPUT_IO));
gpio_set_level(GPIO_OUTPUT_IO_0, 1);
vTaskDelay(500 / portTICK_PERIOD_MS);
gpio_set_level(GPIO_OUTPUT_IO_0, 0);
gpio_set_level(GPIO_OUTPUT_IO_1, 1);
vTaskDelay(500 / portTICK_PERIOD_MS);
gpio_set_level(GPIO_OUTPUT_IO_1, 0);
gpio_set_level(GPIO_OUTPUT_IO_2, 1);
vTaskDelay(500 / portTICK_PERIOD_MS);
gpio_set_level(GPIO_OUTPUT_IO_2, 0);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
fflush(stdout);
esp_restart();
}
我们可以查看串口的输出内容:
如果我们按下,那么GPIO处就显示为0,否则就是1.
接下来我们继续进行讨论,使用轮询的方式去读取GPIO的输入有2个问题:
1.轮询会占用不少的CPU
2.轮询的方式会因为CPU处理其他内容而导致实时性降低
所以,在很多情况下使用中断输入模式会更加好一些。
中断输入
也一样需要把预定义修改为非中断输入一样的,然后我们需要中断函数。
static xQueueHandle gpio_evt_queue = NULL;
static void 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;
for (;;) {
if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
}
}
}
接下来,我们还需要对GPIO进行配置:
gpio_config_t io_input_conf;
io_input_conf.intr_type = GPIO_INTR_NEGEDGE;
io_input_conf.mode = GPIO_MODE_INPUT;
io_input_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
io_input_conf.pull_up_en = 1;
gpio_config(&io_input_conf);
然后粘贴完整代码:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#define GPIO_INPUT_IO 0
#define GPIO_INPUT_PIN_SEL (1ULL << GPIO_INPUT_IO)
#define GPIO_OUTPUT_IO_0 2
#define GPIO_OUTPUT_IO_1 4
#define GPIO_OUTPUT_IO_2 5
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1) | (1ULL<<GPIO_OUTPUT_IO_2))
static xQueueHandle gpio_evt_queue = NULL;
static void 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;
for (;;) {
if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
}
}
}
void app_main()
{
uart_set_baudrate(0, 74880);
printf("Esp8266 Hello world!\n");
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
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_config_t io_input_conf;
io_input_conf.intr_type = GPIO_INTR_NEGEDGE;
io_input_conf.mode = GPIO_MODE_INPUT;
io_input_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
io_input_conf.pull_up_en = 1;
gpio_config(&io_input_conf);
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
printf("This is ESP8266 chip with %d CPU cores, WiFi, ",
chip_info.cores);
printf("silicon revision %d, ", chip_info.revision);
printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
xTaskCreate(gpio_task_example, "gpio_task_example", 1024, NULL, 10, NULL);
gpio_install_isr_service(0);
gpio_isr_handler_add(GPIO_INPUT_IO, gpio_isr_handler, (void *) GPIO_INPUT_IO);
while(true) {
gpio_set_level(GPIO_OUTPUT_IO_0, 1);
vTaskDelay(500 / portTICK_PERIOD_MS);
gpio_set_level(GPIO_OUTPUT_IO_0, 0);
gpio_set_level(GPIO_OUTPUT_IO_1, 1);
vTaskDelay(500 / portTICK_PERIOD_MS);
gpio_set_level(GPIO_OUTPUT_IO_1, 0);
gpio_set_level(GPIO_OUTPUT_IO_2, 1);
vTaskDelay(500 / portTICK_PERIOD_MS);
gpio_set_level(GPIO_OUTPUT_IO_2, 0);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
fflush(stdout);
esp_restart();
}
最后查看一下实际运行图:
每按下一次,就输出一次,其实不光可以下降沿输出,还可以上升沿输入和边缘沿输入,在这里就不再赘述了,可以直接查看官方的API文档:GPIO API文档;还参考了半颗心脏的博客。下一次,我应该会开始学习WIFI的使用了。