126 lines
4.9 KiB
C
126 lines
4.9 KiB
C
/**
|
|
* @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 100 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"
|
|
#include "esp_timer.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
|
|
#define ZH_TACHOMETER_INIT_CONFIG_DEFAULT() \
|
|
{ \
|
|
.a_gpio_number = GPIO_NUM_MAX, \
|
|
.b_gpio_number = GPIO_NUM_MAX, \
|
|
.pullup = true, \
|
|
.encoder_pulses = 0}
|
|
|
|
#ifdef __cplusplus
|
|
extern "C"
|
|
{
|
|
#endif
|
|
|
|
/**
|
|
* @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 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 Initialize the tachometer with the provided configuration.
|
|
*
|
|
* Allocates a handle, configures the ESP-Timer (100 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
|
|
*/
|
|
esp_err_t zh_tachometer_init(const zh_tachometer_init_config_t *config, zh_tachometer_handle_t **handle);
|
|
|
|
/**
|
|
* @brief Deinitialize the tachometer and release all resources.
|
|
*
|
|
* 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.
|
|
*
|
|
* @param[in,out] handle Pointer to the tachometer handle (must not be NULL)
|
|
*
|
|
* @note After deinitialization, the handle is invalidated and must not be
|
|
* used. Call zh_tachometer_init() again to reinitialize.
|
|
*
|
|
* @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_deinit(zh_tachometer_handle_t **handle);
|
|
|
|
/**
|
|
* @brief Retrieve the current RPM value from the tachometer.
|
|
*
|
|
* 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.
|
|
*
|
|
* @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_get(zh_tachometer_handle_t **handle, uint16_t *value);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif |