ZCD and timers for millis and fire/cease triac trigger

This commit is contained in:
Heuideog Yi @ PC RnD1 2026-04-18 08:48:43 +09:00
parent 97e9e68d35
commit 1dd537b5d0
13 changed files with 219 additions and 259 deletions

View File

@ -92,7 +92,8 @@ typedef struct CONFIG_STRUCT {
uint64_t nBLESensorAddr2;
uint8_t nBLESensorAddrBytes2[8];
};
char bExtra[64 - 8 * sizeof(bool) - 4 * sizeof(uint8_t) - 6 * sizeof(float) - sizeof(uint64_t)];
uint8_t ac1ControlMode, ac2ControlMode; // ZCD_CONTROL || PHASE_CONTROL
char bExtra[64 - 8 * sizeof(bool) - 4 * sizeof(uint8_t) - 6 * sizeof(float) - sizeof(uint64_t) - 2 * sizeof(uint8_t)];
// Block 2 - Sensor and TargetTemperature and Himidity
// Offset 64 + 2

View File

@ -10,7 +10,7 @@ void WiFiEvent(WiFiEvent_t event) {
case IP_EVENT_STA_GOT_IP:
DPRINTF("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
g_bWiFiHasBeenConnected = true;
ledcWrite(PIN_LED_WIFI, PWM_FULL * 9 / 10); // LED_OFF
ledcWrite(PIN_LED_WIFI, PWM_FULL * 19 / 20); // LED_OFF
if (!g_bWiFiSetupExecuted) setupPostWiFi(false);
break;
case WIFI_EVENT_STA_DISCONNECTED:
@ -74,7 +74,7 @@ void checkWiFi(unsigned long tickMillis) {
if (status == WL_DISCONNECTED && g_bWiFiHasBeenConnected) {
DPRINTLN("Attempting WiFi reconnection...");
WiFi.reconnect();
ledcWrite(PIN_LED_WIFI, PWM_FULL * 4 / 5); // Light Blink
ledcWrite(PIN_LED_WIFI, PWM_FULL * 19 / 20); // Light Blink
lastAttempt = tickMillis;
bConnecting = true;
}
@ -93,7 +93,7 @@ void checkWiFi(unsigned long tickMillis) {
DPRINTF(" WiFi.Begin(%s,%s) returned %d\n", config.ssid, config.pw, ret);
lastAttempt = tickMillis;
ledcWrite(PIN_LED_WIFI, PWM_FULL * 4 / 5); // Light Blink
ledcWrite(PIN_LED_WIFI, PWM_FULL / 10); // Light Blink
lastAttempt = tickMillis;
bConnecting = true;
}

View File

@ -20,7 +20,7 @@
#ifndef DEBUG
#define DEBUG 1 // Set to 0 to disable debug output
#endif
#undef DEBUG
//#undef DEBUG
//#define BLE_DEBUG

View File

@ -12,6 +12,7 @@
#include "BLEScan.h"
#include "hal/timer_ll.h"
#if defined(ESP32)
#include "esp_wifi.h"
#endif
@ -22,7 +23,8 @@ STATUS_TYPE status;
// Time
volatile unsigned short g_nYear, g_nMonth, g_nDay, g_nHour, g_nMinute, g_nSecond;
volatile uint32_t g_millis = 0l;
extern timg_dev_t *tg1;
extern timg_dev_t *tg0;
// Environment
bool bShowSensor = false;
@ -39,6 +41,7 @@ void controlLight(short hour, short min, unsigned long tick);
void controlFanDuty();
void controlMotorDuty();
void controlLightDuty();
void setACLoadStatus(uint32_t tNow);
// ==================================================================================
@ -49,19 +52,17 @@ void controlLightDuty();
MY_IRAM_ATTR void loop() {
static unsigned long lastTickSecond = 0;
static uint8_t lastSecond = -1;
g_millis = (uint32_t)timer_ll_get_counter_value(tg1, 1);
// Read timer from TG0-Timer0
tg0->hw_timer[0].update.val = 1;
uint32_t tNow = tg0->hw_timer[0].lo.val;
g_millis = (uint32_t) (tNow / 10);
unsigned long tickMillis = g_millis;
unsigned long tickSecond = tickMillis / 1000;
// Un-Conditional Loop
{
//ESP_LOGI(TAG_MAIN,"Checking WiFi2");
//checkWiFi(tickMillis);
//ESP_LOGI(TAG_MAIN,"Host Loop");
//host.Loop(tickMillis);
// UI Button Check
ui.loopButton(tickMillis);
}
@ -69,12 +70,11 @@ MY_IRAM_ATTR void loop() {
if (tickSecond != lastTickSecond)
{
// Time and ZCD
setZCD();
setACLoadStatus(tNow);
setTime();
// Temperature and Humidity
readSensors();
//ble.loop(tickMillis);
// Fan, Motor, Light Duties
controlFanDuty();
@ -664,23 +664,3 @@ inline void setTime() {
g_nYear = timeinfo.tm_year + 1900;
}
inline void setZCD() {
// ZCD
status.zcdAC = zcdACCount;
zcdACCount = 0;
status.zcdLoad = zcdLoadCount;
zcdLoadCount = 0;
if (status.zcdAC < 118 || status.zcdAC > 122) {
status.nFlags |= FLAG_ZCD_AC;
}
else {
status.nFlags &= ~FLAG_ZCD_AC;
}
if (status.zcdLoad < 118 || status.zcdLoad > 122) {
status.nFlags |= FLAG_ZCD_LOAD;
}
else {
status.nFlags &= ~FLAG_ZCD_LOAD;
}
}

View File

@ -1,52 +0,0 @@
#include "LED0.h"
CLED0 led0;
void CLED0::setup(uint8_t _pin, uint16_t _freq, uint16_t _channel) {
freq = _freq;
channel = _channel;
pin = _pin;
bPWMMode = true;
bAC = false;
bLoad = false;
duty = 0;
ledcAttachChannel(pin, _freq, PWM_RESOLUTION, channel);
setDuty(duty);
};
void CLED0::setFreq(uint16_t _freq) {
if (freq != _freq) {
if (_freq == 0) {
ledcDetach(pin);
pinMode(pin, OUTPUT);
digitalWrite(pin, LED_OFF);
bPWMMode = false;
}
else {
ledcAttachChannel(pin, _freq, PWM_RESOLUTION, channel);
bPWMMode = true;
}
freq = _freq;
}
}
void CLED0::setDuty() {
uint16_t _duty;
if (bAC) _duty = LED0_DUTY_AC;
else if (bLoad) _duty = LED0_DUTY_LOAD;
else _duty = duty;
ledcWrite(PIN_LED_WIFI, PWM_FULL * (100 - _duty) / 100); // Light Blink
}
void CLED0::setDuty(uint16_t _duty) {
if (duty != _duty) {
if (bPWMMode)
ledcWrite(PIN_LED_WIFI, PWM_FULL * (100 - _duty) / 100); // Light Blink
else
digitalWrite(pin, _duty ? LED_ON : LED_OFF);
duty = _duty;
}
};

38
LED0.h
View File

@ -1,38 +0,0 @@
#ifndef __LED0_H
#define __LED0_H
#ifndef __HERMIT_CRAB_H
#include "HermitCrab.h"
#endif
#define LED0_DUTY_BOOT 20
#define LED0_DUTY_CONNECTED 0
#define LED0_DUTY_CONNECTING 5
#define LED0_DUTY_SMART_CONFIG 100
#define LED0_DUTY_CLIENT 1
#define LED0_DUTY_AC 90
#define LED0_DUTY_LOAD 80
class CLED0 {
public:
void setup(uint8_t _pin, uint16_t _freq, uint16_t _channel);
void loop();
void setFreq(uint16_t _freq);
void setDuty(uint16_t _duty);
void setDuty();
inline void setAC() { bAC = true; };
inline void clearAC() { bAC = false; };
inline void setLoad() { bLoad = true; };
inline void clearLoad() { bLoad = false; };
private:
uint16_t channel;
uint16_t freq;
uint16_t duty;
uint8_t pin;
bool bPWMMode;
bool bAC, bLoad;
};
extern CLED0 led0;
#endif

View File

@ -17,7 +17,7 @@
// OTA
//
// ==============================================================
const char *HC__VERSION = "20260416002";
const char *HC__VERSION = "2026041814";
#define UPDATE_PORT ((uint16_t) 443)
const char *url = "visionsoft.kr";
const char *uri = "/sc/pages/firmware_download.php";

View File

@ -12,7 +12,7 @@
#include "BLEScan.h"
#include <esp_wifi.h>
#include "esp_coexist.h"
#include "hal/timer_ll.h"
#define TAG_SETUP "TAG_SETUP"
// Task handle
@ -23,7 +23,6 @@ bool g_bWiFiHasBeenConnected = false;
extern STATUS_TYPE status;
extern CHistory history;
extern timg_dev_t *tg1;
void setupConfig();
void setupStatus();
@ -37,14 +36,6 @@ void setup_BLE();
void scanI2C();
void setup() {
timer_ll_set_clock_prescale(tg1, 1, 80000);
timer_ll_set_count_direction(tg1, 1, GPTIMER_COUNT_UP);
tg1->hw_timer[1].loadhi.val = 0UL;
tg1->hw_timer[1].loadlo.val = 0UL;
tg1->hw_timer[1].load.val = 1UL;
timer_ll_enable_alarm(tg1, 1, false); // No ISR needed for watchdog
timer_ll_enable_counter(tg1, 1, true);
// put your setup code here, to run once:
#ifdef DEBUG
Serial.begin(115200);
@ -149,7 +140,7 @@ void setupWiFi() {
DPRINTLN();
if (WiFi.status() == WL_CONNECTED) {
ledcWrite(PIN_LED_WIFI, PWM_FULL); // LED_OFF
ledcWrite(PIN_LED_WIFI, PWM_FULL * 9 / 10); // LED_OFF
ui.message(0, (char *) "WiFi...OK!");
DPRINTLN("WiFi - Connected at SETUP");
DPRINTF("WiFi - SSID(%s) PW(%s) IP(%s)\n", config.ssid, config.pw, WiFi.localIP().toString().c_str());
@ -180,7 +171,9 @@ void setupPostWiFi(bool bBoot = false) {
DPRINTLN("\n===============================\n");
DPRINTLN(" Trying OTA");
ui.message(2, (char *) "Update check...");
ledcWrite(PIN_LED_WIFI, PWM_FULL / 20);
checkOTA(true);
ledcWrite(PIN_LED_WIFI, PWM_FULL * 19 / 20);
ui.message(2, (char *) "Update OK!");
DPRINTLN(" OTA Process completed!");
DPRINTLN("===============================\n");
@ -228,7 +221,7 @@ void setupPins() {
ledcWrite(PIN_FAN, PWM_OFF);
ledcWrite(PIN_LIGHT, PWM_OFF);
ledcWrite(PIN_MIST, PWM_OFF);
ledcWrite(PIN_LED_WIFI, (PWM_FULL * 4) / 5); // 80% Initial Blink
ledcWrite(PIN_LED_WIFI, PWM_FULL / 10); // 80% Initial Blink
// Heaters (Inverted logic for MOSFET safety)
ledcWrite(PIN_LED_HEATER1, PWM_FULL - PWM_OFF);

View File

@ -25,7 +25,7 @@ MY_IRAM_ATTR void core0Task(void *pvParameters) {
ESP_LOGI(TAG_TASK0,"Core 0 Task Started");
DPRINTLN("Core 0 Task Started");
wl_status_t lastWiFiStatus = WL_DISCONNECTED;
unsigned long tickMillis = g_millis;
unsigned long tickMillis;
unsigned long tickSecond;
uint8_t slot;
uint8_t lastSlot = 255;
@ -36,7 +36,7 @@ MY_IRAM_ATTR void core0Task(void *pvParameters) {
while (true) {
esp_task_wdt_reset();
tickMillis = g_millis;
tickMillis = millis();
tickSecond = tickMillis / 1000;
slot = (tickMillis % 1000) / 50;

6
UI.cpp
View File

@ -1135,18 +1135,18 @@ void CUI::checkButtonStates(unsigned long currentMillis) {
// ISR for the Set button handling
ARDUINO_ISR_ATTR void buttonSetISR() {
// Record the time of the button interrupt and set a flag
ui.buttonSetChangeTime = g_millis;
ui.buttonSetChangeTime = millis();
ui.bButtonSetChanged = true; // Flag for main loop to process
}
// ISR for the Up button handling
ARDUINO_ISR_ATTR void buttonUpISR() {
ui.buttonUpChangeTime = g_millis;
ui.buttonUpChangeTime = millis();
ui.bButtonUpChanged = true; // Flag for main loop to process
}
// ISR for the Down button handling
ARDUINO_ISR_ATTR void buttonDownISR() {
ui.buttonDownChangeTime = g_millis;
ui.buttonDownChangeTime = millis();
ui.bButtonDownChanged = true; // Flag for main loop to process
}

View File

@ -188,7 +188,7 @@ void CWiFiHost::Loop(unsigned long clock)
m_bClientConnected = true;
m_bHelloSent = false;
m_nMode = MODE_PACKET;
ledcWrite(PIN_LED_WIFI, PWM_FULL - 10);
ledcWrite(PIN_LED_WIFI, PWM_FULL * 9 / 10);
}
return;
}
@ -198,7 +198,7 @@ void CWiFiHost::Loop(unsigned long clock)
if (!wifiClient || !wifiClient.connected()) {
m_bClientConnected = false;
m_nMode = MODE_WAITING;
ledcWrite(PIN_LED_WIFI, PWM_FULL - 2);
ledcWrite(PIN_LED_WIFI, PWM_FULL / 10 );
return;
}
@ -397,7 +397,7 @@ void CWiFiHost::ProcessPacket(TCP_PACKET& pkt)
{
// System
case CMD_HEARTBEAT:
m_nLastReceivedTime = g_millis;
m_nLastReceivedTime = millis();
//ESP_LOGI(TAG_WIFI_HOST,"H");
break;
case CMD_HELLO:

271
zcd.cpp
View File

@ -4,36 +4,39 @@
#include <math.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/gptimer.h>
// Timer Headers
#include "hal/timer_ll.h"
#include "hal/timer_types.h"
#include "driver/timer.h" // Needed for timer_isr_register
#include "rom/ets_sys.h"
// Assuming tg0 is defined as: timg_dev_t *tg0 = &TIMG0;
timg_dev_t *tg0 = &TIMERG0;
timg_dev_t *tg1 = &TIMERG1;
#define SET_TIMER_1(duty) do { \
timer_ll_enable_counter(tg0, 0, false); \
tg0->hw_timer[0].loadhi.val = 0; \
tg0->hw_timer[0].loadlo.val = 0; \
tg0->hw_timer[0].load.val = 1; \
timer_ll_set_alarm_value(tg0, 0, duty); \
timer_ll_enable_alarm(tg0, 0, true); \
timer_ll_enable_counter(tg0, 0, true); \
timer_ll_enable_counter(tg1, 0, false); \
tg1->hw_timer[0].loadhi.val = 0; \
tg1->hw_timer[0].loadlo.val = 0; \
tg1->hw_timer[0].load.val = 1; \
timer_ll_set_alarm_value(tg1, 0, duty); \
timer_ll_enable_alarm(tg1, 0, true); \
timer_ll_enable_counter(tg1, 0, true); \
} while(0)
#define SET_TIMER_2(duty) do { \
timer_ll_enable_counter(tg0, 1, false); \
tg0->hw_timer[1].loadhi.val = 0; \
tg0->hw_timer[1].loadlo.val = 0; \
tg0->hw_timer[1].load.val = 1; \
timer_ll_set_alarm_value(tg0, 1, duty); \
timer_ll_enable_alarm(tg0, 1, true); \
timer_ll_enable_counter(tg0, 1, true); \
timer_ll_enable_counter(tg1, 1, false); \
tg1->hw_timer[1].loadhi.val = 0; \
tg1->hw_timer[1].loadlo.val = 0; \
tg1->hw_timer[1].load.val = 1; \
timer_ll_set_alarm_value(tg1, 1, duty); \
timer_ll_enable_alarm(tg1, 1, true); \
timer_ll_enable_counter(tg1, 1, true); \
} while(0)
#define ESP_INTR_FLAG_LEVEL3 (1<<3)
#define ESP_INTR_FLAG_LEVEL1 (1<<1)
#define TAG_ZCD "ZCD"
// Constants
@ -48,6 +51,7 @@ const uint32_t AC_CYCLE_TIME_CLOCKS = 8333; // Half cycle of 60Hz AC in clock cy
const uint32_t EFFECTIVE_HALF_CYCLE = EFFECTIVE_POWER * AC_CYCLE_TIME_CLOCKS; // Effective half cycle in clock cycles
const uint32_t LEADING_PULSE_COUNT = EFFECTIVE_HALF_CYCLE * LEADING_TIME_RATIO; // Leading pulse count
const uint32_t MAX_PULSE_COUNT = LEADING_PULSE_COUNT + EFFECTIVE_HALF_CYCLE; // Maximum valid pulse count
const uint32_t LEADING_ZCD_COUNT = LEADING_PULSE_COUNT / 2;
volatile uint32_t dutyHeater1; // Calculated timerHeater1 count for TRIAC firing
@ -65,18 +69,33 @@ volatile uint8_t fireStatusTimer2 = 0;
hw_timer_t *timerHeater1;
hw_timer_t *timerHeater2;
const char fireTable[9][8] { {0, 0, 0, 0, 0, 0, 0, 0}, // 0 - 0%
{1, 0, 0, 0, 0, 0, 0, 0}, // 1 - 12.5%
{1, 0, 0, 0, 1, 0, 0, 0}, // 2 - 25%
{1, 0, 0, 1, 0, 0, 1, 0}, // 3 - 37.5%
{1, 0, 1, 0, 1, 0, 1, 0}, // 4 - 50%
{1, 1, 0, 1, 1, 0, 1, 0}, // 5 - 62.5%
{1, 1, 1, 0, 1, 1, 1, 0}, // 6 - 75%
{1, 1, 1, 1, 1, 1, 1, 0}, // 7 - 87.5%
{1, 1, 1, 1, 1, 1, 1, 1}}; // 8 - 100%
const char fireTable[17][16] {
{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}, // 0 0%
{1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}, // 1 6.25% (Dist: 16)
{1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0}, // 2 12.5% (Dist: 8, 8)
{1,0,0,0, 0,1,0,0, 0,0,0,1, 0,0,0,0}, // 3 18.75% (Dist: 5, 6, 5)
{1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0}, // 4 25% (Dist: 4, 4, 4, 4)
{1,0,0,1, 0,0,1,0, 0,1,0,0, 1,0,0,0}, // 5 31.25% (Dist: 3, 3, 3, 3, 4)
{1,0,1,0, 0,1,0,0, 1,0,1,0, 0,1,0,0}, // 6 37.5% (Dist: 2, 3, 3, 2, 3, 3)
{1,0,1,0, 1,0,0,1, 0,1,0,1, 0,1,0,0}, // 7 43.75% (Dist: 2, 2, 3, 2, 2, 2, 3)
{1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0}, // 8 50% (Perfect Toggle)
{0,1,0,1, 0,1,1,0, 1,0,1,0, 1,0,1,1}, // 9 56.25% (Inv 7: spaced 0s)
{0,1,0,1, 1,0,1,1, 0,1,0,1, 1,0,1,1}, // 10 62.5% (Inv 6: spaced 0s)
{0,1,1,0, 1,1,0,1, 1,0,1,1, 0,1,1,1}, // 11 68.75% (Inv 5: spaced 0s)
{0,1,1,1, 0,1,1,1, 0,1,1,1, 0,1,1,1}, // 12 75% (Inv 4: spaced 0s)
{0,1,1,1, 1,0,1,1, 1,1,0,1, 1,1,1,0}, // 13 81.25% (Inv 3: spaced 0s)
{0,1,1,1, 1,1,1,1, 0,1,1,1, 1,1,1,1}, // 14 87.5% (Inv 2: spaced 0s)
{0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1}, // 15 93.75% (Inv 1: spaced 0s)
{1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1} // 16 100%
};
uint8_t seqStep = 0;
uint8_t dutyAC1 = 0;
uint8_t dutyAC2 = 0;
uint8_t dutyAC1TableIndex = 0;
uint8_t dutyAC2TableIndex = 0;
// AC Frequency and health status
volatile uint8_t zcdACISRCount = 0;
volatile uint8_t zcdLoadISRCount = 0;
void setAC1ControlMode(uint8_t mode) { ac1ControlMode = mode; }
void setAC2ControlMode(uint8_t mode) { ac2ControlMode = mode; }
@ -96,7 +115,8 @@ short getHeater2Duty() {
// Function to set the duty based on percentage (0 to 10000)
ARDUINO_ISR_ATTR void setHeater1Duty(short duty) {
if (ac1ControlMode == ZCD_CONTROL) {
dutyAC1 = (uint8_t)((duty + 10000/16)/1250);
if (duty > 10000) duty = 10000;
dutyAC1TableIndex = (uint8_t)((duty + 10000/32)/625);
} else {
if (duty <= 0) {
dutyHeater1 = 0; // If 0% duty, no pulse (turn off TRIAC)
@ -134,6 +154,10 @@ ARDUINO_ISR_ATTR void setHeater2Duty(short duty) {
duty = 0;
}
dutyHeater2 = 0;
} else {
if (ac2ControlMode == ZCD_CONTROL) {
if (duty > 10000) duty = 10000;
dutyAC2TableIndex = (uint8_t)((duty + 10000/32)/625);
} else {
if (duty <= 0) {
dutyHeater2 = 0; // If 0% duty, no pulse (turn off TRIAC)
@ -154,83 +178,68 @@ ARDUINO_ISR_ATTR void setHeater2Duty(short duty) {
dutyHeater2 = LEADING_PULSE_COUNT + EFFECTIVE_HALF_CYCLE - pulseCount;
}
}
}
uint32_t nDuty = duty * PWM_FULL / 10000;
ledcWrite(PIN_LED_HEATER2, PWM_FULL - nDuty);
ESP_LOGD(TAG_ZCD,"Set Duty: %.2f%%, Timer Count: %u clock cycles\n", duty, dutyHeater1);
ESP_LOGD(TAG_ZCD,"Set Duty: %.2f%%, Timer Count: %u clock cycles\n", duty, dutyHeater2);
}
void ARDUINO_ISR_ATTR onTimer1(void *) {
if (fireStatusTimer1) {
REG_WRITE(GPIO_OUT1_W1TC_REG, (1UL << (PIN_HEATER1 - 32))); // Clear (Low)
} else {
REG_WRITE(GPIO_OUT1_W1TS_REG, (1UL << (PIN_HEATER1 - 32))); // Set (High)
tg1->int_clr_timers.t0_int_clr = 1; // Clear Interrupt
if (fireStatusTimer1 == 0) {
// First Trigger: Turn ON
REG_WRITE(GPIO_OUT1_W1TS_REG, (1UL << (PIN_HEATER1 - 32)));
fireStatusTimer1 = 1;
SET_TIMER_1(8); // 8 us
SET_TIMER_1(8); // Schedule OFF pulse 8us later
} else {
// Second Trigger: Turn OFF
REG_WRITE(GPIO_OUT1_W1TC_REG, (1UL << (PIN_HEATER1 - 32)));
}
}
void ARDUINO_ISR_ATTR onTimer2(void *) {
if (fireStatusTimer2) {
REG_WRITE(GPIO_OUT1_W1TC_REG, (1UL << (PIN_HEATER2 - 32))); // Clear (Low)
} else {
REG_WRITE(GPIO_OUT1_W1TS_REG, (1UL << (PIN_HEATER2 - 32))); // Set (High)
fireStatusTimer2 = 1;
SET_TIMER_2(8); // 8 us
}
}
tg1->int_clr_timers.t1_int_clr = 1;
if (fireStatusTimer2 == 0) {
REG_WRITE(GPIO_OUT1_W1TS_REG, (1UL << (PIN_HEATER2 - 32)));
fireStatusTimer2 = 1;
SET_TIMER_2(8);
} else {
REG_WRITE(GPIO_OUT1_W1TC_REG, (1UL << (PIN_HEATER2 - 32)));
}
}
// Zero-Cross Detection Interrupt Service Routine
void ARDUINO_ISR_ATTR zcdACISR() {
// 1. Check the dedicated Watchdog Timer (Group 1)
uint32_t elapsed = timer_ll_get_counter_value(tg1, 0);
if (elapsed < 8000) return; // Reject noise based on absolute time since last ZCD
tg1->hw_timer[0].loadhi.val = 0UL;
tg1->hw_timer[0].loadlo.val = 0UL;
tg1->hw_timer[0].load.val = 1UL;
void ARDUINO_ISR_ATTR zcdACISR(void *) {
// 1. Power side AC ZCD Count
zcdACISRCount++;
zcdACCount++;
fireStatusTimer1 = 0;
fireStatusTimer2 = 0;
// Heater 1
// 3. Heater 1
if (ac1ControlMode == ZCD_CONTROL) {
if (fireTable[dutyAC1][seqStep]) {
onTimer1(NULL);
}
} else {
if (dutyHeater1 >= LEADING_PULSE_COUNT && dutyHeater1 < MAX_PULSE_COUNT) {
// Stop the timer, configure new alarm, then explicitly start
SET_TIMER_1(dutyHeater1); // Set alarm with updated duty
}
else if (dutyHeater1 == MAX_PULSE_COUNT) {
onTimer1(NULL);
}
if (fireTable[dutyAC1TableIndex][seqStep]) SET_TIMER_1(LEADING_ZCD_COUNT);
} else if ( dutyHeater1 >= LEADING_PULSE_COUNT){
SET_TIMER_1(dutyHeater1);
}
// Heater 2
// 4. Heater 2
if (ac2ControlMode == ZCD_CONTROL) {
if (fireTable[dutyAC2][seqStep]) {
onTimer2(NULL);
}
}
else {
if (dutyHeater2 >= LEADING_PULSE_COUNT && dutyHeater2 < MAX_PULSE_COUNT) {
// Stop the timer, configure new alarm, then explicitly start
SET_TIMER_2(dutyHeater2); // Set alarm with updated duty
}
else if (dutyHeater2 == MAX_PULSE_COUNT) {
onTimer2(NULL);
}
if (fireTable[dutyAC2TableIndex][seqStep]) SET_TIMER_2(LEADING_ZCD_COUNT);
} else if (dutyHeater2 >= LEADING_PULSE_COUNT) {
SET_TIMER_2(dutyHeater2);
}
seqStep = ++seqStep & 0x07;
seqStep = ++seqStep & 0x0F;
}
void ARDUINO_ISR_ATTR zcdLoadISR() {
++zcdLoadCount;
void ARDUINO_ISR_ATTR zcdLoadISR(void *) {
// Load side AC ZCD Count
zcdLoadISRCount++;
}
void setupZCD() {
@ -238,6 +247,8 @@ void setupZCD() {
pinMode(PIN_ZCD_LOAD, INPUT);
pinMode(PIN_HEATER1, OUTPUT);
pinMode(PIN_HEATER2, OUTPUT);
digitalWrite(PIN_HEATER1, HEATER_OFF);
digitalWrite(PIN_HEATER2, HEATER_OFF);
dutyHeater1 = 0; // Calculated timerHeater1 count for TRIAC firing
dutyHeater2 = 0; // Calculated timerZCD count for TRIAC firing
@ -245,28 +256,94 @@ void setupZCD() {
zcdLoadCount = 0;
timerHeater1 = NULL;
// --- 1. Basic Hardware Config for TG0 (Heaters) ---
// Timer 0
timer_ll_set_clock_prescale(tg0, 0, 80);
// Test
config.ac1ControlMode = PHASE_CONTROL;
config.ac2ControlMode = PHASE_CONTROL;
ac1ControlMode = config.ac1ControlMode;
ac2ControlMode = config.ac2ControlMode;
// --- 0. Hardware Config for TG0-Timer0 (g_millis)
timer_ll_set_clock_prescale(tg0, 0, 8000); // 10KHz
timer_ll_set_count_direction(tg0, 0, GPTIMER_COUNT_UP);
timer_ll_enable_alarm(tg0, 0, true);
tg0->hw_timer[0].loadhi.val = 0UL;
tg0->hw_timer[0].loadlo.val = 0UL;
tg0->hw_timer[0].load.val = 1UL;
timer_ll_enable_alarm(tg0, 0, false); // No ISR needed for g_millis
timer_ll_enable_counter(tg0, 0, true);
// --- 1. Hardware Config for TG0-Timer1 (Watchdog) ---
tg0->hw_timer[1].loadhi.val = 0UL;
tg0->hw_timer[1].loadlo.val = 0UL;
tg0->hw_timer[1].load.val = 1UL;
timer_ll_set_clock_prescale(tg0, 1, 80); // 1MHz
timer_ll_set_count_direction(tg0, 1, GPTIMER_COUNT_UP);
timer_ll_enable_alarm(tg0, 1, false); // No ISR needed for zcdACISR Watchdog
timer_ll_enable_counter(tg0, 1, true);
// --- 2. Basic Hardware Config for TG1 (Heaters) ---
// Timer 0
timer_ll_set_clock_prescale(tg1, 0, 80);
timer_ll_set_count_direction(tg1, 0, GPTIMER_COUNT_UP);
timer_ll_enable_alarm(tg1, 0, true);
// Timer 1
timer_ll_set_clock_prescale(tg0, 1, 80);
timer_ll_set_count_direction(tg0, 1, GPTIMER_COUNT_UP);
timer_ll_enable_alarm(tg0, 1, true);
timer_ll_set_clock_prescale(tg1, 1, 80);
timer_ll_set_count_direction(tg1, 1, GPTIMER_COUNT_UP);
timer_ll_enable_alarm(tg1, 1, true);
// --- 2. Manual ISR Registration ---
// ESP_INTR_FLAG_IRAM ensures the ISR stays in IRAM for fast context switching.
timer_isr_register((timer_group_t)0, (timer_idx_t)0, onTimer1, NULL, ESP_INTR_FLAG_IRAM, NULL);
timer_isr_register((timer_group_t)0, (timer_idx_t)1, onTimer2, NULL, ESP_INTR_FLAG_IRAM, NULL);
// --- 3. Manual ISR Registration ---
esp_intr_alloc(ETS_TG1_T0_LEVEL_INTR_SOURCE, ESP_INTR_FLAG_IRAM, onTimer1, NULL, NULL);
esp_intr_alloc(ETS_TG1_T1_LEVEL_INTR_SOURCE, ESP_INTR_FLAG_IRAM, onTimer2, NULL, NULL);
// --- 3. Hardware Config for TG1 (Watchdog) ---
timer_ll_set_clock_prescale(tg1, 0, 80000);
timer_ll_set_count_direction(tg1, 0, GPTIMER_COUNT_UP);
tg1->hw_timer[0].loadhi.val = 0UL;
tg1->hw_timer[0].loadlo.val = 0UL;
tg1->hw_timer[0].load.val = 1UL;
timer_ll_enable_alarm(tg1, 0, false); // No ISR needed for watchdog
timer_ll_enable_counter(tg1, 0, true);
tg0->hw_timer[0].update.val = 1;
uint32_t startupTime = timer_ll_get_counter_value(tg0, 0);
zcdACISRCount = 0;
zcdLoadISRCount = 0;
// --- 4. Hardware Trigger ISR Registration ---
//attachInterrupt(PIN_ZCD_AC, zcdACISR, CHANGE); // Attach zero-cross detection ISR
//attachInterrupt(PIN_ZCD_LOAD, zcdLoadISR, CHANGE); // Attach zero-cross detection ISR
// Attach Load ISR with higher priority
esp_intr_alloc(ETS_GPIO_INTR_SOURCE, ESP_INTR_FLAG_LEVEL3, zcdLoadISR, NULL, NULL);
// Attach AC ISR with normal/lower priority
esp_intr_alloc(ETS_GPIO_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1, zcdACISR, NULL, NULL);
}
void setACLoadStatus(uint32_t tNow) {
static uint32_t lastTick = 0;
// --- 1. Process AC/Load Window (Every 1s) ---
if (tNow - lastTick >= 9999) {
zcdACCount = zcdACISRCount;
zcdLoadCount = zcdLoadISRCount;
zcdACISRCount = 0;
zcdLoadISRCount = 0;
lastTick = tNow;
}
// Safety: If no ISR has fired in over 1.5 seconds, force count to 0
if (tNow - lastTick > 15000) {
zcdACCount = 0;
zcdLoadCount = 0;
}
// ZCD
status.zcdAC = zcdACCount;
status.zcdLoad = zcdLoadCount;
if (status.zcdAC < 119 || status.zcdAC > 121) {
status.nFlags |= FLAG_ZCD_AC;
}
else {
status.nFlags &= ~FLAG_ZCD_AC;
}
if (status.zcdLoad < 6 || status.zcdLoad > 181) {
status.nFlags |= FLAG_ZCD_LOAD;
}
else {
status.nFlags &= ~FLAG_ZCD_LOAD;
}
}

1
zcd.h
View File

@ -1,6 +1,5 @@
#ifndef __ZCD_H
#define __ZCD_H
extern volatile uint8_t zcdACCount, zcdLoadCount;
// ZCD
void setupZCD();