I'm trying to implement AC phase cutting control, but the problem is that instead of the common zero crossing, my circuit is based on peak detection (reason: instead of a big resistor that runs hot, I use a capacitor to reduce current).
This peak detection works fine - I see peaks of 1.8 ms wide and a much sharper falling edge than rising edge, so using the falling edge as timing point. This means that the zero crossing is 4.1 ms past the peak (50 Hz mains AC).
The problem is in the timing of the gate trigger: this is 4.1 ms plus 0-10 ms (half wave). This means that when I'm below about 45% the gate trigger should happen past the next peak, and this is where the common zero crossing code fails.
One solution could be to have the peak detection set a timer interrupt that acts as zero crossing point, and subsequently a new timer is started for the actual phase cutting. But that means I would have to use both timer1 and timer2.
Is there any way to make this happen with just one timer?
Current test code (under development so maybe not all comments make sense). I didn't implement the timer2 part, that should be no problem to do. Mostly wondering if there's a way of doing this with a single timer.
// Pulse length: 1,800 us (according to scope).
// Rising edge is much slower than falling edge (10k too weak a pull-up??) so better use falling edge. Pulse length
// measured by Arduino is about 1480 us.
// Interrupt: should look for FALLING edge, which occurs 4,100 us before the zero crossing.
// Cut-off points for the phase cutting: 4,100 - 14,100 us in timer counts, one count = 4 us.
const uint16_t startACCycle = 1025;
const uint16_t endACCycle = 3525;
const byte ZERO_CROSSING_PIN = 6;
const byte GATE_PIN = 5;
volatile uint8_t pulses;
// Auto/manual control states.
enum controlModes {
CONTROL_AUTO,
CONTROL_MANUAL
};
uint8_t controlMode = CONTROL_MANUAL;
uint8_t manualPowerLevel = 0;
void setup() {
Serial.begin(115200);
pinMode(ZERO_CROSSING_PIN, INPUT_PULLUP);
pinMode(GATE_PIN, OUTPUT);
// set up Timer1
TIMSK1 = 0x03; // Enable comparator A and overflow interrupts
TCCR1A = 0x00; // Timer control registers set for
TCCR1B = 0x00; // normal operation, timer disabled
*digitalPinToPCMSK(ZERO_CROSSING_PIN) |= bit (digitalPinToPCMSKbit(ZERO_CROSSING_PIN)); // enable pin
PCIFR |= bit(digitalPinToPCICRbit(ZERO_CROSSING_PIN)); // clear any outstanding interrupt
PCICR |= bit(digitalPinToPCICRbit(ZERO_CROSSING_PIN)); // enable interrupt for the group
}
uint32_t lastChange;
bool rising = true;
void loop() {
if (millis() - lastChange > 250) {
lastChange = millis();
if (rising) {
manualPowerLevel++;
}
else {
manualPowerLevel--;
}
if (manualPowerLevel == 0) {
rising = true;
}
else if (manualPowerLevel == 100) {
rising = false;
}
if (manualPowerLevel % 10 == 0) {
Serial.print(F("Current level: "));
Serial.print(manualPowerLevel);
Serial.print(F(", timer: "));
Serial.print(startACCycle + (uint32_t)(100 - manualPowerLevel) * (endACCycle - startACCycle) / 100.0);
Serial.print(F(", OCR1A: "));
Serial.println(OCR1A);
pulses = 0;
}
}
handlePump();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pump handling.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void handlePump() {
OCR1A = startACCycle + (uint32_t)manualPowerLevel * (endACCycle - startACCycle) / 100.0;
}
#define PULSE 63 // Trigger pulse width (counts) - 252 us.
// Interrupt Service Routines
ISR (PCINT2_vect) { // Zero crossing is D6, PCINT2.
if (digitalRead(ZERO_CROSSING_PIN) == LOW) { // Falling edge.
TCCR1B = 0x03; // Start timer with divide by 64 input = 4 us per tick at 16 MHz.
TCNT1 = 0; // Reset timer - count from zero
}
}
ISR(TIMER1_COMPA_vect) { // Comparator match.
digitalWrite(GATE_PIN, HIGH); // Set TRIAC gate to high.
TCNT1 = 65536 - PULSE; // Trigger pulse width.
}
ISR(TIMER1_OVF_vect) { // Timer1 overflow.
digitalWrite(GATE_PIN, LOW); // Turn off TRIAC gate.
TCCR1B = 0x00; // Disable timer to stop unintended triggers.
}
Circuit used: