feat: added support and automatic detection for the 30–500 hz frequency range

This commit is contained in:
aZholtikov committed 2026-10-11 11:16:40 +03:00
1 parent a83e6c0dc5
commit a4f27bf433
5 files changed
+197 -74

No files matched your search

+33 -19
View File
@@ -5,17 +5,20 @@
*
* This module provides an interface for controlling AC power via phase-angle
* dimming using ESP-IDF gptimer and GPIO peripherals. It detects the AC
* mains zero-crossing point and fires the TRIAC with a programmable delay
* to regulate the average power delivered to the load.
* mains zero-crossing point, measures the zero-crossing period, and fires the
* TRIAC with a programmable delay to regulate the average power delivered to
* the load.
*
* Key features:
* - Zero-crossing detection via GPIO interrupt
* - Zero-crossing detection via GPIO interrupt with period measurement
* - TRIAC gate control through ESP-IDF gptimer
* - Configurable dimming level (0–100 %)
* - Automatic runtime calibration of the hardware base offset
* - Start/stop/set/get API
*
* @note The driver assumes a 400 Hz AC mains frequency (1250 us period).
* Adjust the timing constants in `zh_ac_dimmer.c` for other frequencies.
* @note The supported mains frequency range is defined by
* `ZH_AC_DIMMER_FREQ_MIN_HZ` and `ZH_AC_DIMMER_FREQ_MAX_HZ`. The measured
* zero-crossing period is filtered and used to calculate the firing delay.
* @warning This module interacts with mains voltage. Incorrect wiring or
* insufficient isolation can cause electric shock or hardware damage.
* Ensure proper opto-isolation and safety compliance before deployment.
@@ -32,6 +35,9 @@
#include "esp_log.h"
#include "driver/gpio.h"
#define ZH_AC_DIMMER_FREQ_MIN_HZ 30
#define ZH_AC_DIMMER_FREQ_MAX_HZ 500
#define ZH_AC_DIMMER_INIT_CONFIG_DEFAULT() \
{ \
.zero_cross_gpio = GPIO_NUM_MAX, \
@@ -55,6 +61,9 @@ extern "C"
*
* Specifies the GPIO pins used for zero-crossing detection and TRIAC
* gate control. Both pins must be valid and different from each other.
*
* @note The firing-delay offset is calibrated automatically at runtime and
* is not part of the configuration.
*/
typedef struct
{
@@ -67,32 +76,36 @@ extern "C"
*
* Allocates memory for the dimmer handle, validates the configuration,
* initializes GPIO pins (TRIAC as output, zero-crossing as input with
* positive-edge interrupt), installs the ISR service, and creates the
* gptimer instance.
* both-edge interrupt), installs the ISR service, and creates the gptimer.
*
* @param[in] config Pointer to the initialization configuration structure (may point to a temporary variable, not stored internally)
* @param[out] handle Pointer to receive the created dimmer handle (must be NULL)
* @param[in] config Pointer to the initialization configuration structure (may point to a
* temporary variable, not stored internally)
* @param[out] handle Pointer to receive the created dimmer handle (must not be NULL and must point to NULL)
*
* @return ESP_OK on success
* @return ESP_ERR_INVALID_ARG if handle is NULL or GPIO pins are invalid
* @return ESP_ERR_INVALID_STATE if a dimmer instance is already initialized
* @return ESP_ERR_INVALID_ARG if config or handle is NULL
* @return ESP_ERR_INVALID_STATE if the handle pointer already points to an initialized instance
* @return ESP_ERR_NO_MEM if memory allocation failed
* @return ESP_FAIL if GPIO, ISR service, or timer initialization failed
* @return ESP_FAIL if configuration validation, GPIO, ISR service, or timer initialization failed
*
* @note The driver supports only one instance per zero-crossing GPIO pin. Initializing a
* second instance on the same pin replaces the ISR handler of the first one, so each
* pin must be claimed by a single dimmer instance.
*/
esp_err_t zh_ac_dimmer_init(const zh_ac_dimmer_init_config_t *config, zh_ac_dimmer_handle_t **handle);
/**
* @brief Deinitialize the AC dimmer driver.
*
* Stops and deletes the gptimer, removes the GPIO ISR handler, resets
* the GPIO pins to input mode, frees the dimmer handle, and sets the
* output handle to NULL.
* Disables TRIAC firing, removes the GPIO ISR handler, resets the GPIO
* pins, stops, disables and deletes the gptimer, frees the dimmer handle
* and sets the output handle to NULL. Cleanup steps are executed
* unconditionally and their failures are only logged.
*
* @param[in,out] handle Pointer to the dimmer handle, set to NULL on return (must not be NULL)
*
* @return ESP_OK on success
* @return ESP_ERR_INVALID_ARG if handle or *handle is NULL
* @return ESP_FAIL if timer or GPIO cleanup failed
*
* @warning After calling this function, the handle is invalid and must not
* be used. Call `zh_ac_dimmer_init()` again to reinitialize.
@@ -116,7 +129,8 @@ extern "C"
* @brief Stop the AC dimmer.
*
* Disables TRIAC firing on zero-crossing events. The dimmer can be
* restarted with `zh_ac_dimmer_start()`.
* restarted with `zh_ac_dimmer_start()`. The TRIAC gate stays in the
* state left by the last half-cycle until the next zero-crossing event.
*
* @param[in] handle Pointer to the dimmer handle (must not be NULL)
*
@@ -132,7 +146,7 @@ extern "C"
* zero-crossing event.
*
* @param[in] handle Pointer to the dimmer handle (must not be NULL)
* @param[in] value Dimming level from 0 (off) to 100 (full brightness) (must be <= 100)
* @param[in] value Dimming level in percent, where 0 keeps the load off and 100 fires the TRIAC immediately (must be <= 100)
*
* @return ESP_OK on success
* @return ESP_ERR_INVALID_ARG if handle, *handle, or value is invalid
@@ -143,7 +157,7 @@ extern "C"
* @brief Get the current AC dimmer brightness level.
*
* @param[in] handle Pointer to the dimmer handle (must not be NULL)
* @param[out] value Pointer to receive the current dimming level (0–100) (must not be NULL)
* @param[out] value Pointer to receive the current dimming level in percent (must not be NULL)
*
* @return ESP_OK on success
* @return ESP_ERR_INVALID_ARG if handle, *handle, or value is NULL