201 lines
6.0 KiB
C
201 lines
6.0 KiB
C
#include <stdio.h>
|
||
#include "freertos/FreeRTOS.h"
|
||
#include "freertos/task.h"
|
||
#include "freertos/queue.h"
|
||
#include "driver/gpio.h"
|
||
#include "esp_log.h"
|
||
#include "keypad.h"
|
||
|
||
// ───────────── CONFIGURATION ─────────────
|
||
|
||
typedef enum {
|
||
BUTTON1_GPIO = GPIO_NUM_36,
|
||
BUTTON2_GPIO = GPIO_NUM_39,
|
||
} button_gpio_t;
|
||
|
||
#define BUTTON_COUNT 2
|
||
|
||
// If your buttons are active‐low, set this to 0. If active‐high, set it to 1.
|
||
#define KEYPAD_INACTIVE 1
|
||
#define KEYPAD_ACTIVE 0
|
||
|
||
// Now we run the task every 50 ms and assume that is enough to debounce:
|
||
#define DEBOUNCE_INTERVAL_MS 50 // Poll once every 50 ms
|
||
// No more stable‐count logic—one sample/50 ms is considered “debounced.”
|
||
|
||
// Short vs. long press threshold (in ms):
|
||
#define LONG_PRESS_MS 1000 // ≥1000 ms = “long press”
|
||
|
||
// Queue length for pending button events:
|
||
#define BTN_EVT_QUEUE_LEN 10
|
||
|
||
// ───────────── EVENT ENUM & QUEUE HANDLE ─────────────
|
||
|
||
typedef struct
|
||
{
|
||
keycode_t key;
|
||
gpio_num_t gpio;
|
||
int prevState;
|
||
int currentState;
|
||
TickType_t activeTime;
|
||
uint32_t event;
|
||
} button_t;
|
||
|
||
static button_t _buttons[2];
|
||
|
||
// The queue handle that the debounce task will push events into:
|
||
static QueueHandle_t s_btn_evt_queue = NULL;
|
||
|
||
// Call this to get the queue, so the main task can xQueueReceive():
|
||
QueueHandle_t keypad_getQueue(void) {
|
||
return s_btn_evt_queue;
|
||
}
|
||
|
||
// ───────────── INTERNAL STATE ─────────────
|
||
|
||
// Current debounced state: 0 = released, 1 = pressed
|
||
static int s_debounced[2] = { 0, 0 };
|
||
|
||
// Tick count when each button was debounced to “pressed”
|
||
static TickType_t s_press_tick[2] = { 0, 0 };
|
||
|
||
// If we suppress an individual‐button event (because it was part of a “both” press), this is true
|
||
static bool s_suppress_event[2] = { false, false };
|
||
|
||
// Tracks if we are currently in “both buttons pressed” phase:
|
||
static bool s_was_both = false;
|
||
|
||
// Tick count when “both buttons” first became debounced‐pressed:
|
||
static TickType_t s_both_press_tick = 0;
|
||
|
||
// ───────────── TAG FOR LOGGING ─────────────
|
||
|
||
static const char *TAG = "btn_debounce";
|
||
|
||
// ───────────── GPIO SETUP ─────────────
|
||
|
||
static void init_button_gpio(void)
|
||
{
|
||
// Configure both as inputs with pull‐up (assuming active‐low buttons)
|
||
gpio_config_t io_conf = {
|
||
.pin_bit_mask = (1ULL << BUTTON1_GPIO) | (1ULL << BUTTON2_GPIO),
|
||
.mode = GPIO_MODE_INPUT,
|
||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||
.intr_type = GPIO_INTR_DISABLE
|
||
};
|
||
ESP_ERROR_CHECK(gpio_config(&io_conf));
|
||
|
||
_buttons[0].activeTime = 0;
|
||
_buttons[0].gpio = BUTTON1_GPIO;
|
||
_buttons[0].key = KEY0;
|
||
_buttons[0].prevState = KEYPAD_INACTIVE;
|
||
_buttons[0].currentState = KEYPAD_INACTIVE;
|
||
|
||
_buttons[1].activeTime = 0;
|
||
_buttons[1].gpio = BUTTON2_GPIO;
|
||
_buttons[1].key = KEY1;
|
||
_buttons[1].prevState = KEYPAD_INACTIVE;
|
||
_buttons[1].currentState = KEYPAD_INACTIVE;
|
||
|
||
}
|
||
|
||
// ───────────── BUTTON DEBOUNCE & PRESS‐TYPE TASK ─────────────
|
||
|
||
static void vButtonDebounceTask(void *arg)
|
||
{
|
||
(void)arg;
|
||
|
||
TickType_t now = xTaskGetTickCount();
|
||
TickType_t xLastWake = now;
|
||
const TickType_t long_press_thresh = pdMS_TO_TICKS(LONG_PRESS_MS);
|
||
|
||
while (1) {
|
||
|
||
now = xTaskGetTickCount();
|
||
|
||
for (int i=0; i < BUTTON_COUNT; ++i)
|
||
{
|
||
button_t *button = &_buttons[i];
|
||
|
||
button->currentState = gpio_get_level(button->gpio);
|
||
|
||
if (button->currentState == KEYPAD_ACTIVE)
|
||
{
|
||
// just pressed
|
||
if (button->prevState == KEYPAD_INACTIVE)
|
||
{
|
||
button->activeTime = now;
|
||
}
|
||
else
|
||
{
|
||
if ((now - button->activeTime) >= long_press_thresh)
|
||
{
|
||
if (button->event == 0)
|
||
{
|
||
ESP_LOGI(TAG, "%d LONG", button->key);
|
||
button->event = (button->key << KEY_LONG_PRESS);
|
||
xQueueSend(s_btn_evt_queue, &button->event, 0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
if (button->currentState == KEYPAD_INACTIVE)
|
||
{
|
||
// just released
|
||
if (button->prevState == KEYPAD_ACTIVE)
|
||
{
|
||
if (button->event == 0)
|
||
{
|
||
ESP_LOGI(TAG, "%d SHORT", button->key);
|
||
button->event = (button->key << KEY_SHORT_PRESS);
|
||
xQueueSend(s_btn_evt_queue, &button->event, 0);
|
||
}
|
||
else
|
||
{
|
||
ESP_LOGI(TAG, "%d LONG RELEASE", button->key);
|
||
}
|
||
button->event = 0;
|
||
|
||
}
|
||
button->activeTime = 0;
|
||
}
|
||
|
||
button->prevState = button->currentState;
|
||
}
|
||
|
||
|
||
|
||
|
||
// 7) Wait 50 ms and repeat
|
||
vTaskDelayUntil(&xLastWake, pdMS_TO_TICKS(DEBOUNCE_INTERVAL_MS));
|
||
xLastWake = now;
|
||
}
|
||
}
|
||
|
||
// ───────────── PUBLIC “START” FUNCTION ─────────────
|
||
|
||
void keypad_start(void)
|
||
{
|
||
// 1) Create the queue for button events
|
||
s_btn_evt_queue = xQueueCreate(BTN_EVT_QUEUE_LEN, sizeof(uint32_t));
|
||
if (s_btn_evt_queue == NULL) {
|
||
ESP_LOGE(TAG, "Failed to create button event queue");
|
||
return;
|
||
}
|
||
|
||
// 2) Initialize GPIOs & initial state
|
||
init_button_gpio();
|
||
|
||
// 3) Launch the FreeRTOS task (low priority)
|
||
xTaskCreate(vButtonDebounceTask,
|
||
"btn_debounce",
|
||
2048,
|
||
NULL,
|
||
tskIDLE_PRIORITY + 1,
|
||
NULL);
|
||
|
||
}
|
||
|