/*
Monitor colorido mais simples do mundo
(use a roda do mouse para diminuir o zoom e ver o monitor inteiro)

*/

#define DATA PD4  
#define LATCH PD7
#define CLOCK PB0

#define VERMELHO 1
#define VERDE 2
#define AZUL 3
#define AMARELO 4
#define CIANO 5
#define MAGENTA 6
#define BRANCO 7

const uint8_t linhas = 5; //quantidade de linhas
const uint8_t colunas = 32; //quantidade de colunas
const uint8_t quantidadeLEDs = linhas*colunas; // quantidade de LEDS
byte framebuffer[quantidadeLEDs]; // array de byte para funcionar como framebuffer para rasterização
byte memvideo[quantidadeLEDs]; // array de byte para funcionar como memória de vídeo

unsigned long counter = 0;

int dec = 31;
int dec2 = 31;
int passo = 1;

int main() { 
 // configurando as portas B e D setando pinos em OUTPUT 
  DDRB |= B00111111; 
  DDRD |= B11111100; 

// limpando registrador TCCR1A
  TCCR1A = 0;

 // colocando o timer no modo CTC (Clear Timer on Compare-match)
  TCCR1B &= ~(B00000001 << WGM13);
  TCCR1B |= (B00000001 << WGM12);

 // setando prescaler do timer para 64 (16 mHz / 64 = 250 kHz)
  TCCR1B &= ~(B00000001 << CS12);
  TCCR1B |= (B00000001 << CS11);
  TCCR1B |= (B00000001 << CS10);

  TCNT1 = 0;
  OCR1A = 2500; // um centésimo de 250 mil para o Compare-match

  TIMSK1 = (1 << OCIE1A);
	
  asm( "sei \n"); // habilitando interrupções com inline assembly

  PintaTela(VERMELHO);

  while(true){  // substituto do loop() vazio
        
  }

}


ISR(TIMER1_COMPA_vect){ // Rotina de Serviço de Interrupção (ISR) do Timer
    counter++;
    Condicionais();          
    Rasterizar();
}

void Condicionais(){  
  
  if(passo == 1){
    PintaMemVideo(AZUL);
    EscreveLetraMemoria('D', 1, AMARELO);
    EscreveLetraMemoria('A', 7, AMARELO);   
    EscreveLetraMemoria('N', 13, AMARELO);   
    EscreveLetraMemoria('T', 19, AMARELO);  
    EscreveLetraMemoria('E', 25, AMARELO); 
  
    if(dec >=0){
      if((counter % 15)==0){
        CopiaFramebufferDaMemoria(dec);
        dec = dec-1;
        }
    }else{
      if( (counter % 600) == 0){       
        PintaTela(VERDE);
        dec2= 31;
        passo = 2;
      }
    }   
 }

   if(passo == 2){
    PintaMemVideo(BRANCO);
    EscreveLetraMemoria('M', 2, VERMELHO);
    EscreveLetraMemoria('E', 9, VERMELHO);   
    EscreveLetraMemoria('I', 13, VERMELHO);   
    EscreveLetraMemoria('R', 18, VERMELHO);  
    EscreveLetraMemoria('A', 24, VERMELHO); 
  
    if(dec2 >=0){
      if((counter % 15)==0){
        CopiaFramebufferDaMemoria(dec2);
        dec2 = dec2-1;
        }
    }  
 }
}

void ApagaTudo(){
for (int i = quantidadeLEDs-1; i >= 0; i--) {       
        framebuffer[i] = 0;
    }
}

void PintaTela(byte cor){
for (int i = quantidadeLEDs-1; i >= 0; i--) {       
        framebuffer[i] = cor;
    }
}

void PintaMemVideo(byte cor){
for (int i = quantidadeLEDs-1; i >= 0; i--) {       
        memvideo[i] = cor;
    }
}

void CopiaFramebufferDaMemoria(uint8_t colInicio){
 int resto;  
    for (int i = quantidadeLEDs - 1; i >= 0; i--) {
        resto = i % colunas;
         if(resto >= colInicio){ framebuffer[i] =  memvideo[i-colInicio]; }
        }
}





void EscreveLetraMemoria(char letra, uint8_t pos, byte cor){
  
  if(letra == 'A'){
     for(int i = pos+1; i < pos+4; i++){
     memvideo[i] =  cor;
    }  
    memvideo[pos+32] =  cor;
    memvideo[pos+36] =  cor;
    for(int i = pos+64; i < pos+69; i++){
     memvideo[i] =  cor;
    }   
    memvideo[pos+96] =  cor;
    memvideo[pos+100] =  cor;    
    memvideo[pos+128] =  cor;
    memvideo[pos+132] =  cor;      
  }

  if(letra == 'D'){
    for(int i = pos; i < pos+4; i++){
     memvideo[i] =  cor;
    }
    memvideo[pos+33] =  cor; 
    memvideo[pos+36] =  cor; 
    memvideo[pos+65] =  cor; 
    memvideo[pos+68] =  cor; 
    memvideo[pos+97] =  cor; 
    memvideo[pos+100] =  cor; 
    for(int i = pos+128; i < pos+132; i++){
     memvideo[i] =  cor;
    }   
  }

    if(letra == 'E'){
      for(int i = pos; i < pos+4; i++){
          memvideo[i] =  cor;
          }  
          memvideo[pos+32] =  cor;
        for(int i = pos+64; i < pos+68; i++){
          memvideo[i] =  cor;
          }  
          memvideo[pos+96] =  cor;
       for(int i = pos+128; i < pos+132; i++){
          memvideo[i] =  cor;
          }
  
  }

     if(letra == 'I'){
     memvideo[pos+2] =  cor; 
     memvideo[pos+34] =  cor;
     memvideo[pos+66] =  cor;
     memvideo[pos+98] =  cor;
     memvideo[pos+130] =  cor;
  }

    if(letra == 'M'){
    for(int i = pos; i < pos+129; i = i+32){
     memvideo[i] =  cor;
    }
    for(int i = pos+4; i < pos+133; i = i+32){
     memvideo[i] =  cor;
    }
    memvideo[pos+33] =  cor;
    memvideo[pos+66] =  cor;
    memvideo[pos+35] =  cor;

  }

    if(letra == 'N'){
    for(int i = pos; i < pos+129; i = i+32){
     memvideo[i] =  cor;
    }
    for(int i = pos+4; i < pos+133; i = i+32){
     memvideo[i] =  cor;
    }
    memvideo[pos+33] =  cor;
    memvideo[pos+66] =  cor;
    memvideo[pos+99] =  cor;

  }

  if(letra == 'P'){

     memvideo[pos] =  cor;
     memvideo[pos+1] =  cor; 
     memvideo[pos+2] =  cor; 
     memvideo[pos+3] =  cor; 
     memvideo[pos+36] =  cor; 
     memvideo[pos+32] =  cor;
     memvideo[pos+64] =  cor;
     memvideo[pos+65] =  cor;
     memvideo[pos+66] =  cor;
     memvideo[pos+67] =  cor;
     memvideo[pos+96] =  cor;
     memvideo[pos+128] =  cor;
  }

  if(letra == 'R'){

     memvideo[pos] =  cor;
     memvideo[pos+1] =  cor; 
     memvideo[pos+2] =  cor; 
     memvideo[pos+3] =  cor; 
     memvideo[pos+36] =  cor; 
     memvideo[pos+32] =  cor;
     memvideo[pos+64] =  cor;
     memvideo[pos+65] =  cor;
     memvideo[pos+66] =  cor;
     memvideo[pos+67] =  cor;
     memvideo[pos+96] =  cor;
     memvideo[pos+98] =  cor;
     memvideo[pos+128] =  cor;
     memvideo[pos+131] =  cor;
  }

    if(letra == 'T'){

     memvideo[pos] =  cor;
     memvideo[pos+1] =  cor; 
     memvideo[pos+2] =  cor; 
     memvideo[pos+3] =  cor; 
     memvideo[pos+4] =  cor; 
     memvideo[pos+34] =  cor;
     memvideo[pos+66] =  cor;
     memvideo[pos+98] =  cor;
     memvideo[pos+130] =  cor;
  }

   

}

void Rasterizar(){
    byte cor;

    LatchLow();

    for (int i = quantidadeLEDs-1; i >= 0; i--) {  
        
        cor = framebuffer[i];

        switch(cor){
          case 0:
          EnviaApagado();
          break;
          case 1:
          EnviaVermelho();
          break;
          case 2:
          EnviaVerde();
          break;    
          case 3:
          EnviaAzul();
          break;
          case 4:
          EnviaAmarelo();
          break; 
          case 5:
          EnviaCiano();
          break;  
          case 6:
          EnviaMagenta();
          break;  
          case 7:
          EnviaBranco();
          break;                                       
        }

        }

      LatchHigh();
      LatchLow();   
}



void EnviaApagado(){ 
  ClockLow(); 

  DataLow(); 

  ClockLow(); 
  ClockHigh(); 

  DataLow();

  ClockLow(); 
  ClockHigh();  

  DataLow();

  ClockLow(); 
  ClockHigh();  
  
}

void EnviaVermelho(){ 
  ClockLow(); 

  DataLow(); // Azul = LOW

  ClockLow(); 
  ClockHigh(); 

  DataLow(); // Verde = LOW

  ClockLow(); 
  ClockHigh();  

  DataHigh(); // Vermelho = HIGH

  ClockLow(); 
  ClockHigh();  
  
}

void EnviaVerde(){ 
  ClockLow(); 

  DataLow();

  ClockLow(); 
  ClockHigh(); 

  DataHigh();

  ClockLow(); 
  ClockHigh();  

  DataLow();

  ClockLow(); 
  ClockHigh();  
  
}

void EnviaAzul(){ 
  ClockLow(); 

  DataHigh();

  ClockLow(); 
  ClockHigh(); 

  DataLow();

  ClockLow(); 
  ClockHigh();  

  DataLow();

  ClockLow(); 
  ClockHigh();  
  
}

void EnviaAmarelo(){ 
  ClockLow(); 

  DataLow();

  ClockLow(); 
  ClockHigh(); 

  DataHigh();

  ClockLow(); 
  ClockHigh();  

  DataHigh();

  ClockLow(); 
  ClockHigh();  
  
}

void EnviaCiano(){ 
  ClockLow(); 

  DataHigh();

  ClockLow(); 
  ClockHigh(); 

  DataHigh();

  ClockLow(); 
  ClockHigh();  

  DataLow();

  ClockLow(); 
  ClockHigh();  
  
}

void EnviaMagenta(){ 
  ClockLow(); 

  DataHigh();

  ClockLow(); 
  ClockHigh(); 

  DataLow();

  ClockLow(); 
  ClockHigh();  

  DataHigh();

  ClockLow(); 
  ClockHigh();  
  
}

void EnviaBranco(){ 
  ClockLow(); 

  DataHigh();

  ClockLow(); 
  ClockHigh(); 

  DataHigh();

  ClockLow(); 
  ClockHigh();  

  DataHigh();

  ClockLow(); 
  ClockHigh();  
  
}

void LatchLow(){
 PORTD &= ~(B00000001 << LATCH); // colocando o LATCH em LOW direto no registrador PORTD 
}

void LatchHigh(){
 PORTD |= (B00000001 << LATCH); // colocando o LATCH em HIGH direto no registrador PORTD
}

void ClockLow(){
 PORTB &= ~(B00000001 << CLOCK); // colocando o CLOCK em LOW direto no registrador PORTB
}

void ClockHigh(){
  PORTB |= (B00000001 << CLOCK); // colocando o CLOCK em HIGH direto no registrador PORTB  
}


void DataLow(){
 PORTD &= ~(B00000001 << DATA); // colocando o DATA em LOW direto no registrador PORTD
}

void DataHigh(){
 PORTD |= (B00000001 << DATA); // colocando o DATA em HIGH direto no registrador PORTD
}
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595