From 50f6b07d7e0a76f6ff145ffcce0439412fb7a68a Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Sat, 29 Jun 2024 23:49:21 +0200 Subject: [PATCH] Fix ZCD pulse and frequency detection (cherry picked from commit 21158c936e6701581100ea5d46cb3fb2d640115d) --- lib/DimmableLight/src/thyristor.cpp | 66 +++-------------------------- lib/DimmableLight/src/thyristor.h | 2 +- lib/MycilaDimmer/MycilaDimmer.h | 4 +- lib/MycilaDimmer/MycilaZCD.cpp | 5 +-- lib/MycilaDimmer/MycilaZCD.h | 8 +--- 5 files changed, 13 insertions(+), 72 deletions(-) diff --git a/lib/DimmableLight/src/thyristor.cpp b/lib/DimmableLight/src/thyristor.cpp index 1f284aa..b2d5f63 100644 --- a/lib/DimmableLight/src/thyristor.cpp +++ b/lib/DimmableLight/src/thyristor.cpp @@ -272,7 +272,6 @@ void ARDUINO_ISR_ATTR zero_cross_pulse_int() { // I decided to use "16" because is a power of 2, very fast to be computed. if (semiPeriodLength && diff > semiPeriodLength * 16) { queue.reset(); - pulses.reset(); } else { // If filtering has passed, I can update the moving average queue.add(diff); @@ -469,75 +468,24 @@ void Thyristor::setFrequency(float frequency) { } uint16_t Thyristor::getPulseWidth() { - noInterrupts(); - uint32_t diff = micros() - lastTime; - if (semiPeriodLength && diff > semiPeriodLength) { - pulses.reset(); - } - uint16_t avg = pulses.avg(); - interrupts(); - return avg; + return pulses.avg(); } uint16_t Thyristor::getMaxPulseWidth() { - noInterrupts(); - uint32_t diff = micros() - lastTime; - if (semiPeriodLength && diff > semiPeriodLength) { - pulses.reset(); - } - uint16_t max = pulses.max(); - interrupts(); - return max; + return pulses.max(); } uint16_t Thyristor::getMinPulseWidth() { - noInterrupts(); - uint32_t diff = micros() - lastTime; - if (semiPeriodLength && diff > semiPeriodLength) { - pulses.reset(); - } - uint16_t min = pulses.min(); - interrupts(); - return min; + return pulses.min(); } uint16_t Thyristor::getLastPulseWidth() { - noInterrupts(); - uint32_t diff = micros() - lastTime; - if (semiPeriodLength && diff > semiPeriodLength) { - pulses.reset(); - } - uint16_t last = pulses.last(); - interrupts(); - return last; + return pulses.last(); } -float Thyristor::getDetectedFrequency() { - float c; - float tot; - { - // Stop interrupt to freeze variables modified or accessed in the interrupt - noInterrupts(); - - // "diff" is correct even when rolling back, because all of them are unsigned - uint32_t diff = micros() - lastTime; - - // if diff is very very greater than the theoretical value, the electrical signal - // can be considered as lost for a while. - // I decided to use "16" because is a power of 2, very fast to be computed. - if (semiPeriodLength && diff > semiPeriodLength * 16) { - queue.reset(); - } - - c = queue.count(); - tot = queue.sum(); - interrupts(); - } - - // We need at least a sample to return a value different from 0 - // *1000000: us - // /2: from semiperiod to full period - return tot > 0 ? (c * 500000 / tot) : 0; +float Thyristor::getPulseFrequency() { + uint32_t pulsePeriod = queue.avg(); + return pulsePeriod == 0 ? 0 : 1000000 / (float)pulsePeriod; } Thyristor::Thyristor(int pin) : pin(pin), delay(semiPeriodLength) { diff --git a/lib/DimmableLight/src/thyristor.h b/lib/DimmableLight/src/thyristor.h index 5c68989..eb38bc0 100644 --- a/lib/DimmableLight/src/thyristor.h +++ b/lib/DimmableLight/src/thyristor.h @@ -130,7 +130,7 @@ class Thyristor { * * NOTE: when (re)starting, it will take a while before returning a value different from 0. */ - static float getDetectedFrequency(); + static float getPulseFrequency(); static const uint8_t N = 2; diff --git a/lib/MycilaDimmer/MycilaDimmer.h b/lib/MycilaDimmer/MycilaDimmer.h index a2aa0bd..c267979 100644 --- a/lib/MycilaDimmer/MycilaDimmer.h +++ b/lib/MycilaDimmer/MycilaDimmer.h @@ -71,8 +71,8 @@ namespace Mycila { // At 0% power, the phase angle is equal to PI // At 100% power, the phase angle is equal to 0 float getPhaseAngle() const { - // angle_rad = PI * delay_s / period_s - return PI * _zcd->getPulseFrequency() * getFiringDelay() / 1000000; + // angle_rad = PI * delay_us / period_us + return PI * getFiringDelay() / _zcd->getSemiPeriod(); } private: diff --git a/lib/MycilaDimmer/MycilaZCD.cpp b/lib/MycilaDimmer/MycilaZCD.cpp index f9d0ba8..c7bdfed 100644 --- a/lib/MycilaDimmer/MycilaZCD.cpp +++ b/lib/MycilaDimmer/MycilaZCD.cpp @@ -51,15 +51,12 @@ void Mycila::ZCD::begin(const int8_t pin, const uint8_t frequency) { Thyristor::setSyncPin(_pin); Thyristor::begin(); - _frequencyUpdater.attach(1, +[](ZCD* instance) { instance->_measuredGridFrequency = Thyristor::getDetectedFrequency(); }, this); - _enabled = true; } void Mycila::ZCD::end() { if (_enabled) { LOGI(TAG, "Disable Zero-Cross Detection..."); - _frequencyUpdater.detach(); _enabled = false; Thyristor::setFrequency(0); Thyristor::end(); @@ -68,6 +65,8 @@ void Mycila::ZCD::end() { } uint16_t Mycila::ZCD::getSemiPeriod() const { return _enabled ? Thyristor::getSemiPeriod() : 0; } + +float Mycila::ZCD::getPulseFrequency() const { return _enabled ? Thyristor::getPulseFrequency() : 0; } uint16_t Mycila::ZCD::getAvgPulseWidth() const { return _enabled ? Thyristor::getPulseWidth() : 0; } uint16_t Mycila::ZCD::getMaxPulseWidth() const { return _enabled ? Thyristor::getMaxPulseWidth() : 0; } uint16_t Mycila::ZCD::getLastPulseWidth() const { return _enabled ? Thyristor::getLastPulseWidth() : 0; } diff --git a/lib/MycilaDimmer/MycilaZCD.h b/lib/MycilaDimmer/MycilaZCD.h index f242909..28950ef 100644 --- a/lib/MycilaDimmer/MycilaZCD.h +++ b/lib/MycilaDimmer/MycilaZCD.h @@ -5,7 +5,6 @@ #pragma once #include -#include #include @@ -23,11 +22,8 @@ namespace Mycila { // 60Hz => 8333 uint16_t getSemiPeriod() const; - // Grid frequency in Hz based on the Zero-Cross pulse frequency - float getCurrentFrequency() const { return _enabled ? _measuredGridFrequency : 0; } - // Zero-Cross pulse information - float getPulseFrequency() const { return _enabled ? _measuredGridFrequency * 2 : 0; } + float getPulseFrequency() const; uint16_t getAvgPulseWidth() const; uint16_t getLastPulseWidth() const; uint16_t getMaxPulseWidth() const; @@ -45,8 +41,6 @@ namespace Mycila { private: bool _enabled = false; - volatile float _measuredGridFrequency = 0; - Ticker _frequencyUpdater; gpio_num_t _pin = GPIO_NUM_NC; }; } // namespace Mycila