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: 12112

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 Judenilson Araujo em 22 agosto 2013 às 1:31

Esquece o q postei. Consegui!!!!

Acho q vai ficando mais tarde eu vou me aperriando pra dormir e a cabeça começa a raciocinar mais rápido pra eu poder deitar! kkkkkkkkk

Valeu.

Comentário de Judenilson Araujo em 22 agosto 2013 às 0:43

Fala Mauro, fui dormir ontem às 4:30 da madruga, mas consegui fazer os displays funcionarem, por sinal muito bem, não tem lag nem pisca! Tá bacana!

Mas agora estou com outro problema! Consigo colocar um número estático para aparecer nos displays ou até mesmo um contador, mas isso não vai me servir para o projeto, tenho q coletar dados da serial e passar para os Displays. 

Estou usando o Monitor Serial para analisar e não estou conseguindo enviar dados posteriores. Deixa eu tentar ser entendido...

Assim eu digito SP12345678901234567890123 e através de uma função if e switch eu analiso as duas letras e mudo as variáveis dos caracteres. Mas, se eu digitar novamente outros número NADA ACONTECE.

Tem algum exemplo de alguma coisa aí com relação a pegar dados da serial e transformar em conjunto de Bytes exibindo num display de 7 segmentos?

Abraço. 

Comentário de Mauro Assis em 20 agosto 2013 às 20:31

O problema não é a velocidade para mudar os números, mas sim para alternar entre os displays.

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

Eu também não preciso de velocidade para mudar os dados, pois a maioria do tempo será estático, só quando fizer alguma alteração no piloto automático que vai ser mudado e geralmente alguns digitos. Vou botar a cabeça pra pensar e ver o q sai.

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

Pensei noutra coisa também, como tenho 2 displays q passam informações fixas, um com a letra A e outro com sinal -, posso colocá-los isolados em duas portas digitais do arduino. Já diminui 2! kkkkkkkkk Mas ainda sim ficam 21!

Tenho q tentar resolver!

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

FSX é Flight Simulator X.

Vc não lembra qual o nome desse Circuito de 35 pinos não? 

Se bem q não sei como, mas já tem algumas pessoas que estão fazendo isso que eu quero com Arduino, mas sabe como é né!?

Faz, usa até aplicativos de terceiros, coloca no youtube para se mostrar e não compartilha o conhecimento. Mas, tudo bem, minha idéia e fazer e colocar na internet para quem quizer copiar.

Estou fazendo do ZERO, já fiz a programação em C# para comunicação entre a placa do Arduino e o Simulador, mas tá faltando apenas essa parte dos displays!!! As funções dos botões e encoders já estão escritas.

Vou continuar a luta aqui e ver o q consigo fazer. Sim, baixando para 15 digitos fica perfeito!!!

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

Desculpe-me a ignorância, mas o que é FSX?

Vi que já tá usando o Mega, então tá muito rápido já. O Arduino Due, com processador ARM, é bem mais rápido, mas ele funciona com 3,3V, não sei como ficaria a comunicação com os HC595, só testando.

Duas funções não vão ajudar em nada, vai ficar é mais lento. Só se fossem duas placas controladoras, uma só para os displays e a outra para as outras funcionalidades. É que vai ficar enrolado vc gerenciar um monte de outras funções e ainda "tocar" esse monte de displays.

Eu tentei fazer um multitarefa com esses displays (a rotina do display rodando em background), mas o Arduino tb não aguentou.

Uma outra tentativa seria substituir o HC595 por um chip maior que tenha a mesma função. Outro dia vi um de 35 pinos em um circuito. Não sei se faria muita diferença, só testando.

Comentário de Judenilson Araujo em 20 agosto 2013 às 18:59

Respondendo sua pergunta, separar a função, seria colocar mais 2 CIs e fazer 2 funções, uma pra controlar 10 displays e outra para 13.

Comentário de Judenilson Araujo em 20 agosto 2013 às 18:54

Olá Mauro, como eu sou mané msm! Perdi quase a tarde toda querendo achar o erro no código e vc deve ter achado assim q viu!!!! Putz.

estou tentando fazer um piloto automático para o FSX.

Está assim:

E quero q fique assim:

O problema é que além dos números temos 2 displays q tem q exibir se vc contar da esquerda p direita 4 display letra "A" e 16 display sinal "-".

Comentário de Mauro Assis em 20 agosto 2013 às 17:13

Essa sua função (funcionando, claro) serve para até 24 dígitos (8x3).

Essa instrução está errada:

if (8 > display <= 16)

O certo seria:

if ((display > 8) && (display <= 16))

O que é "separar a função"?

O que faz ficar lento é o uint64_t, o inteiro com 64 bits é muito para o processador do Arduino.]

Acontece que não tem outro jeito de representar um número tão grande, então temo que não dá para ficar muito mas rápido, ainda que possa-se tentar algum tipo de otimização.

Mas por que raios vc quer representar um número tão grande??? 

Destaques

Registre-se no
Lab de Garagem
Clicando aqui

Convide um
amigo para fazer
parte

curso gratis de arduino

© 2020   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço