Tutorial - como comandar "n" displays de LED multiplexando com o 74HC595

Outro dia apareceu no nosso Sabugosa (apelido do Hacker Clube de São José dos Campos) um monte de displays de oito segmentos e três dígitos, desses daí ao lado. O resultado você pode ver no vídeo abaixo.

Clique para ver o datasheet.


Na figura ao lado, o esquema  visual do display. O dígito mais à esquerda é o 1, os segmentos são identificados por letras de A a G e o ponto decimal é o DP (Dot Point). Essa identificação aparece em 100% dos displays que vi até hoje.

Essa outra figura é o esquema elétrico do display O display é de anodo comum, ou seja, o terminal positivo é conectado em comum para cada display (pinos 8,9,10).

Isso significa que, para ligar um determinado segmento, temos que conectar o pino do segmento correspondente ao terra. Para desligar o segmento, é só alimentar o pino correspondente com a mesma tensão do anodo, 5V no caso de nosso circuito. Isso vai contra o senso comum. que é 5V liga, 0V desliga, mas é assim mesmo.

Temos então um pino que é o anodo de todos os segmentos de um determinado display. O catodo é comum aos segmentos iguais dos diferentes displays, e os segmentos são conectados aos pinos 7,6,3 etc. Observe que eles são interconectados nos três displays, quer dizer, se eu conecto +5V ao pino 8 e 0V aos pinos 6 e 3 vai aparecer o número 1 nos três displays.

Como faço então para mostrar números diferentes nos displays? É só alternar o número a ser mostrado com o display ligado. Supondo que temos que mostrar o número 1,2,3 temos que fazer assim:

1) Habilito o display 1 (5V pino 10).
2) Desenho o número 3 (0V em 7,6,3,2,4)
3) Desligo o display 1 (0V em 10)
4) Ligo o display 2 (5V pino 9)
5) Desenho o número 2 (0V em 7,6,2,1,4)
6) Desligo o display 2 (0V pino 9)
7) Ligo o display 3 (5V pino 8)
8) Desenho o número 1 (5V pinos 6,3)
9) Volto ao passo 1.

E para que é feito dessa forma? Porque não ter um pino para cada segmento? R: Para diminuir o número de conexões necessárias ao acionamento do display. Se fosse um para cada seriam 25 pinos a serem comandados, um para cada segmento mais um anodo comum. No display que usamos temos apenas 11 conexões.

Na minha montagem, para diminuir ainda mais o número de pontos eu conectei todos os segmentos iguais dos quatro displays que usei. Assim se ativamos todos os displays e mostramos um 4, o que aparece é o que vc vê aí embaixo.
Temos então 8 segmentos mais 1 pino para cada chip a comandar, o que dá "apenas" 20 pinos para comandar 12 displays, muito menos que os exatos 100 que seriam necessários caso fossem ligados a todos os pinos (96 LEDs mais 1 catodo comum para cada conjunto de 3 displays).

De toda forma, 20 pinos digitais é o limite máximo do Arduino Uno (14 digitais "verdadeiros" mais os 6 analógicos que podem ser usados como digitais), ou seja, se ligarmos o display direto no Arduino não dá para usar o Arduino prá mais nada. Temos que diminuir mais ainda o número de portas necessárias. Aí entra o 74HC595.

Em 2012 eu fiz alguns posts no meu blog e aqui tb sobre o uso desse chip para controlar LEDs RGB. Lá eu descrevo detalhadamente o funcionamento dele: Parte IParte II. Tem também outros posts sobre a conexão de displays de oito segmentos isolados, então não vou repetir a descrição do bicho. Vou apenas dizer que ele é um conversor serial-paralelo, ou seja, com apenas 3 pinos eu comando oito pinos de saída. Além disso eles podem ser interconectados, o que faz com que eu possa controlar, teoricamente, "n" pinos com apenas três pinos do Arduino.

No nosso caso, usamos 3 chips 595: um deles comanda os segmentos e os outros dois selecionam cada um dos displays. Observem que a gente poderia então ter mais quatro displays sendo comandados pelos mesmos três chips, já que estou usando apenas 12 dos 16 possíveis na saída de dois 595. Não coloquei mais porque simplesmente não coube na protoboard e eu não tinha outra.

Abaixo, um vídeo do sistema funcionando, só que com 100 ms entre cada exibição de display:

Dá para ver "em câmera lenta" os dígitos sendo exibidos. Observe que, como é um contador, o primeiro dígito (à direta, claro) vai mudando.

Quando a gente acelera (retira o delay), tem-se o resultado do primeiro vídeo.

Não vou postar o esquema (Fritzing) do circuito por razão óbvia: a preguiça de desenhar esse "aranzel", como se diz em minha terra. Mas dá para explicar como fiz. Identifique os CIs na figura logo abaixo.

Os dois 595 à esquerda (chamarei de 1 e 2) são responsáveis pela seleção do display corrente, e o do direita, o 3, comanda os segmentos.

Para conectar os CIs entre si são ligados os pinos 11 e 12 de cada CI entre si. O pino 9 do CI 1 é ligado ao pino 14 do CI 2. Essa conexão é a que será usada pelo CI 1 para "exportar" os dados para o CI 2. Da mesma forma, conectamos o CI 2 ao 3.

Abaixo um esquema com dois 595 conectados. O nome dessa conexão em inglês e "daisy chain (seriam "margaridas encadeadas"???), em português se usa "cascata".

Os pinos de saída do chip são conectados aos displays, da seguinte forma:

Vamos chamar os displays de 1 a 4 do menos significativo ao mais significativo, ou seja, da direita para a esquerda.

O esquema de ligação dos CIs está descrito na tabela abaixo:

       
           CI
                        Pino                  Display                  Pino            CI       Pino Arduino        Pino Função
1 1 4 9 Selec. display 2
1 2 4 8 Selec. display 3
1 3 3 10 Selec. display 4
1 4 3 9 Selec. display 5
1 5 3 8 Selec. display 6
1 6 2 10 Selec. display 7
1 7 2 9 Selec. display 8
1 8 GND GND
1 9 2 14   Daisy chain
1 10 Vcc Vcc
1 11 2 11  12   Clock
1 12 2 12  8   Latch
1 13 GND GND
1 14 11  Data
1 15 4 10 Selec. display 1
1 16 Vcc
2 1 1 10 Selec. display 10
2 2 1 9 Selec. display 11
2 3 1 8 Selec. display 12
2 4 Não usado
2 5 Não usado
2 6 Não usado
2 7 Não usado
2 8 GND GND
2 9 3 14   Daisy chain
2 10 Vcc Vcc
2 11 3 11   Clock
2 12 3 12   Latch
2 13 GND GND
2 14 1 9   Data
2 15 2 8 Seleciona display 9
2 16 Vcc Vcc
3 1 1 6 Segmento B
3 2 1 3 Segmento C
3 3 1 2 Segmento D
3 4 1 1 Segmento E
3 5 1 5 Segmento F
3 6 1 4 Segmento G
3 7 1 11 dot
3 8 GND GND
3 9 2 14   Daisy chain
3 10 Vcc Vcc
3 11 2 11   Clock
3 12 2 12   Latch
3 13 GND GND
3 14 Data
3 15 1 7 Segmento A
3 16 Vcc Vcc

Observe que tem pinos que são conectados a mais de um ponto simultãneamente, ex: os pinos 9, 11 e 12 do CI 1 estão também conectados ao CI 2 e também ao Arduino.

Além dessas conexões é necessário ligar os segmentos correspondentes da seguinte forma: conecte os pinos 7,6,3,2,1,5,4,11 dos quatro chips aos seus correspondentes, ou seja o pino 7 do display 1 ao pino 7 do display 2, o pino 7 do display 2 ao pino 7 do display 3, o pino 6 do display 1 ao pino 6 do display 2 e assim por diante. Uma sugestão: faça PRIMEIRO essa conexão entre os displays, acho que deve ser mais fácil assim.

Agora, o  software:

/*****************************************************************
 Controle de "n" displays de LED 3x8 tipo Para Light A-573H
******************************************************************/
 
// Este programa funcionará com qualquer número de dígitos
#define nDigits 12 // número de dígitos a serem usados
#define nCIs    3  // número de CIs 595 existentes no
                   // circuito, no mínimo 2.
 
// pinos para comunicação com o CI 74HC595
#define latchPin 8
#define clockPin 12
#define dataPin  11
 
/* máscara de bits para os dígitos, conforme diagrama abaixo:
    A
    --
  F|  |B
   |G |
    --
  E|  |C
   |  |
    -- *d
    D
*/
byte digits[] =
//  dGFEDCBA - segmentos
  {B00111111, // 0
   B00000110, // 1
   B01011011, // 2
   B01001111, // 3
   B01100110, // 4
   B01101101, // 5
   B01111101, // 6
   B00000111, // 7
   B01111111, // 8
   B01101111, // 9
   B10000000, // dot
   B00000000}; // blank 
 
// valor apresentado no display
uint64_t displayValue = 0;
 
// rotina que envia um byte para o 595 de maneira serial,
// um bit de cada vez
 
void shiftOut(byte dataOut)
{
  boolean pinState;
// prepara o envio
  digitalWrite(dataPin, LOW);
  digitalWrite(clockPin, LOW);
  for (int i=0; i<=7; i++)
  {
    digitalWrite(clockPin, LOW);
// se o bit "i" está ligado, envia para o pino de dados um byte com
// só o primeiro bit ligado (HIGH), se não, envia 0 (LOW)
    if ( dataOut & (1 i) )
      pinState = HIGH;
    else
      pinState = LOW;
// envia o bit
    digitalWrite(dataPin, pinState);
// informa ao chip que terminou o envio
    digitalWrite(clockPin, HIGH);
    digitalWrite(dataPin, LOW);
  }
  digitalWrite(clockPin, LOW);
}
 
// "desenha" um determinado dígito (digit) no display
// (display). O número 1 é o menos significativo
// (primeiro da direita) e assim por diante.
void displayDigit(int display, int digit)
{
// primeiro montamos o endereço do display, quer dizer,
// vamos indicar qual display será
// habilitado e mostrará o dígito.
  int data1, data2;
  if(display <= 8)
  {
    data1 = B00000001 (8-display);
    data2 = B00000000;
  }
  else
  {
    data1 = B00000000;
    data2 = B00000001 (16-display);
  }
  // prepara o envio
  digitalWrite(latchPin, LOW);
  // envia o dígito para o CI 3
  shiftOut(digits[digit]);
  // envia o endereço (máscara de bits) indicando
  // o display a ser ativado
  shiftOut(data1);
  // se tiver mais de 1 CI de endereçamento, envia
  // a outra parte do endereço
  if (nCIs > 2)
    shiftOut(data2);
  // termina o envio
  digitalWrite(latchPin, HIGH);
}
 
// exibe um número inteiro
void display(uint64_t number)
{
    for (int i = 1;i <= nDigits && number != 0;i++)
    {
      // pega o i-ésimo dígito
      int digit = number % 10LL;
      // envia para exibição
      displayDigit(i,digit);
      // trunca o número, eliminando o último
      // dígito exibido
      number /= 10LL;
    }
}
 
void setup()
{
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin,  OUTPUT); 
  // "inverte" a máscara de bits, porque os displays são
  // anodo comum => o bit "1" desliga o LED.
  // Se os seus displays forem catodo comum,
  // delete as próximas duas linhas.
  for(int i=0; i /code> sizeof(digits)/sizeof(digits[0]);i++)
    digits[i]=~digits[i];
}
 
void loop()
{
  displayValue=188459823945LL;
  while(displayValue < 9999999999999LL)
  {
    display(displayValue);
    displayValue++;
  }
}

O fonte está todo comentado acima.

As constantes nDigits nCIs servem para vc adaptar o programa ao seu circuito, informando quantos CIs 595 vc tem e o número de dígitos que vc usou. Para usar mais ou menos CIs (e dígitos, consequentemente) vc deve adaptar o circuito que publiquei, não é difícil. Se forem, por exemplo, seis displays, vc pode usar 2 CIs, um para controlar os segmentos e outro para multiplexar (selecionar) os displays. Nesse caso a diferença é que vc não terá o CI 1 e consequentemente nenhuma de suas conexões e o pino de dados do CI 2 (pino 14) será conectado ao Arduino (pino 11). Mas nada impede que vc controle menos displays com algum CI sobrando, ou seja, os números são "quase" independentes: para controlar 10 displays vc é obrigado a ter no mínimo 3 CIs, já para controlar 4 displays apenas 2 são necessários, mas se vc tiver ligado 3 o programa funcionará.

Uma curiosidade desse sketch é que eu usei o tipo de dado uint64_t, que é um inteiro 64 bits suportado pelo Arduino. Usando-se esse tipo de dados podemos manipular um decimal até 20 dígitos.

É isso, abracadabraço a todos!

Exibições: 13787

Comentar

Você precisa ser um membro de Laboratorio de Garagem (arduino, eletrônica, robotica, hacking) para adicionar comentários!

Entrar em Laboratorio de Garagem (arduino, eletrônica, robotica, hacking)

Comentário de Mauro Assis em 19 agosto 2013 às 21:01

Putz, bicho obrigado! Vc achou a amolação, vou consertar.

Agora, quanto a fuçar no código, se está acendendo tudo o problema é na ligação. E ela é difícil de fazer, muito fio prá ligar.

Comentário de Judenilson Araujo em 19 agosto 2013 às 20:28

Na verdade faltava declarar Arduino.h no escopo! Blz, compilou, mas agora acende tudo vixe! kkkkk vou fuçar no código aki.

Comentário de Judenilson Araujo em 19 agosto 2013 às 19:31

Não é algum parâmetro nos define não?

Comentário de Judenilson Araujo em 19 agosto 2013 às 19:19

Olá Mauro, peguei o código do seu blog, os erros diminuiram (eba!!!!) mas ainda tem: kkkkk

In file included from sketch_aug19a.ino:27:
/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/cores/arduino/Arduino.h:113: error: expected ',' or '...' before numeric constant
/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/cores/arduino/Arduino.h:114: error: expected ',' or '...' before numeric constant

Infelizmente estou aperreando pq não conheço esse erro.

Comentário de Mauro Assis em 19 agosto 2013 às 19:12

IMPORTANTE: quem for usar o código desse post favor pegar a versão em:

http://automatobr.blogspot.com.br/2013/08/mais-sobre-displays-de-le...

Comentário de Mauro Assis em 19 agosto 2013 às 19:11

Judenilson,

Agora reparei que o editor do Lab ferrou meu código! Por favor, pegue lá no meu blog o código correto.

http://automatobr.blogspot.com.br/2013/08/mais-sobre-displays-de-le...

Desculpaí!

Mauro

Comentário de Judenilson Araujo em 19 agosto 2013 às 18:44

Boa noite Mauro ao tentar compilar o código aparecem os erros:

In file included from sketch_aug19a.ino:22:
/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/cores/arduino/Arduino.h:113: error: expected ',' or '...' before numeric constant
/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/cores/arduino/Arduino.h:114: error: expected ',' or '...' before numeric constant
sketch_aug19a.ino: In function 'void shiftOut(byte)':
sketch_aug19a:54: error: expected `)' before 'i'
sketch_aug19a:55: error: expected `)' before ';' token
sketch_aug19a.ino: In function 'void displayDigit(int, int)':
sketch_aug19a:78: error: '1' cannot be used as a function
sketch_aug19a:84: error: '1' cannot be used as a function
sketch_aug19a.ino: In function 'void setup()':
sketch_aug19a:125: error: 'code' was not declared in this scope

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço