mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-25 11:46:31 +00:00
Enabled ZCDimmerSet for ESP8266 (#18518)
* Enabled ZCDimmerSet for ESP8266 - Increased interrupts time to get stablity. Savedata issue is gone. - Increased accuracy to trigger an on sinus curve. - Enabled high resolution trigger ZCDimmerSet for ESP8266 * Update xdrv_68_zerocrossDimmer.ino
This commit is contained in:
parent
6702321784
commit
60855bf367
@ -24,23 +24,25 @@
|
|||||||
|
|
||||||
#define XDRV_68 68
|
#define XDRV_68 68
|
||||||
|
|
||||||
static const uint32_t GATE_ENABLE_TIME = 100;
|
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;
|
||||||
|
|
||||||
struct AC_ZERO_CROSS_DIMMER {
|
struct AC_ZERO_CROSS_DIMMER {
|
||||||
uint32_t cycle_time_us;
|
uint32_t cycle_time_us; // Time (in micros()) of last ZC signal
|
||||||
/// Time (in micros()) of last ZC signal
|
uint32_t crossed_zero_at; // Time (in micros()) of last ZC signal
|
||||||
uint32_t crossed_zero_at;
|
bool timer_iterrupt_started = false; // verification of the interrupt running
|
||||||
/// Time since last ZC pulse to enable gate pin. 0 means not set.
|
bool dimmer_in_use = false; // Check if interrupt has to be run. Is stopped if all lights off
|
||||||
bool timer_iterrupt_started = false;
|
uint32_t enable_time_us[MAX_PWMS]; // Time since last ZC pulse to enable gate pin. 0 means no disable.
|
||||||
bool dimmer_in_use = false;
|
uint32_t disable_time_us[MAX_PWMS]; // Time since last ZC pulse to disable gate pin. 0 means no disable.
|
||||||
// Check if 50µs timer is running.
|
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 enable_time_us[MAX_PWMS];
|
uint32_t lastlight[MAX_PWMS]; // Store the light value. Set 1 if controlled through ZCDimmerSet
|
||||||
/// Time since last ZC pulse to disable gate pin. 0 means no disable.
|
uint16_t detailpower[MAX_PWMS]; // replaces dimmer and light controll 0..10000. required savedata 0.
|
||||||
uint32_t disable_time_us[MAX_PWMS];
|
uint32_t accurracy[MAX_PWMS]; // offset of the time to fire the triac and the real time when it fired
|
||||||
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)
|
uint8_t triggertime = GATE_ENABLE_TIME; // copy of the Time for the gate keep open to start TRIAC
|
||||||
uint32_t lastlight[MAX_PWMS];
|
uint32_t intr_counter = 0; // counter internally on interrerupt calls
|
||||||
uint16_t detailpower[MAX_PWMS]; // replaces dimmer and light controll 0..10000. required savedata 0.
|
uint32_t missed_zero_cross; // count up all missed Zero-cross events.
|
||||||
uint32_t intr_counter = 0;
|
|
||||||
} ac_zero_cross_dimmer;
|
} ac_zero_cross_dimmer;
|
||||||
|
|
||||||
|
|
||||||
@ -49,12 +51,13 @@ struct AC_ZERO_CROSS_DIMMER {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define D_PRFX_ZCDIMMER "ZCDimmer"
|
#define D_PRFX_ZCDIMMER "ZCDimmer"
|
||||||
#define D_CMND_DIMMERSET "Set"
|
#define D_CMND_DIMMERSET "Set" // Set percent as float value for dimmer
|
||||||
|
#define D_CMND_GATEENABLE "GateTime" // Override the GATE ON time DEBUG PURPOSE
|
||||||
|
|
||||||
const char kZCDimmerCommands[] PROGMEM = D_PRFX_ZCDIMMER "|" D_CMND_DIMMERSET;
|
const char kZCDimmerCommands[] PROGMEM = D_PRFX_ZCDIMMER "|" D_CMND_DIMMERSET "|" D_CMND_GATEENABLE;
|
||||||
|
|
||||||
void (* const ZCDimmerCommand[])(void) PROGMEM = {
|
void (* const ZCDimmerCommand[])(void) PROGMEM = {
|
||||||
&CmndZCDimmerSet
|
&CmndZCDimmerSet//,&CmndZCGateEnableTime
|
||||||
};
|
};
|
||||||
|
|
||||||
void IRAM_ATTR ACDimmerZeroCross(uint32_t time) {
|
void IRAM_ATTR ACDimmerZeroCross(uint32_t time) {
|
||||||
@ -66,12 +69,12 @@ void IRAM_ATTR ACDimmerZeroCross(uint32_t time) {
|
|||||||
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
|
// Dimmer is physically off. Skip swich on
|
||||||
ac_zero_cross_dimmer.current_state_in_phase[i] = 0;
|
ac_zero_cross_dimmer.current_state_in_phase[i] = 0;
|
||||||
if (100 * ac_zero_cross_dimmer.enable_time_us[i] > 95 * ac_zero_cross_dimmer.cycle_time_us ) {
|
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.current_state_in_phase[i] = 1;
|
||||||
ac_zero_cross_dimmer.disable_time_us[i] = ac_zero_cross_dimmer.cycle_time_us / 2;
|
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 full cycle is required keep pin HIGH, skip LOW by skipping phase
|
||||||
if (100 * ac_zero_cross_dimmer.enable_time_us[i] < 15 * ac_zero_cross_dimmer.cycle_time_us) {
|
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;
|
ac_zero_cross_dimmer.current_state_in_phase[i] = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,13 +115,7 @@ void ACDimmerInterruptDisable(bool disable)
|
|||||||
// For ESP32, we can't use dynamic interval calculation because the timerX functions
|
// For ESP32, we can't use dynamic interval calculation because the timerX functions
|
||||||
// are not callable from ISR (placed in flash storage).
|
// are not callable from ISR (placed in flash storage).
|
||||||
// Here we just use an interrupt firing every 75 µs.
|
// Here we just use an interrupt firing every 75 µs.
|
||||||
if (Settings->save_data == 0) {
|
timerAlarmWrite(dimmer_timer, TRIGGER_PERIOD , true);
|
||||||
AddLog(LOG_LEVEL_INFO, PSTR("ZCD: Save disabled. High frequency scan enabled. DO NOT USE SAVEDATA if channel is on"));
|
|
||||||
timerAlarmWrite(dimmer_timer, 30, true);
|
|
||||||
} else {
|
|
||||||
AddLog(LOG_LEVEL_INFO, PSTR("ZCD: Save on. 75µs scan enabled"));
|
|
||||||
timerAlarmWrite(dimmer_timer, 75, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
timerAlarmEnable(dimmer_timer);
|
timerAlarmEnable(dimmer_timer);
|
||||||
#endif
|
#endif
|
||||||
@ -139,9 +136,12 @@ void IRAM_ATTR ACDimmerTimer_intr() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
uint32_t time_since_zc = now - ac_zero_cross_dimmer.crossed_zero_at;
|
uint32_t time_since_zc = now - ac_zero_cross_dimmer.crossed_zero_at;
|
||||||
|
|
||||||
|
// 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));
|
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++;
|
||||||
time_since_zc = now - ac_zero_cross_dimmer.crossed_zero_at;
|
time_since_zc = now - ac_zero_cross_dimmer.crossed_zero_at;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +149,7 @@ void IRAM_ATTR ACDimmerTimer_intr() {
|
|||||||
if (Pin(GPIO_PWM1, i) == -1) continue;
|
if (Pin(GPIO_PWM1, i) == -1) continue;
|
||||||
switch (ac_zero_cross_dimmer.current_state_in_phase[i]) {
|
switch (ac_zero_cross_dimmer.current_state_in_phase[i]) {
|
||||||
case 1:
|
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]) {
|
if (time_since_zc >= ac_zero_cross_dimmer.disable_time_us[i]) {
|
||||||
digitalWrite(Pin(GPIO_PWM1, i), LOW);
|
digitalWrite(Pin(GPIO_PWM1, i), LOW);
|
||||||
ac_zero_cross_dimmer.current_state_in_phase[i]++;
|
ac_zero_cross_dimmer.current_state_in_phase[i]++;
|
||||||
@ -156,9 +157,17 @@ void IRAM_ATTR ACDimmerTimer_intr() {
|
|||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
case 3:
|
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.
|
||||||
|
while (time_since_zc < ac_zero_cross_dimmer.enable_time_us[i]) {
|
||||||
|
now = micros();
|
||||||
|
time_since_zc = now - ac_zero_cross_dimmer.crossed_zero_at;
|
||||||
|
}
|
||||||
|
}
|
||||||
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);
|
digitalWrite(Pin(GPIO_PWM1, i), HIGH);
|
||||||
ac_zero_cross_dimmer.current_state_in_phase[i]++;
|
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];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -173,21 +182,25 @@ void ACDimmerControllTrigger(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;
|
||||||
ac_zero_cross_dimmer.lastlight[i] = Light.fade_running ? Light.fade_cur_10[i] : Light.fade_start_10[i];
|
|
||||||
|
if (ac_zero_cross_dimmer.detailpower[i]){
|
||||||
|
ac_zero_cross_dimmer.lastlight[i] = changeUIntScale(ac_zero_cross_dimmer.detailpower[i]/10, 0, 1000, 0, 1023);
|
||||||
|
} else {
|
||||||
|
ac_zero_cross_dimmer.lastlight[i] = Light.fade_running ? Light.fade_cur_10[i] : Light.fade_start_10[i];
|
||||||
|
}
|
||||||
ac_zero_cross_dimmer.enable_time_us[i] = (ac_zero_cross_dimmer.cycle_time_us * (1023 - ac_zero_cross_power(ac_zero_cross_dimmer.lastlight[i]))) / 1023;
|
ac_zero_cross_dimmer.enable_time_us[i] = (ac_zero_cross_dimmer.cycle_time_us * (1023 - ac_zero_cross_power(ac_zero_cross_dimmer.lastlight[i]))) / 1023;
|
||||||
|
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
if (ac_zero_cross_dimmer.detailpower[i]){
|
if (ac_zero_cross_dimmer.detailpower[i]){
|
||||||
float state = (float)(1 - (ac_zero_cross_dimmer.detailpower[i]/10000.0));
|
float state = (float)(1 - (ac_zero_cross_dimmer.detailpower[i]/10000.0));
|
||||||
//state = std::acos(1 - (2 * state)) / 3.14159 * ac_zero_cross_dimmer.cycle_time_us;
|
//state = std::acos(1 - (2 * state)) / 3.14159 * ac_zero_cross_dimmer.cycle_time_us;
|
||||||
state = std::acos(1 - (2 * state)) / 3.14159 * 10000;
|
state = std::acos(1 - (2 * state)) / 3.14159 * ac_zero_cross_dimmer.cycle_time_us;
|
||||||
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ZCD: Float2: %*_f"),0,&state);
|
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ZCD: Float2: %*_f"),0,&state);
|
||||||
ac_zero_cross_dimmer.enable_time_us[i] = (uint32_t)state;
|
ac_zero_cross_dimmer.enable_time_us[i] = (uint32_t)state;
|
||||||
ac_zero_cross_dimmer.lastlight[i] = 1;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ac_zero_cross_dimmer.disable_time_us[i] = ac_zero_cross_dimmer.enable_time_us[i] + GATE_ENABLE_TIME;
|
ac_zero_cross_dimmer.disable_time_us[i] = ac_zero_cross_dimmer.enable_time_us[i] + ac_zero_cross_dimmer.triggertime;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -204,17 +217,18 @@ void ACDimmerLogging(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("ZCD: ZeroEnable %d -> %d, Alarm %d, intr: %ld, cycle time: %ld µs"),
|
AddLog(LOG_LEVEL_DEBUG, PSTR("ZCD: ZeroEnable %d -> %d, Alarm %d, intr: %ld, cycle time: %ld µs, missed zc %ld"),
|
||||||
ac_zero_cross_dimmer.dimmer_in_use, ac_zero_cross_dimmer.timer_iterrupt_started, alarmEnabled, timercounter, ac_zero_cross_dimmer.cycle_time_us
|
ac_zero_cross_dimmer.dimmer_in_use, ac_zero_cross_dimmer.timer_iterrupt_started, alarmEnabled, timercounter,
|
||||||
|
ac_zero_cross_dimmer.cycle_time_us, ac_zero_cross_dimmer.missed_zero_cross
|
||||||
);
|
);
|
||||||
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"),
|
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],
|
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]
|
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],
|
||||||
|
ac_zero_cross_dimmer.accurracy[i]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -232,6 +246,14 @@ void CmndZCDimmerSet(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* void CmndZCGateEnableTime(void)
|
||||||
|
{
|
||||||
|
if (XdrvMailbox.payload > 0) {
|
||||||
|
ac_zero_cross_dimmer.triggertime = XdrvMailbox.payload;
|
||||||
|
}
|
||||||
|
ResponseCmndNumber(ac_zero_cross_dimmer.triggertime);
|
||||||
|
} */
|
||||||
|
|
||||||
/*********************************************************************************************\
|
/*********************************************************************************************\
|
||||||
* Interface
|
* Interface
|
||||||
\*********************************************************************************************/
|
\*********************************************************************************************/
|
||||||
@ -256,21 +278,16 @@ bool Xdrv68(uint32_t function)
|
|||||||
ACDimmerControllTrigger();
|
ACDimmerControllTrigger();
|
||||||
break;
|
break;
|
||||||
case FUNC_INTERRUPT_STOP:
|
case FUNC_INTERRUPT_STOP:
|
||||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ZCD: FUNC_INTERRUPT_STOP"));
|
|
||||||
ACDimmerInterruptDisable(true);
|
ACDimmerInterruptDisable(true);
|
||||||
break;
|
break;
|
||||||
case FUNC_INTERRUPT_START:
|
case FUNC_INTERRUPT_START:
|
||||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ZCD: FUNC_INTERRUPT_START"));
|
|
||||||
ACDimmerInterruptDisable(false);
|
ACDimmerInterruptDisable(false);
|
||||||
break;
|
break;
|
||||||
case FUNC_COMMAND:
|
case FUNC_COMMAND:
|
||||||
result = DecodeCommand(kZCDimmerCommands, ZCDimmerCommand);
|
result = DecodeCommand(kZCDimmerCommands, ZCDimmerCommand);
|
||||||
//result = DecodeCommand(kShutterCommands, ShutterCommand);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // USE_AC_ZERO_CROSS_DIMMER
|
#endif // USE_AC_ZERO_CROSS_DIMMER
|
||||||
|
Loading…
x
Reference in New Issue
Block a user