From 75134fbb2e29be38be72c4e78c0446a353d07cd2 Mon Sep 17 00:00:00 2001 From: Alexey Zholtikov Date: Sat, 5 Sep 2026 11:10:41 +0300 Subject: [PATCH] feat!: main structure of handle changed to double pointer --- README.md | 44 +++++--- idf_component.yml | 8 ++ include/zh_tachometer.h | 118 ++++++++++++++------- version.txt | 2 +- zh_tachometer.c | 226 ++++++++++++++++++++++++++-------------- 5 files changed, 264 insertions(+), 134 deletions(-) create mode 100644 idf_component.yml diff --git a/README.md b/README.md index 9b66259..de8825f 100755 --- a/README.md +++ b/README.md @@ -1,20 +1,22 @@ # ESP32 ESP-IDF component for tachometer (via rotary optical encoder) -## Tested on - -1. [ESP32 ESP-IDF v6.0.0](https://docs.espressif.com/projects/esp-idf/en/v6.0/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 up to 8 tachometers on one device. +1. Quadrature encoder support via ESP-IDF PCNT peripheral in quadrature decoder mode. +2. Automatic RPM calculation based on configurable pulses-per-revolution. +3. Periodic RPM sampling via ESP-Timer at 100 Hz (10 ms interval). +4. Configurable GPIO pull-up resistors for encoder A/B phases. +5. PCNT glitch filter (1000 ns) to reject spurious transitions. +6. Watch points at ±32767 for overflow detection. +7. Multiple tachometers supported — each requires its own PCNT unit and timer. -## Attention +## Note -1. For correct operation, please enable the following settings in the menuconfig: +Enable the following settings in menuconfig: ```text PCNT_CTRL_FUNC_IN_IRAM @@ -25,7 +27,7 @@ PCNT_ISR_IRAM_SAF In an existing project, run the following command to install the components: -```text +```bash cd ../your_project/components git clone https://github.com/aZholtikov/zh_tachometer ``` @@ -36,27 +38,37 @@ In the application, add the component: #include "zh_tachometer.h" ``` -## Examples +## Example ```c #include "zh_tachometer.h" -zh_tachometer_handle_t tachometer_handle = {0}; +static zh_tachometer_handle_t *tachometer_handle = NULL; void app_main(void) { esp_log_level_set("zh_tachometer", ESP_LOG_ERROR); + zh_tachometer_init_config_t config = ZH_TACHOMETER_INIT_CONFIG_DEFAULT(); config.a_gpio_number = GPIO_NUM_26; config.b_gpio_number = GPIO_NUM_27; config.encoder_pulses = 3600; - zh_tachometer_init(&config, &tachometer_handle); - for (;;) - { - uint16_t value = 0; - zh_tachometer_get(&tachometer_handle, &value); - printf("Tachometer value is %d rpm.\n", value); + + esp_err_t ret = zh_tachometer_init(&config, &tachometer_handle); + if (ret != ESP_OK) { + ESP_LOG_ERROR("zh_tachometer", "Initialization failed: %s", esp_err_to_name(ret)); + return; + } + + for (;;) { + uint16_t rpm = 0; + ret = zh_tachometer_get(&tachometer_handle, &rpm); + if (ret == ESP_OK) { + printf("Tachometer value is %d rpm.\n", rpm); + } vTaskDelay(100 / portTICK_PERIOD_MS); } + + zh_tachometer_deinit(&tachometer_handle); } ``` diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 0000000..ce4fcfa --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,8 @@ +description: ESP32 ESP-IDF component for tachometer (via optical rotary encoder) +url: https://github.com/aZholtikov/zh_tachometer +version: "2.0.0" +license: "Apache-2.0" +maintainers: + - "Alexey Zholtikov " +dependencies: + idf: ">=5.0" \ No newline at end of file diff --git a/include/zh_tachometer.h b/include/zh_tachometer.h index d68b0b2..93277fe 100644 --- a/include/zh_tachometer.h +++ b/include/zh_tachometer.h @@ -1,9 +1,28 @@ /** * @file zh_tachometer.h + * + * @brief ESP-IDF driver for a quadrature encoder-based tachometer using PCNT + * and ESP-Timer to measure rotational speed in RPM. + * + * The module leverages the ESP-IDF PCNT peripheral in quadrature decoder mode + * to count encoder pulses and an ESP-Timer running at 10 Hz to sample the + * accumulated count and compute RPM. The result is a non-blocking, RTOS-friendly + * interface suitable for motor speed monitoring. + * + * Key features: + * - Quadrature encoder support via two GPIO lines (A/B phases) + * - Automatic RPM calculation based on configurable pulses-per-revolution + * - Configurable GPIO pull-up resistors + * - Thread-safe access through FreeRTOS-compatible handles + * + * @note The internal PCNT unit uses accumulation mode with watch points at + * ±32767. RPM values above 32767 may overflow — ensure the encoder + * pulses-per-revolution and expected RPM range stay within safe bounds. */ #pragma once +#include "math.h" #include "esp_log.h" #include "driver/gpio.h" #include "driver/pulse_cnt.h" @@ -11,9 +30,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" -/** - * @brief Tachometer initial default values. - */ #define ZH_TACHOMETER_INIT_CONFIG_DEFAULT() \ { \ .a_gpio_number = GPIO_NUM_MAX, \ @@ -27,62 +43,84 @@ extern "C" #endif /** - * @brief Structure for initial initialization of tachometer. + * @brief Opaque handle to the tachometer instance. + * + * Used to identify and access a specific tachometer throughout its + * lifetime. The internal structure is defined in the implementation + * file and must not be accessed directly. + */ + typedef struct _zh_tachometer_handle_t zh_tachometer_handle_t; + + /** + * @brief Initialization configuration for the quadrature encoder tachometer. + * + * This structure is passed to `zh_tachometer_init()` to configure the GPIO + * pins, pull-up resistors, and the encoder's pulses-per-revolution ratio. + * + * @note Both GPIO pins must be assigned before initialization. + * Use GPIO_NUM_MAX to indicate an unassigned pin. */ typedef struct { - uint8_t a_gpio_number; /*!< Encoder A GPIO number. */ - uint8_t b_gpio_number; /*!< Encoder B GPIO number. */ - bool pullup; /*!< Pullup GPIO enable/disable. */ - uint16_t encoder_pulses; /*!< Number of pulses per one rotation. */ + uint8_t a_gpio_number; /*!< Encoder A phase GPIO number */ + uint8_t b_gpio_number; /*!< Encoder B phase GPIO number */ + bool pullup; /*!< Enable or disable GPIO pull-up resistors */ + uint16_t encoder_pulses; /*!< Number of pulses per one full rotation */ } zh_tachometer_init_config_t; /** - * @brief Tachometer handle. + * @brief Initialize the tachometer with the provided configuration. + * + * Allocates a handle, configures the ESP-Timer (10 Hz sampling), and sets up + * the PCNT peripheral in quadrature decoder mode with glitch filtering. + * + * @param[in] config Pointer to the initialization configuration (must not be NULL) + * @param[out] handle Pointer to receive the created tachometer handle (must be NULL) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if `config` or `handle` is NULL, or `encoder_pulses` is zero + * @return ESP_ERR_INVALID_STATE if the handle is already initialized. + * @return ESP_ERR_NO_MEM if memory allocation fails + * @return ESP_FAIL if PCNT initialization or configuration failed + * */ - typedef struct - { - pcnt_unit_handle_t pcnt_unit_handle; /*!< Tachometer unique pcnt unit handle. */ - pcnt_channel_handle_t pcnt_channel_a_handle; /*!< Tachometer unique pcnt channel handle. */ - pcnt_channel_handle_t pcnt_channel_b_handle; /*!< Tachometer unique pcnt channel handle. */ - esp_timer_handle_t esp_timer_handle; /*!< Tachometer unique timer handle. */ - uint16_t value; /*!< Tachometer value. */ - uint16_t encoder_pulses; /*!< Number of pulses per one rotation. */ - bool is_initialized; /*!< Tachometer initialization flag. */ - } zh_tachometer_handle_t; + esp_err_t zh_tachometer_init(const zh_tachometer_init_config_t *config, zh_tachometer_handle_t **handle); /** - * @brief Initialize tachometer. + * @brief Deinitialize the tachometer and release all resources. * - * @param[in] config Pointer to tachometer initialized configuration structure. Can point to a temporary variable. - * @param[out] handle Pointer to unique tachometer handle. + * Stops the PCNT unit, removes channels and watch points, deletes the + * ESP-Timer, and frees the handle memory. The handle pointer is set to + * NULL on success. * - * @note Before initialize the tachometer recommend initialize zh_tachometer_init_config_t structure with default values. + * @param[in,out] handle Pointer to the tachometer handle (must not be NULL) * - * @code zh_tachometer_init_config_t config = ZH_TACHOMETER_INIT_CONFIG_DEFAULT() @endcode + * @note After deinitialization, the handle is invalidated and must not be + * used. Call zh_tachometer_init() again to reinitialize. * - * @return ESP_OK if success or an error code otherwise. + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if `handle` is NULL or `*handle` is NULL + * @return ESP_FAIL if PCNT stop, disable, or deletion failed */ - esp_err_t zh_tachometer_init(const zh_tachometer_init_config_t *config, zh_tachometer_handle_t *handle); + esp_err_t zh_tachometer_deinit(zh_tachometer_handle_t **handle); /** - * @brief Deinitialize tachometer. + * @brief Retrieve the current RPM value from the tachometer. * - * @param[in, out] handle Pointer to unique tachometer handle. + * Returns the last sampled RPM value computed by the timer callback. + * The value represents absolute RPM (direction is discarded); negative + * rotation is reported as a positive value. * - * @return ESP_OK if success or an error code otherwise. + * @param[in] handle Pointer to the tachometer handle (must not be NULL) + * @param[out] value Pointer to receive the RPM value (must not be NULL) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if `handle` or `value` is NULL + * + * @note This function is non-blocking and returns the most recent value + * without accessing the PCNT peripheral directly. */ - esp_err_t zh_tachometer_deinit(zh_tachometer_handle_t *handle); - - /** - * @brief Get tachometer value. - * - * @param[in] handle Pointer to unique tachometer handle. - * @param[out] value Tachometer value. - * - * @return ESP_OK if success or an error code otherwise. - */ - esp_err_t zh_tachometer_get(const zh_tachometer_handle_t *handle, uint16_t *value); + esp_err_t zh_tachometer_get(zh_tachometer_handle_t **handle, uint16_t *value); #ifdef __cplusplus } diff --git a/version.txt b/version.txt index 1cc5f65..359a5b9 100755 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.1.0 \ No newline at end of file +2.0.0 \ No newline at end of file diff --git a/zh_tachometer.c b/zh_tachometer.c index 6b74ce4..21b2bc6 100644 --- a/zh_tachometer.c +++ b/zh_tachometer.c @@ -13,52 +13,132 @@ return err; \ } +#define ZH_ERROR_CHECK_VOID(cond, cleanup, msg, ...) \ + if (!(cond)) \ + { \ + ZH_LOGE(msg, ESP_FAIL, ##__VA_ARGS__); \ + cleanup; \ + return; \ + } + +/** + * @brief Internal handle structure for the tachometer driver. + * + * Holds references to the PCNT unit and channels, the ESP-Timer used for + * periodic RPM sampling, the last computed RPM value, and the encoder's + * pulses-per-revolution configuration. + * + * @note This structure is opaque to callers and must only be accessed + * through the public API functions. + */ +struct _zh_tachometer_handle_t +{ + pcnt_unit_handle_t pcnt_unit_handle; /*!< PCNT unit handle for quadrature decoding */ + pcnt_channel_handle_t pcnt_channel_a_handle; /*!< PCNT channel A handle (edge detector) */ + pcnt_channel_handle_t pcnt_channel_b_handle; /*!< PCNT channel B handle (edge detector) */ + esp_timer_handle_t esp_timer_handle; /*!< ESP-Timer handle for 100 Hz RPM sampling */ + uint16_t value; /*!< Last computed RPM value (absolute) */ + uint16_t encoder_pulses; /*!< Pulses per one full rotation */ +}; + +/** + * @brief Validate the initialization configuration. + * + * Checks that `encoder_pulses` is greater than zero. + * + * @param config Pointer to the initialization configuration + * + * @return ESP_OK on valid configuration + * @return ESP_ERR_INVALID_ARG if `encoder_pulses` is zero or less + */ static esp_err_t _zh_tachometer_validate_config(const zh_tachometer_init_config_t *config); + +/** + * @brief Initialize the PCNT peripheral in quadrature decoder mode. + * + * Configures the PCNT unit with accumulation mode, glitch filter (1000 ns), + * two channels for encoder phases A and B, edge/level actions for direction + * counting, and watch points at ±32767. Starts the unit and clears the + * counter. Optionally disables GPIO pull-ups if configured. + * + * @param config Pointer to the initialization configuration + * @param handle Pointer to the initialized tachometer handle + * + * @return ESP_OK on successful PCNT initialization + * @return ESP_ERR_INVALID_ARG if GPIO numbers are invalid or identical + * @return ESP_FAIL on any PCNT configuration failure + */ static esp_err_t _zh_tachometer_pcnt_init(const zh_tachometer_init_config_t *config, zh_tachometer_handle_t *handle); + +/** + * @brief Initialize the ESP-Timer for periodic RPM sampling. + * + * Creates a periodic timer that fires every 10 ms (100 Hz) and invokes + * the alarm callback to read and convert the PCNT count to RPM. + * + * @param handle Pointer to the initialized tachometer handle + * + * @return ESP_OK on successful timer initialization + * @return ESP_FAIL on timer creation or start failure + */ static esp_err_t _zh_tachometer_timer_init(zh_tachometer_handle_t *handle); + +/** + * @brief ESP-Timer alarm callback for RPM sampling. + * + * Executed every 10 ms from ISR context. Reads the accumulated PCNT count, + * clears it, and converts it to RPM using the formula: + * + * RPM = (count * 100.0 / pulses_per_rev) * 60 + * + * The result is stored as an absolute value (direction discarded). + * + * @param arg Pointer to the tachometer handle + */ static void _zh_tachometer_timer_on_alarm_cb(void *arg); -esp_err_t zh_tachometer_init(const zh_tachometer_init_config_t *config, zh_tachometer_handle_t *handle) // -V2008 +esp_err_t zh_tachometer_init(const zh_tachometer_init_config_t *config, zh_tachometer_handle_t **handle) // -V2008 { ZH_LOGI("Tachometer initialization started."); ZH_ERROR_CHECK(config != NULL && handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Tachometer initialization failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == false, ESP_ERR_INVALID_STATE, NULL, "Tachometer initialization failed. Tachometer is already initialized."); + ZH_ERROR_CHECK(*handle == NULL, ESP_ERR_INVALID_STATE, NULL, "Tachometer initialization failed. Tachometer is already initialized."); ZH_ERROR_CHECK(_zh_tachometer_validate_config(config) == ESP_OK, ESP_FAIL, NULL, "Tachometer initialization failed. Initial configuration check failed."); - ZH_ERROR_CHECK(_zh_tachometer_timer_init(handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer initialization failed. Timer initialization failed."); - ZH_ERROR_CHECK(_zh_tachometer_pcnt_init(config, handle) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(esp_timer_stop(handle->esp_timer_handle) == ESP_OK, ESP_FAIL, NULL, "Timer stop fail."); - ZH_ERROR_CHECK(esp_timer_delete(handle->esp_timer_handle) == ESP_OK, ESP_FAIL, NULL, "Timer delete fail."), "Tachometer initialization failed. PCNT initialization failed."); - handle->encoder_pulses = config->encoder_pulses; - handle->is_initialized = true; + *handle = heap_caps_calloc(1, sizeof(zh_tachometer_handle_t), MALLOC_CAP_8BIT); + ZH_ERROR_CHECK(*handle != NULL, ESP_ERR_NO_MEM, NULL, "Tachometer initialization failed. Failed to allocate tachometer handle."); + ZH_ERROR_CHECK(_zh_tachometer_timer_init(*handle) == ESP_OK, ESP_FAIL, heap_caps_free(*handle); *handle = NULL, "Tachometer initialization failed. Timer initialization failed."); + ZH_ERROR_CHECK(_zh_tachometer_pcnt_init(config, *handle) == ESP_OK, ESP_FAIL, + {ZH_ERROR_CHECK(esp_timer_stop((*handle)->esp_timer_handle) == ESP_OK, ESP_FAIL, NULL, "Timer stop fail.")}; + {ZH_ERROR_CHECK(esp_timer_delete((*handle)->esp_timer_handle) == ESP_OK, ESP_FAIL, NULL, "Timer delete fail.")}; + heap_caps_free(*handle); *handle = NULL, "Tachometer initialization failed. PCNT initialization failed."); + (*handle)->encoder_pulses = config->encoder_pulses; ZH_LOGI("Tachometer initialization completed successfully."); return ESP_OK; } -esp_err_t zh_tachometer_deinit(zh_tachometer_handle_t *handle) // -V2008 +esp_err_t zh_tachometer_deinit(zh_tachometer_handle_t **handle) // -V2008 { ZH_LOGI("Tachometer deinitialization started."); - ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Tachometer deinitialization failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Tachometer deinitialization failed. Tachometer not initialized."); - ZH_ERROR_CHECK(pcnt_unit_stop(handle->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT unit stop fail."); - ZH_ERROR_CHECK(pcnt_unit_disable(handle->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT unit disable fail."); - ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(handle->pcnt_unit_handle, 32767) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT unit remove watch point fail."); - ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(handle->pcnt_unit_handle, -32767) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT unit remove watch point fail."); - ZH_ERROR_CHECK(pcnt_del_channel(handle->pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_channel(handle->pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_unit(handle->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT delete unit fail."); - ZH_ERROR_CHECK(esp_timer_stop(handle->esp_timer_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. Timer stop fail."); - ZH_ERROR_CHECK(esp_timer_delete(handle->esp_timer_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. Timer delete fail."); - handle->is_initialized = false; + ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Tachometer deinitialization failed. Invalid argument."); + ZH_ERROR_CHECK(pcnt_unit_stop((*handle)->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT unit stop fail."); + ZH_ERROR_CHECK(pcnt_unit_disable((*handle)->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT unit disable fail."); + ZH_ERROR_CHECK(pcnt_unit_remove_watch_point((*handle)->pcnt_unit_handle, 32767) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT unit remove watch point fail."); + ZH_ERROR_CHECK(pcnt_unit_remove_watch_point((*handle)->pcnt_unit_handle, -32767) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT unit remove watch point fail."); + ZH_ERROR_CHECK(pcnt_del_channel((*handle)->pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT delete channel fail."); + ZH_ERROR_CHECK(pcnt_del_channel((*handle)->pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT delete channel fail."); + ZH_ERROR_CHECK(pcnt_del_unit((*handle)->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. PCNT delete unit fail."); + ZH_ERROR_CHECK(esp_timer_stop((*handle)->esp_timer_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. Timer stop fail."); + ZH_ERROR_CHECK(esp_timer_delete((*handle)->esp_timer_handle) == ESP_OK, ESP_FAIL, NULL, "Tachometer deinitialization failed. Timer delete fail."); + heap_caps_free(*handle); + *handle = NULL; ZH_LOGI("Tachometer deinitialization completed successfully."); return ESP_OK; } -esp_err_t zh_tachometer_get(const zh_tachometer_handle_t *handle, uint16_t *value) +esp_err_t zh_tachometer_get(zh_tachometer_handle_t **handle, uint16_t *value) { ZH_LOGI("Tachometer get position started."); - ZH_ERROR_CHECK(handle != NULL && value != NULL, ESP_ERR_INVALID_ARG, NULL, "Tachometer get position failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Tachometer get position failed. Tachometer not initialized."); - *value = handle->value; + ZH_ERROR_CHECK(handle != NULL && *handle != NULL && value != NULL, ESP_ERR_INVALID_ARG, NULL, "Tachometer get position failed. Invalid argument."); + *value = (*handle)->value; ZH_LOGI("Tachometer get position completed successfully."); return ESP_OK; } @@ -84,67 +164,67 @@ static esp_err_t _zh_tachometer_pcnt_init(const zh_tachometer_init_config_t *con .max_glitch_ns = 1000, }; ZH_ERROR_CHECK(pcnt_unit_set_glitch_filter(pcnt_unit_handle, &pcnt_glitch_filter_config) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail."), "PCNT initialization failed."); + {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed."); pcnt_chan_config_t pcnt_chan_a_config = { .edge_gpio_num = config->a_gpio_number, .level_gpio_num = config->b_gpio_number, }; pcnt_channel_handle_t pcnt_channel_a_handle = NULL; ZH_ERROR_CHECK(pcnt_new_channel(pcnt_unit_handle, &pcnt_chan_a_config, &pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail."), "PCNT initialization failed."); + {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed."); pcnt_chan_config_t pcnt_chan_b_config = { .edge_gpio_num = config->b_gpio_number, .level_gpio_num = config->a_gpio_number, }; pcnt_channel_handle_t pcnt_channel_b_handle = NULL; ZH_ERROR_CHECK(pcnt_new_channel(pcnt_unit_handle, &pcnt_chan_b_config, &pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail."), "PCNT initialization failed."); + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed."); ZH_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_channel_a_handle, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail."), "PCNT initialization failed."); + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed."); ZH_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_channel_a_handle, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_HOLD) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail."), "PCNT initialization failed."); + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed."); ZH_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_channel_b_handle, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail."), "PCNT initialization failed."); + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed."); ZH_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_channel_b_handle, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_HOLD) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail."), "PCNT initialization failed."); + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed."); ZH_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit_handle, 32767) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail."), "PCNT initialization failed."); + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed."); ZH_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit_handle, -32767) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, 32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail."), "PCNT initialization failed."); + {ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, 32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed."); ZH_ERROR_CHECK(pcnt_unit_enable(pcnt_unit_handle) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, 32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail."); - ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, -32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail."), "PCNT initialization failed."); + {ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, 32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail.")}; + {ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, -32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed."); ZH_ERROR_CHECK(pcnt_unit_clear_count(pcnt_unit_handle) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(pcnt_unit_disable(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT unit disable fail."); - ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, 32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail."); - ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, -32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail."), "PCNT initialization failed."); + {ZH_ERROR_CHECK(pcnt_unit_disable(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT unit disable fail.")}; + {ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, 32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail.")}; + {ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, -32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed."); ZH_ERROR_CHECK(pcnt_unit_start(pcnt_unit_handle) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(pcnt_unit_disable(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT unit disable fail."); - ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, 32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail."); - ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, -32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail."), "PCNT initialization failed."); + {ZH_ERROR_CHECK(pcnt_unit_disable(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT unit disable fail.")}; + {ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, 32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail.")}; + {ZH_ERROR_CHECK(pcnt_unit_remove_watch_point(pcnt_unit_handle, -32767) == ESP_OK, ESP_FAIL, NULL, "PCNT unit remove watch point fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")}; + {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed."); if (config->pullup == false) { ZH_ERROR_CHECK(gpio_pullup_dis((gpio_num_t)config->a_gpio_number) == ESP_OK, ESP_FAIL, NULL, "GPIO pullup disable fail."); @@ -164,7 +244,7 @@ static esp_err_t _zh_tachometer_timer_init(zh_tachometer_handle_t *handle) }; ZH_ERROR_CHECK(esp_timer_create(&timer_args, &handle->esp_timer_handle) == ESP_OK, ESP_FAIL, NULL, "Timer initialization failed."); ZH_ERROR_CHECK(esp_timer_start_periodic(handle->esp_timer_handle, 10000) == ESP_OK, ESP_FAIL, - ZH_ERROR_CHECK(esp_timer_delete(handle->esp_timer_handle) == ESP_OK, ESP_FAIL, NULL, "Timer delete fail."), "Timer initialization failed."); + {ZH_ERROR_CHECK(esp_timer_delete(handle->esp_timer_handle) == ESP_OK, ESP_FAIL, NULL, "Timer delete fail.")}, "Timer initialization failed."); return ESP_OK; } @@ -172,16 +252,8 @@ static void IRAM_ATTR _zh_tachometer_timer_on_alarm_cb(void *arg) { zh_tachometer_handle_t *handle = (zh_tachometer_handle_t *)arg; int pcnt_count = 0; - if (pcnt_unit_get_count(handle->pcnt_unit_handle, &pcnt_count) != ESP_OK) - { - ZH_LOGE("PCNT internal error.", ESP_FAIL); - return; - } - if (pcnt_unit_clear_count(handle->pcnt_unit_handle) != ESP_OK) - { - ZH_LOGE("PCNT internal error.", ESP_FAIL); - return; - } + ZH_ERROR_CHECK_VOID(pcnt_unit_get_count(handle->pcnt_unit_handle, &pcnt_count) == ESP_OK, NULL, "PCNT internal error."); + ZH_ERROR_CHECK_VOID(pcnt_unit_clear_count(handle->pcnt_unit_handle) == ESP_OK, NULL, "PCNT internal error."); float value_temp = ((pcnt_count * 100.0) / handle->encoder_pulses) * 60; - handle->value = (value_temp < 0) ? -(uint16_t)value_temp : (uint16_t)value_temp; // -V2004 + handle->value = (uint16_t)fabs(value_temp); // -V2004 } \ No newline at end of file