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

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 20 agosto 2013 às 16:13

Fiz umas modificações no displayDigit e está funcionando. Mas, ainda tem bronca!

void displayDigit(int display, int digit)
{
int data1, data2, data3;
if(display <= 8)
{
data1 = B00000001 *Deslocamento de bits à esquerda (8-display);
data2 = B00000000;
data3 = B00000000;
}else
if (8 > display <= 16)
{
data1 = B00000000;
data2 = B00000001 *Deslocamento de bits à esquerda (16-display);
data3 = B00000000;
}else
if (16 > display)
{
data1 = B00000000;
data2 = B00000000;
data3 = B00000001 *Deslocamento de bits à esquerda (24-display);
}
// prepara o envio
digitalWrite(latchPin, LOW);
shiftOut(data1);
shiftOut(data2);
shiftOut(data3); 
shiftOut(digits[digit]);
digitalWrite(latchPin, HIGH);
}

Como enviar um número com 23 digitos?

Acredito que vou ter q separar a função.

Ainda dá pra sentir os leds piscarem, tem como fazer a troca mais rápido?

*Deslocamento de bits à esquerda substituir pelo operador, pq aqui ao publicar o mesmo some!!!!!!!!!!

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

Valeu Mauro, depois dessa explicação ficou mais fácil entender. 

Praticamente o Ci passa os dados depois q "fica cheio"!

Agora sim... dá pra entender a estrutura. Vou mecher no código e tentar resolver aqui. Assim q conseguir aviso e posto, pode servir p outra pessoa tb!

Abraço.

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

Judenilson,

Os dados são transmitidos ao CI pelo shiftOut. Primeiro eu mando o byte que vai para o último CD, depois o byte do penúltimo e por fim o do primeiro. O trecho do código é esse:

 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);

Quando eu mando o primeiro byte, o primeiro CI recebe. Quando eu começo a mandar o segundo, ele passa a passar os dados para o segundo CI, numa reação em cadeia. O mesmo acotnece com o 3o. byte, que "empurra" os outros dois. Quando eu faço o latchPin=HIGH (e os pinos LATCH dos três CIs estão interligados ao pino 8 do Arduino), eles entendem que acabou a transmissão e ligam os bits de saída, ou seja, mandam cada bit para a sua perna correspondente.

O CI 3 desenha os números em todos os displays ao mesmo tempo. E porque não acende tudo junto? Porque os CIs 1 e 2 são responsáveis por selecionar um único display para ser mostrador por vez. Vc pode reparar no código que calcula os valores deles que só um bit é ligado por vez, correspondendo ao display que deverá ser mostrado. No primeiro post tem lá uma descrição de como isso é feito no caso de uso de três displays, dê uma olhada lá.  Como no meu exemplo eu tenho 3 displays, um só chip pode ser usado para a seleção. Na montagem que fiz são 12, então precisa de mais um, e ainda sobram 4 bits, já que cada CI controla 8.

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

Não deu certo com menos display tb não, na verdade desliguei só a conexão de comunicação do Ci4, mas os leds ainda acendiam.

Olha só, consegui fazer os Leds pelo menos mostrarem os números certos, numa contagem de 0 a 9, mas com outro código do site oficial do arduino.

Eu não estou entendendo é:

Os CIs são ligados em série, Pino 9 Ci1 para o 14 Ci2, 9 Ci2 para 14 Ci3, 9 Ci3 para 14 Ci4, que vai ficar sobrando com o pino 9. Até aí tudo bem, mas não entendo como os dados são transmitidos, para selecionar qual Ci vai acender o q?

O primeiro Ci q é responsável pela gestão dos caracteres, blz, ele é o primeiro a receber os dados.

Tipo assim, para entender melhor, como envio um conjunto de bits que acenda o número 5 no 3 display da esquerda para direita? E se for no 10 display?

Abraço.

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

Tem que mexer também mais embaixo, nsa chamadas do shiftOut. Vou dormir pq estou com sono. Amanhã arrumo. Mas funcionou com menos displays?

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

È, vc tem razão, teria que mudar sim. Com três funcionou?

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

Teria como vc explicar melhor essa parte do código:

 int data1, data2;
  if(display <= 8)
  {
    data1 = B00000001 (8-display);
    data2 = B00000000;
  }
  else
  {
    data1 = B00000000;
    data2 = B00000001 (16-display);
  }
Acredito q com mais de 3 CIs deveria ter alguma mudança aí, ou estou enganado?
Comentário de Judenilson Araujo em 19 agosto 2013 às 23:29

Mauro, me tira uma dúvida, vc tentou rodar o programa com mais de 3 CIs? Estou tentando com 4, mas não estou conseguindo.

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

É, será prudente fazer isso. Eu só testei com 3 chips, não fiz com quatro, faltou protoboard.

Vc tá montando isso só por diversão ou vai usar em algo maior?

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

kkkk ta uma zona aki!!!! uma bagunça na exibição dos displays! Com uma pulga atrás da orelha, fui verificar todos novamente (23) e achei a porcaria de um curto entre o pino 6 e 7. Fiquei surpreso e pensando q ia ficar bacana! Me enganei! kkkkkkkkk Ainda tá uma zona!

Estou usando 4 CIs para os 23 displays, vou desligar 2 e tentar moer com dois apenas e ver se faço pelo menos funcionar! Será possível! kkkkk

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço