多功能按键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

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐