mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-23 10:46:31 +00:00
Worked on ESP32 dimmer with Zero cross (#18481)
* Worked on ESP32 dimmer with Zero cross Until now the ESP32 does not support zero-cross dimmer. I take a sneak how they did in in ESPhome and adapted the approach to TASMOTA. At the end it works that smooth that likely i will change ESP8266 either so we have a common code. Currently ESP8266 is not touched. There is a minor issue with savedata == default. When changing the dimmer value the interrupts get stopped during write of the config data to flash. * ESP8266 Dimmer added Worked all so well and the code is much smaller. There is no need for reconfiguration on existing users. But there are settings not needed anymore. Will work on the documentation. Anyhow existing installations can upgrade without hickup * Optimized endpoints at dimmer 0 and 100 * Removed debug stuff * Fix Issue at dimmer = 0 * Small bugfix * Final checked Version * Update xsns_01_counter.ino * Add missing func * Update xsns_01_counter.ino * Moved out of the house of counter and build my own one * New ZeroCross Driver * Update xdrv_91_zerocrossDimmer.ino * evolving * Delete xdrv_91_zerocrossDimmer.ino * Add files via upload * Changed drv number from 1 to 68 * Commit to merge
This commit is contained in:
parent
fba15dea71
commit
39abde2583
@ -45,6 +45,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
||||
extern "C" void resetPins();
|
||||
extern "C" int startWaveformClockCycles(uint8_t pin, uint32_t highCcys, uint32_t lowCcys,
|
||||
uint32_t runTimeCcys, int8_t alignPhase, uint32_t phaseOffsetCcys, bool autoPwm);
|
||||
extern "C" void setTimer1Callback(uint32_t (*fn)());
|
||||
#ifdef USE_SERIAL_BRIDGE
|
||||
void SerialBridgePrintf(PGM_P formatP, ...);
|
||||
#endif
|
||||
|
@ -40,7 +40,7 @@ const gamma_table_t ac_dimmer_table[] = { // don't put in PROGMEM for performa
|
||||
{ 900, 704 },
|
||||
{ 950, 748 },
|
||||
{ 990, 850 },
|
||||
{ 1024, 1024 },
|
||||
{ 1023, 1023 },
|
||||
{ 0xFFFF, 0xFFFF } // fail-safe if out of range
|
||||
};
|
||||
|
||||
|
276
tasmota/tasmota_xdrv_driver/xdrv_68_zerocrossDimmer.ino
Normal file
276
tasmota/tasmota_xdrv_driver/xdrv_68_zerocrossDimmer.ino
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
xdrv_68_zerocrossdimmer.ino - Zero-Cross Dimmer support for Tasmota
|
||||
|
||||
Copyright (C) 2023 Stefan Bode
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_AC_ZERO_CROSS_DIMMER
|
||||
/*********************************************************************************************\
|
||||
* Zero-Cross AC Dimmer PMM 1..xx use
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XDRV_68 68
|
||||
|
||||
static const uint32_t GATE_ENABLE_TIME = 100;
|
||||
|
||||
struct AC_ZERO_CROSS_DIMMER {
|
||||
uint32_t cycle_time_us;
|
||||
/// Time (in micros()) of last ZC signal
|
||||
uint32_t crossed_zero_at;
|
||||
/// Time since last ZC pulse to enable gate pin. 0 means not set.
|
||||
bool timer_iterrupt_started = false;
|
||||
bool dimmer_in_use = false;
|
||||
// Check if 50µs timer is running.
|
||||
uint32_t enable_time_us[MAX_PWMS];
|
||||
/// Time since last ZC pulse to disable gate pin. 0 means no disable.
|
||||
uint32_t disable_time_us[MAX_PWMS];
|
||||
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];
|
||||
uint16_t detailpower[MAX_PWMS]; // replaces dimmer and light controll 0..10000. required savedata 0.
|
||||
uint32_t intr_counter = 0;
|
||||
} ac_zero_cross_dimmer;
|
||||
|
||||
|
||||
#ifdef ESP32
|
||||
static hw_timer_t *dimmer_timer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
#endif
|
||||
|
||||
#define D_PRFX_ZCDIMMER "ZCDimmer"
|
||||
#define D_CMND_DIMMERSET "Set"
|
||||
|
||||
const char kZCDimmerCommands[] PROGMEM = D_PRFX_ZCDIMMER "|" D_CMND_DIMMERSET;
|
||||
|
||||
void (* const ZCDimmerCommand[])(void) PROGMEM = {
|
||||
&CmndZCDimmerSet
|
||||
};
|
||||
|
||||
void IRAM_ATTR ACDimmerZeroCross(uint32_t time) {
|
||||
ac_zero_cross_dimmer.dimmer_in_use = false;
|
||||
ac_zero_cross_dimmer.cycle_time_us = time - ac_zero_cross_dimmer.crossed_zero_at;
|
||||
ac_zero_cross_dimmer.crossed_zero_at = time;
|
||||
for (uint8_t i=0; i < MAX_PWMS; i++) {
|
||||
if (Pin(GPIO_PWM1, i) == -1) continue;
|
||||
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] > 95 * 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] < 15 * ac_zero_cross_dimmer.cycle_time_us) {
|
||||
ac_zero_cross_dimmer.current_state_in_phase[i] = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t IRAM_ATTR ACDimmerTimer_intr_ESP8266() {
|
||||
//ACDimmerTimer_intr();
|
||||
ACDimmerTimer_intr();
|
||||
return 4000;
|
||||
}
|
||||
|
||||
void ACDimmerInterruptDisable(bool disable)
|
||||
{
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("ZCD: Zero-CrossDimmer enabled: %d"),!disable);
|
||||
ac_zero_cross_dimmer.timer_iterrupt_started = !disable;
|
||||
if (disable) {
|
||||
//stop the interrupt
|
||||
#ifdef ESP32
|
||||
if (dimmer_timer != nullptr) {
|
||||
timerAlarmDisable(dimmer_timer);
|
||||
}
|
||||
#endif
|
||||
#ifdef ESP8266
|
||||
//setTimer1Callback(NULL);
|
||||
#endif
|
||||
} else {
|
||||
for (uint8_t i = 0 ; i < MAX_PWMS; i++) {
|
||||
if (Pin(GPIO_PWM1, i) != -1) {
|
||||
pinMode(Pin(GPIO_PWM1, i), OUTPUT);
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("ZCD: Zero-CrossDimmer Pin %d set"),Pin(GPIO_PWM1, i));
|
||||
}
|
||||
}
|
||||
#ifdef ESP32
|
||||
if (dimmer_timer == nullptr) {
|
||||
// 80 Divider -> 1 count=1µs
|
||||
dimmer_timer = timerBegin(0, 80, true);
|
||||
timerAttachInterrupt(dimmer_timer, &ACDimmerTimer_intr, true);
|
||||
// For ESP32, we can't use dynamic interval calculation because the timerX functions
|
||||
// are not callable from ISR (placed in flash storage).
|
||||
// Here we just use an interrupt firing every 75 µs.
|
||||
if (Settings->save_data == 0) {
|
||||
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);
|
||||
#endif
|
||||
|
||||
#ifdef ESP8266
|
||||
// Uses ESP8266 waveform (soft PWM) class
|
||||
// PWM and AcDimmer can even run at the same time this way
|
||||
//setTimer1Callback(&ACDimmerTimer_intr_ESP8266);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR ACDimmerTimer_intr() {
|
||||
// If no ZC signal received yet.
|
||||
uint32_t now = micros();
|
||||
ac_zero_cross_dimmer.intr_counter++;
|
||||
if (ac_zero_cross_dimmer.crossed_zero_at == 0)
|
||||
return;
|
||||
|
||||
uint32_t time_since_zc = now - ac_zero_cross_dimmer.crossed_zero_at;
|
||||
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;
|
||||
time_since_zc = now - ac_zero_cross_dimmer.crossed_zero_at;
|
||||
}
|
||||
|
||||
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:
|
||||
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 >= ac_zero_cross_dimmer.enable_time_us[i]) {
|
||||
digitalWrite(Pin(GPIO_PWM1, i), HIGH);
|
||||
ac_zero_cross_dimmer.current_state_in_phase[i]++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ACDimmerControllTrigger(void) {
|
||||
|
||||
if (ac_zero_cross_dimmer.timer_iterrupt_started != ac_zero_cross_dimmer.dimmer_in_use) {
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ZCD: ZeroEnable %d --> %d ... change..."),ac_zero_cross_dimmer.timer_iterrupt_started, ac_zero_cross_dimmer.dimmer_in_use);
|
||||
ACDimmerInterruptDisable(!ac_zero_cross_dimmer.dimmer_in_use);
|
||||
}
|
||||
for (uint8_t i = 0; i < MAX_PWMS; i++){
|
||||
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];
|
||||
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
|
||||
if (ac_zero_cross_dimmer.detailpower[i]){
|
||||
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 * 10000;
|
||||
//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.lastlight[i] = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
ac_zero_cross_dimmer.disable_time_us[i] = ac_zero_cross_dimmer.enable_time_us[i] + GATE_ENABLE_TIME;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ACDimmerLogging(void)
|
||||
{
|
||||
bool alarmEnabled = false;
|
||||
uint32_t timercounter = ac_zero_cross_dimmer.intr_counter;
|
||||
|
||||
#ifdef ESP32
|
||||
if (dimmer_timer != nullptr) {
|
||||
alarmEnabled = timerAlarmEnabled(dimmer_timer);
|
||||
timercounter = (uint32_t)timerRead(dimmer_timer);
|
||||
}
|
||||
#endif
|
||||
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("ZCD: ZeroEnable %d -> %d, Alarm %d, intr: %ld, cycle time: %ld µs"),
|
||||
ac_zero_cross_dimmer.dimmer_in_use, ac_zero_cross_dimmer.timer_iterrupt_started, alarmEnabled, timercounter, ac_zero_cross_dimmer.cycle_time_us
|
||||
);
|
||||
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"),
|
||||
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]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
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));
|
||||
}
|
||||
ResponseCmndIdxFloat((float)(ac_zero_cross_dimmer.detailpower[XdrvMailbox.index-1]) / 100, 2);
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xdrv68(uint32_t function)
|
||||
{
|
||||
bool result = false;
|
||||
if (Settings->flag4.zerocross_dimmer) {
|
||||
switch (function) {
|
||||
case FUNC_INIT:
|
||||
#ifdef ESP32
|
||||
//ACDimmerInterruptDisable(false);
|
||||
#endif
|
||||
#ifdef ESP8266
|
||||
setTimer1Callback(&ACDimmerTimer_intr_ESP8266);
|
||||
#endif
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
ACDimmerLogging();
|
||||
break;
|
||||
case FUNC_EVERY_100_MSECOND:
|
||||
ACDimmerControllTrigger();
|
||||
break;
|
||||
case FUNC_INTERRUPT_STOP:
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ZCD: FUNC_INTERRUPT_STOP"));
|
||||
ACDimmerInterruptDisable(true);
|
||||
break;
|
||||
case FUNC_INTERRUPT_START:
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ZCD: FUNC_INTERRUPT_START"));
|
||||
ACDimmerInterruptDisable(false);
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kZCDimmerCommands, ZCDimmerCommand);
|
||||
//result = DecodeCommand(kShutterCommands, ShutterCommand);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_AC_ZERO_CROSS_DIMMER
|
@ -2,7 +2,6 @@
|
||||
xsns_01_counter.ino - Counter sensors (water meters, electricity meters etc.) sensor support for Tasmota
|
||||
|
||||
Copyright (C) 2021 Maarten Damen and Theo Arends
|
||||
Stefan Bode (Zero-Cross Dimmer)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -48,22 +47,6 @@ struct COUNTER {
|
||||
|
||||
} Counter;
|
||||
|
||||
#ifdef USE_AC_ZERO_CROSS_DIMMER
|
||||
struct AC_ZERO_CROSS_DIMMER {
|
||||
bool startReSync = false; // set to TRUE if zero-cross event occurs
|
||||
bool startMeasurePhase[MAX_COUNTERS] ; // set to TRUE if channel is ON and zero-cross occurs to initiate phase measure on channel
|
||||
bool pwm_defined[MAX_COUNTERS]; // check if all GPIO are set and zerocross enabled. Then ADD dimmer.
|
||||
bool PWM_ON[MAX_COUNTERS] ; // internal ON/OFF of the channel
|
||||
uint32_t current_cycle_ClockCycles = 0; // amount of clock cycles between two zero-cross events.
|
||||
uint32_t currentPWMCycleCount[MAX_COUNTERS] ; // clock cycle time of PWM channel, required to measure actual phase. [3] is phase of zero-cross
|
||||
int16_t currentShiftClockCycle[MAX_COUNTERS]; // dynamic phase correction per channel in clock cycles
|
||||
uint32_t tobe_cycle_timeClockCycles = 0; // clock cycles between zero-cross events. Depend on main frequency and CPU speed
|
||||
uint32_t lastCycleCount = 0; // Last value of GetCycleCount during zero-cross sychronisation
|
||||
uint32_t currentSteps = 100; // dynamic value of zero-crosses between two sychronisation intervalls (default=20 == 200ms at 100Hz)
|
||||
uint32_t high; // cycle counts for PWM high vaule. needs long enough (4µs) to secure fire TRIAC
|
||||
} ac_zero_cross_dimmer;
|
||||
#endif //USE_AC_ZERO_CROSS_DIMMER
|
||||
|
||||
void IRAM_ATTR CounterIsrArg(void *arg) {
|
||||
uint32_t index = *static_cast<uint8_t*>(arg);
|
||||
|
||||
@ -93,27 +76,7 @@ void IRAM_ATTR CounterIsrArg(void *arg) {
|
||||
// restart PWM each second (german 50Hz has to up to 0.01% deviation)
|
||||
// restart initiated by setting Counter.startReSync = true;
|
||||
#ifdef USE_AC_ZERO_CROSS_DIMMER
|
||||
// if zero-cross events occur ond channel is on. phase on PWM must be measured
|
||||
if ( ac_zero_cross_dimmer.startMeasurePhase[index] == true ) {
|
||||
ac_zero_cross_dimmer.currentPWMCycleCount[index] = ESP.getCycleCount();
|
||||
ac_zero_cross_dimmer.startMeasurePhase[index] = false;
|
||||
}
|
||||
// if zero-cross event occurs (200ms window, 5-times a second) and device is online for >10sec
|
||||
if (index == 3 && RtcSettings.pulse_counter[index]%(Settings->pwm_frequency / 5) == 0 && ac_zero_cross_dimmer.pwm_defined[index] && millis() > 10000) {
|
||||
ac_zero_cross_dimmer.currentPWMCycleCount[index] = ESP.getCycleCount();
|
||||
|
||||
if (ac_zero_cross_dimmer.lastCycleCount > 0) {
|
||||
// start phase measure on PWM channels and initiate phase sync with zero-cross.
|
||||
ac_zero_cross_dimmer.startReSync = true;
|
||||
for (uint8_t k=0; k < MAX_COUNTERS-1; k++ ) {
|
||||
if (ac_zero_cross_dimmer.PWM_ON[k] == true) ac_zero_cross_dimmer.startMeasurePhase[k] = true;
|
||||
}
|
||||
ac_zero_cross_dimmer.currentSteps = (ac_zero_cross_dimmer.currentPWMCycleCount[index]-ac_zero_cross_dimmer.lastCycleCount+(ac_zero_cross_dimmer.tobe_cycle_timeClockCycles/2))/(ac_zero_cross_dimmer.tobe_cycle_timeClockCycles);
|
||||
ac_zero_cross_dimmer.current_cycle_ClockCycles = (ac_zero_cross_dimmer.currentPWMCycleCount[index]-ac_zero_cross_dimmer.lastCycleCount)/ac_zero_cross_dimmer.currentSteps;
|
||||
}
|
||||
ac_zero_cross_dimmer.lastCycleCount = ac_zero_cross_dimmer.currentPWMCycleCount[index];
|
||||
}
|
||||
|
||||
if (index == 3) ACDimmerZeroCross(time);
|
||||
#endif //USE_AC_ZERO_CROSS_DIMMER
|
||||
return;
|
||||
}
|
||||
@ -162,26 +125,9 @@ bool CounterPinState(void)
|
||||
|
||||
void CounterInit(void)
|
||||
{
|
||||
|
||||
for (uint32_t i = 0; i < MAX_COUNTERS; i++) {
|
||||
if (PinUsed(GPIO_CNTR1, i)) {
|
||||
#ifdef USE_AC_ZERO_CROSS_DIMMER
|
||||
if (Settings->flag4.zerocross_dimmer) {
|
||||
ac_zero_cross_dimmer.current_cycle_ClockCycles = ac_zero_cross_dimmer.tobe_cycle_timeClockCycles = microsecondsToClockCycles(1000000 / Settings->pwm_frequency);
|
||||
// short fire on PWM to ensure not to hit next sinus curve but trigger the TRIAC. 0.78% of duty cycle (10ms) ~4µs
|
||||
ac_zero_cross_dimmer.high = ac_zero_cross_dimmer.current_cycle_ClockCycles / 256;
|
||||
|
||||
// Support for dimmer 1-3. Counter4 reseverd for zero-cross signal
|
||||
if ((i < MAX_COUNTERS-1 && PinUsed(GPIO_PWM1, i)) || ( i == MAX_COUNTERS-1) ) {
|
||||
ac_zero_cross_dimmer.pwm_defined[i] = true;
|
||||
if (i == 3) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("ZeroCross initialized"));
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("Dimmer: [%d] initialized. READY. Dimmer %d"), i+1, Light.fade_running ? Light.fade_cur_10[i] : Light.fade_start_10[i]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif //USE_AC_ZERO_CROSS_DIMMER
|
||||
Counter.any_counter = true;
|
||||
pinMode(Pin(GPIO_CNTR1, i), bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP);
|
||||
if ((0 == Settings->pulse_counter_debounce_low) && (0 == Settings->pulse_counter_debounce_high) && !Settings->flag4.zerocross_dimmer) {
|
||||
@ -260,78 +206,6 @@ void CounterShow(bool json)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_AC_ZERO_CROSS_DIMMER
|
||||
void SyncACDimmer(void)
|
||||
{
|
||||
if (ac_zero_cross_dimmer.startReSync ) {
|
||||
// currently only support one AC Dimmer PWM. Plan to support up to 4 Dimmer on same Phase.
|
||||
for (uint32_t i = 0; i < MAX_COUNTERS-1; i++) {
|
||||
if (Light.fade_start_10[i] == 0 && Light.fade_cur_10[i] == 0 && ac_zero_cross_dimmer.PWM_ON[i]==false ) continue;
|
||||
if (ac_zero_cross_dimmer.pwm_defined[i] && (ac_zero_cross_dimmer.startMeasurePhase[i] == 0 || ac_zero_cross_dimmer.PWM_ON[i] == false ) )
|
||||
{
|
||||
uint32_t phaseStart_ActualClockCycles; // As-Is positon of PWM after Zero Cross
|
||||
uint32_t phaseStart_ToBeClockCycles; // To be position after zero-cross to fire PWM start
|
||||
int16_t phaseShift_ClockCycles; //
|
||||
|
||||
|
||||
// reset trigger for PWM sync
|
||||
ac_zero_cross_dimmer.startReSync = false;
|
||||
// calculate timeoffset to fire PWM based on Dimmer
|
||||
phaseStart_ToBeClockCycles = (ac_zero_cross_dimmer.tobe_cycle_timeClockCycles * (1024 - ac_zero_cross_power(Light.fade_running ? Light.fade_cur_10[i] : Light.fade_start_10[i]))) / 1024;
|
||||
|
||||
// Limit range to avoid overshoot and undershoot
|
||||
phaseStart_ToBeClockCycles = tmin(tmax(phaseStart_ToBeClockCycles, 160000), 0.95* ac_zero_cross_dimmer.tobe_cycle_timeClockCycles);
|
||||
|
||||
// Switch OFF dimmer
|
||||
if (Light.fade_start_10[i] == 0 && !Light.fade_running) {
|
||||
ac_zero_cross_dimmer.PWM_ON[i]=false;
|
||||
Light.fade_cur_10[i] = 0;
|
||||
digitalWrite(Pin(GPIO_PWM1, i), LOW);
|
||||
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CNT2: [%d], curr: %d, final: %d, fading: %d, phase-shift: %d, ON/OFF: %d"),i, Light.fade_cur_10[i], Light.fade_start_10[i], Light.fade_running, phaseStart_ToBeClockCycles,ac_zero_cross_dimmer.PWM_ON[i]);
|
||||
continue;
|
||||
}
|
||||
// Calculyte clockcycles between zero-cross [3] and start of the current PWM signal [i]
|
||||
phaseStart_ActualClockCycles = ac_zero_cross_dimmer.currentPWMCycleCount[i]-ac_zero_cross_dimmer.currentPWMCycleCount[3];
|
||||
|
||||
// Calulate additional or less clockcycles to move current phase position to should be position
|
||||
phaseShift_ClockCycles = (int32_t)((int32_t)phaseStart_ToBeClockCycles-(int32_t)phaseStart_ActualClockCycles)/100;
|
||||
|
||||
if ( ac_zero_cross_dimmer.PWM_ON[i] == 0 ) {
|
||||
// because in LOOP calculate the timelag to fire PWM correctly with zero-cross
|
||||
uint32_t timelag_ClockCycles = (ESP.getCycleCount() - ac_zero_cross_dimmer.currentPWMCycleCount[3])%ac_zero_cross_dimmer.tobe_cycle_timeClockCycles;
|
||||
timelag_ClockCycles = ((phaseStart_ToBeClockCycles + ac_zero_cross_dimmer.tobe_cycle_timeClockCycles) - timelag_ClockCycles)%ac_zero_cross_dimmer.tobe_cycle_timeClockCycles;
|
||||
|
||||
delayMicroseconds(clockCyclesToMicroseconds(timelag_ClockCycles));
|
||||
ac_zero_cross_dimmer.PWM_ON[i]=true;
|
||||
pinMode(Pin(GPIO_PWM1, i), OUTPUT);
|
||||
} else {
|
||||
// currentShiftClockCycle is an I-Controller (not PID) to realign the phase. grace time are 5 clock cycles
|
||||
ac_zero_cross_dimmer.currentShiftClockCycle[i] += phaseShift_ClockCycles > 5 ? 1 : (phaseShift_ClockCycles < -5 ? -1 : 0);
|
||||
ac_zero_cross_dimmer.current_cycle_ClockCycles += ac_zero_cross_dimmer.currentShiftClockCycle[i]+phaseShift_ClockCycles;
|
||||
}
|
||||
#ifdef ESP8266
|
||||
// Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t
|
||||
startWaveformClockCycles(Pin(GPIO_PWM1, i), ac_zero_cross_dimmer.high, ac_zero_cross_dimmer.current_cycle_ClockCycles - ac_zero_cross_dimmer.high, 0, -1, 0, true);
|
||||
#endif // ESP8266
|
||||
#ifdef ESP32
|
||||
// Under investigation. Still not working
|
||||
double esp32freq = 1000000.0 / clockCyclesToMicroseconds(ac_zero_cross_dimmer.current_cycle_ClockCycles);
|
||||
ledcSetup(i, esp32freq, 10);
|
||||
ledcAttachPin(Pin(GPIO_PWM1, i), i);
|
||||
ledcWrite(i, 5);
|
||||
|
||||
#endif // ESP32
|
||||
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CNT: [%d], shift: %d, dimm_time_CCs %d, phaseShift_CCs %d, currentPWMcylce: %lu, current_cycle_CC: %lu, lastcc %lu, currentSteps %lu, currDIM %lu, last delta:%lu"),
|
||||
i, ac_zero_cross_dimmer.currentShiftClockCycle[i], phaseStart_ToBeClockCycles,phaseShift_ClockCycles,ac_zero_cross_dimmer.currentPWMCycleCount[i],ac_zero_cross_dimmer.current_cycle_ClockCycles , ac_zero_cross_dimmer.lastCycleCount, ac_zero_cross_dimmer.currentSteps, Light.fade_cur_10[i],phaseStart_ActualClockCycles); // Light fading
|
||||
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CNT: [%d], curr: %d, final: %d, fading: %d, phase-shift: %d, ON/OFF: %d"),i, Light.fade_cur_10[i], Light.fade_start_10[i], Light.fade_running, phaseStart_ToBeClockCycles,ac_zero_cross_dimmer.PWM_ON[i]);
|
||||
|
||||
} // do sync onchannel
|
||||
} // loop on counter
|
||||
} // zero cross detected
|
||||
} // end SyncACDimmer
|
||||
#endif //USE_AC_ZERO_CROSS_DIMMER
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Commands
|
||||
\*********************************************************************************************/
|
||||
@ -406,11 +280,6 @@ bool Xsns01(uint32_t function)
|
||||
case FUNC_JSON_APPEND:
|
||||
CounterShow(1);
|
||||
break;
|
||||
#ifdef USE_AC_ZERO_CROSS_DIMMER
|
||||
case FUNC_EVERY_50_MSECOND:
|
||||
SyncACDimmer();
|
||||
break;
|
||||
#endif //USE_AC_ZERO_CROSS_DIMMER
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
CounterShow(0);
|
||||
|
Loading…
x
Reference in New Issue
Block a user