1. 简介
ESP32芯片有34个物理GPIO pad,每个GPIO pad都可用作一个通用IO或连接一个内部的外设信号。IO_MUX、RTC IO_MUX和GPIO交换矩阵用于将信号从外设传输至GPIO pad。
从上面看到,每个pad可以配置成GPIO功能(连接GPIO交换矩阵)或者直连功能(旁路GPIO交换矩阵),功能的切换通过IO_MUX来实现。
ESP32一共有162个外设输入信号和176个外设输出信号,要使用某个外设,就要把外设信号连接到对应的GPIO pad上,这个功能就是由GPIO交换矩阵来实现的。
直连功能和GPIO功能有什么不同呢?快速信号如以太网、SDIO、SPI、JTAG、UART 等,通过旁路 GPIO 交换矩阵可以实现更好的高频数字特性,所以高速信号会直接通过 IO_MUX 输入和输出。
ESP32除了有物理pad还有RTC pad,这些pads通过RTC IO_MUX可以配置成RTC GPIO功能,这些管脚主要在低功耗的模式下工作,实现低功耗功能。
但要注意的是,虽然ESP32有34个物理pad,但并不是所有的pad都能任由用户控制或全功能的,下面列出一些开发时要注意的管脚:
- GPIO34-39,这几个管脚只有输入功能,没有输出功能,而且没有上下拉配置;
- GPIO6-11,如果你的模组带NOR Flash的话,这几个管脚是负责Flash的通信的,不能使用;
- GPIO12-15,这几个是JTAG的调试管脚,GPIO12是默认下拉的,GPIO15是默认上拉的,使用时要注意;
- GPIO1和GPIO3,这两个是芯片的调试串口和下载串口,不能用作其他使用;
- GPIO0、GPIO2和GPIO5,这几个是芯片的strapping管脚,GPIO0默认上拉,GPIO2默认下拉,GPIO5默认上拉,使用时要注意。
2. 例程
2.1 LED点灯
这个例程实现一个点灯的代码,LED灯每隔一秒改变一次状态。
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
void app_main()
{
gpio_config_t gpio_cfg = {0};
gpio_cfg.mode = GPIO_MODE_OUTPUT;
gpio_cfg.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_cfg.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_cfg.intr_type = GPIO_INTR_DISABLE;
gpio_cfg.pin_bit_mask = (1 << GPIO_NUM_2);
gpio_config(&gpio_cfg);
while (1)
{
gpio_set_level(GPIO_NUM_2, 1);
vTaskDelay(1000 / portTICK_PERIOD_MS);
gpio_set_level(GPIO_NUM_2, 0);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
GPIO的初始化使用gpio_config一个函数即可,结构体里面配置模式(mode)、下拉(pull_down_en)、上拉(pull_up_en)、中断使能(intr_type)、管脚映射(pin_bit_mask)。
管脚映射是用一个uint64_t实现的,每个bit代表一个管脚,所以要将管脚号左移对应位数来使能对应管脚。
主循环里面使用gpio_set_level函数来控制GPIO输出,因为IDF框架是基于FreeRTOS的,所以延时可以直接调用vTaskDelay实现。
系统启动后的打印如下,可以看到最后一行打印了我们管脚的配置信息,此时开发板上的LED灯就在闪烁了。
2.2 按键输入
这个例程配置管脚为输入模式,接收按键的输入信号。例程使用的是开发板的IO0管脚作为按键输入。
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "esp_log.h"
#define TAG "app"
void app_main()
{
gpio_config_t gpio_cfg = {0};
gpio_cfg.mode = GPIO_MODE_INPUT;
gpio_cfg.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_cfg.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_cfg.intr_type = GPIO_INTR_DISABLE;
gpio_cfg.pin_bit_mask = (1 << GPIO_NUM_0);
gpio_config(&gpio_cfg);
while (1) {
if (0 == gpio_get_level(GPIO_NUM_0)) {
/* 软件去抖 */
while (0 == gpio_get_level(GPIO_NUM_0)) {
vTaskDelay(20 / portTICK_PERIOD_MS);
}
ESP_LOGI(TAG, "Key pressed");
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
GPIO的配置和前面差不多,只是模式改成输入即可,上下拉可以根据自己的开发板设置,我这里设置为上拉。
主循环使用gpio_get_level不断查看按键状态,检测到按键按下后会经过一个软件去抖处理,防止多次触发,最后就是打印一个log信息。
需要注意主循环最后的这个延时必须要有!!!因为IDF是默认开启看门狗的,看门狗的喂狗函数在空闲任务里面,而主任务的优先级是高于空闲任务的;如果不加延时,空闲任务会一直运行不了,导致看门狗超时,系统重启。