Étape 9: Programme microcontrôleur
Le code et le makefile sont rédigés pour travailler avec avr-gcc. Brièvement, il calcule le facteur de marche de l’horloge, calcule le niveau désiré de contrôle LED et crache un signal PWM. Il y a quelques avec une moyenne des valeurs d’entrée et de sortie pour réduire les indésirable clignotement de la LED (comme quand ils commencent tout juste un lever de soleil). Essentiellement, le microcontrôleur agit comme un filtre non linéaire de PWM.Code: (Télécharger le fichier .c plutôt que de couper et de coller ce texte ; il y a quelques problèmes de formatage avec la syntaxe)
/* 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);}