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

* Update tasmota_types.h

* Update xdrv_68_zerocrossDimmer.ino
This commit is contained in:
stefanbode 2023-07-07 19:50:36 +02:00 committed by GitHub
parent 5c9c4e6dad
commit 8ce7cb4ccd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 33 additions and 60 deletions

View File

@ -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 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_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 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 spare10 : 1; // bit 10
uint32_t spare11 : 1; // bit 11 uint32_t spare11 : 1; // bit 11
uint32_t spare12 : 1; // bit 12 uint32_t spare12 : 1; // bit 12
@ -778,8 +778,9 @@ typedef struct {
int8_t temp_comp; // E9E int8_t temp_comp; // E9E
uint8_t weight_change; // E9F uint8_t weight_change; // E9F
uint8_t web_color2[2][3]; // EA0 Needs to be on integer / 3 distance from web_color 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 shift595_device_count; // EC6
uint8_t sta_config; // EC7 uint8_t sta_config; // EC7

View File

@ -24,9 +24,6 @@
#define XDRV_68 68 #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; static const uint8_t TRIGGER_PERIOD = 75;
#define ZCDIMMERSET_SHOW 1 #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 uint32_t crossed_zero_at; // Time (in micros()) of last ZC signal
bool timer_iterrupt_started = false; // verification of the interrupt running 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 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 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 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. 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 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 intr_counter = 0; // counter internally on interrerupt calls
uint32_t missed_zero_cross; // count up all missed Zero-cross events. 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 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; ac_zero_cross_dimmer.crossed_zero_at = time;
for (uint8_t i=0; i < MAX_PWMS; i++) { for (uint8_t i=0; i < MAX_PWMS; i++) {
if (Pin(GPIO_PWM1, i) == -1) continue; 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; 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; 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) void ACDimmerInterruptDisable(bool disable)
{ {
AddLog(LOG_LEVEL_INFO, PSTR("ZCD: Zero-CrossDimmer enabled: %d"),!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++; ac_zero_cross_dimmer.intr_counter++;
// Check for missed Zero-Cross event. Single failure will correct // Check for missed Zero-Cross event. Single failure will correct
if (time_since_zc > 10100) { 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.crossed_zero_at += ac_zero_cross_dimmer.cycle_time_us;
ac_zero_cross_dimmer.missed_zero_cross++; ac_zero_cross_dimmer.missed_zero_cross++;
time_since_zc += ac_zero_cross_dimmer.cycle_time_us; 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; ac_zero_cross_dimmer.actual_tigger_Period = TRIGGER_PERIOD;
for (uint8_t i = 0 ; i < MAX_PWMS; i++ ) { for (uint8_t i = 0 ; i < MAX_PWMS; i++ ) {
if (Pin(GPIO_PWM1, i) == -1) continue; if (Pin(GPIO_PWM1, i) == -1) continue;
switch (ac_zero_cross_dimmer.current_state_in_phase[i]) {
case 1: if (time_since_zc + TRIGGER_PERIOD >= ac_zero_cross_dimmer.enable_time_us[i]){
// Switch off does not need high accuracy. Happens at the next 75µs trigger // Very close to the fire event. Loop the last µseconds to wait.
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.
#ifdef ESP8266 #ifdef ESP8266
// on ESP8266 we can change dynamically the trigger interval // 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)); 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 #endif
#ifdef ESP32 #ifdef ESP32
while (time_since_zc < ac_zero_cross_dimmer.enable_time_us[i]) { while (time_since_zc < ac_zero_cross_dimmer.enable_time_us[i]) {
time_since_zc = micros() - ac_zero_cross_dimmer.crossed_zero_at; time_since_zc = micros() - ac_zero_cross_dimmer.crossed_zero_at;
} }
#endif #endif
} if (time_since_zc >= ac_zero_cross_dimmer.enable_time_us[i]) {
if (time_since_zc >= ac_zero_cross_dimmer.enable_time_us[i]) { digitalWrite(Pin(GPIO_PWM1, i), HIGH ^ ac_zero_cross_dimmer.fallingEdgeDimmer );
digitalWrite(Pin(GPIO_PWM1, i), HIGH); #ifdef ZC_DEBUG
ac_zero_cross_dimmer.current_state_in_phase[i]++; ac_zero_cross_dimmer.accurracy[i] = time_since_zc-ac_zero_cross_dimmer.enable_time_us[i];
#ifdef ZC_DEBUG #endif
ac_zero_cross_dimmer.accurracy[i] = time_since_zc-ac_zero_cross_dimmer.enable_time_us[i]; }
#endif }
}
break;
}
} }
} }
@ -202,8 +183,6 @@ void ACDimmerControllTrigger(void) {
ac_zero_cross_dimmer.enable_time_us[i] = (uint32_t)state; ac_zero_cross_dimmer.enable_time_us[i] = (uint32_t)state;
} }
#endif #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++){ for (uint8_t i = 0; i < MAX_PWMS; i++){
if (Pin(GPIO_PWM1, i) == -1) continue; 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"), 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], ac_zero_cross_dimmer.disable_time_us[i], i+1, ac_zero_cross_dimmer.enable_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], 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] ac_zero_cross_dimmer.accurracy[i]
); );
} }
@ -258,20 +237,12 @@ void CmndZCDimmerSet(void)
{ {
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) {
if (XdrvMailbox.data_len > 0) { 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); 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 * Interface
\*********************************************************************************************/ \*********************************************************************************************/
@ -282,6 +253,7 @@ bool Xdrv68(uint32_t function)
if (Settings->flag4.zerocross_dimmer) { if (Settings->flag4.zerocross_dimmer) {
switch (function) { switch (function) {
case FUNC_INIT: case FUNC_INIT:
ACDimmerInit();
#ifdef ESP32 #ifdef ESP32
//ACDimmerInterruptDisable(false); //ACDimmerInterruptDisable(false);
#endif #endif