Stap 9: Programma microcontroller
De code en de makefile zijn geschreven om te werken met avr-gcc. Kort, het berekent het pulserend sproeien van de klok, berekent de gewenste LED-besturingsniveau en spuugt een PWM-signaal. Er is sommige gemiddeld van de input- en output waarden om ongewenste knipperen van de LED's (zoals wanneer ze net een zonsopgang beginnen). In wezen, de microcontroller fungeert als een niet-lineaire PWM-filter.Code: (download het .c bestand in plaats van knippen en plakken van deze tekst; er zijn sommige problemen met de syntaxis met opmaak)
/* LED microcontroller dimmer for use with Soleil Sun AlarmWritten for Atmel ATMega8 and avr-gccEric J. WilhelmSquid Labs, LLCAttribution-NonCommercial-ShareAlike 2.5You are free: * to copy, distribute, display, and perform the work * to make derivative worksUnder 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 <inttypes.h>#include <avr/io.h>#include <avr/interrupt.h>#include <avr/signal.h>#include <math.h># define OC1 PB1# define DDROC DDRB# define OCR OCR1Avolatile 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;}voidioinit (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 ();}intmain (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);}