Phase 7: EC11 encoder, life counter views, WS2812B RMT + LEDC buzzer feedback, E2E UI state machine; document build/hardware notes
This commit is contained in:
103
main/encoder.c
Normal file
103
main/encoder.c
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user