various bug fixes
This commit is contained in:
121
main/encoder.c
121
main/encoder.c
@@ -1,5 +1,11 @@
|
||||
/*
|
||||
* MTG RFID Companion - EC11 rotary encoder driver
|
||||
*
|
||||
* Implements a true Gray Code quadrature state machine.
|
||||
* Both CLK and DT pins generate interrupts on ANYEDGE.
|
||||
* Contact bounce is inherently rejected by valid transition filtering.
|
||||
* Each mechanical detent ("tick") advances through a fixed number of valid
|
||||
* quadrature transitions (default 4 for standard full-step EC11, or 2 for half-step).
|
||||
*/
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -12,38 +18,87 @@
|
||||
static const char *TAG = "encoder";
|
||||
|
||||
static volatile int32_t s_delta = 0;
|
||||
static volatile int8_t s_accum = 0;
|
||||
static volatile uint8_t s_prev_state = 0x03;
|
||||
static volatile int8_t s_steps_per_detent = 4; /* 4 = full-step, 2 = half-step */
|
||||
|
||||
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.
|
||||
* Quadrature Gray-Code Transition Table
|
||||
* Index: (old_state << 2) | new_state (4-bit, 0..15)
|
||||
* State bits: bit 1 = CLK (GPIO 32), bit 0 = DT (GPIO 33)
|
||||
*
|
||||
* Clockwise sequence: 11 (3) -> 01 (1) -> 00 (0) -> 10 (2) -> 11 (3)
|
||||
* 11->01 (13): +1
|
||||
* 01->00 (4): +1
|
||||
* 00->10 (2): +1
|
||||
* 10->11 (11): +1
|
||||
*
|
||||
* Counter-Clockwise sequence: 11 (3) -> 10 (2) -> 00 (0) -> 01 (1) -> 11 (3)
|
||||
* 11->10 (14): -1
|
||||
* 10->00 (8): -1
|
||||
* 00->01 (1): -1
|
||||
* 01->11 (7): -1
|
||||
*
|
||||
* All bounces (e.g. 11 <-> 01) cancel out: +1 + (-1) = 0.
|
||||
* Invalid diagonal skips (00 <-> 11, 01 <-> 10): 0.
|
||||
*/
|
||||
#define ENC_DIRECTION (1)
|
||||
static const int8_t s_quad_table[16] = {
|
||||
[0] = 0,
|
||||
[1] = -1, // 00 -> 01 (CCW)
|
||||
[2] = 1, // 00 -> 10 (CW)
|
||||
[3] = 0, // 00 -> 11 (invalid)
|
||||
[4] = 1, // 01 -> 00 (CW)
|
||||
[5] = 0,
|
||||
[6] = 0, // 01 -> 10 (invalid)
|
||||
[7] = -1, // 01 -> 11 (CCW)
|
||||
[8] = -1, // 10 -> 00 (CCW)
|
||||
[9] = 0, // 10 -> 01 (invalid)
|
||||
[10] = 0,
|
||||
[11] = 1, // 10 -> 11 (CW)
|
||||
[12] = 0, // 11 -> 00 (invalid)
|
||||
[13] = 1, // 11 -> 01 (CW)
|
||||
[14] = -1, // 11 -> 10 (CCW)
|
||||
[15] = 0,
|
||||
};
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read current levels of both encoder channels */
|
||||
uint8_t clk = (uint8_t)gpio_get_level(HW_ENC_CLK_GPIO);
|
||||
uint8_t dt = (uint8_t)gpio_get_level(HW_ENC_DT_GPIO);
|
||||
uint8_t curr = (clk << 1) | dt;
|
||||
|
||||
if (curr != s_prev_state) {
|
||||
uint8_t idx = ((s_prev_state & 0x03) << 2) | (curr & 0x03);
|
||||
int8_t step = s_quad_table[idx];
|
||||
s_prev_state = curr;
|
||||
|
||||
if (step != 0) {
|
||||
s_accum += step;
|
||||
if (s_accum >= s_steps_per_detent) {
|
||||
s_delta += 1;
|
||||
s_accum = 0;
|
||||
} else if (s_accum <= -s_steps_per_detent) {
|
||||
s_delta -= 1;
|
||||
s_accum = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,20 +116,46 @@ static esp_err_t config_pin(gpio_num_t pin, gpio_int_type_t intr_type)
|
||||
|
||||
esp_err_t encoder_init(void)
|
||||
{
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1);
|
||||
esp_err_t err = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
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_CLK_GPIO, GPIO_INTR_ANYEDGE));
|
||||
ESP_ERROR_CHECK(config_pin(HW_ENC_DT_GPIO, GPIO_INTR_ANYEDGE));
|
||||
ESP_ERROR_CHECK(config_pin(HW_ENC_SW_GPIO, GPIO_INTR_NEGEDGE));
|
||||
|
||||
/* Initialize initial state */
|
||||
uint8_t clk = (uint8_t)gpio_get_level(HW_ENC_CLK_GPIO);
|
||||
uint8_t dt = (uint8_t)gpio_get_level(HW_ENC_DT_GPIO);
|
||||
s_prev_state = (clk << 1) | dt;
|
||||
s_accum = 0;
|
||||
s_delta = 0;
|
||||
|
||||
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_DT_GPIO, encoder_isr, (void *)(uintptr_t)HW_ENC_DT_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);
|
||||
ESP_LOGI(TAG, "EC11 quadrature encoder ready (CLK=%d DT=%d SW=%d steps/tick=%d)",
|
||||
HW_ENC_CLK_GPIO, HW_ENC_DT_GPIO, HW_ENC_SW_GPIO, s_steps_per_detent);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int8_t encoder_get_steps_per_detent(void)
|
||||
{
|
||||
return s_steps_per_detent;
|
||||
}
|
||||
|
||||
void encoder_set_steps_per_detent(int8_t steps)
|
||||
{
|
||||
if (steps != 2 && steps != 4) {
|
||||
steps = 4;
|
||||
}
|
||||
s_steps_per_detent = steps;
|
||||
s_accum = 0;
|
||||
ESP_LOGI(TAG, "Encoder resolution set to %d steps/detent", steps);
|
||||
}
|
||||
|
||||
bool encoder_has_delta(void)
|
||||
{
|
||||
return s_delta != 0;
|
||||
|
||||
Reference in New Issue
Block a user