/* * MTG RFID Companion - EC11 rotary encoder driver */ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "driver/gpio.h" #include "hw_pins.h" #include "encoder.h" static const char *TAG = "encoder"; static volatile int32_t s_delta = 0; static volatile bool s_button_pressed = false; static volatile uint32_t s_last_sw_tick = 0; #define SW_DEBOUNCE_MS (35) /* Quadrature decode: on each CLK edge, sample DT. * DT == CLK level => one direction, != => other. * Physical wiring is corrected here if a knob feels inverted. */ #define DIR_CLK_HIGH (1) /* * If the direction comes out reversed, flip this to -1. */ #define ENC_DIRECTION (1) static void IRAM_ATTR encoder_isr(void *arg) { uint32_t gpio = (uint32_t)(uintptr_t)arg; if (gpio == HW_ENC_CLK_GPIO) { int dt = gpio_get_level(HW_ENC_DT_GPIO); int clk = gpio_get_level(HW_ENC_CLK_GPIO); /* Increment on one edge per detent; using level comparison on both edges * doubles the count, so we only act when CLK transitions are detected as * ticks. To keep this robust we count on every edge and divide the * sense: signed direction * 1. */ s_delta += (dt == clk) ? (ENC_DIRECTION) : (-ENC_DIRECTION); } else if (gpio == HW_ENC_SW_GPIO) { unsigned int now = (unsigned int)xTaskGetTickCountFromISR(); if ((now - s_last_sw_tick) > pdMS_TO_TICKS(SW_DEBOUNCE_MS)) { s_last_sw_tick = now; s_button_pressed = true; } } } static esp_err_t config_pin(gpio_num_t pin, gpio_int_type_t intr_type) { gpio_config_t cfg = { .pin_bit_mask = 1ULL << pin, .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = intr_type, }; return gpio_config(&cfg); } esp_err_t encoder_init(void) { gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1); ESP_ERROR_CHECK(config_pin(HW_ENC_CLK_GPIO, GPIO_INTR_POSEDGE)); ESP_ERROR_CHECK(config_pin(HW_ENC_DT_GPIO, GPIO_INTR_DISABLE)); ESP_ERROR_CHECK(config_pin(HW_ENC_SW_GPIO, GPIO_INTR_NEGEDGE)); ESP_ERROR_CHECK(gpio_isr_handler_add(HW_ENC_CLK_GPIO, encoder_isr, (void *)(uintptr_t)HW_ENC_CLK_GPIO)); ESP_ERROR_CHECK(gpio_isr_handler_add(HW_ENC_SW_GPIO, encoder_isr, (void *)(uintptr_t)HW_ENC_SW_GPIO)); ESP_LOGI(TAG, "EC11 encoder ready (CLK=%d DT=%d SW=%d)", HW_ENC_CLK_GPIO, HW_ENC_DT_GPIO, HW_ENC_SW_GPIO); return ESP_OK; } bool encoder_has_delta(void) { return s_delta != 0; } int32_t encoder_get_delta(void) { return (int32_t)s_delta; } int32_t encoder_get_delta_and_reset(void) { int32_t v = (int32_t)s_delta; s_delta = 0; return v; } bool encoder_button_pressed(void) { return s_button_pressed; } void encoder_clear_button(void) { s_button_pressed = false; }