diff --git a/README.md b/README.md index 589a980..2e632fc 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,22 @@ # ESP32 ESP-IDF component for inclinometer (via optical rotary 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 inclinometers on one device. +1. Quadrature encoder support with configurable GPIO pins +2. Configurable pull-up resistor enablement for encoder channels +3. Support for both clockwise and counter-clockwise rotation directions +4. Configurable pulses per revolution for accurate angle calculation +5. Position tracking with get and reset operations +6. PCNT glitch filter for noise rejection (1000ns) +7. Multiple inclinometer instances support -## 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_inclinometer ``` @@ -36,12 +38,12 @@ In the application, add the component: #include "zh_inclinometer.h" ``` -## Examples +## Example ```c #include "zh_inclinometer.h" -zh_inclinometer_handle_t inclinometer_handle = {0}; +zh_inclinometer_handle_t *inclinometer_handle = NULL; void app_main(void) { diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 0000000..e2163fe --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,8 @@ +description: ESP32 ESP-IDF component for inclinometer (via optical rotary encoder) +url: https://github.com/aZholtikov/zh_inclinometer +version: "3.0.0" +license: "Apache-2.0" +maintainers: + - "Alexey Zholtikov " +dependencies: + idf: ">=5.0" \ No newline at end of file diff --git a/include/zh_inclinometer.h b/include/zh_inclinometer.h index 82af823..2c35f3e 100644 --- a/include/zh_inclinometer.h +++ b/include/zh_inclinometer.h @@ -1,5 +1,21 @@ /** * @file zh_inclinometer.h + * + * @brief Header file for the zh_inclinometer library providing encoder-based + * angle measurement and position tracking functionality. + * + * This library implements an inclinometer interface based on a quadrature + * encoder connected to two GPIO pins. It uses the ESP-IDF pulse counter + * (pcnt) driver to count encoder pulses and calculate the current angular + * position. The library supports configurable GPIO pins, pull-up enablement, + * rotation direction, and pulses per revolution. + * + * Key features: + * - Quadrature encoder support with configurable GPIO pins + * - Configurable pull-up resistor enablement + * - Support for both clockwise and counter-clockwise rotation directions + * - Configurable pulses per revolution for accurate angle calculation + * - Position tracking with get and reset operations */ #pragma once @@ -10,9 +26,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" -/** - * @brief Inclinometer initial default values. - */ #define ZH_INCLINOMETER_INIT_CONFIG_DEFAULT() \ { \ .a_gpio_number = GPIO_NUM_MAX, \ @@ -27,73 +40,106 @@ extern "C" #endif /** - * @brief Structure for initial initialization of inclinometer. + * @brief Opaque handle to the inclinometer internal state. + * + * This handle is returned by zh_inclinometer_init() and must be passed + * to all subsequent API functions to operate on the same inclinometer + * instance. The handle is freed by zh_inclinometer_deinit(). + */ + typedef struct _zh_inclinometer_handle_t zh_inclinometer_handle_t; + + /** + * @brief Structure for initial configuration of the inclinometer. + * + * This structure holds all parameters required to initialize the + * inclinometer, including GPIO pin assignments, electrical configuration, + * rotation direction, and encoder resolution. + * + * @note Both GPIO pins must be assigned before initialization. + * Use GPIO_NUM_MAX to indicate an unassigned pin. + * @warning The encoder_pulses field must be set to a non-zero value + * representing the actual number of pulses per one full rotation + * of the encoder shaft. */ 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. */ - bool rotation; /*!< Encoder rotation (true - positive for CW rotation, false - positive for CCW rotation). */ - uint16_t encoder_pulses; /*!< Number of pulses per one rotation. */ + uint8_t a_gpio_number; /*!< Encoder A channel GPIO number */ + uint8_t b_gpio_number; /*!< Encoder B channel GPIO number */ + bool pullup; /*!< GPIO pull-up resistor enable (true) or disable (false) */ + bool rotation; /*!< Rotation direction: true - positive for CW, false - positive for CCW */ + uint16_t encoder_pulses; /*!< Number of encoder pulses per one full rotation */ } zh_inclinometer_init_config_t; /** - * @brief Inclinometer handle. + * @brief Initialize the inclinometer. + * + * Allocates memory for the inclinometer handle, validates the configuration, + * initializes the PCNT unit with two channels for quadrature decoding, + * and starts the pulse counter. + * + * @param[in] config Pointer to the inclinometer initialization configuration + * structure. Can point to a temporary variable (must not be NULL) + * @param[out] handle Pointer to the unique inclinometer handle (must be NULL) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if config or handle is NULL + * @return ESP_ERR_INVALID_STATE if the inclinometer is already initialized + * @return ESP_ERR_NO_MEM if memory allocation failed + * @return ESP_FAIL if PCNT initialization or configuration failed */ - typedef struct - { - pcnt_unit_handle_t pcnt_unit_handle; /*!< Inclinometer unique pcnt unit handle. */ - pcnt_channel_handle_t pcnt_channel_a_handle; /*!< Inclinometer unique pcnt channel handle. */ - pcnt_channel_handle_t pcnt_channel_b_handle; /*!< Inclinometer unique pcnt channel handle. */ - float degrees_per_pulse; /*!< Number of degrees per pulse. */ - bool rotation; /*!< Encoder rotation. */ - bool is_initialized; /*!< Inclinometer initialization flag. */ - } zh_inclinometer_handle_t; + esp_err_t zh_inclinometer_init(const zh_inclinometer_init_config_t *config, zh_inclinometer_handle_t **handle); /** - * @brief Initialize inclinometer. + * @brief Deinitialize the inclinometer. * - * @param[in] config Pointer to inclinometer initialized configuration structure. Can point to a temporary variable. - * @param[out] handle Pointer to unique inclinometer handle. + * Stops and disables the PCNT unit, deletes both encoder channels and the + * PCNT unit, frees the inclinometer handle memory, and sets the handle to + * NULL. * - * @note Before initialize the inclinometer recommend initialize zh_inclinometer_init_config_t structure with default values. + * @param[in, out] handle Pointer to the unique inclinometer handle (must not be NULL) * - * @code zh_inclinometer_init_config_t config = ZH_INCLINOMETER_INIT_CONFIG_DEFAULT() @endcode + * @note After deinitialization, the handle is invalidated and must not be + * used. Call zh_inclinometer_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 or *handle is NULL + * @return ESP_FAIL if PCNT stop, disable, or deletion failed */ - esp_err_t zh_inclinometer_init(const zh_inclinometer_init_config_t *config, zh_inclinometer_handle_t *handle); + esp_err_t zh_inclinometer_deinit(zh_inclinometer_handle_t **handle); /** - * @brief Deinitialize inclinometer. + * @brief Get the current inclinometer position. * - * @param[in, out] handle Pointer to unique inclinometer handle. + * Reads the PCNT counter value and converts it to an angle in degrees + * using the configured degrees-per-pulse factor. The sign of the angle + * depends on the configured rotation direction. * - * @return ESP_OK if success or an error code otherwise. + * @param[in] handle Pointer to the unique inclinometer handle (must not be NULL) + * @param[out] angle Pointer to a float variable that receives the current + * angle in degrees (must not be NULL) + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if handle, *handle, or angle is NULL + * @return ESP_FAIL if reading the PCNT counter failed */ - esp_err_t zh_inclinometer_deinit(zh_inclinometer_handle_t *handle); + esp_err_t zh_inclinometer_get(zh_inclinometer_handle_t **handle, float *angle); /** - * @brief Get inclinometer position. + * @brief Reset the inclinometer position to zero. * - * @param[in] handle Pointer to unique inclinometer handle. - * @param[out] position Inclinometer position. + * Clears the PCNT counter, setting the current position to 0 degrees. + * The inclinometer continues counting from the new zero position. * - * @return ESP_OK if success or an error code otherwise. + * @param[in] handle Pointer to the unique inclinometer handle (must not be NULL) + * + * @note This does not affect the PCNT unit state (enabled/disabled). + * The unit remains running after reset. + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if handle or *handle is NULL + * @return ESP_FAIL if clearing the PCNT counter failed */ - esp_err_t zh_inclinometer_get(zh_inclinometer_handle_t *handle, float *angle); - - /** - * @brief Reset inclinometer position. - * - * @note The inclinometer will be set to 0 position. - * - * @param[in] handle Pointer to unique inclinometer handle. - * - * @return ESP_OK if success or an error code otherwise. - */ - esp_err_t zh_inclinometer_reset(zh_inclinometer_handle_t *handle); + esp_err_t zh_inclinometer_reset(zh_inclinometer_handle_t **handle); #ifdef __cplusplus } diff --git a/version.txt b/version.txt index e3a4f19..56fea8a 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.2.0 \ No newline at end of file +3.0.0 \ No newline at end of file diff --git a/zh_inclinometer.c b/zh_inclinometer.c index ee63c30..823c3c5 100644 --- a/zh_inclinometer.c +++ b/zh_inclinometer.c @@ -13,57 +13,96 @@ return err; \ } +/** + * @brief Internal structure holding the inclinometer instance state. + * + * This structure contains all resources and configuration needed to operate + * a single inclinometer instance, including PCNT unit and channel handles, + * the calculated angle per pulse, and the configured rotation direction. + */ +struct _zh_inclinometer_handle_t +{ + pcnt_unit_handle_t pcnt_unit_handle; /*!< PCNT unit handle for the encoder */ + pcnt_channel_handle_t pcnt_channel_a_handle; /*!< PCNT channel A handle (encoder A pin) */ + pcnt_channel_handle_t pcnt_channel_b_handle; /*!< PCNT channel B handle (encoder B pin) */ + float degrees_per_pulse; /*!< Degrees per single encoder pulse */ + bool rotation; /*!< Rotation direction flag (true - CW positive) */ +}; + +/** + * @brief Validate the inclinometer initialization configuration. + * + * Checks that all required configuration parameters are within valid ranges. + * + * @param config Pointer to the initialization configuration structure. + * + * @return ESP_OK if the configuration is valid + * @return ESP_ERR_INVALID_ARG if encoder_pulses is zero or negative + */ static esp_err_t _zh_inclinometer_validate_config(const zh_inclinometer_init_config_t *config); + +/** + * @brief Initialize PCNT unit and channels for the inclinometer. + * + * Configures the pulse counter unit with two channels for quadrature + * decoding, sets up glitch filtering, and starts the counter. + * + * @param config Pointer to the initialization configuration structure + * @param handle Pointer to the inclinometer handle to store PCNT resources + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if GPIO pins are invalid or identical + * @return ESP_FAIL if PCNT resource allocation or configuration fails + */ static esp_err_t _zh_inclinometer_pcnt_init(const zh_inclinometer_init_config_t *config, zh_inclinometer_handle_t *handle); -esp_err_t zh_inclinometer_init(const zh_inclinometer_init_config_t *config, zh_inclinometer_handle_t *handle) // -V2008 +esp_err_t zh_inclinometer_init(const zh_inclinometer_init_config_t *config, zh_inclinometer_handle_t **handle) // -V2008 { ZH_LOGI("Inclinometer initialization started."); ZH_ERROR_CHECK(config != NULL && handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Inclinometer initialization failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == false, ESP_ERR_INVALID_STATE, NULL, "Inclinometer initialization failed. Inclinometer is already initialized."); + ZH_ERROR_CHECK(*handle == NULL, ESP_ERR_INVALID_STATE, NULL, "Inclinometer initialization failed. Inclinometer is already initialized."); ZH_ERROR_CHECK(_zh_inclinometer_validate_config(config) == ESP_OK, ESP_FAIL, NULL, "Inclinometer initialization failed. Initial configuration check failed."); - ZH_ERROR_CHECK(_zh_inclinometer_pcnt_init(config, handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer initialization failed. PCNT initialization failed."); - handle->degrees_per_pulse = 360.0 / config->encoder_pulses; - handle->rotation = config->rotation; - handle->is_initialized = true; + *handle = heap_caps_calloc(1, sizeof(zh_inclinometer_handle_t), MALLOC_CAP_8BIT); + ZH_ERROR_CHECK(*handle != NULL, ESP_ERR_NO_MEM, NULL, "Inclinometer initialization failed. Failed to allocate inclinometer handle."); + ZH_ERROR_CHECK(_zh_inclinometer_pcnt_init(config, *handle) == ESP_OK, ESP_FAIL, heap_caps_free(*handle); *handle = NULL, "Inclinometer initialization failed. PCNT initialization failed."); + (*handle)->degrees_per_pulse = 360.0 / config->encoder_pulses; + (*handle)->rotation = config->rotation; ZH_LOGI("Inclinometer initialization completed successfully."); return ESP_OK; } -esp_err_t zh_inclinometer_deinit(zh_inclinometer_handle_t *handle) // -V2008 +esp_err_t zh_inclinometer_deinit(zh_inclinometer_handle_t **handle) // -V2008 { ZH_LOGI("Inclinometer deinitialization started."); - ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Inclinometer deinitialization failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Inclinometer deinitialization failed. Inclinometer not initialized."); - ZH_ERROR_CHECK(pcnt_unit_stop(handle->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT unit stop fail."); - ZH_ERROR_CHECK(pcnt_unit_disable(handle->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT unit disable fail."); - ZH_ERROR_CHECK(pcnt_del_channel(handle->pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_channel(handle->pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT delete channel fail."); - ZH_ERROR_CHECK(pcnt_del_unit(handle->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT delete unit fail."); - handle->is_initialized = false; + ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Inclinometer deinitialization failed. Invalid argument."); + ZH_ERROR_CHECK(pcnt_unit_stop((*handle)->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT unit stop fail."); + ZH_ERROR_CHECK(pcnt_unit_disable((*handle)->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT unit disable fail."); + ZH_ERROR_CHECK(pcnt_del_channel((*handle)->pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT delete channel fail."); + ZH_ERROR_CHECK(pcnt_del_channel((*handle)->pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT delete channel fail."); + ZH_ERROR_CHECK(pcnt_del_unit((*handle)->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT delete unit fail."); + heap_caps_free(*handle); + *handle = NULL; ZH_LOGI("Inclinometer deinitialization completed successfully."); return ESP_OK; } -esp_err_t zh_inclinometer_get(zh_inclinometer_handle_t *handle, float *angle) +esp_err_t zh_inclinometer_get(zh_inclinometer_handle_t **handle, float *angle) { ZH_LOGI("Inclinometer get position started."); - ZH_ERROR_CHECK(handle != NULL && angle != NULL, ESP_ERR_INVALID_ARG, NULL, "Inclinometer get position failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Inclinometer get position failed. Inclinometer not initialized."); + ZH_ERROR_CHECK(handle != NULL && *handle != NULL && angle != NULL, ESP_ERR_INVALID_ARG, NULL, "Inclinometer get position failed. Invalid argument."); int pcnt_count = 0; - ZH_ERROR_CHECK(pcnt_unit_get_count(handle->pcnt_unit_handle, &pcnt_count) == ESP_OK, ESP_FAIL, NULL, "Inclinometer get position failed. PCNT unit get count fail."); - float angle_temp = pcnt_count * handle->degrees_per_pulse; - *angle = (handle->rotation == true) ? angle_temp : ((angle_temp == 0) ? angle_temp : -angle_temp); + ZH_ERROR_CHECK(pcnt_unit_get_count((*handle)->pcnt_unit_handle, &pcnt_count) == ESP_OK, ESP_FAIL, NULL, "Inclinometer get position failed. PCNT unit get count fail."); + float angle_temp = pcnt_count * (*handle)->degrees_per_pulse; + *angle = ((*handle)->rotation == true) ? angle_temp : ((angle_temp == 0) ? angle_temp : -angle_temp); // -V550 ZH_LOGI("Inclinometer get position completed successfully."); return ESP_OK; } -esp_err_t zh_inclinometer_reset(zh_inclinometer_handle_t *handle) +esp_err_t zh_inclinometer_reset(zh_inclinometer_handle_t **handle) { ZH_LOGI("Inclinometer reset started."); - ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Inclinometer reset failed. Invalid argument."); - ZH_ERROR_CHECK(handle->is_initialized == true, ESP_FAIL, NULL, "Inclinometer reset failed. Inclinometer not initialized."); - ZH_ERROR_CHECK(pcnt_unit_clear_count(handle->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer reset failed. PCNT unit clear count fail."); + ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Inclinometer reset failed. Invalid argument."); + ZH_ERROR_CHECK(pcnt_unit_clear_count((*handle)->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer reset failed. PCNT unit clear count fail."); ZH_LOGI("Inclinometer reset completed successfully."); return ESP_OK; } @@ -102,38 +141,38 @@ static esp_err_t _zh_inclinometer_pcnt_init(const zh_inclinometer_init_config_t }; 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_enable(pcnt_unit_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_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_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_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_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_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_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.");