quarta-feira, 6 de abril de 2011

Arduino 8x8 leds




Existe muita coisa na internet sobre matriz de leds 8x8. Inclusive com o arduino. Mesmo assim , partindo da idéia de que quanto mais, melhor, decidi criar esta postagem que descreve a ligação de uma destas matrizes ao arduino com o auxilio de um contador  4017.
Newton C. Braga  neste artigo explica o funcionamento do contador 4017 incluindo exemplos de utilização, vale a pena.

Como o nome diz é uma matriz, naturalmente composta de linhas e colunas, cujos elementos são os leds. Numa matriz unicolor o "endereço" do led é dado por sua linha e coluna na bicolor e  na RGB é preciso determinar além da linha e coluna a cor do led. Para acendermos um determinado led(linha, coluna), julgando que o anodo está ligado ao pino linha e o catodo ao coluna, devemos ligar a linha correspondente ao positivo e a coluna ao negativo da fonte.

Nesta montagem foi usada uma matriz bicolor, entretanto o código só usa a cor vermelha. Numa outra oportunidade postarei algo que use duas cores.


Funcionamento


O 4017 tem 10 saídas. Assim que ele é ligado b0  é colocado em nível alto (+5V) e a cada pulso de clock uma das saídas subsequentes é colocada em nível alto. O ciclo se repete depois que a décima saída é colocada em nível alto, ou que o reset seja acionado, reiniciando a contagem. Aqui o 4017 é responsável pelo +5 de cada linha de led. A cada pulso de clock enviado pelo arduino ele alimenta os anodos dos diodos de uma das linhas da matriz, começando de cima.

As colunas são ligadas diretamente ao arduino que deve colocar a saída correspondente em nível baixo (0V) para acender o led cuja linha esteja sendo alimentada, com 5V, pelo 4017, conforme figura acima.
Cada caractere é armazenado em um vetor de oito bytes onde cada byte corresponde a uma linha da matriz, sempre de cima para baixo. Neste vetor '0' representa led apagado e '1' aceso.
Por exemplo, para a letra A:
  B0      0 0 1 1 1 1 1 0 0   
B1      0 1 1 1 1 1 1 1
B2      1 1 0 0 0 0 0 1 1
B3      1 1 0 0 0 0 0 1 1
B4      1 1 1 1 1 1 1 1 1
B5      1 1 0 0 0 0 0 1 1
B6      1 1 0 0 0 0 0 1 1
B7      1 1 0 0 0 0 0 1 1

Teremos o vertor: A[8] = {B00111100, B01111110, B110000011, B110000011, B111111111, B110000011, B110000011, B110000011}
 
O programa

A dinâmica do programa é a seguinte:
1.    O texto é armazenado em um vetor;

2.    O programa pega caractere por caractere compara com os caracteres disponíveis e  armazena o vetor   correspondente em uma vetor auxiliar;

3.    Cada byte, bit a bit, é armazenado em uma linha da matriz "aux" ; 

4.    A matriz aux é passada para a função que imprime no display;

5.    Para dar a idéia de deslocamento a primeira coluna da matriz aux copia a segunda, a segunda copia a terceira isso até que oitava coluna seja copiada pela sétima e receba a primeira coluna do próximo caractere ou colunas em branco enviadas pela função "apaga_coluna(n° de colunas)".  Existe, também a possibilidade de mostrar, sem deslocamento, o caractere no display atravéz do parametro modo da função escreve.
O código fonte pode ser visto abaixo.
#include <string.h>

              //  0 1 2 3 4 5 6 7
 int aux[8][8]= {0,0,0,0,0,0,0,0, //0     //poderia ter usado dois laços um para colunas outro para linhas pra fazer o mesmo
     /*  k  m */ 0,0,0,0,0,0,0,0, //1     //mas assim parece mais didatico
                 0,0,0,0,0,0,0,0,         // k linha    m columa
                 0,0,0,0,0,0,0,0,
                 0,0,0,0,0,0,0,0,
                 0,0,0,0,0,0,0,0,
                 0,0,0,0,0,0,0,0,
                 0,0,0,0,0,0,0,0};

int aux2[8][8];    // cada byte corresponde a uma linha da letra de cima para baixo        
             // linha 1          2          3           4          5          6          7          8
     byte A[8] = {B00111100, B01111110, B11000011, B11000011, B11111111, B11111111, B11000011, B11000011};            
     byte B[8] = {B11111110, B11111111, B11000111, B11111110, B11111110, B11000111, B11111111, B11111110};               
     byte C[8] = {B11111111, B11111111, B11000000, B11000000, B11000000, B11000000, B11111111, B11111111};               
     byte D[8] = {B11111100, B11111110, B11000111, B11000011, B11000011, B11000111, B11111110, B11111100};      
     byte E[8] = {B11111111, B11111111, B11000000, B11111100, B11111100, B11000000, B11111111, B11111111};                
     byte F[8] = {B11111111, B11111111, B11000000, B11111100, B11111100, B11000000, B11000000, B11000000};
     byte G[8] = {B11111111, B11111111, B11000001, B11000000, B11001111, B11000011, B11111111, B11111111};
     byte H[8] = {B11000011, B11000011, B11000011, B11111011, B11111111, B11000011, B11000011, B11000011}; 
     byte I[8] = {B11111111, B11111111, B00011000, B00011000, B00011000, B00011000, B11111111, B11111111};
     byte J[8] = {B01111111, B01111111, B00001100, B00001100, B11001100, B11001100, B11111100, B01111000};                                    
     byte K[8] = {B11000011, B11000111, B11001110, B11111100, B11111100, B11001100, B11000111, B11000011};         
     byte L[8] = {B11000000, B11000000, B11000000, B11000000, B11000000, B11000000, B11111111, B11111111};
     byte M[8] = {B11000011, B11100111, B11111111, B11011011, B11011011, B11000011, B11000011, B11000011};  
     byte N[8] = {B11000011, B11100011, B11110011, B11111011, B11011111, B11001111, B11000111, B11000011};  
     byte O[8] = {B01111110, B11111111, B11000011, B11000011, B11000011, B11000011, B11111111, B01111110};
     byte P[8] = {B11111110, B11111111, B11000011, B11111111, B11111110, B11000000, B11000000, B11000000};           
     byte Q[8] = {B01111110, B11111111, B11000011, B11000011, B11001011, B11000111, B11111110, B01111101};      
     byte R[8] = {B11111110, B11111111, B11000011, B11000010, B11111100, B11111110, B11000111, B11000111};     
     byte S[8] = {B11111111, B11111111, B11000000, B11111111, B11111111, B00000011, B11111111, B11111111};
     byte T[8] = {B11111111, B11111111, B00011000, B00011000, B00011000, B00011000, B00011000, B00011000};
     byte U[8] = {B11000011, B11000011, B11000011, B11000011, B11000011, B11000011, B11111111, B01111110};
     byte V[8] = {B11000011, B11000011, B11000011, B11000011, B11000011, B01100110, B00111100, B00011000};
     byte X[8] = {B11000011, B11100111, B01100110, B00011000, B00011000, B01100110, B11100111, B11000011};
byte ponto[8] =  {B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00011000, B00011000};
byte coracao[8]= {B01100110, B11111111, B11111111, B11111111, B11111111, B01111110, B00111100, B00011000}; 
byte espaco[8] = {B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000}; 


int cont = 0;

int ckPin = 13;                // clock para o contador
int displayPin[8] = {12,11,10,9,8,7,6,5};// pinos de dados
int rstPin = 3;    // reset contador

void setup()                   
{
  int i;
  
  pinMode(ckPin, OUTPUT);      
  pinMode(rstPin, OUTPUT);
  
  for(i=0;i<8;i++)
  pinMode(displayPin[i], OUTPUT);  // pinos com saida

}
char *frase ={"TECNOMELQUE.BLOGSPOT.COM  "};  // texto a ser mostrado  {"AABBCC"};
int t =0;
void loop()                     //inicio programa principal     n
{
 t =0;
 while(t < strlen(frase))
  {                            // envia letra por letra da frase para funcao escreve
  escreve(frase[t], 1, 100);  // caracte, modo 0 apenas mostra 1 desloca, tempo 
  
  apaga_coluna(2); // espaco entre letras passa quantidade de colunas
  t++;
  }
  apaga_coluna(8);
  escreve('~',0,1000);
  delay(500);
  escreve('~',0,1000);
  delay(500);
  escreve(' ',0,100);

  
 
}  //fim da funcao principal

//////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////

  void escreve(char letra, int modo, int tmp){
  int k;
  byte *Lt;
  
  switch(letra)   // compara a letra e armazena o vetor correspondente em Lt 
  {
  case 'A':
  Lt=A;
  break;
  
  case 'B':
  Lt=B;
  break;
  
 case 'C':
 Lt=C;
 break;

  case 'D':
 Lt=D;
  break;
  
  case 'E':
Lt=E;
  break;
  
  case 'F':
Lt=F;
  break;
  
  case 'G':
Lt=G;
  break;
  
  case 'H':
Lt=H;
  break;
  
  case 'I':
 Lt=I;
  break;
  
  case 'J':
Lt=J;
  break;
  
  case 'K':
Lt=K;
  break;

  case 'L':
 Lt=L;
  break;
  
  case 'M':
  Lt=M;
  break;
  
  case 'N':
  Lt=N;
  break;
 
  case 'O':
  Lt=O;
  break;
  
  case 'P':
  Lt=P;
  break;
  
  case 'Q':
Lt=Q;
  break;


  case 'R':
Lt=R;
  break;

  case 'S':
Lt=S;
  break;
  
  case 'T':
Lt=T;
  break;
  
  case 'U':
Lt=U;
  break;
  
  case 'V':
Lt=V;
  break;
  
 case 'X':
Lt=X;
  break;

  case '.':
  Lt=ponto;
  break;
  
  case '~':
 Lt=coracao;
  break;
  

 case ' ':
 Lt=espaco;
  break;

 }
  

 //copia o caracter para aux
 // bit por bit é armazenado na matriz aux
   for(int i=0; i<8;i++)
   {
     for(int j=0; j<8;j++)
     {
      if(modo == 0)
      aux[i][j] = bitRead(Lt[i], 7-j); 
      else
       aux2[i][j] = bitRead(Lt[i], 7-j); 
     }
   }

  if ( modo == 0)  // mostra ccaracter
 {
    imprime(tmp);
  }
  else if( modo == 1)  // modo 1 desloca caracter anterior
  {
    
  cont = 0;
   
  for (int passo =0; passo <8; passo++)
  {
    
  for(k=0; k<8; k++)  // linhas
  {
    
  for(int m=0; m < 7;  m++)   
  {
    
   aux[k][m] = aux[k][m+1];

  }
   aux[k][7] = aux2[k][cont];   // ultima coluna de aux recebe a primeira coluna de  E
  }
  
  cont ++; 

  imprime(tmp);  // mostra no display

  } //fim da contagem de passos
} // fim de escreve letra

  }
  
 
   

//////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////

void apaga_coluna(int qtd)
{
 int k;
  
for (int passo =0; passo <qtd; passo++)
  {
    
  for(k=0; k<8; k++)  // linhas
  {
    
  for(int m=0; m < 7;  m++)   
  {
    
   aux[k][m] = aux[k][m+1];

  }
   aux[k][7] = 0;   // ultima coluna de aux recebe a primeira coluna de  E
  }
  
  cont ++;
 
  imprime(100);  // mostra no display

 } //fim da contagem de passos

} //fim apaga coluna

//////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////

 void imprime(int tmp)
 {
 // int tmp = 100;

  for(int a=0; a < tmp; a++)   // tmp representa a quantidades de vezes que cada caracter é exibido
  {     
  digitalWrite(rstPin, LOW); // prepara para reset do contador
  
    for(int i =0; i<8; i++) // linhas
    {
     digitalWrite(ckPin, LOW); // como a mudança ocorre na descida do clock esta linha so faz sentido no segund ciclo
  
   for(int j=0;j<8;j++)   // colunas
  {
      if(aux[i][j] == 1) 
      {
       digitalWrite(displayPin[j], LOW);
      }
     else
     {
     digitalWrite(displayPin[j], HIGH);
     }
  }
    
  delay (0.5); // entre linhas
  
   for(int i=0;i<8;i++)                    // apaga a linha
   digitalWrite(displayPin[i], HIGH);
 
  digitalWrite(ckPin, HIGH); 
   
 }
 
  digitalWrite(rstPin, HIGH);  // reset do contador
 
 }  
 }