4 Commits
v1.0.0 ... dev

Author SHA1 Message Date
d5c94158c2 perf: updated error checks 2025-12-03 10:42:51 +03:00
e133e5a807 perf: added task handle 2025-12-03 09:12:34 +03:00
13e04b2cd3 doc: update some comments for doxygen style 2025-11-23 21:58:01 +03:00
b4c8220284 doc: changed example 2025-06-16 19:30:37 +03:00
3 changed files with 91 additions and 71 deletions

View File

@@ -66,6 +66,6 @@ void app_main(void)
void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) 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; zh_encoder_event_on_isr_t *event = event_data;
printf("Encoder number %d position %.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. 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.
} }
``` ```

View File

@@ -1,3 +1,7 @@
/**
* @file zh_encoder.h
*/
#pragma once #pragma once
#include "esp_log.h" #include "esp_log.h"
@@ -6,6 +10,9 @@
#include "freertos/task.h" #include "freertos/task.h"
#include "esp_event.h" #include "esp_event.h"
/**
* @brief Encoder initial default values.
*/
#define ZH_ENCODER_INIT_CONFIG_DEFAULT() \ #define ZH_ENCODER_INIT_CONFIG_DEFAULT() \
{ \ { \
.task_priority = 10, \ .task_priority = 10, \
@@ -23,38 +30,51 @@ extern "C"
{ {
#endif #endif
typedef struct // Structure for initial initialization of encoder. extern TaskHandle_t zh_encoder; /*!< Unique encoder Task Handle. */
/**
* @brief Structure for initial initialization of encoder.
*/
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. 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. 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 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 a_gpio_number; /*!< Encoder A GPIO number. */
uint8_t b_gpio_number; // Encoder B 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_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. 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. 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. */
} zh_encoder_init_config_t; } zh_encoder_init_config_t;
typedef struct // Encoder handle. /**
* @brief Encoder handle.
*/
typedef struct
{ {
uint8_t a_gpio_number; // Encoder A GPIO number. uint8_t a_gpio_number; /*!< Encoder A GPIO number. */
uint8_t b_gpio_number; // Encoder B GPIO number. uint8_t b_gpio_number; /*!< Encoder B GPIO number. */
int32_t encoder_min_value; // Encoder min value. int32_t encoder_min_value; /*!< Encoder min value. */
int32_t encoder_max_value; // Encoder max value. int32_t encoder_max_value; /*!< Encoder max value. */
double encoder_step; // Encoder step. double encoder_step; /*!< Encoder step. */
double encoder_position; // Encoder position. double encoder_position; /*!< Encoder position. */
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 is_initialized; // Encoder initialization flag. bool is_initialized; /*!< Encoder initialization flag. */
} zh_encoder_handle_t; } zh_encoder_handle_t;
ESP_EVENT_DECLARE_BASE(ZH_ENCODER); 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. /**
* @brief Structure for sending data to the event handler when cause an interrupt.
*
* @note Should be used with ZH_ENCODER event base.
*/
typedef struct
{ {
uint8_t encoder_number; // Encoder unique number. uint8_t encoder_number; /*!< Encoder unique number. */
double encoder_position; // Encoder current position. double encoder_position; /*!< Encoder current position. */
} zh_encoder_event_on_isr_t; } zh_encoder_event_on_isr_t;
/** /**

View File

@@ -2,15 +2,14 @@
#define TAG "zh_encoder" #define TAG "zh_encoder"
#define ZH_ENCODER_LOGI(msg, ...) ESP_LOGI(TAG, msg, ##__VA_ARGS__) #define ZH_LOGI(msg, ...) ESP_LOGI(TAG, msg, ##__VA_ARGS__)
#define ZH_ENCODER_LOGW(msg, ...) ESP_LOGW(TAG, msg, ##__VA_ARGS__) #define ZH_LOGE(msg, err, ...) ESP_LOGE(TAG, "[%s:%d:%s] " msg, __FILE__, __LINE__, esp_err_to_name(err), ##__VA_ARGS__)
#define ZH_ENCODER_LOGE(msg, ...) ESP_LOGE(TAG, msg, ##__VA_ARGS__)
#define ZH_ENCODER_LOGE_ERR(msg, err, ...) ESP_LOGE(TAG, "[%s:%d:%s] " msg, __FILE__, __LINE__, esp_err_to_name(err), ##__VA_ARGS__)
#define ZH_ENCODER_CHECK(cond, err, msg, ...) \ #define ZH_ERROR_CHECK(cond, err, cleanup, msg, ...) \
if (!(cond)) \ if (!(cond)) \
{ \ { \
ZH_ENCODER_LOGE_ERR(msg, err); \ ZH_LOGE(msg, err, ##__VA_ARGS__); \
cleanup; \
return err; \ return err; \
} }
@@ -26,6 +25,7 @@ static const uint8_t _encoder_matrix[7][4] = {
{0x03, 0x05, 0x03, 0x00}, {0x03, 0x05, 0x03, 0x00},
}; };
TaskHandle_t zh_encoder = NULL;
static QueueHandle_t _queue_handle = NULL; static QueueHandle_t _queue_handle = NULL;
static bool _is_initialized = false; static bool _is_initialized = false;
@@ -41,84 +41,84 @@ 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)
{ {
ZH_ENCODER_LOGI("Encoder initialization started."); ZH_LOGI("Encoder initialization started.");
esp_err_t err = _zh_encoder_validate_config(config); esp_err_t err = _zh_encoder_validate_config(config);
ZH_ENCODER_CHECK(err == ESP_OK, err, "Encoder initialization failed. Initial configuration check failed."); ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "Encoder initialization failed. Initial configuration check failed.");
ZH_ENCODER_LOGI("Encoder initial configuration check completed successfully."); ZH_LOGI("Encoder initial configuration check completed successfully.");
handle->encoder_number = config->encoder_number; handle->encoder_number = config->encoder_number;
handle->encoder_min_value = config->encoder_min_value; handle->encoder_min_value = config->encoder_min_value;
handle->encoder_max_value = config->encoder_max_value; handle->encoder_max_value = config->encoder_max_value;
handle->encoder_step = config->encoder_step; handle->encoder_step = config->encoder_step;
handle->encoder_position = (handle->encoder_min_value + handle->encoder_max_value) / 2; handle->encoder_position = (handle->encoder_min_value + handle->encoder_max_value) / 2;
err = _zh_encoder_gpio_init(config); err = _zh_encoder_gpio_init(config);
ZH_ENCODER_CHECK(err == ESP_OK, err, "Encoder initialization failed. GPIO initialization failed."); ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "Encoder initialization failed. GPIO initialization failed.");
ZH_ENCODER_LOGI("Encoder GPIO initialization completed successfully."); ZH_LOGI("Encoder GPIO initialization completed successfully.");
handle->a_gpio_number = config->a_gpio_number; handle->a_gpio_number = config->a_gpio_number;
handle->b_gpio_number = config->b_gpio_number; handle->b_gpio_number = config->b_gpio_number;
err = _zh_encoder_configure_interrupts(config, handle); err = _zh_encoder_configure_interrupts(config, handle);
ZH_ENCODER_CHECK(err == ESP_OK, err, "Encoder initialization failed. Interrupt initialization failed."); ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "Encoder initialization failed. Interrupt initialization failed.");
ZH_ENCODER_LOGI("Encoder interrupt initialization completed successfully."); ZH_LOGI("Encoder interrupt initialization completed successfully.");
err = _zh_encoder_init_resources(config); err = _zh_encoder_init_resources(config);
ZH_ENCODER_CHECK(err == ESP_OK, err, "Encoder initialization failed. Resources initialization failed."); ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "Encoder initialization failed. Resources initialization failed.");
ZH_ENCODER_LOGI("Encoder resources initialization completed successfully."); ZH_LOGI("Encoder resources initialization completed successfully.");
err = _zh_encoder_create_task(config); err = _zh_encoder_create_task(config);
ZH_ENCODER_CHECK(err == ESP_OK, err, "Encoder initialization failed. Processing task initialization failed."); ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "Encoder initialization failed. Processing task initialization failed.");
ZH_ENCODER_LOGI("Encoder processing task initialization completed successfully."); ZH_LOGI("Encoder processing task initialization completed successfully.");
handle->is_initialized = true; handle->is_initialized = true;
_is_initialized = true; _is_initialized = true;
ZH_ENCODER_LOGI("Encoder initialization completed successfully."); ZH_LOGI("Encoder initialization completed successfully.");
return ESP_OK; 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_ENCODER_LOGI("Encoder set position started."); ZH_LOGI("Encoder set position started.");
ZH_ENCODER_CHECK(handle->is_initialized == true, ESP_FAIL, "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_ENCODER_CHECK(position <= handle->encoder_max_value && position >= handle->encoder_min_value, ESP_ERR_INVALID_ARG, "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.");
handle->encoder_position = position; handle->encoder_position = position;
ZH_ENCODER_LOGI("Encoder set position completed successfully."); ZH_LOGI("Encoder set position completed successfully.");
return ESP_OK; return ESP_OK;
} }
esp_err_t zh_encoder_get(const zh_encoder_handle_t *handle, double *position) esp_err_t zh_encoder_get(const zh_encoder_handle_t *handle, double *position)
{ {
ZH_ENCODER_LOGI("Encoder get position started."); ZH_LOGI("Encoder get position started.");
ZH_ENCODER_CHECK(handle->is_initialized == true, ESP_FAIL, "Encoder get position failed. Encoder not initialized."); ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Encoder get position failed. Encoder not initialized.");
*position = handle->encoder_position; *position = handle->encoder_position;
ZH_ENCODER_LOGI("Encoder get position completed successfully."); ZH_LOGI("Encoder get position completed successfully.");
return ESP_OK; return ESP_OK;
} }
esp_err_t zh_encoder_reset(zh_encoder_handle_t *handle) esp_err_t zh_encoder_reset(zh_encoder_handle_t *handle)
{ {
ZH_ENCODER_LOGI("Encoder reset started."); ZH_LOGI("Encoder reset started.");
ZH_ENCODER_CHECK(handle->is_initialized == true, ESP_FAIL, "Encoder reset failed. Encoder not initialized."); ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Encoder reset failed. Encoder not initialized.");
handle->encoder_position = (handle->encoder_min_value + handle->encoder_max_value) / 2; handle->encoder_position = (handle->encoder_min_value + handle->encoder_max_value) / 2;
ZH_ENCODER_LOGI("Encoder reset completed successfully."); ZH_LOGI("Encoder reset completed successfully.");
return ESP_OK; return ESP_OK;
} }
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)
{ {
ZH_ENCODER_CHECK(config != NULL, ESP_ERR_INVALID_ARG, "Invalid configuration."); ZH_ERROR_CHECK(config != NULL, ESP_ERR_INVALID_ARG, NULL, "Invalid configuration.");
ZH_ENCODER_CHECK(config->task_priority >= 10 && config->stack_size >= 3072, ESP_ERR_INVALID_ARG, "Invalid task settings."); ZH_ERROR_CHECK(config->task_priority >= 10 && config->stack_size >= 3072, ESP_ERR_INVALID_ARG, NULL, "Invalid task settings.");
ZH_ENCODER_CHECK(config->queue_size >= 10, ESP_ERR_INVALID_ARG, "Invalid queue size."); ZH_ERROR_CHECK(config->queue_size >= 10, ESP_ERR_INVALID_ARG, NULL, "Invalid queue size.");
ZH_ENCODER_CHECK(config->encoder_max_value > config->encoder_min_value, ESP_ERR_INVALID_ARG, "Invalid encoder min/max value."); ZH_ERROR_CHECK(config->encoder_max_value > config->encoder_min_value, ESP_ERR_INVALID_ARG, NULL, "Invalid encoder min/max value.");
ZH_ENCODER_CHECK(config->encoder_step > 0, ESP_ERR_INVALID_ARG, "Invalid encoder step."); ZH_ERROR_CHECK(config->encoder_step > 0, ESP_ERR_INVALID_ARG, NULL, "Invalid encoder step.");
return ESP_OK; return ESP_OK;
} }
static esp_err_t _zh_encoder_gpio_init(const zh_encoder_init_config_t *config) static esp_err_t _zh_encoder_gpio_init(const zh_encoder_init_config_t *config)
{ {
ZH_ENCODER_CHECK(config->a_gpio_number < GPIO_NUM_MAX || config->b_gpio_number < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG, "Invalid GPIO number.") 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_ENCODER_CHECK(config->a_gpio_number != config->b_gpio_number, ESP_ERR_INVALID_ARG, "Invalid GPIO number.") ZH_ERROR_CHECK(config->a_gpio_number != config->b_gpio_number, ESP_ERR_INVALID_ARG, NULL, "Invalid GPIO number.")
gpio_config_t pin_config = { gpio_config_t pin_config = {
.mode = GPIO_MODE_INPUT, .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),
.pull_up_en = GPIO_PULLUP_ENABLE, .pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_ANYEDGE}; .intr_type = GPIO_INTR_ANYEDGE};
esp_err_t err = gpio_config(&pin_config); esp_err_t err = gpio_config(&pin_config);
ZH_ENCODER_CHECK(err == ESP_OK, err, "GPIO initialization failed."); ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "GPIO initialization failed.");
return ESP_OK; return ESP_OK;
} }
@@ -126,9 +126,9 @@ static esp_err_t _zh_encoder_configure_interrupts(const zh_encoder_init_config_t
{ {
gpio_install_isr_service(0); gpio_install_isr_service(0);
esp_err_t err = gpio_isr_handler_add(config->a_gpio_number, _zh_encoder_isr_handler, handle); esp_err_t err = gpio_isr_handler_add(config->a_gpio_number, _zh_encoder_isr_handler, handle);
ZH_ENCODER_CHECK(err == ESP_OK, err, "Interrupt initialization failed."); ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "Interrupt initialization failed.");
err = gpio_isr_handler_add(config->b_gpio_number, _zh_encoder_isr_handler, handle); err = gpio_isr_handler_add(config->b_gpio_number, _zh_encoder_isr_handler, handle);
ZH_ENCODER_CHECK(err == ESP_OK, err, "Interrupt initialization failed."); ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "Interrupt initialization failed.");
return ESP_OK; return ESP_OK;
} }
@@ -137,7 +137,7 @@ static esp_err_t _zh_encoder_init_resources(const zh_encoder_init_config_t *conf
if (_is_initialized == false) if (_is_initialized == false)
{ {
_queue_handle = xQueueCreate(config->queue_size, sizeof(zh_encoder_handle_t)); _queue_handle = xQueueCreate(config->queue_size, sizeof(zh_encoder_handle_t));
ZH_ENCODER_CHECK(_queue_handle != NULL, ESP_FAIL, "Queue creation failed."); ZH_ERROR_CHECK(_queue_handle != NULL, ESP_FAIL, NULL, "Queue creation failed.");
} }
return ESP_OK; return ESP_OK;
} }
@@ -152,9 +152,9 @@ static esp_err_t _zh_encoder_create_task(const zh_encoder_init_config_t *config)
config->stack_size, config->stack_size,
NULL, NULL,
config->task_priority, config->task_priority,
NULL, &zh_encoder,
tskNO_AFFINITY); tskNO_AFFINITY);
ZH_ENCODER_CHECK(err == pdPASS, ESP_FAIL, "Task creation failed."); ZH_ERROR_CHECK(err == pdPASS, ESP_FAIL, NULL, "Task creation failed.");
} }
return ESP_OK; return ESP_OK;
} }
@@ -204,15 +204,15 @@ static void IRAM_ATTR _zh_encoder_isr_processing_task(void *pvParameter)
zh_encoder_event_on_isr_t encoder_data = {0}; zh_encoder_event_on_isr_t encoder_data = {0};
while (xQueueReceive(_queue_handle, &queue, portMAX_DELAY) == pdTRUE) while (xQueueReceive(_queue_handle, &queue, portMAX_DELAY) == pdTRUE)
{ {
ZH_ENCODER_LOGI("Encoder isr processing begin."); ZH_LOGI("Encoder isr processing begin.");
encoder_data.encoder_number = queue.encoder_number; encoder_data.encoder_number = queue.encoder_number;
encoder_data.encoder_position = queue.encoder_position; 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); 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) if (err != ESP_OK)
{ {
ZH_ENCODER_LOGE_ERR("Encoder isr processing failed. Failed to post interrupt event.", err); ZH_LOGE("Encoder isr processing failed. Failed to post interrupt event.", err);
} }
ZH_ENCODER_LOGI("Encoder isr processing completed successfully."); ZH_LOGI("Encoder isr processing completed successfully.");
} }
vTaskDelete(NULL); vTaskDelete(NULL);
} }