文章目录
前言
按键输入是各类MCU的GPIO输入开发学习的常用例程。本文将通过【ESP32按键控制LED(非中断实现)】这个例程来学习使用ESP32的GPIO输入,了解GPIO输入功能配置、操作的相关函数。
目标
- 熟悉ESP-IDF+VScode开发环境
- 了解按键消抖原理
- 学习ESP32 GPIO输入相关配置方法、函数
- 创建一个按键的工程,并编程实现按键控制LED亮灭
一、按键消抖
按键消抖通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。
按键触发时其理想工作波形和实际工作波形如下图所示,在按键闭合和断开时(即区域①和区域③)会出现抖动,使得输入GPIO时无法检测其有效电平,因此需要进行消抖,使得输入GPIO的电平为有效电平(区域②)。对于按键消抖的方法,主要有软件法和硬件法两类,在已经设计好的电路中,常采用软件法进行消抖,即通过软件编程,添加一个延时,在检测GPIO电平时,将区域①或区域②延时过去,仅保留有效电平,本文采用的就是软件消抖的方式。
二、ESP-IDF GPIO输入相关配置
1.GPIO输入初始化配置
官方配置内容如下
struct gpio_config_t //GPIO参数初始化配置结构体
//结构体成员
uint64_t pin_bit_mask //GPIO引脚配置
gpio_mode_t mode //GPIO模式配置:有输入和输出两种,若配置输出模式,设置:GPIO_MODE_INPUT
gpio_pullup_t pull_up_en //GPIO上拉输入配置,根据需求设置,若使能,设置:GPIO_PULLUP_ENABLE
gpio_pulldown_t pull_down_en //GPIO下拉输入配置,根据此需求设置,若使能,设置:GPIO_PULLDOWN_ENABLE
--------------------------------------------------------------------------------------------------------------
esp_err_t gpio_config(const gpio_config_t *pGPIOConfig)
//GPIO初始化函数,初始化上述结构体
2.GPIO输入操作函数
官方提供的相关操作函数如下:
int gpio_get_level(gpio_num_t gpio_num)
//读取GPIO电平
/*
参数: gpio_num -- GPIO number. If you want to get the logic level of e.g. pin GPIO16,
gpio_num should be GPIO_NUM_16 (16);
返回: 0 the GPIO input level is 0
1 the GPIO input level is 1
*/
对于GPIO的相关配置和操作常用的就是上述介绍的,更详细的内容可参考官方ESP-IDF文档(v5.3版本)自行学习,若有疑惑可在评论区探讨。
三、按键KEY初始化配置
根据前文对GPIO输入相关配置介绍,配置按键GPIO如下
//按键初始化配置
void key_init(void)
{
gpio_config_t key_pin_config; //定义按键初始化结构体
key_pin_config.pin_bit_mask = 1ULL << KEY_IO; //配置按键引脚,本文按键链接的是GPIO0,以宏定义的形式赋值给KEY_IO
key_pin_config.mode = GPIO_MODE_INPUT; //配置GPIO为输入模式
key_pin_config.pull_up_en = GPIO_PULLUP_ENABLE; //使能上拉输入,根据需求设置即可
key_pin_config.pull_down_en = GPIO_PULLDOWN_DISABLE; //失能下拉输入,根据需求设置即可
gpio_config(&key_pin_config); //初始化按键配置结构体
printf("key init\n"); //输出按键初始化状态
}
四、工程实战——按键控制点灯
功能:通过检测按键动作来控制LED。每按一次按键,LED的状态发生翻转,即按一次亮,再按一次灭。
注意:本工程并非采用中断形式来控制LED的亮灭,若想通过外部中断形式实现此功能,可自行学习ESP外部中断相关操作,或者继续关注此系列教程后续更新。
1、新建空白工程
参考【ESP32-IDF+VScode】开发笔记(一):从点灯开始——点亮LED
2、相关配置、代码
【配置CMakeLists】
首先配置自定义组件key
的CMake文件:components->key->CMakeLists.txt
完整配置内容如下:
file(TO_CMAKE_PATH "$ENV{IDF_PATH}" IDF_PATH)
idf_component_register(SRCS "key.c"
INCLUDE_DIRS "include"
"${IDF_PATH}/components/driver/gpio/include"
)
然后配置自定义组件led
的CMake文件:components->led->CMakeLists.txt
完整配置内容如下:
file(TO_CMAKE_PATH "$ENV{IDF_PATH}" IDF_PATH)
idf_component_register(SRCS "led.c"
INCLUDE_DIRS "include"
"${IDF_PATH}/components/driver/gpio/include"
)
【配置自定义组件头文件】
components->key->include->key.h
#ifndef __KEY_H__
#define __KEY_H__
#include "driver/gpio.h"
void key_init(void);
uint8_t key_scan(void);
#endif
components->led->include->led.h
#ifndef __LED_H
#define __LED_H
#include "driver/gpio.h"
void led_init(void);
void led_on(void);
void led_off(void);
#endif
【编写自定义组件驱动】
components->key->key.c
#include <stdio.h>
#include "key.h"
#include "freertos/FreeRTOS.h"
#include "FreeRTOS/task.h"
#define KEY_IO 0
void key_init(void)
{
gpio_config_t key_pin_config;
key_pin_config.pin_bit_mask = 1ULL << KEY_IO;
key_pin_config.mode = GPIO_MODE_INPUT;
key_pin_config.pull_up_en = GPIO_PULLUP_ENABLE;
key_pin_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&key_pin_config);
printf("key init\n");
}
uint8_t key_scan(void)
{
static uint8_t key_up = 1;
if (key_up && (gpio_get_level(KEY_IO) == 0))
{
vTaskDelay(10);
key_up = 0;
return 1;
}
else if (gpio_get_level(KEY_IO) == 1)
{
key_up = 1;
}
return 0;
}
components->led->led.c
#include <stdio.h>
#include "led.h"
#define LED_PIN 2
void led_init(void)
{
gpio_config_t led_pin_config;
led_pin_config.pin_bit_mask = 1<<LED_PIN;
led_pin_config.mode = GPIO_MODE_OUTPUT;
led_pin_config.pull_up_en = GPIO_PULLUP_DISABLE;
led_pin_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&led_pin_config);
}
void led_on(void)
{
gpio_set_level(LED_PIN, 1);
}
void led_off(void)
{
gpio_set_level(LED_PIN, 0);
}
【编写主函数】
main->main.c
,这里注意头文件的包含。
#include <stdio.h>
#include "led.h"
#include "key.h"
#include "FreeRTOS/freertos.h"
#include "FreeRTOS/task.h"
void app_main(void)
{
uint8_t led_status = true; //定义LED电平状态
led_init(); //初始化LED
key_init(); //初始化按键
while(1)
{
if(gpio_get_level(0) == 0)
{
vTaskDelay(20); //通过延时进行按键消抖
if(gpio_get_level(0) == 0)
{
printf("key down\n"); //按键按下状态输出
led_status = !led_status; //翻转LED电平状态
}
}
gpio_set_level(2, led_status); //设置LED电平
}
}
3、烧录前准备和烧录
参考【ESP32-IDF+VScode】开发笔记(一):从点灯开始——点亮LED
4、Wokwi 在线仿真
【仿真链接】
【仿真说明】
在wokwi里仿真,自定义函数都要写在main.c
中,不能像在VScode中那样将外设驱动单独列出,否则仿真会出错。
【仿真视频】
【ESP32-Wokwi仿真】:KEY CTRL LED
【Wokwi仿真代码示例】
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#define KEY_IO 0
#define LED_GPIO 2
void key_init(void)
{
gpio_config_t key_pin_config;
key_pin_config.pin_bit_mask = 1ULL << KEY_IO;
key_pin_config.mode = GPIO_MODE_INPUT;
key_pin_config.pull_up_en = GPIO_PULLUP_ENABLE;
key_pin_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&key_pin_config);
printf("key init\n");
}
void led_init(void)
{
gpio_config_t led_pin_config;
led_pin_config.pin_bit_mask = 1 << LED_GPIO;
led_pin_config.mode = GPIO_MODE_OUTPUT;
led_pin_config.pull_up_en = GPIO_PULLUP_DISABLE;
led_pin_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&led_pin_config);
}
void app_main(void)
{
uint8_t led_status = true; //定义LED电平状态
led_init(); //初始化LED
key_init(); //初始化按键
while(1)
{
if(gpio_get_level(0) == 0)
{
vTaskDelay(20); //通过延时进行按键消抖
if(gpio_get_level(0) == 0)
{
printf("key down\n"); //按键按下状态输出
led_status = !led_status; //翻转LED电平状态
}
}
gpio_set_level(2, led_status); //设置LED电平
}
}
五、常见问题及解决方案
待发现
总结
本文章主要讲解ESP-IDF配置ESP32GPIO输入相关操作,实现按键控制LED亮灭的功能,后续还会按照学习流程,更新用ESP-IDF开发ESP32的每一步教程,如果有兴趣的话,就点赞收藏关注不迷路!!!