一、特殊引脚说明
仅输入引脚
- GPIO 34-39是GPIO仅输入引脚。这些引脚没有内部上拉或下拉电阻。它们不能用作输出,因此只能将这些引脚用于输入
- GPIO 34
- GPIO 35
- GPIO 36
- GPIO 37
- GPIO 38
- GPIO 39
集成在ESP-WROOM-32上的SPI闪存
- 在一些ESP32开发板中,GPIO 6-GPIO 11是暴露的。但是,这些引脚连接到ESP-WROOM-32芯片上的集成SPI闪存,不建议用于其他用途。
- GPIO 6 (SCK/CLK)
- GPIO 7 (SDO/SD0)
- GPIO 8 (SDI/SD1)
- GPIO 9 (SHD/SD2)
- GPIO 10 (SWP/SD3)
- GPIO 11 (CSC/CMD)
电容式触摸GPIO
-
ESP32有10个内部电容式触摸传感器。这些传感器可以感知任何带有电荷的东西的变化,比如人的皮肤。因此,它们可以检测到用手指触摸GPIO时引起的变化。这些引脚可以很容易地集成到电容式焊盘中,并取代机械按钮。电容式触摸引脚还可以用来将ESP32从深度睡眠中唤醒。那些内部触摸传感器就连接到这些GPIO上:
- T0 (GPIO 4)
- T1 (GPIO 0)
- T2 (GPIO 2)
- T3 (GPIO 15)
- T4 (GPIO 13)
- T5 (GPIO 12)
- T6 (GPIO 14)
- T7 (GPIO 27)
- T8 (GPIO 33)
- T9 (GPIO 32)
二、头文件位置
components/driver/include/driver/gpio.h
三、常用类型
gpio_num_t
- GPIO编号的枚举值,比如
gpio_num_1
或者1
表示GPIO 1
gpio_mode_t
- GPIO输入、输出模式枚举:
GPIO_MODE_DISABLE
: disable input and outputGPIO_MODE_INPUT
: input onlyGPIO_MODE_OUTPUT
: output only modeGPIO_MODE_OUTPUT_OD
: output only with open-drain modeGPIO_MODE_INPUT_OUTPUT_OD
: output and input with open-drain modeGPIO_MODE_INPUT_OUTPUT
: output and input mode
gpio_pull_mode_t
- GPIO上、下拉电阻枚举:
GPIO_PULLUP_ONLY
: 仅上拉GPIO_PULLDOWN_ONLY
: 仅下拉GPIO_PULLUP_PULLDOWN
: 上拉+下拉GPIO_FLOATING
: 悬空
gpio_int_type_t
- GPIO中断枚举:
GPIO_INTR_DISABLE
: 禁用GPIO中断GPIO_INTR_POSEDGE
: 上升沿触发GPIO_INTR_NEGEDGE
: 下降沿触发GPIO_INTR_ANYEDGE
: 任意沿触发GPIO_INTR_LOW_LEVEL
: 低电平触发GPIO_INTR_HIGH_LEVEL
: 高电平触发
gpio_pulldown_t
- 下拉电阻使能枚举
GPIO_PULLDOWN_DISABLE
GPIO_PULLDOWN_ENABLE
gpio_pullup_t
- 上拉电阻使能枚举
GPIO_PULLUP_DISABLE
GPIO_PULLUP_ENABLE
gpio_config_t结构体
typedef struct {
uint64_t pin_bit_mask; /*!< GPIO pin: set with bit mask, each bit maps to a GPIO */
gpio_mode_t mode; /*!< GPIO mode: set input/output mode */
gpio_pullup_t pull_up_en; /*!< GPIO pull-up */
gpio_pulldown_t pull_down_en; /*!< GPIO pull-down */
gpio_int_type_t intr_type; /*!< GPIO interrupt type */
#if SOC_GPIO_SUPPORT_PIN_HYS_FILTER
gpio_hys_ctrl_mode_t hys_ctrl_mode; /*!< GPIO hysteresis: hysteresis filter on slope input */
#endif
} gpio_config_t;
pin_bit_mask
: 位图,大小64位,表示所有GPIOmode
: GPIO的输入、输出模式pull_up_en
: 上拉电阻使能,前提已配置上拉电阻pull_down_en
: 下拉电阻使能,前提已配置下拉电阻intr_type
: 中断类型:
四、常用API
gpio_config
esp_err_t gpio_config(const gpio_config_t *pGPIOConfig);
- 通过gpio_config_t结构体整体配置GPIO
- 参数:
- pGPIOConfig: gpio_config_t类型指针
- 返回
ESP_OK
: 配置成功ESP_ERR_INVALID_ARG
: 参数错误
gpio_reset_pin
esp_err_t gpio_reset_pin(gpio_num_t gpio_num);
- 恢复GPIO默认设置
- 参数:
- gpio_num: 要reset的GPIO
- 返回: 永远返回
ESP_OK
gpio_set_direction
esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode);
- 设置GPIO的mode
- 参数:
- gpio_num: 要设置的GPIO
- mode:GPIO要设置的模式
- 返回:
ESP_OK
: 成功ESP_ERR_INVALID_ARG
: 参数错误
gpio_set_level
esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level);
- 设置GPIO输出电平
- 参数:
- gpio_num: 要设置的GPIO
- level: 0为低电平,1为高电平
- 返回:
ESP_OK
: 成功ESP_ERR_INVALID_ARG
: 参数错误
gpio_get_level
int gpio_get_level(gpio_num_t gpio_num);
- 获取GPIO的输入电平
- 参数:
- gpio_num: 要获取电平的GPIO
- 返回:
0
: 输入为底电平或未配置GPIO1
: 输入为高电平
gpio_set_pull_mode
//配置内部电阻的上拉、下拉、上拉+下拉或悬空
esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull);
//下面函数可直接用于GPIO上、下拉电阻的使能与关闭
esp_err_t gpio_pullup_en(gpio_num_t gpio_num);
esp_err_t gpio_pullup_dis(gpio_num_t gpio_num);
esp_err_t gpio_pulldown_en(gpio_num_t gpio_num);
esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num);
- 只有同时支持输入和输出的引脚集成了上拉和下拉电阻
- 参数:
- gpio_num: 要设置的GPIO
- pull: 设置的类型
- 返回:
ESP_OK
: 成功ESP_ERR_INVALID_ARG
: 参数错误
点亮一个LED
#include <stdio.h>
#include "driver/gpio.h"
void app_main(void)
{
//设置GPIO 17输出并拉高电平
gpio_set_direction(GPIO_NUM_17,GPIO_MODE_OUTPUT);
gpio_set_level(GPIO_NUM_17,1);
//串口打印信息
printf("你LED亮了!\n");
}
五、GPIO中断
API
gpio_set_intr_type
esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type);
//下面函数可用于对应GPIO中断的使能于关闭
esp_err_t gpio_intr_enable(gpio_num_t gpio_num);
esp_err_t gpio_intr_disable(gpio_num_t gpio_num);
- 设置GPIO中断类型,或者在gpio_config_t里设置
- 参数:
- gpio_num: 要设置中断的GPIO
- intr_type: 中断类型
- 返回:
ESP_OK
: 成功
ESP_ERR_INVALID_ARG
: 参数错误
gpio_isr_register
esp_err_t gpio_isr_register(void (*fn)(void *), void *arg, int intr_alloc_flags, gpio_isr_handle_t *handle);
- 注册 GPIO 中断处理程序
- 任一 GPIO 中断时,都会调用此 ISR 函数
- 可用
gpio_install_isr_service
+gpio_isr_handler_add
替换该函数 - 参数:
- fn: 中断处理函数
- arg: fn的参数
- intr_alloc_flags: 分配中断的标志
- handle: 中断句柄
- 返回:
ESP_OK
: 成功 ;ESP_ERR_INVALID_ARG
: GPIO 错误ESP_ERR_NOT_FOUND
: 未找到具有指定标志的空闲中断
gpio_install_isr_service
esp_err_t gpio_install_isr_service(int intr_alloc_flags);
- 安装驱动程序的GPIO ISR处理程序服务,该服务允许每针GPIO中断处理程序
- 参数:
- intr_alloc_flags: 分配中断的标志
- 返回:
ESP_OK
: 成功ESP_ERR_NO_MEM
: 没有内存来安装此服务ESP_ERR_INVALID_STATE
: 已安装ISR 服务。ESP_ERR_NOT_FOUND
: 未找到具有指定标志的空闲中断ESP_ERR_INVALID_ARG
: GPIO 错误
gpio_uninstall_isr_service
void gpio_uninstall_isr_service(void);
- 卸载GPIO ISR服务的驱动,释放相关资源。
gpio_isr_handler_add
esp_err_t gpio_isr_handler_add(gpio_num_t gpio_num, gpio_isr_t isr_handler, void *args);
- 为相应的 GPIO 引脚添加 ISR 处理程序
- 引脚 ISR 处理程序不再需要用 IRAM_ATTR 声明,除非你在gpio_install_isr_service中分配ISR时传递了
ESP_INTR_FLAG_IRAM
标志 - 参数:
- gpio_num: 要设置中断处理程序的GPIO
- isr_handler: 中断处理函数
- arg: 中断处理函数的参数
- 返回:
ESP_OK
: 成功
ESP_ERR_INVALID_STATE
: 状态错误,则 ISR 服务尚未初始化。
ESP_ERR_INVALID_ARG
: 参数错误
gpio_isr_handler_remove
esp_err_t gpio_isr_handler_remove(gpio_num_t gpio_num);
- 卸载GPIO管脚中断服务程序
- 参数
- gpio_num: 要卸载的GPIO管脚
- 返回
ESP_OK
: 成功
ESP_ERR_INVALID_STATE
: 状态错误,则 ISR 服务尚未初始化。
ESP_ERR_INVALID_ARG
: 参数错误
中断的使用
- 中断程序要求短而快,不能占用太多时间,而且不能打印日志。
- 通常的中断处理方式是使用freertos的api,把耗时的程序放在其他Task任务中,各任务之间可以看成是并发的。使用消息队列进行任务间或中断与任务的通信。中断函数仅仅往消息队列发送消息,其他任务读取消息并处理。
- 可能用到的api如下,不过多介绍
//创建任务
BaseType_t xTaskCreate(
TaskFunction_t pvTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask
);
//任务队列创建,指定队列容量和消息大小
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
//任务从消息队列读、写消息
BaseType_t xQueueSend(QueueHandle_t xQueue,const void * pvItemToQueue,TickType_t xTicksToWait);
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer,TickType_t xTicksToWait );
//中断函数从消息队列读、写消息
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t pxHigherPriorityTaskWoken);
BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxHigherPriorityTaskWoken);
- 下面程序主函数每kun秒打印一句话,task_led不断从消息队列读消息,进行led的闪烁或关闭。中断引脚触发中断后向消息队列写入true或false,代表led闪烁与否。
#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
//消息队列句柄
static QueueHandle_t s_queue_led = NULL;
//中断处理函数
void ledIsr(void* arg){
//向队列写true或false,虽然变量传的是地址,实际上是值传递
if(xQueueSendFromISR(s_queue_led,(bool*)arg,NULL) != pdPASS)
return ;
*(bool*)arg = !*(bool*)arg; //真假翻转
}
//与main并发的任务
void task_led(void* arg){
//配置GPIO17输出,此脚连接led
gpio_set_direction(GPIO_NUM_17,GPIO_MODE_OUTPUT);
bool led_flag = true;
int ret = 0;
while(true){
//非阻塞读,读错或队列空不影响led闪烁
ret = xQueueReceive(s_queue_led,&led_flag,0);
if(led_flag ){
printf("led正在闪烁...%d\n",led_flag);
gpio_set_level(GPIO_NUM_17,1);
vTaskDelay(500/portTICK_PERIOD_MS);
gpio_set_level(GPIO_NUM_17,0);
}else if(ret == pdPASS){
printf("led停止闪烁%d\n",led_flag);
gpio_set_level(GPIO_NUM_17,0);
}
vTaskDelay(500/portTICK_PERIOD_MS);
}
}
void app_main(void)
{
//配置GPIO18下降沿中断并指定中断函数
gpio_set_direction(GPIO_NUM_18,GPIO_MODE_INPUT);
gpio_set_level(GPIO_NUM_18,1);
gpio_set_pull_mode(GPIO_NUM_18,GPIO_PULLUP_ONLY);
gpio_set_intr_type(GPIO_NUM_18,GPIO_INTR_NEGEDGE);
if(gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1) != ESP_OK){
printf("led中断服务错误,函数返回\n");
return;
}
static bool s_isr_flag = true;
gpio_isr_handler_add(GPIO_NUM_18,ledIsr,&s_isr_flag);
//创建任务
if(xTaskCreate(task_led,"led",2048,NULL,10,NULL) == pdFALSE){
printf("task_led创建失败,函数返回");
return ;
}
//创建消息队列
s_queue_led = xQueueCreate( 1,sizeof(bool));
if(s_queue_led == NULL){
printf("消息队列创建失败,函数返回");
return ;
}
while(true){
printf("只因你太美\n");
vTaskDelay(2500/portTICK_PERIOD_MS);
}
}