gcjr:
why are you using a PWM signal? how do you synchronize it to the zero crossing?knowing when there is a zero crossing, why don't you simply generate a short (1 usec) pulse after an appropriate delay to trigger the Triac?
I appreciate your approach and I truly understand the simplicity of it.
Unfortunately, there are some major drawbacks to it.
as for your questions they are absolutely valid and I hope to answer them.
why are you using a PWM signal?
because I can literally ignore it for the most part. I can potentially skip sensing zero crossings and still, the track will be triggered properly. PWM does not require lines of code to traverse rapidly to generate the signals Literally all that is needed is to mess with the counter to synchronize it. Pulse goes high and triac turns on turn be sure that the pulse is low before the zero-crossings and the triac will be ready for the next pulse. set the pulse cycle to match the AC frequency of the location and start the timer on just prior to zero crossing and all works like a champ. I don't even need to be cautious about delays in my code and other blocking code will not affect the resulting output.
If the only thing I needed to do was to control the triac I would consider just use your method. With that said my method is so lightweight accurate and fast, it would be hard for me to not implement my method in everything I use it for in the future.
how do you synchronize it to the zero crossing?
Well, that's the magic.
in the startup, I have the following line lining the interrupt to the function ZeroCrossing
attachInterrupt(0, ZeroCross, FALLING);
easy to read function which also allows me to pass a parameter to the shifted Start function
void ZeroCross(){
shiftedStart(PHASEDURATION * 0.5); //and start it up with timer at zero
}
now for the hard work
We calibrate PWM timer to the exact moment of zero crossing
void shiftedStart(long microseconds){
long cycles = (F_CPU / 2000000) * microseconds;
unsigned int tcnt1;
TIMSK1 &= ~_BV(TOIE1);
GTCCR |= _BV(PSRSYNC); // reset prescaler (NB: shared with all 16 bit timers);
char oldSREG = SREG; // save status register
cli(); // Disable interrupts
TCNT1 = cycles;
SREG = oldSREG; // Restore status register
TCCR1B |= clockSelectBits;
}
I can see it you might say wow that's quite a bit to de every time we cross zero. but remember we only trigger this as an interrupt once at the exact time we cross zero. we don't have to continually poll an input.
So with that said you could do the same with your code and trigger zcUsec to match the time
a one-liner could be
#define ZeroCrPin 2// interrupt 0
volatile unsigned long zcUsec = 0; // must be set to volatile when used within an interrupt.
attachInterrupt(0, []{zcUsec = micros()}, FALLING); // used an anonomous function (lambda funciton) to set zcUsec to micros()
now in your code, you wouldn't have to continually check the pin for zero-crossing by pulling it. This would capture the time at that moment using an interrupt.
With that improvement, you could save time and find that your triggering of the triac to be more accurate.
The next and biggest difference is that I don't need to be concerned when the trigger event must occur. loop function has no responsibility ore requirement to be fast
example: I can set the cycle once and it will stay that way. no additional code is required in the loop function.
void setup(){
...
// add this
setPwmDuty(512); // set the triac output to 50% 0 ~ 1024
}
void loop(){
// nothing here
}
I can be careless in my loop() with blocking code and never have to worry that the output will be affected.
Why I went with this method occurred as I was testing with an LCD display and Serial output, reading several sensors, and responding to requests. I noticed that the incandescent light I was using to visualize my output flickered when events occurred I concluded that I needed to be more precise with this output without having to rework all the other code. this not so simple solution was the result and I shared it with the community.
Z
P.S.
At this time I have not made any modifications to my original posted code that would alter this method of trial control. I am open to any feedback and even improvements you might find and would be willing to share.