From 8ce7cb4ccd699dd82d603b1e67f210d990d83cc7 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Fri, 7 Jul 2023 19:50:36 +0200 Subject: [PATCH] Enhance ZC-Dimmer for falling and leading edge dimmer (#19054) * Update tasmota_types.h * Update xdrv_68_zerocrossDimmer.ino --- tasmota/include/tasmota_types.h | 5 +- .../xdrv_68_zerocrossDimmer.ino | 88 +++++++------------ 2 files changed, 33 insertions(+), 60 deletions(-) diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h index 758da9625883..eac333b777cb 100644 --- a/tasmota/include/tasmota_types.h +++ b/tasmota/include/tasmota_types.h @@ -188,7 +188,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t bistable_single_pin : 1; // bit 6 (v12.5.0.1) - SetOption152 - (Power) Switch between two (0) or one (1) pin bistable relay control uint32_t berry_no_autoexec : 1; // bit 7 (v12.5.0.3) - SetOption153 - (Berry) Disable autoexec.be on restart (1) uint32_t berry_light_scheme : 1; // bit 8 (v12.5.0.3) - SetOption154 - (Berry) Handle berry led using RMT0 as additional WS2812 scheme - uint32_t spare09 : 1; // bit 9 + uint32_t zcfallingedge : 1; // bit 9 (v12.5.0.4) - SetOption155 - ZC Dimmer enable rare falling Edge dimmer instead of leading edge uint32_t spare10 : 1; // bit 10 uint32_t spare11 : 1; // bit 11 uint32_t spare12 : 1; // bit 12 @@ -778,8 +778,9 @@ typedef struct { int8_t temp_comp; // E9E uint8_t weight_change; // E9F uint8_t web_color2[2][3]; // EA0 Needs to be on integer / 3 distance from web_color + uint16_t zcdimmerset[5]; // EA6 - uint8_t free_ea6[32]; // EA6 + uint8_t free_eb0[22]; // EB0 22 bytes uint8_t shift595_device_count; // EC6 uint8_t sta_config; // EC7 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_68_zerocrossDimmer.ino b/tasmota/tasmota_xdrv_driver/xdrv_68_zerocrossDimmer.ino index 9ec32e59e8a1..1a3f52210012 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_68_zerocrossDimmer.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_68_zerocrossDimmer.ino @@ -24,9 +24,6 @@ #define XDRV_68 68 -static const uint8_t GATE_ENABLE_TIME = 100; -static const uint8_t MIN_PERCENT = 5; -static const uint8_t MAX_PERCENT = 99; static const uint8_t TRIGGER_PERIOD = 75; #define ZCDIMMERSET_SHOW 1 @@ -37,13 +34,11 @@ struct AC_ZERO_CROSS_DIMMER { uint32_t crossed_zero_at; // Time (in micros()) of last ZC signal bool timer_iterrupt_started = false; // verification of the interrupt running bool dimmer_in_use = false; // Check if interrupt has to be run. Is stopped if all lights off + bool fallingEdgeDimmer = false; // Work as a fallwing edge dimmer uint32_t enable_time_us[MAX_PWMS]; // Time since last ZC pulse to enable gate pin. 0 means no disable. - uint32_t disable_time_us[MAX_PWMS]; // Time since last ZC pulse to disable gate pin. 0 means no disable. - uint8_t current_state_in_phase[MAX_PWMS]; // 0=before fire HIGH, 1=HIGH, 2=after setting LOW, 3=before HIGH without setting LOW (POWER ON) uint32_t lastlight[MAX_PWMS]; // Store the light value. Set 1 if controlled through ZCDimmerSet uint16_t detailpower[MAX_PWMS]; // replaces dimmer and light controll 0..10000. required savedata 0. uint32_t accurracy[MAX_PWMS]; // offset of the time to fire the triac and the real time when it fired - uint8_t triggertime = GATE_ENABLE_TIME; // copy of the Time for the gate keep open to start TRIAC uint32_t intr_counter = 0; // counter internally on interrerupt calls uint32_t missed_zero_cross; // count up all missed Zero-cross events. uint8_t actual_tigger_Period = TRIGGER_PERIOD; // copy of tigger period to change during runtime @@ -70,17 +65,8 @@ void IRAM_ATTR ACDimmerZeroCross(uint32_t time) { ac_zero_cross_dimmer.crossed_zero_at = time; for (uint8_t i=0; i < MAX_PWMS; i++) { if (Pin(GPIO_PWM1, i) == -1) continue; + digitalWrite(Pin(GPIO_PWM1, i), LOW ^ ac_zero_cross_dimmer.fallingEdgeDimmer); ac_zero_cross_dimmer.dimmer_in_use |= ac_zero_cross_dimmer.lastlight[i] > 0; - // Dimmer is physically off. Skip swich on - ac_zero_cross_dimmer.current_state_in_phase[i] = 0; - if (100 * ac_zero_cross_dimmer.enable_time_us[i] > MAX_PERCENT * ac_zero_cross_dimmer.cycle_time_us ) { - ac_zero_cross_dimmer.current_state_in_phase[i] = 1; - ac_zero_cross_dimmer.disable_time_us[i] = ac_zero_cross_dimmer.cycle_time_us / 2; - } - // If full cycle is required keep pin HIGH, skip LOW by skipping phase - if (100 * ac_zero_cross_dimmer.enable_time_us[i] < MIN_PERCENT * ac_zero_cross_dimmer.cycle_time_us) { - ac_zero_cross_dimmer.current_state_in_phase[i] = 3; - } } } @@ -90,6 +76,14 @@ uint32_t IRAM_ATTR ACDimmerTimer_intr_ESP8266() { return ac_zero_cross_dimmer.actual_tigger_Period * 80; } +void ACDimmerInit() +{ + for (uint8_t i = 0 ; i < 5; i++) { + ac_zero_cross_dimmer.detailpower[i] = Settings->zcdimmerset[i]; + ac_zero_cross_dimmer.fallingEdgeDimmer = Settings->flag6.zcfallingedge; + } +} + void ACDimmerInterruptDisable(bool disable) { AddLog(LOG_LEVEL_INFO, PSTR("ZCD: Zero-CrossDimmer enabled: %d"),!disable); @@ -133,7 +127,6 @@ void IRAM_ATTR ACDimmerTimer_intr() { ac_zero_cross_dimmer.intr_counter++; // Check for missed Zero-Cross event. Single failure will correct if (time_since_zc > 10100) { - memset(&ac_zero_cross_dimmer.current_state_in_phase, 0x00, sizeof(ac_zero_cross_dimmer.current_state_in_phase)); ac_zero_cross_dimmer.crossed_zero_at += ac_zero_cross_dimmer.cycle_time_us; ac_zero_cross_dimmer.missed_zero_cross++; time_since_zc += ac_zero_cross_dimmer.cycle_time_us; @@ -142,37 +135,25 @@ void IRAM_ATTR ACDimmerTimer_intr() { ac_zero_cross_dimmer.actual_tigger_Period = TRIGGER_PERIOD; for (uint8_t i = 0 ; i < MAX_PWMS; i++ ) { if (Pin(GPIO_PWM1, i) == -1) continue; - switch (ac_zero_cross_dimmer.current_state_in_phase[i]) { - case 1: - // Switch off does not need high accuracy. Happens at the next 75µs trigger - if (time_since_zc >= ac_zero_cross_dimmer.disable_time_us[i]) { - digitalWrite(Pin(GPIO_PWM1, i), LOW); - ac_zero_cross_dimmer.current_state_in_phase[i]++; - } - break; - case 0: - case 3: - if (time_since_zc + TRIGGER_PERIOD >= ac_zero_cross_dimmer.enable_time_us[i]){ - // Very close to the fire event. Loop the last µseconds to wait. + + if (time_since_zc + TRIGGER_PERIOD >= ac_zero_cross_dimmer.enable_time_us[i]){ + // Very close to the fire event. Loop the last µseconds to wait. #ifdef ESP8266 - // on ESP8266 we can change dynamically the trigger interval - ac_zero_cross_dimmer.actual_tigger_Period = tmin(ac_zero_cross_dimmer.actual_tigger_Period,tmax(5,ac_zero_cross_dimmer.enable_time_us[i] - time_since_zc)); + // on ESP8266 we can change dynamically the trigger interval + ac_zero_cross_dimmer.actual_tigger_Period = tmin(ac_zero_cross_dimmer.actual_tigger_Period,tmax(5,ac_zero_cross_dimmer.enable_time_us[i] - time_since_zc)); #endif #ifdef ESP32 - while (time_since_zc < ac_zero_cross_dimmer.enable_time_us[i]) { - time_since_zc = micros() - ac_zero_cross_dimmer.crossed_zero_at; - } + while (time_since_zc < ac_zero_cross_dimmer.enable_time_us[i]) { + time_since_zc = micros() - ac_zero_cross_dimmer.crossed_zero_at; + } #endif - } - if (time_since_zc >= ac_zero_cross_dimmer.enable_time_us[i]) { - digitalWrite(Pin(GPIO_PWM1, i), HIGH); - ac_zero_cross_dimmer.current_state_in_phase[i]++; -#ifdef ZC_DEBUG - ac_zero_cross_dimmer.accurracy[i] = time_since_zc-ac_zero_cross_dimmer.enable_time_us[i]; -#endif - } - break; - } + if (time_since_zc >= ac_zero_cross_dimmer.enable_time_us[i]) { + digitalWrite(Pin(GPIO_PWM1, i), HIGH ^ ac_zero_cross_dimmer.fallingEdgeDimmer ); + #ifdef ZC_DEBUG + ac_zero_cross_dimmer.accurracy[i] = time_since_zc-ac_zero_cross_dimmer.enable_time_us[i]; + #endif + } + } } } @@ -202,8 +183,6 @@ void ACDimmerControllTrigger(void) { ac_zero_cross_dimmer.enable_time_us[i] = (uint32_t)state; } #endif - - ac_zero_cross_dimmer.disable_time_us[i] = ac_zero_cross_dimmer.enable_time_us[i] + ac_zero_cross_dimmer.triggertime; } } @@ -226,9 +205,9 @@ void ACDimmerLogging(void) ); for (uint8_t i = 0; i < MAX_PWMS; i++){ if (Pin(GPIO_PWM1, i) == -1) continue; - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ZCD: PWM[%d] en: %ld µs, dis: %ld µs, state %d, fade: %d, cur: %d, end: %d, lastlight: %d, acc: %ld"), - i+1, ac_zero_cross_dimmer.enable_time_us[i], ac_zero_cross_dimmer.disable_time_us[i], - ac_zero_cross_dimmer.current_state_in_phase[i], Light.fade_cur_10[i], Light.fade_start_10[i], Light.fade_end_10[i], ac_zero_cross_dimmer.lastlight[i], + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ZCD: PWM[%d] en: %ld µs, fade: %d, cur: %d, end: %d, lastlight: %d, acc: %ld"), + i+1, ac_zero_cross_dimmer.enable_time_us[i], + Light.fade_cur_10[i], Light.fade_start_10[i], Light.fade_end_10[i], ac_zero_cross_dimmer.lastlight[i], ac_zero_cross_dimmer.accurracy[i] ); } @@ -258,20 +237,12 @@ void CmndZCDimmerSet(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) { if (XdrvMailbox.data_len > 0) { - ac_zero_cross_dimmer.detailpower[XdrvMailbox.index-1] = (uint16_t)(100 * CharToFloat(XdrvMailbox.data)); + Settings->zcdimmerset[XdrvMailbox.index-1] = ac_zero_cross_dimmer.detailpower[XdrvMailbox.index-1] = (uint16_t)(100 * CharToFloat(XdrvMailbox.data)); } ResponseCmndIdxFloat((float)(ac_zero_cross_dimmer.detailpower[XdrvMailbox.index-1]) / 100, 2); } } -/* void CmndZCGateEnableTime(void) -{ - if (XdrvMailbox.payload > 0) { - ac_zero_cross_dimmer.triggertime = XdrvMailbox.payload; - } - ResponseCmndNumber(ac_zero_cross_dimmer.triggertime); -} */ - /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -282,6 +253,7 @@ bool Xdrv68(uint32_t function) if (Settings->flag4.zerocross_dimmer) { switch (function) { case FUNC_INIT: + ACDimmerInit(); #ifdef ESP32 //ACDimmerInterruptDisable(false); #endif