5 Commits

Author SHA1 Message Date
020194431b doc: updated readme 2025-12-28 20:10:12 +03:00
4efbe96de1 perf: updated critical section 2025-12-26 09:06:19 +03:00
7d2bd7fd2e feat: added reinit 2025-12-26 09:04:25 +03:00
b70d625a05 doc: updated example 2025-12-25 17:13:59 +03:00
28d067eb98 refactor: refactored by pvs-studio 2025-12-25 16:44:34 +03:00
4 changed files with 86 additions and 41 deletions

View File

@@ -4,6 +4,10 @@
1. [ESP32 ESP-IDF v5.5.1](https://docs.espressif.com/projects/esp-idf/en/v5.5.1/esp32/index.html) 1. [ESP32 ESP-IDF v5.5.1](https://docs.espressif.com/projects/esp-idf/en/v5.5.1/esp32/index.html)
## SAST Tools
[PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code.
## Features ## Features
1. Support some encoders on one device. 1. Support some encoders on one device.
@@ -11,6 +15,11 @@
## Attention ## Attention
1. If the button is not used, specify any free GPIO in the initial configuration. 1. If the button is not used, specify any free GPIO in the initial configuration.
2. For correct operation, please enable the following settings in the menuconfig:
```text
GPIO_CTRL_FUNC_IN_IRAM
```
## Using ## Using
@@ -47,18 +56,18 @@ void app_main(void)
esp_log_level_set("zh_encoder", ESP_LOG_ERROR); esp_log_level_set("zh_encoder", ESP_LOG_ERROR);
esp_event_loop_create_default(); esp_event_loop_create_default();
esp_event_handler_instance_register(ZH_ENCODER, ESP_EVENT_ANY_ID, &zh_encoder_event_handler, NULL, NULL); esp_event_handler_instance_register(ZH_ENCODER, ESP_EVENT_ANY_ID, &zh_encoder_event_handler, NULL, NULL);
zh_encoder_init_config_t encoder_init_config = ZH_ENCODER_INIT_CONFIG_DEFAULT(); zh_encoder_init_config_t config = ZH_ENCODER_INIT_CONFIG_DEFAULT();
encoder_init_config.task_priority = 5; config.task_priority = 5;
encoder_init_config.stack_size = configMINIMAL_STACK_SIZE; config.stack_size = configMINIMAL_STACK_SIZE;
encoder_init_config.queue_size = 5; config.queue_size = 5;
encoder_init_config.a_gpio_number = GPIO_NUM_27; config.a_gpio_number = GPIO_NUM_27;
encoder_init_config.b_gpio_number = GPIO_NUM_26; config.b_gpio_number = GPIO_NUM_26;
encoder_init_config.s_gpio_number = GPIO_NUM_17; config.s_gpio_number = GPIO_NUM_17;
encoder_init_config.encoder_min_value = 0; config.encoder_min_value = 0;
encoder_init_config.encoder_max_value = 100; config.encoder_max_value = 100;
encoder_init_config.encoder_step = 0.1; config.encoder_step = 0.1;
encoder_init_config.encoder_number = ENCODER_NUMBER; config.encoder_number = ENCODER_NUMBER;
zh_encoder_init(&encoder_init_config, &encoder_handle); zh_encoder_init(&config, &encoder_handle);
zh_encoder_get(&encoder_handle, &encoder_position); zh_encoder_get(&encoder_handle, &encoder_position);
printf("Encoder position %0.2f.\n", encoder_position); printf("Encoder position %0.2f.\n", encoder_position);
for (;;) for (;;)

View File

@@ -39,34 +39,34 @@ extern "C"
*/ */
typedef struct typedef struct
{ {
uint8_t task_priority; /*!< Task priority for the encoder isr processing. @note Minimum value is 1. */ double encoder_step; /*!< Encoder step. @note Must be greater than 0. */
uint16_t stack_size; /*!< Stack size for task for the encoder isr processing processing. @note The minimum size is configMINIMAL_STACK_SIZE. */ double encoder_min_value; /*!< Encoder min value. @note Must be less than encoder_max_value. */
uint8_t queue_size; /*!< Queue size for task for the encoder processing. @note Minimum value is 1. */ double encoder_max_value; /*!< Encoder max value. @note Must be greater than encoder_min_value. */
uint8_t a_gpio_number; /*!< Encoder A GPIO number. */ uint8_t task_priority; /*!< Task priority for the encoder isr processing. @note Minimum value is 1. */
uint8_t b_gpio_number; /*!< Encoder B GPIO number. */ uint8_t queue_size; /*!< Queue size for task for the encoder processing. @note Minimum value is 1. */
uint8_t s_gpio_number; /*!< Encoder button GPIO number. */ uint8_t a_gpio_number; /*!< Encoder A GPIO number. */
int32_t encoder_min_value; /*!< Encoder min value. @note Must be less than encoder_max_value. */ uint8_t b_gpio_number; /*!< Encoder B GPIO number. */
int32_t encoder_max_value; /*!< Encoder max value. @note Must be greater than encoder_min_value. */ uint8_t s_gpio_number; /*!< Encoder button GPIO number. */
double encoder_step; /*!< Encoder step. @note Must be greater than 0. */ uint8_t encoder_number; /*!< Unique encoder number. */
uint8_t encoder_number; /*!< Unique encoder number. */ uint16_t stack_size; /*!< Stack size for task for the encoder isr processing processing. @note The minimum size is configMINIMAL_STACK_SIZE. */
} zh_encoder_init_config_t; } zh_encoder_init_config_t;
/** /**
* @brief Encoder handle. * @brief Encoder handle.
*/ */
typedef struct typedef struct // -V802
{ {
uint8_t a_gpio_number; /*!< Encoder A GPIO number. */ double encoder_step; /*!< Encoder step. */
uint8_t b_gpio_number; /*!< Encoder B GPIO number. */ double encoder_position; /*!< Encoder position. */
uint8_t s_gpio_number; /*!< Encoder button GPIO number. */ double encoder_min_value; /*!< Encoder min value. */
int32_t encoder_min_value; /*!< Encoder min value. */ double encoder_max_value; /*!< Encoder max value. */
int32_t encoder_max_value; /*!< Encoder max value. */ uint8_t a_gpio_number; /*!< Encoder A GPIO number. */
double encoder_step; /*!< Encoder step. */ uint8_t b_gpio_number; /*!< Encoder B GPIO number. */
double encoder_position; /*!< Encoder position. */ uint8_t s_gpio_number; /*!< Encoder button GPIO number. */
bool button_status; /*!< Encoder button status. */ uint8_t encoder_number; /*!< Encoder unique number. */
uint8_t encoder_number; /*!< Encoder unique number. */ uint8_t encoder_state; /*!< Encoder internal state. */
uint8_t encoder_state; /*!< Encoder internal state. */ bool button_status; /*!< Encoder button status. */
bool is_initialized; /*!< Encoder initialization flag. */ bool is_initialized; /*!< Encoder initialization flag. */
} zh_encoder_handle_t; } zh_encoder_handle_t;
/** /**
@@ -88,8 +88,8 @@ extern "C"
*/ */
typedef struct typedef struct
{ {
uint8_t encoder_number; /*!< Encoder unique number. */
double encoder_position; /*!< Encoder current position. */ double encoder_position; /*!< Encoder current position. */
uint8_t encoder_number; /*!< Encoder unique number. */
bool button_status; /*!< Encoder button status. */ bool button_status; /*!< Encoder button status. */
} zh_encoder_event_on_isr_t; } zh_encoder_event_on_isr_t;
@@ -116,6 +116,20 @@ extern "C"
*/ */
esp_err_t zh_encoder_deinit(zh_encoder_handle_t *handle); esp_err_t zh_encoder_deinit(zh_encoder_handle_t *handle);
/**
* @brief Reinitialize encoder (change min, max and step values).
*
* @note The encoder will be set to the position (encoder_min_value + encoder_max_value)/2.
*
* @param[in, out] handle Pointer to unique encoder handle.
* @param[in] min Encoder min value. @note Must be less than encoder_max_value.
* @param[in] max Encoder max value. @note Must be greater than encoder_min_value.
* @param[in] step Encoder step. @note Must be greater than 0.
*
* @return ESP_OK if success or an error code otherwise.
*/
esp_err_t zh_encoder_reinit(zh_encoder_handle_t *handle, double min, double max, double step);
/** /**
* @brief Set encoder position. * @brief Set encoder position.
* *

View File

@@ -1 +1 @@
1.3.0 1.4.0

View File

@@ -29,6 +29,7 @@ static const uint8_t _encoder_matrix[7][4] = {
TaskHandle_t zh_encoder = NULL; TaskHandle_t zh_encoder = NULL;
static QueueHandle_t _queue_handle = NULL; static QueueHandle_t _queue_handle = NULL;
static portMUX_TYPE _spinlock = portMUX_INITIALIZER_UNLOCKED;
static volatile uint64_t _prev_us = 0; static volatile uint64_t _prev_us = 0;
static uint8_t _encoder_counter = 0; static uint8_t _encoder_counter = 0;
@@ -44,7 +45,7 @@ static void _zh_encoder_isr_processing_task(void *pvParameter);
ESP_EVENT_DEFINE_BASE(ZH_ENCODER); ESP_EVENT_DEFINE_BASE(ZH_ENCODER);
esp_err_t zh_encoder_init(const zh_encoder_init_config_t *config, zh_encoder_handle_t *handle) esp_err_t zh_encoder_init(const zh_encoder_init_config_t *config, zh_encoder_handle_t *handle) // -V2008
{ {
ZH_LOGI("Encoder initialization started."); ZH_LOGI("Encoder initialization started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Encoder initialization failed. Invalid argument."); ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Encoder initialization failed. Invalid argument.");
@@ -131,13 +132,32 @@ esp_err_t zh_encoder_deinit(zh_encoder_handle_t *handle)
return ESP_OK; return ESP_OK;
} }
esp_err_t zh_encoder_reinit(zh_encoder_handle_t *handle, double min, double max, double step)
{
ZH_LOGI("Encoder reinitialization started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Encoder reinitialization failed. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Encoder reinitialization failed. Encoder not initialized.");
ZH_ERROR_CHECK(max > min, ESP_ERR_INVALID_ARG, NULL, "Encoder reinitialization failed. Invalid encoder min/max value.");
ZH_ERROR_CHECK(step > 0, ESP_ERR_INVALID_ARG, NULL, "Encoder reinitialization failed. Invalid encoder step.");
taskENTER_CRITICAL(&_spinlock);
handle->encoder_min_value = min;
handle->encoder_max_value = max;
handle->encoder_step = step;
handle->encoder_position = (handle->encoder_min_value + handle->encoder_max_value) / 2;
taskEXIT_CRITICAL(&_spinlock);
ZH_LOGI("Encoder reinitialization completed successfully.");
return ESP_OK;
}
esp_err_t zh_encoder_set(zh_encoder_handle_t *handle, double position) esp_err_t zh_encoder_set(zh_encoder_handle_t *handle, double position)
{ {
ZH_LOGI("Encoder set position started."); ZH_LOGI("Encoder set position started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Encoder set position failed. Invalid argument."); ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Encoder set position failed. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Encoder set position failed. Encoder not initialized."); ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Encoder set position failed. Encoder not initialized.");
ZH_ERROR_CHECK(position <= handle->encoder_max_value && position >= handle->encoder_min_value, ESP_ERR_INVALID_ARG, NULL, "Encoder set position failed. Invalid argument."); ZH_ERROR_CHECK(position <= handle->encoder_max_value && position >= handle->encoder_min_value, ESP_ERR_INVALID_ARG, NULL, "Encoder set position failed. Invalid argument.");
taskENTER_CRITICAL(&_spinlock);
handle->encoder_position = position; handle->encoder_position = position;
taskEXIT_CRITICAL(&_spinlock);
ZH_LOGI("Encoder set position completed successfully."); ZH_LOGI("Encoder set position completed successfully.");
return ESP_OK; return ESP_OK;
} }
@@ -157,7 +177,9 @@ esp_err_t zh_encoder_reset(zh_encoder_handle_t *handle)
ZH_LOGI("Encoder reset started."); ZH_LOGI("Encoder reset started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Encoder reset failed. Invalid argument."); ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Encoder reset failed. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Encoder reset failed. Encoder not initialized."); ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Encoder reset failed. Encoder not initialized.");
taskENTER_CRITICAL(&_spinlock);
handle->encoder_position = (handle->encoder_min_value + handle->encoder_max_value) / 2; handle->encoder_position = (handle->encoder_min_value + handle->encoder_max_value) / 2;
taskEXIT_CRITICAL(&_spinlock);
ZH_LOGI("Encoder reset completed successfully."); ZH_LOGI("Encoder reset completed successfully.");
return ESP_OK; return ESP_OK;
} }
@@ -186,7 +208,7 @@ static esp_err_t _zh_encoder_validate_config(const zh_encoder_init_config_t *con
return ESP_OK; return ESP_OK;
} }
static esp_err_t _zh_encoder_gpio_init(const zh_encoder_init_config_t *config, zh_encoder_handle_t *handle) static esp_err_t _zh_encoder_gpio_init(const zh_encoder_init_config_t *config, zh_encoder_handle_t *handle) // -V2008
{ {
ZH_ERROR_CHECK(config->a_gpio_number < GPIO_NUM_MAX && config->b_gpio_number < GPIO_NUM_MAX && config->s_gpio_number < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG, NULL, "Invalid GPIO number.") ZH_ERROR_CHECK(config->a_gpio_number < GPIO_NUM_MAX && config->b_gpio_number < GPIO_NUM_MAX && config->s_gpio_number < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG, NULL, "Invalid GPIO number.")
ZH_ERROR_CHECK(config->a_gpio_number != config->b_gpio_number, ESP_ERR_INVALID_ARG, NULL, "Encoder A and B GPIO is same.") ZH_ERROR_CHECK(config->a_gpio_number != config->b_gpio_number, ESP_ERR_INVALID_ARG, NULL, "Encoder A and B GPIO is same.")
@@ -207,9 +229,9 @@ static esp_err_t _zh_encoder_gpio_init(const zh_encoder_init_config_t *config, z
_is_prev_gpio_isr_handler = true; _is_prev_gpio_isr_handler = true;
} }
} }
err = gpio_isr_handler_add(config->a_gpio_number, _zh_encoder_isr_handler, handle); err = gpio_isr_handler_add((gpio_num_t)config->a_gpio_number, _zh_encoder_isr_handler, handle);
ZH_ERROR_CHECK(err == ESP_OK, err, gpio_reset_pin((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->b_gpio_number), "Interrupt initialization failed."); ZH_ERROR_CHECK(err == ESP_OK, err, gpio_reset_pin((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->b_gpio_number), "Interrupt initialization failed.");
err = gpio_isr_handler_add(config->b_gpio_number, _zh_encoder_isr_handler, handle); err = gpio_isr_handler_add((gpio_num_t)config->b_gpio_number, _zh_encoder_isr_handler, handle);
if (_is_prev_gpio_isr_handler == true) if (_is_prev_gpio_isr_handler == true)
{ {
ZH_ERROR_CHECK(err == ESP_OK, err, gpio_isr_handler_remove((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->b_gpio_number), "Interrupt initialization failed."); ZH_ERROR_CHECK(err == ESP_OK, err, gpio_isr_handler_remove((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->b_gpio_number), "Interrupt initialization failed.");
@@ -218,7 +240,7 @@ static esp_err_t _zh_encoder_gpio_init(const zh_encoder_init_config_t *config, z
{ {
ZH_ERROR_CHECK(err == ESP_OK, err, gpio_isr_handler_remove((gpio_num_t)config->a_gpio_number); gpio_uninstall_isr_service(); gpio_reset_pin((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->b_gpio_number), "Interrupt initialization failed."); ZH_ERROR_CHECK(err == ESP_OK, err, gpio_isr_handler_remove((gpio_num_t)config->a_gpio_number); gpio_uninstall_isr_service(); gpio_reset_pin((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->b_gpio_number), "Interrupt initialization failed.");
} }
err = gpio_isr_handler_add(config->s_gpio_number, _zh_encoder_isr_handler, handle); err = gpio_isr_handler_add((gpio_num_t)config->s_gpio_number, _zh_encoder_isr_handler, handle);
if (_is_prev_gpio_isr_handler == true) if (_is_prev_gpio_isr_handler == true)
{ {
ZH_ERROR_CHECK(err == ESP_OK, err, gpio_isr_handler_remove((gpio_num_t)config->a_gpio_number); gpio_isr_handler_remove((gpio_num_t)config->b_gpio_number); ZH_ERROR_CHECK(err == ESP_OK, err, gpio_isr_handler_remove((gpio_num_t)config->a_gpio_number); gpio_isr_handler_remove((gpio_num_t)config->b_gpio_number);