Compare commits

...
10 Commits
6 changed files with 229 additions and 113 deletions

No files matched your search

+6 -1
View File
@@ -1 +1,6 @@
idf_component_register(SRCS "zh_inclinometer.c" INCLUDE_DIRS "include" REQUIRES driver) if(${IDF_VERSION_MAJOR} STREQUAL 6)
set(requires esp_driver_gpio esp_driver_pcnt)
else()
set(requires driver)
endif()
idf_component_register(SRCS "zh_inclinometer.c" INCLUDE_DIRS "include" REQUIRES ${requires})
+15 -13
View File
@@ -1,16 +1,18 @@
# ESP32 ESP-IDF component for inclinometer (via rotary encoder) # ESP32 ESP-IDF component for inclinometer (via optical rotary encoder)
## Tested on ## Features
1. [ESP32 ESP-IDF v5.5.2](https://docs.espressif.com/projects/esp-idf/en/v5.5.2/esp32/index.html) 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.
## SAST Tools ## Note
[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. Enable the following settings in menuconfig:
## Attention
1. For correct operation, please enable the following settings in the menuconfig:
```text ```text
PCNT_CTRL_FUNC_IN_IRAM PCNT_CTRL_FUNC_IN_IRAM
@@ -21,9 +23,9 @@ PCNT_ISR_IRAM_SAF
In an existing project, run the following command to install the components: In an existing project, run the following command to install the components:
```text ```bash
cd ../your_project/components cd ../your_project/components
git clone http://git.zh.com.ru/esp_components/zh_inclinometer git clone https://github.com/aZholtikov/zh_inclinometer
``` ```
In the application, add the component: In the application, add the component:
@@ -32,12 +34,12 @@ In the application, add the component:
#include "zh_inclinometer.h" #include "zh_inclinometer.h"
``` ```
## Examples ## Example
```c ```c
#include "zh_inclinometer.h" #include "zh_inclinometer.h"
zh_inclinometer_handle_t inclinometer_handle = {0}; zh_inclinometer_handle_t *inclinometer_handle = NULL;
void app_main(void) void app_main(void)
{ {
+8
View File
@@ -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 <alexey.zholtikov@yandex.ru>"
dependencies:
idf: ">=5.0"
+93 -44
View File
@@ -1,5 +1,21 @@
/** /**
* @file zh_inclinometer.h * @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 #pragma once
@@ -10,14 +26,12 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
/**
* @brief Inclinometer initial default values.
*/
#define ZH_INCLINOMETER_INIT_CONFIG_DEFAULT() \ #define ZH_INCLINOMETER_INIT_CONFIG_DEFAULT() \
{ \ { \
.a_gpio_number = GPIO_NUM_MAX, \ .a_gpio_number = GPIO_NUM_MAX, \
.b_gpio_number = GPIO_NUM_MAX, \ .b_gpio_number = GPIO_NUM_MAX, \
.pullup = true, \ .pullup = true, \
.rotation = true, \
.encoder_pulses = 0} .encoder_pulses = 0}
#ifdef __cplusplus #ifdef __cplusplus
@@ -26,71 +40,106 @@ extern "C"
#endif #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 typedef struct
{ {
uint8_t a_gpio_number; /*!< Encoder A GPIO number. */ uint8_t a_gpio_number; /*!< Encoder A channel GPIO number */
uint8_t b_gpio_number; /*!< Encoder B GPIO number. */ uint8_t b_gpio_number; /*!< Encoder B channel GPIO number */
bool pullup; /*!< Pullup GPIO enable/disable. */ bool pullup; /*!< GPIO pull-up resistor enable (true) or disable (false) */
uint16_t encoder_pulses; /*!< Number of pulses per one rotation. */ 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; } 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 esp_err_t zh_inclinometer_init(const zh_inclinometer_init_config_t *config, zh_inclinometer_handle_t **handle);
{
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 is_initialized; /*!< Inclinometer initialization flag. */
} zh_inclinometer_handle_t;
/** /**
* @brief Initialize inclinometer. * @brief Deinitialize the inclinometer.
* *
* @param[in] config Pointer to inclinometer initialized configuration structure. Can point to a temporary variable. * Stops and disables the PCNT unit, deletes both encoder channels and the
* @param[out] handle Pointer to unique inclinometer handle. * 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. * Clears the PCNT counter, setting the current position to 0 degrees.
* @param[out] position Inclinometer position. * 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); esp_err_t zh_inclinometer_reset(zh_inclinometer_handle_t **handle);
/**
* @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);
#ifdef __cplusplus #ifdef __cplusplus
} }
+1 -1
View File
@@ -1 +1 @@
2.0.0 3.0.0
+106 -54
View File
@@ -13,58 +13,96 @@
return err; \ 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); 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); 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) esp_err_t zh_inclinometer_init(const zh_inclinometer_init_config_t *config, zh_inclinometer_handle_t **handle)
{ {
ZH_LOGI("Inclinometer initialization started."); 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(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.");
esp_err_t err = _zh_inclinometer_validate_config(config); ZH_ERROR_CHECK(_zh_inclinometer_validate_config(config) == ESP_OK, ESP_FAIL, NULL, "Inclinometer initialization failed. Initial configuration check failed.");
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "Inclinometer initialization failed. Initial configuration check failed."); *handle = heap_caps_calloc(1, sizeof(zh_inclinometer_handle_t), MALLOC_CAP_8BIT);
err = _zh_inclinometer_pcnt_init(config, handle); ZH_ERROR_CHECK(*handle != NULL, ESP_ERR_NO_MEM, NULL, "Inclinometer initialization failed. Failed to allocate inclinometer handle.");
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "Inclinometer initialization failed. PCNT initialization failed."); 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)->degrees_per_pulse = 360.0 / config->encoder_pulses;
handle->is_initialized = true; (*handle)->rotation = config->rotation;
ZH_LOGI("Inclinometer initialization completed successfully."); ZH_LOGI("Inclinometer initialization completed successfully.");
return ESP_OK; return ESP_OK;
} }
esp_err_t zh_inclinometer_deinit(zh_inclinometer_handle_t *handle) esp_err_t zh_inclinometer_deinit(zh_inclinometer_handle_t **handle)
{ {
ZH_LOGI("Inclinometer deinitialization started."); ZH_LOGI("Inclinometer deinitialization started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Inclinometer deinitialization failed. Invalid argument."); ZH_ERROR_CHECK(handle != NULL && *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.");
pcnt_unit_stop(handle->pcnt_unit_handle); ZH_ERROR_CHECK(pcnt_unit_disable((*handle)->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT unit disable fail.");
pcnt_unit_disable(handle->pcnt_unit_handle); ZH_ERROR_CHECK(pcnt_del_channel((*handle)->pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT delete channel fail.");
pcnt_del_channel(handle->pcnt_channel_a_handle); ZH_ERROR_CHECK(pcnt_del_channel((*handle)->pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT delete channel fail.");
pcnt_del_channel(handle->pcnt_channel_b_handle); ZH_ERROR_CHECK(pcnt_del_unit((*handle)->pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "Inclinometer deinitialization failed. PCNT delete unit fail.");
pcnt_del_unit(handle->pcnt_unit_handle); heap_caps_free(*handle);
handle->is_initialized = false; *handle = NULL;
ZH_LOGI("Inclinometer deinitialization completed successfully."); ZH_LOGI("Inclinometer deinitialization completed successfully.");
return ESP_OK; 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_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 != NULL && *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.");
int pcnt_count = 0; int pcnt_count = 0;
pcnt_unit_get_count(handle->pcnt_unit_handle, &pcnt_count); 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; float angle_temp = pcnt_count * (*handle)->degrees_per_pulse;
*angle = (angle_temp < 0) ? -angle_temp : angle_temp; *angle = ((*handle)->rotation == true) ? angle_temp : ((angle_temp == 0) ? angle_temp : -angle_temp);
ZH_LOGI("Inclinometer get position completed successfully."); ZH_LOGI("Inclinometer get position completed successfully.");
return ESP_OK; 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_LOGI("Inclinometer reset started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Inclinometer reset failed. Invalid argument."); ZH_ERROR_CHECK(handle != NULL && *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.");
pcnt_unit_clear_count(handle->pcnt_unit_handle);
ZH_LOGI("Inclinometer reset completed successfully."); ZH_LOGI("Inclinometer reset completed successfully.");
return ESP_OK; return ESP_OK;
} }
@@ -75,7 +113,7 @@ static esp_err_t _zh_inclinometer_validate_config(const zh_inclinometer_init_con
return ESP_OK; return ESP_OK;
} }
static esp_err_t _zh_inclinometer_pcnt_init(const zh_inclinometer_init_config_t *config, zh_inclinometer_handle_t *handle) // -V2008 static esp_err_t _zh_inclinometer_pcnt_init(const zh_inclinometer_init_config_t *config, zh_inclinometer_handle_t *handle)
{ {
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 < 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, "Encoder A and B GPIO is same.") ZH_ERROR_CHECK(config->a_gpio_number != config->b_gpio_number, ESP_ERR_INVALID_ARG, NULL, "Encoder A and B GPIO is same.")
@@ -84,47 +122,61 @@ static esp_err_t _zh_inclinometer_pcnt_init(const zh_inclinometer_init_config_t
.low_limit = -config->encoder_pulses, .low_limit = -config->encoder_pulses,
}; };
pcnt_unit_handle_t pcnt_unit_handle = NULL; pcnt_unit_handle_t pcnt_unit_handle = NULL;
esp_err_t err = pcnt_new_unit(&pcnt_unit_config, &pcnt_unit_handle); ZH_ERROR_CHECK(pcnt_new_unit(&pcnt_unit_config, &pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT initialization failed.");
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "PCNT initialization failed.");
pcnt_glitch_filter_config_t pcnt_glitch_filter_config = { pcnt_glitch_filter_config_t pcnt_glitch_filter_config = {
.max_glitch_ns = 1000, .max_glitch_ns = 1000,
}; };
err = pcnt_unit_set_glitch_filter(pcnt_unit_handle, &pcnt_glitch_filter_config); ZH_ERROR_CHECK(pcnt_unit_set_glitch_filter(pcnt_unit_handle, &pcnt_glitch_filter_config) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(err == ESP_OK, err, pcnt_del_unit(pcnt_unit_handle), "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 = { pcnt_chan_config_t pcnt_chan_a_config = {
.edge_gpio_num = config->a_gpio_number, .edge_gpio_num = config->a_gpio_number,
.level_gpio_num = config->b_gpio_number, .level_gpio_num = config->b_gpio_number,
}; };
pcnt_channel_handle_t pcnt_channel_a_handle = NULL; pcnt_channel_handle_t pcnt_channel_a_handle = NULL;
err = pcnt_new_channel(pcnt_unit_handle, &pcnt_chan_a_config, &pcnt_channel_a_handle); ZH_ERROR_CHECK(pcnt_new_channel(pcnt_unit_handle, &pcnt_chan_a_config, &pcnt_channel_a_handle) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(err == ESP_OK, err, pcnt_del_unit(pcnt_unit_handle), "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 = { pcnt_chan_config_t pcnt_chan_b_config = {
.edge_gpio_num = config->b_gpio_number, .edge_gpio_num = config->b_gpio_number,
.level_gpio_num = config->a_gpio_number, .level_gpio_num = config->a_gpio_number,
}; };
pcnt_channel_handle_t pcnt_channel_b_handle = NULL; pcnt_channel_handle_t pcnt_channel_b_handle = NULL;
err = pcnt_new_channel(pcnt_unit_handle, &pcnt_chan_b_config, &pcnt_channel_b_handle); ZH_ERROR_CHECK(pcnt_new_channel(pcnt_unit_handle, &pcnt_chan_b_config, &pcnt_channel_b_handle) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(err == ESP_OK, err, pcnt_del_channel(pcnt_channel_a_handle); pcnt_del_unit(pcnt_unit_handle), "PCNT initialization failed."); {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")};
err = pcnt_channel_set_edge_action(pcnt_channel_a_handle, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD); {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed.");
ZH_ERROR_CHECK(err == ESP_OK, err, pcnt_del_channel(pcnt_channel_a_handle); pcnt_del_channel(pcnt_channel_b_handle); pcnt_del_unit(pcnt_unit_handle), "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,
err = pcnt_channel_set_level_action(pcnt_channel_a_handle, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_HOLD); {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")};
ZH_ERROR_CHECK(err == ESP_OK, err, pcnt_del_channel(pcnt_channel_a_handle); pcnt_del_channel(pcnt_channel_b_handle); pcnt_del_unit(pcnt_unit_handle), "PCNT initialization failed."); {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")};
err = pcnt_channel_set_edge_action(pcnt_channel_b_handle, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD); {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed.");
ZH_ERROR_CHECK(err == ESP_OK, err, pcnt_del_channel(pcnt_channel_a_handle); pcnt_del_channel(pcnt_channel_b_handle); pcnt_del_unit(pcnt_unit_handle), "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,
err = pcnt_channel_set_level_action(pcnt_channel_b_handle, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_HOLD); {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")};
ZH_ERROR_CHECK(err == ESP_OK, err, pcnt_del_channel(pcnt_channel_a_handle); pcnt_del_channel(pcnt_channel_b_handle); pcnt_del_unit(pcnt_unit_handle), "PCNT initialization failed."); {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")};
err = pcnt_unit_enable(pcnt_unit_handle); {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed.");
ZH_ERROR_CHECK(err == ESP_OK, err, pcnt_del_channel(pcnt_channel_a_handle); pcnt_del_channel(pcnt_channel_b_handle); pcnt_del_unit(pcnt_unit_handle), "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,
err = pcnt_unit_clear_count(pcnt_unit_handle); {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")};
ZH_ERROR_CHECK(err == ESP_OK, err, pcnt_unit_disable(pcnt_unit_handle); pcnt_del_channel(pcnt_channel_a_handle); pcnt_del_channel(pcnt_channel_b_handle); {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_b_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")};
pcnt_del_unit(pcnt_unit_handle), "PCNT initialization failed."); {ZH_ERROR_CHECK(pcnt_del_unit(pcnt_unit_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete unit fail.")}, "PCNT initialization failed.");
err = pcnt_unit_start(pcnt_unit_handle); 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(err == ESP_OK, err, pcnt_unit_disable(pcnt_unit_handle); pcnt_del_channel(pcnt_channel_a_handle); pcnt_del_channel(pcnt_channel_b_handle); {ZH_ERROR_CHECK(pcnt_del_channel(pcnt_channel_a_handle) == ESP_OK, ESP_FAIL, NULL, "PCNT delete channel fail.")};
pcnt_del_unit(pcnt_unit_handle), "PCNT initialization failed."); {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_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_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.");
if (config->pullup == false) if (config->pullup == false)
{ {
gpio_pullup_dis((gpio_num_t)config->a_gpio_number); ZH_ERROR_CHECK(gpio_pullup_dis((gpio_num_t)config->a_gpio_number) == ESP_OK, ESP_FAIL, NULL, "GPIO pullup disable fail.");
gpio_pullup_dis((gpio_num_t)config->b_gpio_number); ZH_ERROR_CHECK(gpio_pullup_dis((gpio_num_t)config->b_gpio_number) == ESP_OK, ESP_FAIL, NULL, "GPIO pullup disable fail.");
} }
handle->pcnt_unit_handle = pcnt_unit_handle; handle->pcnt_unit_handle = pcnt_unit_handle;
handle->pcnt_channel_a_handle = pcnt_channel_a_handle; handle->pcnt_channel_a_handle = pcnt_channel_a_handle;