/* LED microcontroller dimmer for use with Soleil Sun Alarm Written for Atmel ATMega8 and avr-gcc Eric J. Wilhelm Squid Labs, LLC Attribution-NonCommercial-ShareAlike 2.5 You are free: * to copy, distribute, display, and perform the work * to make derivative works Under the following conditions: by Attribution. You must attribute the work in the manner specified by the author or licensor. Noncommercial. You may not use this work for commercial purposes. Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one. * For any reuse or distribution, you must make clear to others the license terms of this work. * Any of these conditions can be waived if you get permission from the copyright holder. */ #include #include #include #include #include # define OC1 PB1 # define DDROC DDRB # define OCR OCR1A volatile uint16_t xtimer; volatile uint16_t timer0; volatile uint8_t status; SIGNAL(SIG_OVERFLOW0) { timer0++; TCNT0=96; // preload the timer with 96 to make this interrupt occur every 20 us. } SIGNAL(SIG_OVERFLOW1) { //The interrupts don't work properly without this definition. } // falling edge PWM signal (rising edge at clock; reversed due to optoisolator) SIGNAL(SIG_INTERRUPT1) { // Zero timer0 to count the length of the positive pulse timer0=0; status=1; } //rising edge PWM signal (falling edge at clock; reversed due to optoisolator) SIGNAL(SIG_INTERRUPT0) { //record the length of the positive pwm signal in xtimer // if timer0 is greater than approximately 263 (at 20 us per interrupt) than the pulse was missed if(timer0<270) xtimer=timer0; status=0; } void ioinit (void) { // Timer1 does ~16-bit PWM TCCR1A = _BV (COM1B1) | _BV (WGM11) | _BV (COM1A1); TCCR1B = _BV (WGM13) | _BV (WGM12) | _BV (CS10); ICR1 = 65535; //Timer0 counts TCCR0 = _BV (CS00); TCNT0 = 96; timer0=0; // set PWM value to 0 OCR = 0; //enable OC1 as output DDROC = _BV (OC1); //enable external interrupts GICR = _BV (INT1) | _BV (INT0); //INT0 is rising edge, INT1 is falling edge MCUCR = _BV (ISC01) | _BV (ISC00) | _BV(ISC11); xtimer=0; status=0; //enable timers timer_enable_int (_BV (TOIE1) | _BV (TOIE0) ); // enable interrupts sei (); } int main (void) { #define B -0.00325 #define C -11.09 #define D 0.396503 int i,on=0, oncounter=0; unsigned int x[100], y; long t[100], u; ioinit (); for (;;) { //average xtimer over samples because it jumps around alot for(i=99;i>0;i--) { x[i] = x[i-1]; } x[0] = xtimer; y=0; for(i=0;i<100;i++) { y = y+x[i]; } y=y/100; //divide by 5 because the clock has 120 us resolution on its PWM y=y/5; // average the output so it doesn't jump around for(i=99;i>0;i--) { t[i] = t[i-1]; } //determine what to do if(timer0>270 && status == 1 && on == 1) { //Turn light on t[0] = 0; on=1; } else if(timer0>270 && status == 0) { //Turn light off t[0] = 65535; xtimer=0; on=0; oncounter=0; } else if(timer0<270){ t[0] = 65535*(1-exp((B*y + D)*y + C)); if(t[0]>65535) t[0] = 65535; if(t[0]<0) t[0] = 0; } //oncounter prevents the light from turning on suddenly from an off state if timer0>270, but there's a positive pulse on the PWM //this happens during the very start of a sunrise, when the clock's PWM hasn't quite turned on at the right frequency else if(timer0>270 && status == 1) { if(++oncounter==5) { on = 1; oncounter=0; } } // average the output so it doesn't jump around u=0; for(i=0;i<100;i++) { u=u+t[i]; } //Change the output PWM OCR = u/100; } return (0); }