14 Commits

7 changed files with 3238 additions and 83 deletions

View File

@@ -1,6 +1 @@
if(${IDF_TARGET} STREQUAL esp8266)
set(requires driver)
else()
set(requires driver esp_event)
endif()
idf_component_register(SRCS "zh_encoder.c" INCLUDE_DIRS "include" REQUIRES ${requires})
idf_component_register(SRCS "zh_encoder.c" INCLUDE_DIRS "include" REQUIRES esp_event driver esp_timer)

View File

@@ -1,17 +1,25 @@
# ESP32 ESP-IDF and ESP8266 RTOS SDK component for rotary encoder
# ESP32 ESP-IDF component for rotary encoder
## Tested on
1. [ESP8266 RTOS_SDK v3.4](https://docs.espressif.com/projects/esp8266-rtos-sdk/en/latest/index.html#)
2. [ESP32 ESP-IDF v5.4](https://docs.espressif.com/projects/esp-idf/en/release-v5.4/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
1. Support some encoders on one device.
## Note
## Attention
1. Encoder pins must be pull up to the VCC via 0.1 µf capacitors.
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
@@ -19,7 +27,7 @@ In an existing project, run the following command to install the components:
```text
cd ../your_project/components
git clone http://git.zh.com.ru/alexey.zholtikov/zh_encoder
git clone http://git.zh.com.ru/esp_components/zh_encoder
```
In the application, add the component:
@@ -35,45 +43,61 @@ One encoder on device:
```c
#include "zh_encoder.h"
#define ENCODER_NUMBER 0x01
zh_encoder_handle_t encoder_handle = {0};
double encoder_position = 0;
void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
void app_main(void)
{
esp_log_level_set("zh_encoder", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig.
esp_log_level_set("zh_encoder", ESP_LOG_ERROR);
esp_event_loop_create_default();
#ifdef CONFIG_IDF_TARGET_ESP8266
esp_event_handler_register(ZH_ENCODER, ESP_EVENT_ANY_ID, &zh_encoder_event_handler, NULL);
#else
esp_event_handler_instance_register(ZH_ENCODER, ESP_EVENT_ANY_ID, &zh_encoder_event_handler, NULL, NULL);
#endif
zh_encoder_init_config_t encoder_init_config = ZH_ENCODER_INIT_CONFIG_DEFAULT();
encoder_init_config.a_gpio_number = GPIO_NUM_27;
encoder_init_config.b_gpio_number = GPIO_NUM_26;
// encoder_init_config.encoder_min_value = -10; // Just for example.
// encoder_init_config.encoder_max_value = 20; // Just for example.
// encoder_init_config.encoder_step = 0.1; // Just for example.
encoder_init_config.encoder_number = 1;
zh_encoder_init(&encoder_init_config, &encoder_handle);
double position = 0;
zh_encoder_get(&encoder_handle, &position);
printf("Encoder position %0.2f.\n", position); // For ESP8266 first disable "Component config -> Newlib -> Enable nano formatting options for printf/scanf family" via menuconfig.
// zh_encoder_set(&encoder_handle, 5); // Just for example.
// zh_encoder_reset(&encoder_handle); // Just for example.
zh_encoder_init_config_t config = ZH_ENCODER_INIT_CONFIG_DEFAULT();
config.task_priority = 5;
config.stack_size = configMINIMAL_STACK_SIZE;
config.queue_size = 5;
config.a_gpio_number = GPIO_NUM_27;
config.b_gpio_number = GPIO_NUM_26;
config.s_gpio_number = GPIO_NUM_17;
config.encoder_min_value = 0;
config.encoder_max_value = 100;
config.encoder_step = 0.1;
config.encoder_number = ENCODER_NUMBER;
zh_encoder_init(&config, &encoder_handle);
zh_encoder_get(&encoder_handle, &encoder_position);
printf("Encoder position %0.2f.\n", encoder_position);
for (;;)
{
const zh_encoder_stats_t *stats = zh_encoder_get_stats();
printf("Number of event post error: %ld.\n", stats->event_post_error);
printf("Number of queue overflow error: %ld.\n", stats->queue_overflow_error);
printf("Minimum free stack size: %ld.\n", stats->min_stack_size);
vTaskDelay(10000 / portTICK_PERIOD_MS);
vTaskDelay(60000 / portTICK_PERIOD_MS);
}
}
void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
zh_encoder_event_on_isr_t *event = event_data;
printf("Encoder number %d position %0.2f.\n", event->encoder_number, event->encoder_position); // For ESP8266 first disable "Component config -> Newlib -> Enable nano formatting options for printf/scanf family" via menuconfig.
switch (event->encoder_number)
{
case ENCODER_NUMBER:
if (encoder_position == event->encoder_position)
{
printf("Encoder number %d button %s.\n", event->encoder_number, event->button_status == 1 ? "released" : "pressed");
}
else
{
encoder_position = event->encoder_position;
printf("Encoder number %d position %0.2f with button %s.\n", event->encoder_number, event->encoder_position, event->button_status == 1 ? "released" : "pressed");
}
break;
default:
break;
}
}
```

2981
Schematic.pdf Normal file

File diff suppressed because it is too large Load Diff

View File

View File

@@ -6,6 +6,7 @@
#include "esp_log.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_event.h"
@@ -13,16 +14,17 @@
/**
* @brief Encoder initial default values.
*/
#define ZH_ENCODER_INIT_CONFIG_DEFAULT() \
{ \
.task_priority = 10, \
.stack_size = 3072, \
.queue_size = 10, \
.a_gpio_number = 0, \
.b_gpio_number = 0, \
.encoder_min_value = -100, \
.encoder_max_value = 100, \
.encoder_step = 1, \
#define ZH_ENCODER_INIT_CONFIG_DEFAULT() \
{ \
.task_priority = 1, \
.stack_size = configMINIMAL_STACK_SIZE, \
.queue_size = 1, \
.a_gpio_number = GPIO_NUM_MAX, \
.b_gpio_number = GPIO_NUM_MAX, \
.s_gpio_number = GPIO_NUM_MAX, \
.encoder_min_value = -100, \
.encoder_max_value = 100, \
.encoder_step = 1, \
.encoder_number = 0}
#ifdef __cplusplus
@@ -37,31 +39,34 @@ extern "C"
*/
typedef struct
{
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 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. */
double encoder_step; /*!< Encoder step. @note Must be greater than 0. */
uint8_t encoder_number; /*!< Unique encoder number. */
double encoder_step; /*!< Encoder step. @note Must be greater than 0. */
double encoder_min_value; /*!< Encoder min value. @note Must be less than encoder_max_value. */
double encoder_max_value; /*!< Encoder max value. @note Must be greater than encoder_min_value. */
uint8_t task_priority; /*!< Task priority for the encoder isr processing. @note Minimum value is 1. */
uint8_t queue_size; /*!< Queue size for task for the encoder processing. @note Minimum value is 1. */
uint8_t a_gpio_number; /*!< Encoder A GPIO number. */
uint8_t b_gpio_number; /*!< Encoder B GPIO number. */
uint8_t s_gpio_number; /*!< Encoder button GPIO number. */
uint8_t encoder_number; /*!< Unique encoder number. @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. */
} zh_encoder_init_config_t;
/**
* @brief Encoder handle.
*/
typedef struct
typedef struct // -V802
{
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. */
int32_t encoder_max_value; /*!< Encoder max value. */
double encoder_step; /*!< Encoder step. */
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. */
double encoder_step; /*!< Encoder step. */
double encoder_position; /*!< Encoder position. */
double encoder_min_value; /*!< Encoder min value. */
double encoder_max_value; /*!< Encoder max value. */
uint8_t a_gpio_number; /*!< Encoder A GPIO number. */
uint8_t b_gpio_number; /*!< Encoder B GPIO number. */
uint8_t s_gpio_number; /*!< Encoder button GPIO number. */
uint8_t encoder_number; /*!< Encoder unique number. */
uint8_t encoder_state; /*!< Encoder internal state. */
bool button_status; /*!< Encoder button status. */
bool is_initialized; /*!< Encoder initialization flag. */
} zh_encoder_handle_t;
/**
@@ -83,8 +88,9 @@ extern "C"
*/
typedef struct
{
uint8_t encoder_number; /*!< Encoder unique number. */
double encoder_position; /*!< Encoder current position. */
uint8_t encoder_number; /*!< Encoder unique number. */
bool button_status; /*!< Encoder button status. */
} zh_encoder_event_on_isr_t;
/**
@@ -103,6 +109,27 @@ extern "C"
*/
esp_err_t zh_encoder_init(const zh_encoder_init_config_t *config, zh_encoder_handle_t *handle);
/**
* @brief Deinitialize encoder.
*
* @return ESP_OK if success or an error code otherwise.
*/
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.
*

View File

@@ -1 +1 @@
1.1.0
1.5.0

View File

@@ -16,6 +16,8 @@
#define ZH_ENCODER_DIRECTION_CW 0x10
#define ZH_ENCODER_DIRECTION_CCW 0x20
#define BUTTON_DEBOUNCE_TIME 20
static const uint8_t _encoder_matrix[7][4] = {
{0x03, 0x02, 0x01, 0x00},
{0x23, 0x00, 0x01, 0x00},
@@ -27,9 +29,13 @@ static const uint8_t _encoder_matrix[7][4] = {
TaskHandle_t zh_encoder = NULL;
static QueueHandle_t _queue_handle = NULL;
static bool _is_initialized = false;
static portMUX_TYPE _spinlock = portMUX_INITIALIZER_UNLOCKED;
static volatile uint64_t _prev_us = 0;
static uint8_t _encoder_counter = 0;
static bool _is_prev_gpio_isr_handler = false;
static zh_encoder_stats_t _stats = {0};
static uint8_t _encoder_number_matrix[10] = {0};
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, zh_encoder_handle_t *handle);
@@ -40,9 +46,10 @@ static void _zh_encoder_isr_processing_task(void *pvParameter);
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_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Encoder initialization failed. Invalid argument.");
esp_err_t err = _zh_encoder_validate_config(config);
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "Encoder initialization failed. Initial configuration check failed.");
err = _zh_encoder_gpio_init(config, handle);
@@ -50,13 +57,29 @@ esp_err_t zh_encoder_init(const zh_encoder_init_config_t *config, zh_encoder_han
err = _zh_encoder_resources_init(config);
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); gpio_reset_pin((gpio_num_t)config->a_gpio_number);
gpio_reset_pin((gpio_num_t)config->b_gpio_number), "Encoder initialization failed. Resources initialization failed.");
if (config->s_gpio_number != GPIO_NUM_MAX)
{
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); gpio_isr_handler_remove((gpio_num_t)config->s_gpio_number);
gpio_reset_pin((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->b_gpio_number); gpio_reset_pin((gpio_num_t)config->s_gpio_number), "Encoder initialization failed. Resources initialization failed.");
}
else
{
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); gpio_reset_pin((gpio_num_t)config->a_gpio_number);
gpio_reset_pin((gpio_num_t)config->b_gpio_number), "Encoder initialization failed. Resources initialization failed.");
}
}
else
{
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); gpio_uninstall_isr_service();
gpio_reset_pin((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->b_gpio_number), "Encoder initialization failed. Resources initialization failed.");
if (config->s_gpio_number != GPIO_NUM_MAX)
{
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); gpio_isr_handler_remove((gpio_num_t)config->s_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); gpio_reset_pin((gpio_num_t)config->s_gpio_number), "Encoder initialization failed. Resources initialization failed.");
}
else
{
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); gpio_uninstall_isr_service();
gpio_reset_pin((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->b_gpio_number), "Encoder initialization failed. Resources initialization failed.");
}
}
err = _zh_encoder_task_init(config);
if (_is_prev_gpio_isr_handler == true)
@@ -71,23 +94,87 @@ esp_err_t zh_encoder_init(const zh_encoder_init_config_t *config, zh_encoder_han
}
handle->a_gpio_number = config->a_gpio_number;
handle->b_gpio_number = config->b_gpio_number;
handle->s_gpio_number = config->s_gpio_number;
handle->button_status = gpio_get_level((gpio_num_t)config->s_gpio_number);
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;
handle->is_initialized = true;
_is_initialized = true;
++_encoder_counter;
for (uint8_t i = 0; i < sizeof(_encoder_number_matrix); ++i)
{
if (_encoder_number_matrix[i] == 0)
{
_encoder_number_matrix[i] = handle->encoder_number;
break;
}
}
ZH_LOGI("Encoder initialization completed successfully.");
return ESP_OK;
}
esp_err_t zh_encoder_deinit(zh_encoder_handle_t *handle)
{
ZH_LOGI("Encoder deinitialization started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Encoder deinitialization failed. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Encoder deinitialization failed. Encoder not initialized.");
gpio_isr_handler_remove((gpio_num_t)handle->a_gpio_number);
gpio_isr_handler_remove((gpio_num_t)handle->b_gpio_number);
gpio_isr_handler_remove((gpio_num_t)handle->s_gpio_number);
gpio_reset_pin((gpio_num_t)handle->a_gpio_number);
gpio_reset_pin((gpio_num_t)handle->b_gpio_number);
gpio_reset_pin((gpio_num_t)handle->s_gpio_number);
if (_encoder_counter == 1)
{
vQueueDelete(_queue_handle);
vTaskDelete(zh_encoder);
if (_is_prev_gpio_isr_handler == false)
{
gpio_uninstall_isr_service();
}
}
handle->is_initialized = false;
--_encoder_counter;
for (uint8_t i = 0; i < sizeof(_encoder_number_matrix); ++i)
{
if (_encoder_number_matrix[i] == handle->encoder_number)
{
_encoder_number_matrix[i] = 0;
break;
}
}
ZH_LOGI("Encoder deinitialization completed successfully.");
return ESP_OK;
}
esp_err_t zh_encoder_reinit(zh_encoder_handle_t *handle, double min, double max, double step) // -V2008
{
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)
{
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->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.");
taskENTER_CRITICAL(&_spinlock);
handle->encoder_position = position;
taskEXIT_CRITICAL(&_spinlock);
ZH_LOGI("Encoder set position completed successfully.");
return ESP_OK;
}
@@ -95,6 +182,7 @@ esp_err_t zh_encoder_set(zh_encoder_handle_t *handle, double position)
esp_err_t zh_encoder_get(const zh_encoder_handle_t *handle, double *position)
{
ZH_LOGI("Encoder get position started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Encoder get position failed. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Encoder get position failed. Encoder not initialized.");
*position = handle->encoder_position;
ZH_LOGI("Encoder get position completed successfully.");
@@ -104,8 +192,11 @@ esp_err_t zh_encoder_get(const zh_encoder_handle_t *handle, double *position)
esp_err_t zh_encoder_reset(zh_encoder_handle_t *handle)
{
ZH_LOGI("Encoder reset started.");
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.");
taskENTER_CRITICAL(&_spinlock);
handle->encoder_position = (handle->encoder_min_value + handle->encoder_max_value) / 2;
taskEXIT_CRITICAL(&_spinlock);
ZH_LOGI("Encoder reset completed successfully.");
return ESP_OK;
}
@@ -124,28 +215,34 @@ void zh_encoder_reset_stats(void)
ZH_LOGI("Error statistic reset successfully.");
}
static esp_err_t _zh_encoder_validate_config(const zh_encoder_init_config_t *config)
static esp_err_t _zh_encoder_validate_config(const zh_encoder_init_config_t *config) // -V2008
{
ZH_ERROR_CHECK(config != NULL, ESP_ERR_INVALID_ARG, NULL, "Invalid configuration.");
ZH_ERROR_CHECK(config->task_priority >= 10 && config->stack_size >= 3072, ESP_ERR_INVALID_ARG, NULL, "Invalid task settings.");
ZH_ERROR_CHECK(config->queue_size >= 10, ESP_ERR_INVALID_ARG, NULL, "Invalid queue size.");
ZH_ERROR_CHECK(config->task_priority >= 1 && config->stack_size >= configMINIMAL_STACK_SIZE, ESP_ERR_INVALID_ARG, NULL, "Invalid task settings.");
ZH_ERROR_CHECK(config->queue_size >= 1, ESP_ERR_INVALID_ARG, NULL, "Invalid queue size.");
ZH_ERROR_CHECK(config->encoder_max_value > config->encoder_min_value, ESP_ERR_INVALID_ARG, NULL, "Invalid encoder min/max value.");
ZH_ERROR_CHECK(config->encoder_step > 0, ESP_ERR_INVALID_ARG, NULL, "Invalid encoder step.");
ZH_ERROR_CHECK(config->encoder_number > 0, ESP_ERR_INVALID_ARG, NULL, "Invalid encoder number.");
for (uint8_t i = 0; i < sizeof(_encoder_number_matrix); ++i)
{
ZH_ERROR_CHECK(config->encoder_number != _encoder_number_matrix[i], ESP_ERR_INVALID_ARG, NULL, "Encoder number already present.");
}
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, ESP_ERR_INVALID_ARG, NULL, "Invalid GPIO number.")
ZH_ERROR_CHECK(config->a_gpio_number != config->b_gpio_number, ESP_ERR_INVALID_ARG, NULL, "Both GPIO is same.")
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->s_gpio_number && config->b_gpio_number != config->s_gpio_number, ESP_ERR_INVALID_ARG, NULL, "Encoder GPIO and button GPIO is same.")
gpio_config_t pin_config = {
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = (1ULL << config->a_gpio_number) | (1ULL << config->b_gpio_number),
.pin_bit_mask = (1ULL << config->a_gpio_number) | (1ULL << config->b_gpio_number) | (1ULL << config->s_gpio_number),
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_ANYEDGE};
esp_err_t err = gpio_config(&pin_config);
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "GPIO initialization failed.");
if (_is_initialized == false)
if (_encoder_counter == 0)
{
err = gpio_install_isr_service(ESP_INTR_FLAG_LOWMED);
ZH_ERROR_CHECK(err == ESP_OK || err == ESP_ERR_INVALID_STATE, err, gpio_reset_pin((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->b_gpio_number), "Failed install isr service.");
@@ -154,9 +251,9 @@ static esp_err_t _zh_encoder_gpio_init(const zh_encoder_init_config_t *config, z
_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.");
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)
{
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.");
@@ -165,12 +262,23 @@ 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.");
}
err = gpio_isr_handler_add((gpio_num_t)config->s_gpio_number, _zh_encoder_isr_handler, handle);
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);
gpio_reset_pin((gpio_num_t)config->a_gpio_number); gpio_reset_pin((gpio_num_t)config->b_gpio_number), "Interrupt initialization failed.");
}
else
{
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); 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.");
}
return ESP_OK;
}
static esp_err_t _zh_encoder_resources_init(const zh_encoder_init_config_t *config)
{
if (_is_initialized == false)
if (_encoder_counter == 0)
{
_queue_handle = xQueueCreate(config->queue_size, sizeof(zh_encoder_handle_t));
ZH_ERROR_CHECK(_queue_handle != NULL, ESP_FAIL, NULL, "Failed to create queue.");
@@ -180,7 +288,7 @@ static esp_err_t _zh_encoder_resources_init(const zh_encoder_init_config_t *conf
static esp_err_t _zh_encoder_task_init(const zh_encoder_init_config_t *config)
{
if (_is_initialized == false)
if (_encoder_counter == 0)
{
BaseType_t err = xTaskCreatePinnedToCore(&_zh_encoder_isr_processing_task, "zh_encoder_isr_processing", config->stack_size, NULL, config->task_priority, &zh_encoder, tskNO_AFFINITY);
ZH_ERROR_CHECK(err == pdPASS, ESP_FAIL, NULL, "Failed to create isr processing task.");
@@ -192,7 +300,19 @@ static void IRAM_ATTR _zh_encoder_isr_handler(void *arg)
{
zh_encoder_handle_t *encoder_handle = (zh_encoder_handle_t *)arg;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
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)];
uint64_t _current_us = esp_timer_get_time();
bool button_flag = false;
if (_current_us - _prev_us >= BUTTON_DEBOUNCE_TIME)
{
bool button_status = gpio_get_level((gpio_num_t)encoder_handle->s_gpio_number);
if (encoder_handle->button_status != button_status)
{
button_flag = true;
encoder_handle->button_status = button_status;
}
}
_prev_us = _current_us;
encoder_handle->encoder_state = _encoder_matrix[encoder_handle->encoder_state & 0x0F][(gpio_get_level((gpio_num_t)encoder_handle->b_gpio_number) << 1) | gpio_get_level((gpio_num_t)encoder_handle->a_gpio_number)];
switch (encoder_handle->encoder_state & 0x30)
{
case ZH_ENCODER_DIRECTION_CW:
@@ -224,6 +344,13 @@ static void IRAM_ATTR _zh_encoder_isr_handler(void *arg)
}
break;
default:
if (button_flag == true)
{
if (xQueueSendFromISR(_queue_handle, encoder_handle, &xHigherPriorityTaskWoken) != pdTRUE)
{
++_stats.queue_overflow_error;
}
}
break;
}
if (xHigherPriorityTaskWoken == pdTRUE)
@@ -240,6 +367,7 @@ static void IRAM_ATTR _zh_encoder_isr_processing_task(void *pvParameter)
{
encoder_data.encoder_number = queue.encoder_number;
encoder_data.encoder_position = queue.encoder_position;
encoder_data.button_status = queue.button_status;
esp_err_t err = esp_event_post(ZH_ENCODER, 0, &encoder_data, sizeof(zh_encoder_event_on_isr_t), 1000 / portTICK_PERIOD_MS);
if (err != ESP_OK)
{