/*
    Programme de chenillard illustrant l'utilisation d'un timer comme base de temps.
    Les LEDs sont connectées entre les pins du port D et la masse (=> allumées lorque
    l'I/O est à 1)
    
    Le principe est de faire "tourner" le timer 0, et de récupérer l'interruption
    à chaque passage de 255 à 0.
    
    Etant donné qu'on veut obtenir une cadence de défilement visible (envrion 2/10e
    de seconde, il faut cumuler plusieurs divisions du temps.
    1/ on va appliquer un prescale de 1024 à l'horloge processeur. Etant donnée qu'elle
         est à 8 MHz, la période de base est de 125ns. Le precale va donc établir la
         période de l'incrément du compteur à 125nS * 1024, soit 128us. Un tour complet
         de compteur va donc durer 128us * 256, soit 33ms environ. C'est la période avec
         laquelle l'interruption sera déclenchée
    2/ on va changer l'état des sorties toutes les n interruptions, de manière à avoir
         une cadence de défilement visible

    Cible : ATMega8
    Quartz : 8MHz
    Compilateur : avr-gcc (WInAVR)
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

// compteur du nombre d'interruptions timer
volatile int tmr_cnt = 0 ;

// vu le prescaling et l'étendue du compteur, un intervalle de 100ms correspond à 3 interruptions
#define _100_MS         3

// la liste des patterns d'allumage des LEDs
unsigned char patterns[] = {
  0b10000000,
  0b01000000,
  0b00100000,
  0b00010000,
  0b00001000,
  0b00000100,
  0b00000010,
  0b00000001,
  0b00000010,
  0b00000100,
  0b00001000,
  0b00010000,
  0b00100000,
  0b01000000,
  0b10000000,
  0b00100000,
  0b00001000,
  0b00000010,
  0b00000001,
  0b00000100,
  0b00010000,
  0b01000000,
  0b00000000,
  0b10000000,
  0b11000000,
  0b11100000,
  0b11110000,
  0b11111000,
  0b11111100,
  0b11111110,
  0b11111111,
  0b01111111,
  0b00111111,
  0b00011111,
  0b00001111,
  0b00000111,
  0b00000011,
  0b00000001,
  0b00000000,
  0b10101010,
  0b10101010,
  0b01010101,
  0b01010101,
  0b10101010,
  0b10101010,
  0b01010101,
  0b01010101,
  0b10101010,
  0b10101010,
  0b01010101,
  0b01010101,
  0b11111111,
  0b00000000,
  0b11111111,
  0b00000000,
  0b11111111,
  0b00000000
} ;

// index du pattern courant
unsigned char pattern = 0 ;
 
/*
    Interrupt handler pour l'overflow du timer 0
*/
SIGNAL (SIG_OVERFLOW0) {
    // mise à jour du compteur d'interruptions
  tmr_cnt++ ;
  // si on est arrivé au nombre nécessaire à l'obtention d'un délai de 0.2s,
  // il est temps de changer de motif
  if (tmr_cnt == 2 *_100_MS) {
      // remise à 0 du compteur d'interruptions
        tmr_cnt = 0 ;

        // on modifie l'état des sorties avec le pattern courant
        PORTD = patterns[pattern] ;
    
        // avance de l'index du pattern, avec rebouclage si en fin de séquence
        pattern = (pattern + 1) % sizeof(patterns) ;
  }
}

/*
    Initialisation des ports
*/ 
void port_init(void) {
    // configuration du port D
     // - toutes les I/O en sortie
     DDRD  = 0xFF ;
    // - toutes les LEDs éteintes
     PORTD = 0x00 ;
}

/*
    Initialisation des périphériques
*/
void init_devices(void) {
     cli();                         // inhibition des interruptions (pour avoir la paix)
     port_init();                // initialisation de prts

    // MCU Control Register
    // - interruptions externes sur niveau bas de la ligne
    // - sleep mode désactivé
     MCUCR = 0x00;            
     
     // General Interrupt Control
     // - INT0 et INT1 inactives
     GICR  = 0x00;
     
     // Timer/Counter Interrupt Mask
     // - activation de l'interrupt d'overflow du timer 0
     TIMSK = 0x01;     
     
     // Timer/Counter 0 Control Register
     // - pre-scaling réglé sur 1024
     TCCR0 = 0x05;      
     
     sei();                           // autorisation des interruptions
}

//- --------------------------------------------------------------------------------
int main(void)
{
 init_devices();
 
 // rien à ajouter : tout est fait dans l'interrupt handler
 
}