diff --git a/include/zh_encoder.h b/include/zh_encoder.h index dc7aa62..a17edfd 100644 --- a/include/zh_encoder.h +++ b/include/zh_encoder.h @@ -9,7 +9,7 @@ #define ZH_ENCODER_INIT_CONFIG_DEFAULT() \ { \ .task_priority = 10, \ - .stack_size = 2048, \ + .stack_size = 3072, \ .queue_size = 10, \ .a_gpio_number = 0, \ .b_gpio_number = 0, \ @@ -23,43 +23,16 @@ extern "C" { #endif - typedef int32_t rotary_encoder_position_t; - - // /** - // * @brief Enum representing the direction of rotation. - // */ - typedef enum - { - ROTARY_ENCODER_DIRECTION_NOT_SET = 0, ///< Direction not yet known (stationary since reset) - ROTARY_ENCODER_DIRECTION_CLOCKWISE, - ROTARY_ENCODER_DIRECTION_COUNTER_CLOCKWISE, - } rotary_encoder_direction_t; - -// // Used internally -// ///@cond INTERNAL -#define TABLE_COLS 4 - typedef uint8_t table_row_t[TABLE_COLS]; - // ///@endcond - - // /** - // * @brief Struct represents the current state of the device in terms of incremental position and direction of last movement - // */ - typedef struct - { - rotary_encoder_position_t position; ///< Numerical position since reset. This value increments on clockwise rotation, and decrements on counter-clockewise rotation. Counts full or half steps depending on mode. Set to zero on reset. - rotary_encoder_direction_t direction; ///< Direction of last movement. Set to NOT_SET on reset. - } rotary_encoder_state_t; - typedef struct // Structure for initial initialization of encoder. { uint8_t task_priority; // Task priority for the encoder isr processing. @note It is not recommended to set a value less than 10. - uint16_t stack_size; // Stack size for task for the encoder isr processing processing. @note The minimum size is 2048 bytes. + uint16_t stack_size; // Stack size for task for the encoder isr processing processing. @note The minimum size is 3072 bytes. uint8_t queue_size; // Queue size for task for the encoder processing. @note It is not recommended to set a value less than 10. uint8_t a_gpio_number; // Encoder A GPIO number. uint8_t b_gpio_number; // Encoder B GPIO number. int32_t encoder_min_value; // Encoder min value. @note Must be less than encoder_max_value. int32_t encoder_max_value; // Encoder max value. @note Must be greater than encoder_min_value. - float encoder_step; // Encoder step. @note Must be greater than 0. + double encoder_step; // Encoder step. @note Must be greater than 0. uint8_t encoder_number; // Unique encoder number. } zh_encoder_init_config_t; @@ -69,22 +42,19 @@ extern "C" uint8_t b_gpio_number; // Encoder B GPIO number. int32_t encoder_min_value; // Encoder min value. @note Must be less than encoder_max_value. int32_t encoder_max_value; // Encoder max value. @note Must be greater than encoder_min_value. - float encoder_step; // Encoder step. @note Must be greater than 0. - float encoder_position; // Encoder position. + double encoder_step; // Encoder step. @note Must be greater than 0. + double encoder_position; // Encoder position. uint8_t encoder_number; // Encoder unique number. + uint8_t encoder_state; // Encoder internal state. bool is_initialized; // Encoder initialization flag. - - const table_row_t *table; ///< Pointer to active state transition table - uint8_t table_state; ///< Internal state - volatile rotary_encoder_state_t state; ///< Device state } zh_encoder_handle_t; ESP_EVENT_DECLARE_BASE(ZH_ENCODER); typedef struct // Structure for sending data to the event handler when cause an interrupt. @note Should be used with ZH_ENCODER event base. { - uint8_t encoder_number; // Encoder unique number. - float encoder_position; // Encoder current position. + uint8_t encoder_number; // Encoder unique number. + double encoder_position; // Encoder current position. } zh_encoder_event_on_isr_t; /** @@ -95,7 +65,7 @@ extern "C" * @param[in] config Pointer to encoder initialized configuration structure. Can point to a temporary variable. * @param[out] handle Pointer to unique encoder handle. * - * @note Before initialize the expander recommend initialize zh_encoder_init_config_t structure with default values. + * @note Before initialize the encoder recommend initialize zh_encoder_init_config_t structure with default values. * * @code zh_encoder_init_config_t config = ZH_ENCODER_INIT_CONFIG_DEFAULT() @endcode * @@ -111,7 +81,7 @@ extern "C" * * @return ESP_OK if success or an error code otherwise. */ - esp_err_t zh_encoder_set(zh_encoder_handle_t *handle, float position); + esp_err_t zh_encoder_set(zh_encoder_handle_t *handle, double position); /** * @brief Reset encoder position. @@ -126,139 +96,4 @@ extern "C" #ifdef __cplusplus } -#endif - -// #ifndef ROTARY_ENCODER_H -// #define ROTARY_ENCODER_H - -// #include -// #include - -// #include "freertos/FreeRTOS.h" -// #include "freertos/queue.h" -// #include "esp_err.h" -// #include "driver/gpio.h" - -// #ifdef __cplusplus -// extern "C" { -// #endif - -// typedef int32_t rotary_encoder_position_t; - -// // /** -// // * @brief Enum representing the direction of rotation. -// // */ -// typedef enum -// { -// ROTARY_ENCODER_DIRECTION_NOT_SET = 0, ///< Direction not yet known (stationary since reset) -// ROTARY_ENCODER_DIRECTION_CLOCKWISE, -// ROTARY_ENCODER_DIRECTION_COUNTER_CLOCKWISE, -// } rotary_encoder_direction_t; - -// // // Used internally -// // ///@cond INTERNAL -// #define TABLE_COLS 4 -// typedef uint8_t table_row_t[TABLE_COLS]; -// // ///@endcond - -// // /** -// // * @brief Struct represents the current state of the device in terms of incremental position and direction of last movement -// // */ -// typedef struct -// { -// rotary_encoder_position_t position; ///< Numerical position since reset. This value increments on clockwise rotation, and decrements on counter-clockewise rotation. Counts full or half steps depending on mode. Set to zero on reset. -// rotary_encoder_direction_t direction; ///< Direction of last movement. Set to NOT_SET on reset. -// } rotary_encoder_state_t; - -// /** -// * @brief Struct carries all the information needed by this driver to manage the rotary encoder device. -// * The fields of this structure should not be accessed directly. -// */ -// typedef struct -// { -// gpio_num_t pin_a; ///< GPIO for Signal A from the rotary encoder device -// gpio_num_t pin_b; ///< GPIO for Signal B from the rotary encoder device -// QueueHandle_t queue; ///< Handle for event queue, created by ::rotary_encoder_create_queue -// const table_row_t * table; ///< Pointer to active state transition table -// uint8_t table_state; ///< Internal state -// volatile rotary_encoder_state_t state; ///< Device state -// } rotary_encoder_info_t; - -// /** -// * @brief Struct represents a queued event, used to communicate current position to a waiting task -// */ -// typedef struct -// { -// rotary_encoder_state_t state; ///< The device state corresponding to this event -// } rotary_encoder_event_t; - -// /** -// * @brief Initialise the rotary encoder device with the specified GPIO pins and full step increments. -// * This function will set up the GPIOs as needed, -// * Note: this function assumes that gpio_install_isr_service(0) has already been called. -// * @param[in, out] info Pointer to allocated rotary encoder info structure. -// * @param[in] pin_a GPIO number for rotary encoder output A. -// * @param[in] pin_b GPIO number for rotary encoder output B. -// * @return ESP_OK if successful, ESP_FAIL or ESP_ERR_* if an error occurred. -// */ -// esp_err_t rotary_encoder_init(rotary_encoder_info_t * info, gpio_num_t pin_a, gpio_num_t pin_b); - -// /** -// * @brief Enable half-stepping mode. This generates twice as many counted steps per rotation. -// * @param[in] info Pointer to initialised rotary encoder info structure. -// * @param[in] enable If true, count half steps. If false, only count full steps. -// * @return ESP_OK if successful, ESP_FAIL or ESP_ERR_* if an error occurred. -// */ -// esp_err_t rotary_encoder_enable_half_steps(rotary_encoder_info_t * info, bool enable); - -// /** -// * @brief Reverse (flip) the sense of the direction. -// * Use this if clockwise/counterclockwise are not what you expect. -// * @param[in] info Pointer to initialised rotary encoder info structure. -// * @return ESP_OK if successful, ESP_FAIL or ESP_ERR_* if an error occurred. -// */ -// esp_err_t rotary_encoder_flip_direction(rotary_encoder_info_t * info); - -// /** -// * @brief Remove the interrupt handlers installed by ::rotary_encoder_init. -// * Note: GPIOs will be left in the state they were configured by ::rotary_encoder_init. -// * @param[in] info Pointer to initialised rotary encoder info structure. -// * @return ESP_OK if successful, ESP_FAIL or ESP_ERR_* if an error occurred. -// */ -// esp_err_t rotary_encoder_uninit(rotary_encoder_info_t * info); - -// /** -// * @brief Create a queue handle suitable for use as an event queue. -// * @return A handle to a new queue suitable for use as an event queue. -// */ -// QueueHandle_t rotary_encoder_create_queue(void); - -// /** -// * @brief Set the driver to use the specified queue as an event queue. -// * It is recommended that a queue constructed by ::rotary_encoder_create_queue is used. -// * @param[in] info Pointer to initialised rotary encoder info structure. -// * @param[in] queue Handle to queue suitable for use as an event queue. See ::rotary_encoder_create_queue. -// * @return ESP_OK if successful, ESP_FAIL or ESP_ERR_* if an error occurred. -// */ -// esp_err_t rotary_encoder_set_queue(rotary_encoder_info_t * info, QueueHandle_t queue); - -// /** -// * @brief Get the current position of the rotary encoder. -// * @param[in] info Pointer to initialised rotary encoder info structure. -// * @param[in, out] state Pointer to an allocated rotary_encoder_state_t struct that will -// * @return ESP_OK if successful, ESP_FAIL or ESP_ERR_* if an error occurred. -// */ -// esp_err_t rotary_encoder_get_state(const rotary_encoder_info_t * info, rotary_encoder_state_t * state); - -// /** -// * @brief Reset the current position of the rotary encoder to zero. -// * @param[in] info Pointer to initialised rotary encoder info structure. -// * @return ESP_OK if successful, ESP_FAIL or ESP_ERR_* if an error occurred. -// */ -// esp_err_t rotary_encoder_reset(rotary_encoder_info_t * info); - -// #ifdef __cplusplus -// } -// #endif - -// #endif // ROTARY_ENCODER_H +#endif \ No newline at end of file diff --git a/zh_encoder.c b/zh_encoder.c index e4742d2..68fe770 100644 --- a/zh_encoder.c +++ b/zh_encoder.c @@ -14,12 +14,8 @@ return err; \ } -#define TABLE_ROWS 7 -#define TABLE_COLS 4 - -#define DIR_NONE 0x0 // No complete step yet. -#define DIR_CW 0x10 // Clockwise step. -#define DIR_CCW 0x20 // Anti-clockwise step. +#define ZH_ENCODER_DIRECTION_CW 0x10 +#define ZH_ENCODER_DIRECTION_CCW 0x20 // Create the half-step state table (emits a code at 00 and 11) #define R_START 0x0 @@ -29,7 +25,7 @@ #define H_CW_BEGIN_M 0x4 #define H_CCW_BEGIN_M 0x5 -static const uint8_t _encoder_matrix[TABLE_ROWS][TABLE_COLS] = { +static const uint8_t _encoder_matrix[7][4] = { // 00 01 10 11 // BA {H_START_M, H_CW_BEGIN, H_CCW_BEGIN, R_START}, // R_START (00) {H_START_M | DIR_CCW, R_START, H_CCW_BEGIN, R_START}, // H_CCW_BEGIN @@ -42,54 +38,6 @@ static const uint8_t _encoder_matrix[TABLE_ROWS][TABLE_COLS] = { static QueueHandle_t _queue_handle = NULL; static bool _is_initialized = false; -// #define ROTARY_ENCODER_DEBUG - -// Use a single-item queue so that the last value can be easily overwritten by the interrupt handler -// #define EVENT_QUEUE_LENGTH 10 - -// #define TABLE_ROWS 7 - -// #define DIR_NONE 0x0 // No complete step yet. -// #define DIR_CW 0x10 // Clockwise step. -// #define DIR_CCW 0x20 // Anti-clockwise step. - -// // Create the half-step state table (emits a code at 00 and 11) -// #define R_START 0x0 -// #define H_CCW_BEGIN 0x1 -// #define H_CW_BEGIN 0x2 -// #define H_START_M 0x3 -// #define H_CW_BEGIN_M 0x4 -// #define H_CCW_BEGIN_M 0x5 - -// static const uint8_t _ttable_half[TABLE_ROWS][TABLE_COLS] = { -// // 00 01 10 11 // BA -// {H_START_M, H_CW_BEGIN, H_CCW_BEGIN, R_START}, // R_START (00) -// {H_START_M | DIR_CCW, R_START, H_CCW_BEGIN, R_START}, // H_CCW_BEGIN -// {H_START_M | DIR_CW, H_CW_BEGIN, R_START, R_START}, // H_CW_BEGIN -// {H_START_M, H_CCW_BEGIN_M, H_CW_BEGIN_M, R_START}, // H_START_M (11) -// {H_START_M, H_START_M, H_CW_BEGIN_M, R_START | DIR_CW}, // H_CW_BEGIN_M -// {H_START_M, H_CCW_BEGIN_M, H_START_M, R_START | DIR_CCW}, // H_CCW_BEGIN_M -// }; - -// // Create the full-step state table (emits a code at 00 only) -// #define F_CW_FINAL 0x1 -// #define F_CW_BEGIN 0x2 -// #define F_CW_NEXT 0x3 -// #define F_CCW_BEGIN 0x4 -// #define F_CCW_FINAL 0x5 -// #define F_CCW_NEXT 0x6 - -// static const uint8_t _ttable_full[TABLE_ROWS][TABLE_COLS] = { -// // 00 01 10 11 // BA -// {R_START, F_CW_BEGIN, F_CCW_BEGIN, R_START}, // R_START -// {F_CW_NEXT, R_START, F_CW_FINAL, R_START | DIR_CW}, // F_CW_FINAL -// {F_CW_NEXT, F_CW_BEGIN, R_START, R_START}, // F_CW_BEGIN -// {F_CW_NEXT, F_CW_BEGIN, F_CW_FINAL, R_START}, // F_CW_NEXT -// {F_CCW_NEXT, R_START, F_CCW_BEGIN, R_START}, // F_CCW_BEGIN -// {F_CCW_NEXT, F_CCW_FINAL, R_START, R_START | DIR_CCW}, // F_CCW_FINAL -// {F_CCW_NEXT, F_CCW_FINAL, F_CCW_BEGIN, R_START}, // F_CCW_NEXT -// }; - static esp_err_t _zh_encoder_validate_config(const zh_encoder_init_config_t *config); static esp_err_t _zh_encoder_gpio_init(const zh_encoder_init_config_t *config); static esp_err_t _zh_encoder_configure_interrupts(const zh_encoder_init_config_t *config, zh_encoder_handle_t *handle); @@ -102,21 +50,36 @@ ESP_EVENT_DEFINE_BASE(ZH_ENCODER); esp_err_t zh_encoder_init(const zh_encoder_init_config_t *config, zh_encoder_handle_t *handle) { - _zh_encoder_validate_config(config); - _zh_encoder_gpio_init(config); + ZH_ENCODER_LOGI("Encoder initialization started."); + esp_err_t err = _zh_encoder_validate_config(config); + ZH_ENCODER_CHECK(err == ESP_OK, err, "Encoder initialization failed. Initial configuration check failed."); + ZH_ENCODER_LOGI("Encoder initial configuration check completed successfully."); + handle->encoder_number = config->encoder_number; + handle->encoder_min_value = config->encoder_min_value; + handle->encoder_max_value = config->encoder_max_value; + handle->encoder_step = config->encoder_step; + handle->encoder_position = (handle->encoder_min_value + handle->encoder_max_value) / 2; + err = _zh_encoder_gpio_init(config); + ZH_ENCODER_CHECK(err == ESP_OK, err, "Encoder initialization failed. GPIO initialization failed."); + ZH_ENCODER_LOGI("Encoder GPIO initialization completed successfully."); handle->a_gpio_number = config->a_gpio_number; handle->b_gpio_number = config->b_gpio_number; - _zh_encoder_configure_interrupts(config, handle); - _zh_encoder_init_resources(config); - _zh_encoder_create_task(config); - handle->table = &_encoder_matrix[0]; // enable_half_step ? &_ttable_half[0] : &_ttable_full[0]; - handle->table_state = R_START; - handle->state.position = 0; - handle->state.direction = ROTARY_ENCODER_DIRECTION_NOT_SET; + err = _zh_encoder_configure_interrupts(config, handle); + ZH_ENCODER_CHECK(err == ESP_OK, err, "Encoder initialization failed. Interrupt initialization failed."); + ZH_ENCODER_LOGI("Encoder interrupt initialization completed successfully."); + err = _zh_encoder_init_resources(config); + ZH_ENCODER_CHECK(err == ESP_OK, err, "Encoder initialization failed. Resources initialization failed."); + ZH_ENCODER_LOGI("Encoder resources initialization completed successfully."); + err = _zh_encoder_create_task(config); + ZH_ENCODER_CHECK(err == ESP_OK, err, "Encoder initialization failed. Processing task initialization failed."); + ZH_ENCODER_LOGI("Encoder processing task initialization completed successfully."); + handle->is_initialized = true; + _is_initialized = true; + ZH_ENCODER_LOGI("Encoder initialization completed successfully."); return ESP_OK; } -esp_err_t zh_encoder_set(zh_encoder_handle_t *handle, float position) +esp_err_t zh_encoder_set(zh_encoder_handle_t *handle, double position) { ZH_ENCODER_LOGI("Encoder set position started."); ZH_ENCODER_CHECK(handle->is_initialized == true, ESP_FAIL, "Encoder set position failed. Encoder not initialized."); @@ -134,219 +97,14 @@ esp_err_t zh_encoder_reset(zh_encoder_handle_t *handle) ZH_ENCODER_LOGI("Encoder reset completed successfully."); return ESP_OK; } -// static uint8_t _process(rotary_encoder_info_t *info) -// { -// uint8_t event = 0; -// if (info != NULL) -// { -// // Get state of input pins. -// uint8_t pin_state = (gpio_get_level(info->pin_b) << 1) | gpio_get_level(info->pin_a); - -// // Determine new state from the pins and state table. -// #ifdef ROTARY_ENCODER_DEBUG -// uint8_t old_state = info->table_state; -// #endif -// info->table_state = info->table[info->table_state & 0xf][pin_state]; - -// // Return emit bits, i.e. the generated event. -// event = info->table_state & 0x30; -// #ifdef ROTARY_ENCODER_DEBUG -// ESP_EARLY_LOGD(TAG, "BA %d%d, state 0x%02x, new state 0x%02x, event 0x%02x", -// pin_state >> 1, pin_state & 1, old_state, info->table_state, event); -// #endif -// } -// return event; -// } - -// static void IRAM_ATTR _isr_rotenc(void *args) -// { -// rotary_encoder_info_t *info = (rotary_encoder_info_t *)args; -// uint8_t event = _process(info); -// bool send_event = false; - -// switch (event) -// { -// case DIR_CW: -// ++info->state.position; -// info->state.direction = ROTARY_ENCODER_DIRECTION_CLOCKWISE; -// send_event = true; -// break; -// case DIR_CCW: -// --info->state.position; -// info->state.direction = ROTARY_ENCODER_DIRECTION_COUNTER_CLOCKWISE; -// send_event = true; -// break; -// default: -// break; -// } - -// if (send_event && info->queue) -// { -// rotary_encoder_event_t queue_event = -// { -// .state = -// { -// .position = info->state.position, -// .direction = info->state.direction, -// }, -// }; -// BaseType_t task_woken = pdFALSE; -// xQueueSendFromISR(info->queue, &queue_event, &task_woken); -// if (task_woken) -// { -// portYIELD_FROM_ISR(); -// } -// } -// } - -// esp_err_t rotary_encoder_init(rotary_encoder_info_t *info, gpio_num_t pin_a, gpio_num_t pin_b) -// { -// esp_err_t err = ESP_OK; -// if (info) -// { -// info->pin_a = pin_a; -// info->pin_b = pin_b; -// info->table = &_ttable_full[0]; // enable_half_step ? &_ttable_half[0] : &_ttable_full[0]; -// info->table_state = R_START; -// info->state.position = 0; -// info->state.direction = ROTARY_ENCODER_DIRECTION_NOT_SET; - -// gpio_config_t pin_config = { -// .mode = GPIO_MODE_INPUT, -// .pin_bit_mask = (1ULL << info->pin_a) | (1ULL << info->pin_b), -// .pull_up_en = GPIO_PULLUP_ENABLE, -// .intr_type = GPIO_INTR_ANYEDGE}; -// gpio_config(&pin_config); -// // configure GPIOs -// // gpio_pad_select_gpio(info->pin_a); -// // gpio_set_pull_mode(info->pin_a, GPIO_PULLUP_ONLY); -// // gpio_set_direction(info->pin_a, GPIO_MODE_INPUT); -// // gpio_set_intr_type(info->pin_a, GPIO_INTR_ANYEDGE); - -// // gpio_pad_select_gpio(info->pin_b); -// // gpio_set_pull_mode(info->pin_b, GPIO_PULLUP_ONLY); -// // gpio_set_direction(info->pin_b, GPIO_MODE_INPUT); -// // gpio_set_intr_type(info->pin_b, GPIO_INTR_ANYEDGE); - -// // install interrupt handlers -// gpio_isr_handler_add(info->pin_a, _isr_rotenc, info); -// gpio_isr_handler_add(info->pin_b, _isr_rotenc, info); -// } -// else -// { -// ESP_LOGE(TAG, "info is NULL"); -// err = ESP_ERR_INVALID_ARG; -// } -// return err; -// } - -// esp_err_t rotary_encoder_enable_half_steps(rotary_encoder_info_t *info, bool enable) -// { -// esp_err_t err = ESP_OK; -// if (info) -// { -// info->table = enable ? &_ttable_half[0] : &_ttable_full[0]; -// info->table_state = R_START; -// } -// else -// { -// ESP_LOGE(TAG, "info is NULL"); -// err = ESP_ERR_INVALID_ARG; -// } -// return err; -// } - -// esp_err_t rotary_encoder_flip_direction(rotary_encoder_info_t *info) -// { -// esp_err_t err = ESP_OK; -// if (info) -// { -// gpio_num_t temp = info->pin_a; -// info->pin_a = info->pin_b; -// info->pin_b = temp; -// } -// else -// { -// ESP_LOGE(TAG, "info is NULL"); -// err = ESP_ERR_INVALID_ARG; -// } -// return err; -// } - -// esp_err_t rotary_encoder_uninit(rotary_encoder_info_t *info) -// { -// esp_err_t err = ESP_OK; -// if (info) -// { -// gpio_isr_handler_remove(info->pin_a); -// gpio_isr_handler_remove(info->pin_b); -// } -// else -// { -// ESP_LOGE(TAG, "info is NULL"); -// err = ESP_ERR_INVALID_ARG; -// } -// return err; -// } - -// QueueHandle_t rotary_encoder_create_queue(void) -// { -// return xQueueCreate(EVENT_QUEUE_LENGTH, sizeof(rotary_encoder_event_t)); -// } - -// esp_err_t rotary_encoder_set_queue(rotary_encoder_info_t *info, QueueHandle_t queue) -// { -// esp_err_t err = ESP_OK; -// if (info) -// { -// info->queue = queue; -// } -// else -// { -// ESP_LOGE(TAG, "info is NULL"); -// err = ESP_ERR_INVALID_ARG; -// } -// return err; -// } - -// esp_err_t rotary_encoder_get_state(const rotary_encoder_info_t *info, rotary_encoder_state_t *state) -// { -// esp_err_t err = ESP_OK; -// if (info && state) -// { -// // make a snapshot of the state -// state->position = info->state.position; -// state->direction = info->state.direction; -// } -// else -// { -// ESP_LOGE(TAG, "info and/or state is NULL"); -// err = ESP_ERR_INVALID_ARG; -// } -// return err; -// } - -// esp_err_t rotary_encoder_reset(rotary_encoder_info_t *info) -// { -// esp_err_t err = ESP_OK; -// if (info) -// { -// info->state.position = 0; -// info->state.direction = ROTARY_ENCODER_DIRECTION_NOT_SET; -// } -// else -// { -// ESP_LOGE(TAG, "info is NULL"); -// err = ESP_ERR_INVALID_ARG; -// } -// return err; -// } static esp_err_t _zh_encoder_validate_config(const zh_encoder_init_config_t *config) { ZH_ENCODER_CHECK(config != NULL, ESP_ERR_INVALID_ARG, "Invalid configuration."); - ZH_ENCODER_CHECK(config->task_priority >= 10 && config->stack_size >= 2048, ESP_ERR_INVALID_ARG, "Invalid task settings."); + ZH_ENCODER_CHECK(config->task_priority >= 10 && config->stack_size >= 3072, ESP_ERR_INVALID_ARG, "Invalid task settings."); ZH_ENCODER_CHECK(config->queue_size >= 10, ESP_ERR_INVALID_ARG, "Invalid queue size."); + ZH_ENCODER_CHECK(config->encoder_max_value > config->encoder_min_value, ESP_ERR_INVALID_ARG, "Invalid encoder min/max value."); + ZH_ENCODER_CHECK(config->encoder_step > 0, ESP_ERR_INVALID_ARG, "Invalid encoder step."); return ESP_OK; } @@ -371,8 +129,6 @@ static esp_err_t _zh_encoder_configure_interrupts(const zh_encoder_init_config_t ZH_ENCODER_CHECK(err == ESP_OK, err, "Interrupt initialization failed."); err = gpio_isr_handler_add(config->b_gpio_number, _zh_encoder_isr_handler, handle); ZH_ENCODER_CHECK(err == ESP_OK, err, "Interrupt initialization failed."); - // printf("queue.b_gpio_number %d\n", handle->b_gpio_number); - // printf("queue.q_gpio_number %d\n", handle->a_gpio_number); return ESP_OK; } @@ -403,135 +159,60 @@ static esp_err_t _zh_encoder_create_task(const zh_encoder_init_config_t *config) return ESP_OK; } -static void _zh_encoder_isr_handler(void *arg) +static void IRAM_ATTR _zh_encoder_isr_handler(void *arg) { - zh_encoder_handle_t *queue = (zh_encoder_handle_t *)arg; + zh_encoder_handle_t *encoder_handle = (zh_encoder_handle_t *)arg; BaseType_t xHigherPriorityTaskWoken = pdFALSE; - uint8_t pin_state = (gpio_get_level(queue->b_gpio_number) << 1) | gpio_get_level(queue->a_gpio_number); - // printf("pin_state %d\n", pin_state); - queue->table_state = queue->table[queue->table_state & 0xf][pin_state]; - uint8_t event = queue->table_state & 0x30; - switch (event) + encoder_handle->encoder_state = _encoder_matrix[encoder_handle->encoder_state & 0x0F] + [(gpio_get_level(encoder_handle->b_gpio_number) << 1) | gpio_get_level(encoder_handle->a_gpio_number)]; + switch (encoder_handle->encoder_state & 0x30) { - case DIR_CW: - ++queue->state.position; - queue->state.direction = ROTARY_ENCODER_DIRECTION_CLOCKWISE; - // printf("event %d\n", event); - // send_event = true; - // printf("state.position %ld\n", queue->state.position); - xQueueSendFromISR(_queue_handle, queue, &xHigherPriorityTaskWoken); + case ZH_ENCODER_DIRECTION_CW: + if (encoder_handle->encoder_position < encoder_handle->encoder_max_value) + { + encoder_handle->encoder_position = encoder_handle->encoder_position + encoder_handle->encoder_step; + if (encoder_handle->encoder_position > encoder_handle->encoder_max_value) + { + encoder_handle->encoder_position = encoder_handle->encoder_max_value; + } + xQueueSendFromISR(_queue_handle, encoder_handle, &xHigherPriorityTaskWoken); + } break; - case DIR_CCW: - --queue->state.position; - queue->state.direction = ROTARY_ENCODER_DIRECTION_COUNTER_CLOCKWISE; - // printf("event %d\n", event); - // send_event = true; - // printf("state.position %ld\n", queue.state.position); - xQueueSendFromISR(_queue_handle, queue, &xHigherPriorityTaskWoken); + case ZH_ENCODER_DIRECTION_CCW: + if (encoder_handle->encoder_position > encoder_handle->encoder_min_value) + { + encoder_handle->encoder_position = encoder_handle->encoder_position - encoder_handle->encoder_step; + if (encoder_handle->encoder_position < encoder_handle->encoder_min_value) + { + encoder_handle->encoder_position = encoder_handle->encoder_min_value; + } + xQueueSendFromISR(_queue_handle, encoder_handle, &xHigherPriorityTaskWoken); + } break; default: break; } - - // BaseType_t xHigherPriorityTaskWoken = pdFALSE; - // xQueueSendFromISR(_queue_handle, queue, &xHigherPriorityTaskWoken); if (xHigherPriorityTaskWoken == pdTRUE) { portYIELD_FROM_ISR(); }; } -static void _zh_encoder_isr_processing_task(void *pvParameter) +static void IRAM_ATTR _zh_encoder_isr_processing_task(void *pvParameter) { zh_encoder_handle_t queue = {0}; + zh_encoder_event_on_isr_t encoder_data = {0}; while (xQueueReceive(_queue_handle, &queue, portMAX_DELAY) == pdTRUE) { - // printf("queue.b_gpio_number %d\n", queue.b_gpio_number); - // printf("queue.q_gpio_number %d\n", queue.a_gpio_number); - // uint8_t pin_state = (gpio_get_level(queue.b_gpio_number) << 1) | gpio_get_level(queue.a_gpio_number); - // // printf("pin_state %d\n", pin_state); - // queue.table_state = queue.table[queue.table_state & 0xf][pin_state]; - // uint8_t event = queue.table_state & 0x30; - // switch (event) - // { - // case DIR_CW: - // ++queue.state.position; - // queue.state.direction = ROTARY_ENCODER_DIRECTION_CLOCKWISE; - // printf("event %d\n", event); - // // send_event = true; - // printf("state.position %ld\n", queue.state.position); - // break; - // case DIR_CCW: - // --queue.state.position; - // queue.state.direction = ROTARY_ENCODER_DIRECTION_COUNTER_CLOCKWISE; - // printf("event %d\n", event); - // // send_event = true; - // printf("state.position %ld\n", queue.state.position); - // break; - // default: - // break; - // } - printf("state.position %ld\n", queue.state.position); + ZH_ENCODER_LOGI("Encoder isr processing begin."); + encoder_data.encoder_number = queue.encoder_number; + encoder_data.encoder_position = queue.encoder_position; + esp_err_t err = esp_event_post(ZH_ENCODER, 0, &encoder_data, sizeof(zh_encoder_event_on_isr_t), portTICK_PERIOD_MS); + if (err != ESP_OK) + { + ZH_ENCODER_LOGE_ERR("Encoder isr processing failed. Failed to post interrupt event.", err); + } + ZH_ENCODER_LOGI("Encoder isr processing completed successfully."); } vTaskDelete(NULL); - - // rotary_encoder_info_t *info = (rotary_encoder_info_t *)args; - - // uint8_t event = 0; - // if (info != NULL) - // { - // // Get state of input pins. - // uint8_t pin_state = (gpio_get_level(info->pin_b) << 1) | gpio_get_level(info->pin_a); - - // // Determine new state from the pins and state table. - // #ifdef ROTARY_ENCODER_DEBUG - // uint8_t old_state = info->table_state; - // #endif - // queue.table_state = queue.table[queue.table_state & 0xf][pin_state]; - - // // Return emit bits, i.e. the generated event. - // event = queue.table_state & 0x30; - // #ifdef ROTARY_ENCODER_DEBUG - // ESP_EARLY_LOGD(TAG, "BA %d%d, state 0x%02x, new state 0x%02x, event 0x%02x", - // pin_state >> 1, pin_state & 1, old_state, info->table_state, event); - // #endif - // } - // return event; - - // uint8_t event = _process(info); - // bool send_event = false; - - // switch (event) - // { - // case DIR_CW: - // ++info->state.position; - // info->state.direction = ROTARY_ENCODER_DIRECTION_CLOCKWISE; - // // send_event = true; - // break; - // case DIR_CCW: - // --info->state.position; - // info->state.direction = ROTARY_ENCODER_DIRECTION_COUNTER_CLOCKWISE; - // send_event = true; - // break; - // default: - // break; - // } - - // if (send_event && info->queue) - // { - // rotary_encoder_event_t queue_event = - // { - // .state = - // { - // .position = info->state.position, - // .direction = info->state.direction, - // }, - // }; - // BaseType_t task_woken = pdFALSE; - // xQueueSendFromISR(info->queue, &queue_event, &task_woken); - // if (task_woken) - // { - // portYIELD_FROM_ISR(); - // } - // } } \ No newline at end of file