doc: updated readme

This commit is contained in:
aZholtikov committed 2026-10-18 10:23:59 +03:00
1 parent 4de0443b35
commit f2f3030ee7
1 file changed
+220 -49
+220 -49
View File
@@ -1,23 +1,20 @@
# ESP32 ESP-IDF component for rotary encoder using PCNT peripheral
# ESP32 ESP-IDF component for rotary encoder (PCNT peripheral)
## Features
1. Quadrature decoding via PCNT peripheral.
2. Configurable min/max/step values with float precision.
3. Push-button support with debouncing.
4. Multiple simultaneous encoders (up to 8).
5. Event-based notification via esp_event (ISR context).
6. Error statistics tracking for diagnostics.
1. Quadrature decoding through the PCNT peripheral hardware.
2. Configurable value range and step with float precision.
3. Push-button support with software debouncing timer.
4. Multiple encoders sharing a single processing task (limited by PCNT units and GPIOs).
5. Asynchronous rotation and button notifications through esp_event.
6. Built-in error statistics with queue overflow and stack watermark counters.
## Note
## Attention
Enable the following settings in menuconfig:
```text
GPIO_CTRL_FUNC_IN_IRAM
PCNT_CTRL_FUNC_IN_IRAM
PCNT_ISR_IRAM_SAFE
```
1. Encoder number must be unique across all initialized encoders and greater than zero.
2. Task priority, stack size and queue size of the first initialized encoder are used by the shared processing task.
3. Position is initialized to the midpoint of the configured range.
4. Position is clamped to the configured minimum and maximum values.
## Using
@@ -34,62 +31,236 @@ In the application, add the component:
#include "zh_encoder.h"
```
## Example
## Examples
### Basic Example: Init and Get Position
```c
#include "zh_encoder.h"
#define ENCODER_NUMBER 0x01
zh_encoder_handle_t *encoder_handle = NULL;
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_ERROR);
esp_event_loop_create_default();
esp_event_handler_instance_register(ZH_ENCODER, ESP_EVENT_ANY_ID, &zh_encoder_event_handler, NULL, NULL);
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_4;
config.b_gpio_number = GPIO_NUM_16;
config.s_gpio_number = GPIO_NUM_15;
config.encoder_min_value = -10;
config.encoder_max_value = 10;
config.encoder_step = 0.001;
config.encoder_number = ENCODER_NUMBER;
zh_encoder_init(&config, &encoder_handle);
float encoder_position = 0;
zh_encoder_get(&encoder_handle, &encoder_position);
printf("Encoder position %0.3f.\n", encoder_position);
for (;;)
config.encoder_step = 1;
config.encoder_number = 1;
esp_err_t ret = zh_encoder_init(&config, &encoder_handle);
if (ret != ESP_OK)
{
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(60000 / portTICK_PERIOD_MS);
printf("Encoder initialization error: %s\n", esp_err_to_name(ret));
return;
}
float position = 0;
ret = zh_encoder_get(&encoder_handle, &position);
if (ret == ESP_OK)
{
printf("Encoder position: %0.3f\n", position);
}
else
{
printf("Error getting position: %s\n", esp_err_to_name(ret));
}
ret = zh_encoder_deinit(&encoder_handle);
if (ret != ESP_OK)
{
printf("Error deinitializing encoder: %s\n", esp_err_to_name(ret));
}
}
```
### Set and Reset Position Example
```c
#include "zh_encoder.h"
zh_encoder_handle_t *encoder_handle = NULL;
void app_main(void)
{
esp_log_level_set("zh_encoder", ESP_LOG_ERROR);
zh_encoder_init_config_t config = ZH_ENCODER_INIT_CONFIG_DEFAULT();
config.a_gpio_number = GPIO_NUM_4;
config.b_gpio_number = GPIO_NUM_16;
config.encoder_min_value = -100;
config.encoder_max_value = 100;
config.encoder_step = 0.5;
config.encoder_number = 1;
esp_err_t ret = zh_encoder_init(&config, &encoder_handle);
if (ret != ESP_OK)
{
printf("Encoder initialization error: %s\n", esp_err_to_name(ret));
return;
}
float position = 25.0f;
ret = zh_encoder_set(&encoder_handle, position);
if (ret == ESP_OK)
{
printf("Position set to %0.3f\n", position);
}
else
{
printf("Error setting position: %s\n", esp_err_to_name(ret));
}
float current = 0;
ret = zh_encoder_get(&encoder_handle, &current);
if (ret == ESP_OK)
{
printf("Current position: %0.3f\n", current);
}
else
{
printf("Error getting position: %s\n", esp_err_to_name(ret));
}
ret = zh_encoder_reset(&encoder_handle);
if (ret == ESP_OK)
{
printf("Position reset to midpoint\n");
}
else
{
printf("Error resetting position: %s\n", esp_err_to_name(ret));
}
zh_encoder_deinit(&encoder_handle);
}
```
### Reinit Range Example
```c
#include "zh_encoder.h"
zh_encoder_handle_t *encoder_handle = NULL;
void app_main(void)
{
esp_log_level_set("zh_encoder", ESP_LOG_ERROR);
zh_encoder_init_config_t config = ZH_ENCODER_INIT_CONFIG_DEFAULT();
config.a_gpio_number = GPIO_NUM_4;
config.b_gpio_number = GPIO_NUM_16;
config.encoder_min_value = -10;
config.encoder_max_value = 10;
config.encoder_step = 1;
config.encoder_number = 1;
esp_err_t ret = zh_encoder_init(&config, &encoder_handle);
if (ret != ESP_OK)
{
printf("Encoder initialization error: %s\n", esp_err_to_name(ret));
return;
}
ret = zh_encoder_reinit(&encoder_handle, 0, 255, 1);
if (ret == ESP_OK)
{
printf("Encoder range changed to 0..255\n");
}
else
{
printf("Error reinitializing encoder: %s\n", esp_err_to_name(ret));
}
float position = 0;
ret = zh_encoder_get(&encoder_handle, &position);
if (ret == ESP_OK)
{
printf("Position after reinit: %0.3f\n", position);
}
else
{
printf("Error getting position: %s\n", esp_err_to_name(ret));
}
zh_encoder_deinit(&encoder_handle);
}
```
---
### Button Event Example
```c
#include "zh_encoder.h"
zh_encoder_handle_t *encoder_handle = NULL;
void zh_encoder_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
(void)arg;
switch (event_id)
(void)event_base;
if (event_id == ZH_BUTTON_EVENT)
{
case ZH_BUTTON_EVENT:
zh_encoder_button_event_on_isr_t *button_event = event_data;
printf("Encoder number %d button was %s.\n", button_event->encoder_number, (button_event->button_status == true) ? "released" : "pressed");
break;
case ZH_ENCODER_EVENT:
zh_encoder_event_on_isr_t *encoder_event = event_data;
printf("Encoder number %d position %0.3f was %s.\n", encoder_event->encoder_number, encoder_event->encoder_position, (encoder_event->encoder_status == true) ? "increased" : "reduced");
break;
default:
break;
zh_encoder_button_event_on_isr_t *button_event = (zh_encoder_button_event_on_isr_t *)event_data;
printf("Encoder %d button %s\n", button_event->encoder_number, button_event->button_status ? "released" : "pressed");
}
else if (event_id == ZH_ENCODER_EVENT)
{
zh_encoder_event_on_isr_t *encoder_event = (zh_encoder_event_on_isr_t *)event_data;
printf("Encoder %d position %0.3f %s\n", encoder_event->encoder_number, encoder_event->encoder_position, encoder_event->encoder_status ? "increased" : "reduced");
}
}
void app_main(void)
{
esp_log_level_set("zh_encoder", ESP_LOG_ERROR);
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_event_handler_instance_register(ZH_ENCODER, ESP_EVENT_ANY_ID, &zh_encoder_event_handler, NULL, NULL));
zh_encoder_init_config_t config = ZH_ENCODER_INIT_CONFIG_DEFAULT();
config.a_gpio_number = GPIO_NUM_4;
config.b_gpio_number = GPIO_NUM_16;
config.s_gpio_number = GPIO_NUM_15;
config.s_gpio_debounce_time = 50000;
config.encoder_min_value = 0;
config.encoder_max_value = 100;
config.encoder_step = 1;
config.encoder_number = 1;
esp_err_t ret = zh_encoder_init(&config, &encoder_handle);
if (ret != ESP_OK)
{
printf("Encoder initialization error: %s\n", esp_err_to_name(ret));
return;
}
for (;;)
{
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
```
---
### Error Statistics Example
```c
#include "zh_encoder.h"
zh_encoder_handle_t *encoder_handle = NULL;
void app_main(void)
{
esp_log_level_set("zh_encoder", ESP_LOG_ERROR);
zh_encoder_init_config_t config = ZH_ENCODER_INIT_CONFIG_DEFAULT();
config.a_gpio_number = GPIO_NUM_4;
config.b_gpio_number = GPIO_NUM_16;
config.encoder_min_value = -10;
config.encoder_max_value = 10;
config.encoder_step = 1;
config.encoder_number = 1;
esp_err_t ret = zh_encoder_init(&config, &encoder_handle);
if (ret != ESP_OK)
{
printf("Encoder initialization error: %s\n", esp_err_to_name(ret));
return;
}
for (;;)
{
const zh_encoder_stats_t *stats = zh_encoder_get_stats();
printf("Event post errors: %lu\n", (unsigned long)stats->event_post_error);
printf("Queue overflow errors: %lu\n", (unsigned long)stats->queue_overflow_error);
printf("Minimum free stack: %lu\n", (unsigned long)stats->min_stack_size);
zh_encoder_reset_stats();
vTaskDelay(pdMS_TO_TICKS(60000));
}
}
```