ESP32多功能按键模块(基于IDF)
ESP-IDF的按键模块,用于识别按键的单击和长按状态
·
多功能按键1.0
代码可实现的功能为可检测按键单击一次输出和按键长按持续输出,按键快速按下并松开输出一次单击事件,按键长按时持续输出按键长按状态,松开时结束长按事件。
key.h文件
#ifndef __KEY_H__
#define __KEY_H__
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#define KEY1 GPIO_NUM_4
#define KEY2 GPIO_NUM_15
#define KEY3 GPIO_NUM_17
#define KEY4 GPIO_NUM_18
#define KEY5 GPIO_NUM_22
#define LONG_PRESS_TIME_MS 100 // 长按时间阈值,单位毫秒
typedef enum {
KEY_EVENT_NONE,
KEY_EVENT_CLICK,
KEY_EVENT_LONG_PRESS_START,
KEY_EVENT_LONG_PRESS_CONTINUE,
KEY_EVENT_LONG_PRESS_END
} key_event_t;
typedef struct {
uint8_t key_num;
key_event_t event;
} key_message_t;
extern QueueHandle_t key_event_queue;
void Key_Init();
void key_task_example(void* arg);
void key_task_create();
#endif
key.c文件
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_log.h"
#define KEY1 GPIO_NUM_4
#define KEY2 GPIO_NUM_15
#define KEY3 GPIO_NUM_17
#define KEY4 GPIO_NUM_18
#define KEY5 GPIO_NUM_22
#define LONG_PRESS_TIME_MS 100 //长按时间阈值,单位为毫秒
typedef enum {
KEY_EVENT_NONE,
KEY_EVENT_CLICK,
KEY_EVENT_LONG_PRESS_START,
KEY_EVENT_LONG_PRESS_CONTINUE,
KEY_EVENT_LONG_PRESS_END
} key_event_t;
typedef struct {
uint8_t key_num;
key_event_t event;
} key_message_t;
QueueHandle_t key_event_queue;
// 中断注册
static void IRAM_ATTR gpio_isr_handler(void* arg) {
uint32_t gpio_num = (uint32_t) arg;
xQueueSendFromISR(key_event_queue, &gpio_num, NULL);
}
// 按键任务
void key_task_example(void* arg) {
uint32_t io_num;
key_message_t msg;
TickType_t press_start_time;
bool long_press_detected = false;
for (;;) {
vTaskDelay(10);
if (xQueueReceive(key_event_queue, &io_num, portMAX_DELAY)) {
msg.key_num = io_num;
msg.event = KEY_EVENT_NONE;
press_start_time = xTaskGetTickCount();
while (gpio_get_level(io_num) == 0) {
if (xTaskGetTickCount() - press_start_time > pdMS_TO_TICKS(LONG_PRESS_TIME_MS)) {
if (!long_press_detected) {
msg.event = KEY_EVENT_LONG_PRESS_START;
long_press_detected = true;
} else {
msg.event = KEY_EVENT_LONG_PRESS_CONTINUE;
}
xQueueSend(key_event_queue, &msg, portMAX_DELAY);
}
vTaskDelay(20);
}
if (long_press_detected) {
msg.event = KEY_EVENT_LONG_PRESS_END;
xQueueSend(key_event_queue, &msg, portMAX_DELAY);
long_press_detected = false;
} else {
msg.event = KEY_EVENT_CLICK;
xQueueSend(key_event_queue, &msg, portMAX_DELAY);
}
}
}
}
// 按键初始化
void Key_Init() {
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_NEGEDGE; // 下降沿触发中断
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL<<KEY1)|(1ULL<<KEY2)|(1ULL<<KEY3)|(1ULL<<KEY4)|(1ULL<<KEY5);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 1;
gpio_config(&io_conf);
// 创建按键事件队列
key_event_queue = xQueueCreate(10, sizeof(key_message_t));
// 注册中断服务
gpio_install_isr_service(0);
gpio_isr_handler_add(KEY1, gpio_isr_handler, (void*) KEY1);
gpio_isr_handler_add(KEY2, gpio_isr_handler, (void*) KEY2);
gpio_isr_handler_add(KEY3, gpio_isr_handler, (void*) KEY3);
gpio_isr_handler_add(KEY4, gpio_isr_handler, (void*) KEY4);
gpio_isr_handler_add(KEY5, gpio_isr_handler, (void*) KEY5);
}
// 创建按键任务
void key_task_create() {
xTaskCreate(key_task_example, "key_task_example", 2048, NULL, 10, NULL);
}
main.c文件中检测按键的部分
Key_Init();
key_task_create();
key_message_t msg;
while (1) {
vTaskDelay(10);//这个时间不宜过长
if (xQueueReceive(key_event_queue, &msg, portMAX_DELAY)) {
switch (msg.event) {
case KEY_EVENT_CLICK:
ESP_LOGI("KEY", "Key %d clicked", msg.key_num);
break;
case KEY_EVENT_LONG_PRESS_START:
LED_ON();
ESP_LOGI("KEY", "Key %d long press started", msg.key_num);
break;
case KEY_EVENT_LONG_PRESS_CONTINUE:
ESP_LOGI("KEY", "Key %d long press continuing", msg.key_num);
break;
case KEY_EVENT_LONG_PRESS_END:
LED_OFF();
ESP_LOGI("KEY", "Key %d long press ended", msg.key_num);
break;
default:
break;
}
}
}
补充,关于按键长按状态持续输出,不用整这么麻烦,可以创建一个任务,任务每隔20ms读取一下按键的电平状态,当读取的电平为0时表示按键按下了,读取的电平为1时表示按键没有被按下
改版多功能按键2.0
上面的代码是用marscode生成的代码实用性一般,下面是我根据自己的项目详情借鉴非阻塞式程序写的新代码,结构简单实用性强响应也更快
button.c
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "classicBT.h"
#define BUTTON_PIN1 GPIO_NUM_5 //长按按键
#define BUTTON_PIN2 GPIO_NUM_12//单击按键
#define BUTTON_PIN3 GPIO_NUM_14
#define BUTTON_PIN4 GPIO_NUM_15
void button_Init() {
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << BUTTON_PIN1)|(1ULL << BUTTON_PIN2)|(1ULL << BUTTON_PIN3)|(1ULL << BUTTON_PIN4),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
}
uint8_t button_GetState(void)
{
if (gpio_get_level(BUTTON_PIN2) == 0)
{
return 2;
}
if (gpio_get_level(BUTTON_PIN3) == 0)
{
return 3;
}
if (gpio_get_level(BUTTON_PIN4) == 0)
{
return 4;
}
return 0;
}
//蜂鸣器按键任务,每隔20ms检测一次按键状态,检测为按下时发送0x00,检测为松开时发送0x01
//按键状态保存在spp_data[4]中
void button_task(void *pvParam) {
button_Init();
static uint8_t CurrState ,PrevState;
while(1)
{
vTaskDelay(pdMS_TO_TICKS(20));
PrevState = CurrState;
CurrState = button_GetState();
if(gpio_get_level(BUTTON_PIN1)==0)
{
spp_data[4]=0x00;
}
else
{
spp_data[4]=0x01;
}
if (CurrState == 0 && PrevState!= 0)
{
switch (PrevState)
{
case 2:
ESP_LOGI("button_task_short", "button2 pressed");
break;
case 3:
ESP_LOGI("button_task_short", "button3 pressed");
break;
case 4:
ESP_LOGI("button_task_short", "button4 pressed");
break;
default:
break;
}
}
}
}
void button_task_create()
{
xTaskCreate(button_task, "button_task", 2048, NULL, 10, NULL);
}
button.h
#ifndef __BUTTON_H__
#define __BUTTON_H__
void button_task(void *pvParam);
void button_task_create();
#endif
更多推荐



所有评论(0)