diff --git a/tasmota/xsns_01_counter.ino b/tasmota/xsns_01_counter.ino index 5b6de4e4c..fd6b26ca5 100644 --- a/tasmota/xsns_01_counter.ino +++ b/tasmota/xsns_01_counter.ino @@ -24,6 +24,8 @@ #define XSNS_01 1 +#define USE_AC_ZERO_CROSS_DIMMER 1 + #define D_PRFX_COUNTER "Counter" #define D_CMND_COUNTERTYPE "Type" #define D_CMND_COUNTERDEBOUNCE "Debounce" @@ -44,10 +46,20 @@ struct COUNTER { uint8_t no_pullup = 0; // Counter input pullup flag (1 = No pullup) uint8_t pin_state = 0; // LSB0..3 Last state of counter pin; LSB7==0 IRQ is FALLING, LSB7==1 IRQ is CHANGE bool any_counter = false; + } Counter; -uint32_t last_cycle; -uint32_t cycle_time; +#ifdef USE_AC_ZERO_CROSS_DIMMER +struct AC_ZERO_CROSS_DIMMER { + bool startReSync = false; + uint32_t current_cycle_ClockCycles = 0; + uint32_t dimm_timeClockCycles = 0; + uint32_t currentCycleCount = 0; + uint32_t tobe_cycle_timeClockCycles = 0; + uint32_t lastCycleCount = 0; + uint32_t currentSteps = 100; +} ac_zero_cross_dimmer; +#endif void ICACHE_RAM_ATTR CounterIsrArg(void *arg) { uint32_t index = *static_cast(arg); @@ -76,35 +88,20 @@ void ICACHE_RAM_ATTR CounterIsrArg(void *arg) { if bitRead(Counter.pin_state, index) { // PWMfrequency 100 // restart PWM each second (german 50Hz has to up to 0.01% deviation) - // Zero-HIGH is typical 2ms - if (RtcSettings.pulse_counter[index]%100 == 0 && PinUsed(GPIO_PWM1, index) && Settings.flag4.zerocross_dimmer) { - const uint32_t current_cycle = ESP.getCycleCount(); - // stop pwm on PIN to start in Sync with rising edge - // calculate timeoffset to fire PWM - uint16_t cur_col = Light.fade_start_10[0 + Light.pwm_offset]; - uint32_t dimm_time= 1000000 / Settings.pwm_frequency * (1024 - cur_col) / 1024; - digitalWrite(Pin(GPIO_PWM1, index), LOW); + // restart initiated by setting Counter.startReSync = true; +#ifdef USE_AC_ZERO_CROSS_DIMMER + if (RtcSettings.pulse_counter[index]%(Settings.pwm_frequency / (Light.fade_running ? 10:1))== 0 && PinUsed(GPIO_PWM1, index) && Settings.flag4.zerocross_dimmer) { + ac_zero_cross_dimmer.currentCycleCount = ESP.getCycleCount(); + // 1000µs to ensure not to fire on the next sinus wave - if (dimm_time < (1000000 / Settings.pwm_frequency)-1000) { - delayMicroseconds(dimm_time); - // fire small PWM signal to start TRIAC/SSR. Kill on next - // zero phase automatic. - // calculate actual cycle time and adapt frequency im milli Hz steps - // add 100.000 cpu ticks to ensure right step calculation - uint32_t steps = (current_cycle-last_cycle+100000)/(clockCyclesPerMicrosecond() * 10000); - cycle_time = (current_cycle-last_cycle)/steps; -#ifdef ESP8266 - pinMode(Pin(GPIO_PWM1, index), OUTPUT); - uint32_t high = (cycle_time * 5) / 1023; - uint32_t low = cycle_time - high; - // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t - startWaveformClockCycles(Pin(GPIO_PWM1, index), high, low, 0, -1, 0, true); -#else // ESP32 - analogWrite(Pin(GPIO_PWM1, index), 5); -#endif // ESP8266 - ESP32 + if (ac_zero_cross_dimmer.lastCycleCount>0) { + ac_zero_cross_dimmer.startReSync = true; + ac_zero_cross_dimmer.currentSteps = (ac_zero_cross_dimmer.currentCycleCount-ac_zero_cross_dimmer.lastCycleCount+(ac_zero_cross_dimmer.tobe_cycle_timeClockCycles/2))/(ac_zero_cross_dimmer.tobe_cycle_timeClockCycles); + ac_zero_cross_dimmer.current_cycle_ClockCycles = (ac_zero_cross_dimmer.currentCycleCount-ac_zero_cross_dimmer.lastCycleCount)/ac_zero_cross_dimmer.currentSteps; } - last_cycle = current_cycle; + ac_zero_cross_dimmer.lastCycleCount = ac_zero_cross_dimmer.currentCycleCount; } +#endif return; } } @@ -153,6 +150,9 @@ void CounterInit(void) { for (uint32_t i = 0; i < MAX_COUNTERS; i++) { if (PinUsed(GPIO_CNTR1, i)) { +#ifdef USE_AC_ZERO_CROSS_DIMMER + ac_zero_cross_dimmer.tobe_cycle_timeClockCycles = microsecondsToClockCycles(1000000 / Settings.pwm_frequency); +#endif Counter.any_counter = true; pinMode(Pin(GPIO_CNTR1, i), bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); if ((0 == Settings.pulse_counter_debounce_low) && (0 == Settings.pulse_counter_debounce_high) && !Settings.flag4.zerocross_dimmer) { @@ -231,6 +231,45 @@ void CounterShow(bool json) } } +#ifdef USE_AC_ZERO_CROSS_DIMMER +void SyncACDimmer(void) +{ + if (ac_zero_cross_dimmer.startReSync) { + // currently only support one AC Dimmer PWM. Plan to support up to 4 Dimmer on same Phase. + for (uint32_t i = 0; i < 1; i++) { + if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) + { + // get current time because undefined through FUNC_LOOP process. + const uint32_t current_cycle = ESP.getCycleCount(); + digitalWrite(Pin(GPIO_PWM1, i), LOW); + // reset trigger for PWM sync + ac_zero_cross_dimmer.startReSync = false; + // calculate timeoffset to fire PWM + + ac_zero_cross_dimmer.dimm_timeClockCycles = (ac_zero_cross_dimmer.tobe_cycle_timeClockCycles * (1024 - (Light.fade_running ? Light.fade_cur_10[i] : Light.fade_start_10[i]))) / 1024; + ac_zero_cross_dimmer.dimm_timeClockCycles = tmax(ac_zero_cross_dimmer.dimm_timeClockCycles, 16000); + // because in LOOP calculate the timelag to fire PWM correctly with zero-cross + uint32_t timelag_ClockCycles = (current_cycle - ac_zero_cross_dimmer.currentCycleCount)%ac_zero_cross_dimmer.tobe_cycle_timeClockCycles; + timelag_ClockCycles = ((ac_zero_cross_dimmer.dimm_timeClockCycles + ac_zero_cross_dimmer.tobe_cycle_timeClockCycles) - timelag_ClockCycles)%ac_zero_cross_dimmer.tobe_cycle_timeClockCycles; + delayMicroseconds(clockCyclesToMicroseconds(timelag_ClockCycles)); + + #ifdef ESP8266 + pinMode(Pin(GPIO_PWM1, i), OUTPUT); + // short fire to ensure not to hit next sinus curve. 0.78% of duty cycle (10ms) ~4µs + uint32_t high = ac_zero_cross_dimmer.current_cycle_ClockCycles / 256; + uint32_t low = ac_zero_cross_dimmer.current_cycle_ClockCycles - high; + // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t + startWaveformClockCycles(Pin(GPIO_PWM1, i), high, low, 0, -1, 0, true); + #else // ESP32 + analogWrite(Pin(GPIO_PWM1, i), 5); + #endif // ESP8266 - ESP32 + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("CNT: [%d] dimm_time_CCs %d, current_cycle_CC: %d, timelag %lu, lastcc %lu, currentSteps %d, curr %d"), i, ac_zero_cross_dimmer.dimm_timeClockCycles,ac_zero_cross_dimmer.current_cycle_ClockCycles , timelag_ClockCycles, ac_zero_cross_dimmer.currentCycleCount, ac_zero_cross_dimmer.currentSteps, Light.fade_cur_10[i]); + } + } + } +} +#endif + /*********************************************************************************************\ * Commands \*********************************************************************************************/ @@ -305,6 +344,11 @@ bool Xsns01(uint8_t function) case FUNC_JSON_APPEND: CounterShow(1); break; +#ifdef USE_AC_ZERO_CROSS_DIMMER + case FUNC_LOOP: + SyncACDimmer(); + break; +#endif #ifdef USE_WEBSERVER case FUNC_WEB_SENSOR: CounterShow(0);