doc: added wiki
This commit is contained in:
1 parent
51fd157ed2
commit
9bbded33b2
1 file changed
+325
+325
@@ -0,0 +1,325 @@
|
||||
# Описание компонента zh_ac_dimmer
|
||||
|
||||
## Общие сведения
|
||||
|
||||
**zh_ac_dimmer** — это драйвер для ESP-IDF, реализующий фазовое управление мощностью
|
||||
переменного тока (AC dimming) через детектирование перехода через ноль и управление
|
||||
затвором тиристора (TRIAC). Компонент определяет момент перехода сетевого напряжения
|
||||
через ноль, измеряет период полуволны и зажигает TRIAC с программируемой задержкой,
|
||||
регулируя среднюю мощность, подаваемую на нагрузку.
|
||||
|
||||
Компонент предоставляет полный набор функций для инициализации, запуска, остановки
|
||||
и управления уровнем затемнения (0–100 %), а также автоматическую калибровку
|
||||
аппаратного смещения в режиме реального времени.
|
||||
|
||||
### Основные возможности
|
||||
|
||||
1. **Детектирование перехода через ноль** — через GPIO-прерывание с измерением периода.
|
||||
2. **Управление TRIAC** — через ESP-IDF gptimer с программируемой задержкой.
|
||||
3. **Уровень затемнения 0–100 %** — плавная регулировка мощности нагрузки.
|
||||
4. **Автоматическая калибровка** — аппаратное смещение калибруется во время работы.
|
||||
5. **Фильтрация периодов** — низкочастотный фильтр для стабильного измерения частоты сети.
|
||||
6. **Защитный зазор (guard band)** — гарантированное отключение TRIAC до конца полуволны.
|
||||
7. **Валидация частоты** — отбрасывание помех и некорректных импульсов.
|
||||
8. **Старт/стоп API** — простое управление состоянием dimmer.
|
||||
|
||||
---
|
||||
|
||||
## Внутреннее устройство
|
||||
|
||||
### Структура `zh_ac_dimmer_handle_t`
|
||||
|
||||
Внутренняя структура хранит все состояние экземпляра dimmer: GPIO-пины, таймер,
|
||||
состояние синхронизации и текущий уровень затемнения.
|
||||
|
||||
| Поле | Тип | Описание |
|
||||
| ------ | ----- | ---------- |
|
||||
| `zero_cross_gpio` | `gpio_num_t` | GPIO-пин для детектирования перехода через ноль |
|
||||
| `triac_gpio` | `gpio_num_t` | GPIO-пин для управления затвором TRIAC (выход) |
|
||||
| `dimmer_timer_handle` | `gptimer_handle_t` | Экземпляр gptimer для задержки зажигания TRIAC |
|
||||
| `prev_zero_crossing_us` | `uint64_t` | Метка времени предыдущего фронтa (мкс) |
|
||||
| `pulse_start_us` | `uint64_t` | Метка времени текущего фронтa для измерения ширины импульса (мкс) |
|
||||
| `period_us` | `uint32_t` | Отфильтрованный период полуволны в мкс (0 до первого измерения) |
|
||||
| `base_offset_us` | `volatile uint32_t` | Автоматически откалиброванное опережение ISR относительно нуля (мкс) |
|
||||
| `auto_base_samples` | `volatile uint32_t` | Количество образцов, использованных для калибровки |
|
||||
| `dimmer_value` | `volatile uint8_t` | Текущий уровень затемнения, 0 (выкл) .. 100 (полная мощность) |
|
||||
| `is_dimmer_work` | `volatile bool` | Флаг активной работы dimmer |
|
||||
| `last_edge_was_rising` | `bool` | Флаг полярности последнего фронта для отбрасывания дубликатов |
|
||||
|
||||
### Управление памятью
|
||||
|
||||
Память выделяется через `heap_caps_calloc()` в функции `zh_ac_dimmer_init()`
|
||||
с флагом `MALLOC_CAP_8BIT`. Освобождение происходит в `zh_ac_dimmer_deinit()`
|
||||
через `heap_caps_free()`. При неудачной инициализации освобождённая память
|
||||
очищается через цепочку cleanup в макросе `ZH_ERROR_CHECK`.
|
||||
|
||||
### Структура `zh_ac_dimmer_init_config_t`
|
||||
|
||||
Конфигурационная структура, передаваемая пользователем при инициализации.
|
||||
|
||||
| Поле | Тип | Описание |
|
||||
|------|-----|----------|
|
||||
| `zero_cross_gpio` | `gpio_num_t` | GPIO-пин для детектирования перехода через ноль |
|
||||
| `triac_gpio` | `gpio_num_t` | GPIO-пин для управления затвором TRIAC |
|
||||
|
||||
---
|
||||
|
||||
## API (интерфейс)
|
||||
|
||||
### Инициализация и освобождение
|
||||
|
||||
| Функция | Описание |
|
||||
|---------|----------|
|
||||
| `zh_ac_dimmer_init()` | Инициализирует драйвер: выделяет память, настраивает GPIO, устанавливает ISR-обработчик, создаёт gptimer |
|
||||
| `zh_ac_dimmer_deinit()` | Полностью деинициализирует драйвер: удаляет ISR, сбрасывает GPIO, удаляет таймер, освобождает память |
|
||||
|
||||
### Управление состоянием
|
||||
|
||||
| Функция | Описание |
|
||||
|---------|----------|
|
||||
| `zh_ac_dimmer_start()` | Включает dimmer — переходы через ноль начинают запускать TRIAC |
|
||||
| `zh_ac_dimmer_stop()` | Останавливает dimmer — TRIAC не зажигается до следующего перехода через ноль |
|
||||
|
||||
### Установка и получение уровня
|
||||
|
||||
| Функция | Описание |
|
||||
|---------|----------|
|
||||
| `zh_ac_dimmer_set()` | Устанавливает уровень затемнения (0–100 %), применяется на следующем полупериоде |
|
||||
| `zh_ac_dimmer_get()` | Возвращает текущий установленный уровень затемнения |
|
||||
|
||||
---
|
||||
|
||||
## Потокобезопасность
|
||||
|
||||
Компонент **не является потокобезопасным**. Все поля структуры `zh_ac_dimmer_handle_t`,
|
||||
изменяемые как из основного потока, так и из ISR (`dimmer_value`, `is_dimmer_work`,
|
||||
`base_offset_us`, `auto_base_samples`), помечены как `volatile` для предотвращения
|
||||
оптимизаций компилятора. Синхронизация между потоками не предусмотрена — пользователь
|
||||
должен обеспечивать взаимное исключение на уровне приложения, если dimmer используется
|
||||
из нескольких задач FreeRTOS.
|
||||
|
||||
---
|
||||
|
||||
## Алгоритмы
|
||||
|
||||
### Калибровка аппаратного смещения
|
||||
|
||||
При детектировании спадающего фронта импульса перехода через ноль вычисляется ширина
|
||||
импульса (`pulse_width`), которая делится пополам для получения опережения:
|
||||
|
||||
```
|
||||
measured_adv = pulse_width / 2
|
||||
```
|
||||
|
||||
Первое измерение устанавливает `base_offset_us` напрямую. Последующие значения
|
||||
сглаживаются экспоненциальным скользящим средним (EMA) с коэффициентом 15/16:
|
||||
|
||||
```
|
||||
base_offset_us = (base_offset_us * 15 + measured_adv) / 16
|
||||
```
|
||||
|
||||
Валидация импульса: ширина должна быть в диапазоне `[4 мкс, period_us / 4]`.
|
||||
Если `period_us` ещё не измерен, используется верхний предел 2000 мкс.
|
||||
|
||||
### Фильтрация периода полуволны
|
||||
|
||||
Первый принятый фронт устанавливает `period_us` напрямую. Последующие значения
|
||||
сглаживаются EMA с коэффициентом 7/8:
|
||||
|
||||
```
|
||||
period_us = (period_us * 7 + delta_us) / 8
|
||||
```
|
||||
|
||||
Если интервал между фронтами выходит за пределы `[period_min_us, period_max_us]`,
|
||||
состояние сбрасывается: `period_us = 0`, `auto_base_samples = 0`, TRIAC сбрасывается.
|
||||
|
||||
Границы периода рассчитываются из настраиваемой частоты сети:
|
||||
|
||||
```
|
||||
period_min_us = 1 000 000 / (2 * ZH_AC_DIMMER_FREQ_MAX_HZ)
|
||||
period_max_us = 1 000 000 / (2 * ZH_AC_DIMMER_FREQ_MIN_HZ)
|
||||
```
|
||||
|
||||
### Расчёт задержки зажигания TRIAC
|
||||
|
||||
Для каждого принятого фронтa вычисляется задержка до зажигания TRIAC:
|
||||
|
||||
1. Вычисляется целевая задержка на основе уровня затемнения:
|
||||
|
||||
```
|
||||
t_fire = period * (100 - dimmer_value) / 100
|
||||
```
|
||||
|
||||
2. Добавляется откалиброванное смещение:
|
||||
|
||||
```
|
||||
delay_us = t_fire + base_offset_us
|
||||
```
|
||||
|
||||
Если `base_offset_us >= period`, смещение ограничивается значением `period`.
|
||||
3. Если `delay_us <= 0`, TRIAC зажигается немедленно.
|
||||
4. Вычисляется защитный зазор: `guard_us = max(period / 20, 5 мкс)`.
|
||||
5. Если `delay_us > period - guard_us`, задержка урезается до `period - guard_us`.
|
||||
6. Таймер запускается с вычисленной задержкой.
|
||||
|
||||
### Обработка ISR перехода через ноль
|
||||
|
||||
ISR обрабатывает оба фронта сигнала:
|
||||
|
||||
1. **Спадающий фронт** (GPIO = 0, предыдущий фронт был возрастающий):
|
||||
- Измеряется ширина импульса.
|
||||
- Калибруется `base_offset_us` через EMA.
|
||||
- Действий с TRIAC не выполняется.
|
||||
|
||||
2. **Возрастающий фронт** (GPIO = 1):
|
||||
- Сохраняется метка времени `pulse_start_us`.
|
||||
- Если это первый фронт — только запоминается время, TRIAC сбрасывается.
|
||||
- Интервал сравнивается с допустимыми границами:
|
||||
- Если интервал слишком короткий — фронт отбрасывается как помеха.
|
||||
- Если интервал слишком длинный — состояние сбрасывается.
|
||||
- Обновляется отфильтрованный период `period_us`.
|
||||
- Если dimmer не работает или уровень = 0 — таймер останавливается.
|
||||
- Иначе — вычисляется задержка и запускается таймер для зажигания TRIAC.
|
||||
|
||||
---
|
||||
|
||||
## Примеры использования
|
||||
|
||||
### Базовый пример инициализации и управления
|
||||
|
||||
```c
|
||||
#include "zh_ac_dimmer.h"
|
||||
|
||||
zh_ac_dimmer_handle_t *dimmer = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Настройка уровня логирования
|
||||
esp_log_level_set("zh_ac_dimmer", ESP_LOG_ERROR);
|
||||
|
||||
// Конфигурация пинов
|
||||
zh_ac_dimmer_init_config_t config = ZH_AC_DIMMER_INIT_CONFIG_DEFAULT();
|
||||
config.zero_cross_gpio = GPIO_NUM_16;
|
||||
config.triac_gpio = GPIO_NUM_17;
|
||||
|
||||
// Инициализация и запуск
|
||||
zh_ac_dimmer_init(&config, &dimmer);
|
||||
zh_ac_dimmer_start(&dimmer);
|
||||
|
||||
// Установка уровня затемнения 50 %
|
||||
zh_ac_dimmer_set(&dimmer, 50);
|
||||
|
||||
// Чтение текущего уровня
|
||||
uint8_t level = 0;
|
||||
zh_ac_dimmer_get(&dimmer, &level);
|
||||
|
||||
// Остановка и деинициализация
|
||||
zh_ac_dimmer_stop(&dimmer);
|
||||
zh_ac_dimmer_deinit(&dimmer);
|
||||
}
|
||||
```
|
||||
|
||||
### Динамическое изменение яркости
|
||||
|
||||
```c
|
||||
#include "zh_ac_dimmer.h"
|
||||
|
||||
zh_ac_dimmer_handle_t *dimmer = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_ac_dimmer", ESP_LOG_ERROR);
|
||||
|
||||
zh_ac_dimmer_init_config_t config = ZH_AC_DIMMER_INIT_CONFIG_DEFAULT();
|
||||
config.zero_cross_gpio = GPIO_NUM_16;
|
||||
config.triac_gpio = GPIO_NUM_17;
|
||||
|
||||
zh_ac_dimmer_init(&config, &dimmer);
|
||||
zh_ac_dimmer_start(&dimmer);
|
||||
|
||||
// Плавное нарастание яркости от 0 до 100 %
|
||||
for (uint8_t i = 0; i <= 100; ++i)
|
||||
{
|
||||
zh_ac_dimmer_set(&dimmer, i);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
// Плавное затухание от 100 до 0 %
|
||||
for (uint8_t i = 100; i > 0; --i)
|
||||
{
|
||||
zh_ac_dimmer_set(&dimmer, i);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
zh_ac_dimmer_stop(&dimmer);
|
||||
zh_ac_dimmer_deinit(&dimmer);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Логирование
|
||||
|
||||
Компонент использует ESP-IDF логирование с тегом `"zh_ac_dimmer"`.
|
||||
|
||||
```c
|
||||
esp_log_level_set("zh_ac_dimmer", ESP_LOG_ERROR);
|
||||
```
|
||||
|
||||
Доступные уровни: `ESP_LOG_ERROR`, `ESP_LOG_WARN`, `ESP_LOG_INFO`, `ESP_LOG_DEBUG`.
|
||||
Настройка `ESP_LOG_ERROR` оставляет только сообщения об ошибках. Уровень `ESP_LOG_INFO`
|
||||
добавляет сообщения об успешной инициализации, запуске и остановке.
|
||||
|
||||
---
|
||||
|
||||
## Ошибки
|
||||
|
||||
| Код | Описание |
|
||||
| ----- | ---------- |
|
||||
| `ESP_OK` | Успешное выполнение |
|
||||
| `ESP_ERR_INVALID_ARG` | Неверный аргумент: NULL-указатель, некорректный GPIO, одинаковые пины, значение > 100 |
|
||||
| `ESP_ERR_INVALID_STATE` | Драйвер уже инициализирован (handle указывает на существующий экземпляр) |
|
||||
| `ESP_ERR_NO_MEM` | Не удалось выделить память для handle |
|
||||
| `ESP_FAIL` | Не удалось настроить GPIO, установить ISR-обработчик или создать таймер |
|
||||
|
||||
---
|
||||
|
||||
## Ограничения
|
||||
|
||||
1. **Один экземпляр на GPIO-пин** — инициализация второго dimmer на том же
|
||||
zero-cross GPIO заменяет ISR-обработчик первого экземпляра. Каждый пин должен
|
||||
принадлежать одному экземпляру.
|
||||
2. **Не потокобезопасный** — нет внутренней синхронизации между задачами.
|
||||
`volatile` защищает только от оптимизаций компилятора, но не от гонки данных.
|
||||
3. **Диапазон частот сети** — определяется макросами `ZH_AC_DIMMER_FREQ_MIN_HZ`
|
||||
(по умолчанию 30 Гц) и `ZH_AC_DIMMER_FREQ_MAX_HZ` (по умолчанию 500 Гц).
|
||||
Выход за эти границы приводит к сбросу состояния.
|
||||
4. **Симметричный оптоизолятор** — калибровка смещения предполагает симметричный
|
||||
импульс перехода через ноль (`base_offset = pulse_width / 2`). Асимметрия
|
||||
внесёт систематическую погрешность.
|
||||
5. **Защитный зазор** — минимальная задержка перед концом полуволны ограничена
|
||||
`max(period / 20, 5 мкс)`, что может не обеспечить достаточного времени
|
||||
для полного затухания TRIAC на очень низких частотах.
|
||||
|
||||
---
|
||||
|
||||
## Требования для сборки
|
||||
|
||||
- ESP-IDF (фреймворк для ESP32)
|
||||
- FreeRTOS (входит в состав ESP-IDF)
|
||||
- Драйверы `driver/gpio` и `driver/gptimer` (входят в состав ESP-IDF)
|
||||
|
||||
Компонент размещается в директории `components/` проекта ESP-IDF.
|
||||
|
||||
---
|
||||
|
||||
## Установка
|
||||
|
||||
```bash
|
||||
cd ваш_проект/components
|
||||
git clone https://github.com/aZholtikov/zh_ac_dimmer
|
||||
```
|
||||
|
||||
```c
|
||||
#include "zh_ac_dimmer.h"
|
||||
```
|
||||
Reference in new issue
Block a user