Please take a look and help me improve this code.
The code uses PWM and attempts to match it to the AC frequency it is receiving I've reversed the pwm signal so it starts off then turns on after a duration to trigger the triac.
this is decently functioning code but I need to revise and improve it.
What I would like to do is enhance it to do things like Auto detect AC frequency and
fix the glitch when it gets close to the 100% point (1024).
Any advice is appreciated.
Advantages: Non Blocking low overhead control as the PWM handles everything.
/* AC Phase Dimmer Control using PWM
* By ZHomeSlice
* I don't think I've seen this done before anywhere
*/
#define RESOLUTION 65536 // Timer1 is 16 bit
#define PHASEDURATION 16666
unsigned int pwmPeriod;
unsigned char clockSelectBits;
void ZeroCross(){
shiftedStart(PHASEDURATION * 0.5); //and start it up with timer at zero
}
void setPeriod(long microseconds){
long cycles = (F_CPU / 2000000) * microseconds; // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
if (cycles < RESOLUTION) clockSelectBits = _BV(CS10); // no prescale, full xtal
else if ((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11); // prescale by /8
else if ((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11) | _BV(CS10); // prescale by /64
else if ((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12); // prescale by /256
else if ((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12) | _BV(CS10); // prescale by /1024
else cycles = RESOLUTION - 1, clockSelectBits = _BV(CS12) | _BV(CS10); // request was out of bounds, set as maximum
char oldSREG = SREG;
cli(); // Disable interrupts for 16 bit register access
ICR1 = pwmPeriod = cycles; // ICR1 is TOP in p & f correct pwm mode
SREG = oldSREG;
TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
TCCR1B |= clockSelectBits; // reset clock select register, and starts the clock
}
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;
}
void setPwmDuty(int duty){
unsigned long dutyCycle = pwmPeriod;
dutyCycle *= duty;
dutyCycle >>= 10;
char oldSREG = SREG;
cli();
OCR1A = OCR1A = ICR1 - dutyCycle; // invert 0-1024 range
SREG = oldSREG;
}
void disablePwm(char pin){
TCCR1A &= ~_BV(COM1A1); // clear the bit that enables pwm on PB1
}
void setup(){
pinMode(2,INPUT_PULLUP);
TCCR1A = 0; // clear control register A
TCCR1A = _BV(COM1A0) | _BV(COM1A1) // Clear OC1A on Compare Match, set
// OC1A at BOTTOM (Inverting mode)
| _BV(WGM11); // Fast PWM, top at ICR1
TCCR1B = _BV(WGM12) | _BV(WGM13); // ditto
setPeriod(PHASEDURATION);
TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12)); // clears all clock selects bits (stop the counter)
DDRB |= _BV(PORTB1); // sets data direction register for pwm output pin
TCCR1A |= _BV(COM1A1); // activates the output pin
setPwmDuty(0);
attachInterrupt(0, ZeroCross, FALLING);
}
void loop(){
static int Direction = 1;
static int Duty = 0;
static unsigned long ATimer;
if ((unsigned long)(millis() - ATimer) >= (10)){
ATimer = millis();
Duty += Direction;
if (Duty >= 1024) Direction = -1;
if (Duty <= 1) Direction = 1;
setPwmDuty(Duty);
}
}
Thanks in advanced.
Z