Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance ZC-Dimmer for falling and leading edge dimmer #19054

Merged
merged 2 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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