Skip to content

Commit

Permalink
Fix ZCD pulse and frequency detection
Browse files Browse the repository at this point in the history
(cherry picked from commit 21158c936e6701581100ea5d46cb3fb2d640115d)
  • Loading branch information
mathieucarbou committed Jun 29, 2024
1 parent 3325775 commit 50f6b07
Show file tree
Hide file tree
Showing 5 changed files with 13 additions and 72 deletions.
66 changes: 7 additions & 59 deletions lib/DimmableLight/src/thyristor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion lib/DimmableLight/src/thyristor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
4 changes: 2 additions & 2 deletions lib/MycilaDimmer/MycilaDimmer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 2 additions & 3 deletions lib/MycilaDimmer/MycilaZCD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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; }
Expand Down
8 changes: 1 addition & 7 deletions lib/MycilaDimmer/MycilaZCD.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#pragma once

#include <ArduinoJson.h>
#include <Ticker.h>

#include <esp32-hal-gpio.h>

Expand All @@ -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;
Expand All @@ -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

0 comments on commit 50f6b07

Please sign in to comment.