Skip to content

Commit

Permalink
Enhance ZC-Dimmer for falling and leading edge dimmer (#19054)
Browse files Browse the repository at this point in the history
* Update tasmota_types.h

* Update xdrv_68_zerocrossDimmer.ino
  • Loading branch information
stefanbode committed Jul 7, 2023
1 parent 5c9c4e6 commit 8ce7cb4
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 60 deletions.
5 changes: 3 additions & 2 deletions tasmota/include/tasmota_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
88 changes: 30 additions & 58 deletions tasmota/tasmota_xdrv_driver/xdrv_68_zerocrossDimmer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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;
}
}
}

Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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
}
}
}
}

Expand Down Expand Up @@ -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;
}

}
Expand All @@ -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]
);
}
Expand Down Expand Up @@ -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
\*********************************************************************************************/
Expand All @@ -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
Expand Down

0 comments on commit 8ce7cb4

Please sign in to comment.