sexta-feira, fevereiro 27, 2015

Display 16x2 com Interface I2C Conectado ao Arduino

Neste post vamos ver como usar com o Arduino o display I2C que examinamos no post anterior.


Montagem

Do ponto de vista elétrico, a montagem é muito simples. Mas, na prática, existem alguns detalhes a considerar.

O primeiro detalhe é que os pinos do display tem um espaçamento de 0.05", o que é a metade do espaçamento padrão para integrados DIP. Portanto não dá para espetar em uma protoboard. Para o meu teste eu soldei alguns fios grosseiramente. Olhando o display de frente, o pino 1 é o primeiro da esquerda

Além dos pinos na parte inferior do display é preciso ligar o backlight, cujos terminais estão na margem direita. Olhando o display de frente, o terminal superior é o catodo (K) e o inferior é o anodo (A).

Operando a 5V, as ligações necessárias são:
  • Os pinos 1 e 4 devem ser ligados ao 5V do Arduino.
  • Os pinos 2 e 3 não são usados.
  • O pino 5 e o terminal K devem ser ligados ao GND do Arduino.
  • O pino 6 deve ser ligado ao pino SDA do Arduino. No Uno é o pino A4, em outros modelos muda (veja aqui).
  • O pino 7 deve ser ligado ao pino SCL do Arquino, que no Uno é o A5.
  • Liguei o pino 8 (Reset) ao pino 12 do Arduino. Se você está confiante que nada vai dar errado durante o uso, pode ligar através de um resistor de 10K ao 5V.
  • O terminal A deve ser ligado ao 5V através de um resistor. Usando os números do datasheet (tensão do LED do backlight de 3,5V e corrente de 30mA), o valor deveria ser 50 ohms. Eu usei 220 ohms e achei que ficou bom.
I2C

A interface I2C (ou TWI para não usar a marca registrada) é suportada pelo hardware do ATmega e pela biblioteca padrão Wire do Arduino (documentação aqui). Resumindo uma longa história, o I2C usa dois sinais, SCL (clock, gerado pelo mestre) e SDA (dados, bidirecional); sinalizações especiais (start e stop) indicam o início e o fim de uma comunicação. É uma interface do tipo barramento ("varal") onde múltiplos dispositivos podem ser ligados a um mestre (no nosso caso o Arduino). Cada dispositivo tem um endereço (no caso deste display, 0x3E). São necessários resistores de pull-up para os dois sinais, mas a biblioteca Wire ativa os resistores internos do ATmega.

O uso da biblioteca, no nosso caso, ser restringe a 4 funções:
  • Wire.begin: configura o hardware do ATmega
  • Wire.beginTransmission: determina o endereço do dispositivo com que vamos falar
  • Wire.write: coloca um byte na fila de transmissão
  • Wire.endTransmission: transmite os bytes enfileirados, enviando um start no início e um stop ao final
Programa de Teste

Este programa de teste se limita a mostrar uma mensagem e um relógio (tosco) no display. A sequência de iniciação foi copiada diretamente do datasheet; por ela dá para perceber que a interface I2C se conecta ao controlador de display no modo 8 bits. Uma vez que não é possível ler o sinal Busy (ocupado) do controlador, coloquei algumas pausas, grosseiramente baseadas nos tempos indicados no datasheet. O datasheet não explicita isto, mas o sinal de Reset é ativo baixo (isto é, precisa ficar em nível alto durante a operação normal).
  1. // Teste de conexão do display WO1602G ao Arduino  
  2. // http://dqsoft.blogspot.com  
  3.   
  4. #include <Wire.h>  
  5.   
  6. const int pinRst = 12;  
  7. const int addrDisp = 0x3E;  
  8. const byte selReg = 0;  
  9. const byte selDado = 0x40;  
  10.   
  11. byte seg, minuto, hora;  
  12.   
  13. // Iniciação  
  14. void setup()  
  15. {  
  16.   // Pulsa pino de reset  
  17.   pinMode (pinRst, OUTPUT);  
  18.   digitalWrite (pinRst, LOW);  
  19.   delay (1);  
  20.   digitalWrite (pinRst, HIGH);  
  21.   delay (500);  
  22.     
  23.   // Inicia I2C  
  24.   Wire.begin();  
  25.     
  26.   // Configura o display  
  27.   DisplayInit();  
  28.     
  29.   // Mostra mensagens  
  30.   DisplayWrite (0, 0, "DQSoft");   
  31.   DisplayWrite (1, 0, "00:00:00");   
  32. }  
  33.   
  34. // Rotina chamada periodicamente  
  35. // Coloca um "relógio" na segunda linha  
  36. void loop ()  
  37. {  
  38.   if (++seg == 60)  
  39.   {  
  40.     seg = 0;  
  41.     if (++minuto == 60)  
  42.     {  
  43.       minuto = 0;  
  44.       if (++hora == 99)  
  45.         hora = 0;  
  46.       DisplayWriteDec (1, 0, hora);  
  47.     }  
  48.     DisplayWriteDec (1, 3, minuto);  
  49.   }  
  50.   DisplayWriteDec (1, 6, seg);  
  51.   delay (1000);   // aguarda um segundo  
  52. }  
  53.   
  54. // Faz a iniciação do display  
  55. void DisplayInit ()  
  56. {  
  57.   DisplayOut (selReg, 0x38);  
  58.   DisplayOut (selReg, 0x39);  
  59.   DisplayOut (selReg, 0x14);  
  60.   DisplayOut (selReg, 0x79);  
  61.   DisplayOut (selReg, 0x50);  
  62.   DisplayOut (selReg, 0x6C);  
  63.   DisplayOut (selReg, 0x0C);  
  64.   DisplayOut (selReg, 0x01);  
  65.   delay (2);  
  66. }  
  67.   
  68. // Mostra número decimal de 00 a 99  
  69. void DisplayWriteDec (byte l, byte c, byte num)  
  70. {  
  71.   char aux[3];  
  72.     
  73.   aux[0] = 0x30 + num / 10;  
  74.   aux[1] = 0x30 + num % 10;  
  75.   aux[2] = 0;  
  76.   DisplayWrite (l, c, aux);  
  77. }  
  78.   
  79. // Posiciona o cursor e mostra mensagem  
  80. void DisplayWrite (byte l, byte c, char *msg)  
  81. {  
  82.   byte addr = c;  
  83.   if (l == 1)  
  84.     addr += 0x40;  
  85.   DisplayOut (selReg, 0x80 + addr);  
  86.   while (*msg)  
  87.   {  
  88.     DisplayOut (selDado, *msg++);  
  89.   }  
  90. }  
  91.   
  92.   
  93. // Envia um byte de controle e um byte de dado  
  94. void DisplayOut (byte c, byte d)  
  95. {  
  96.   Wire.beginTransmission (addrDisp);  
  97.   Wire.write (c);  
  98.   Wire.write (d);  
  99.   Wire.endTransmission ();  
  100.   delayMicroseconds (30);  
  101. }  
Concluindo, gostei muito deste display. Existem alguns desafios quanto à montagem mecânica, mas o resultado é muito legível e elegante (a foto no início não faz justiça ao display).

Nenhum comentário: