quinta-feira, janeiro 12, 2012

Display Gráfico Nokia 5110 - Parte 4

Continuando a nossa série, vamos ver o hardware e software necessários para interligar o display a um Arduino UNO.


Hardware

O Arduino UNO possui uma saída de 3,3V, porém as saídas digitais operam com 5V. Por este motivo é necessário colocar o divisor resistivo nos sinais lógicos; o display em si é alimentado pela saída de 3,3V. No circuito abaixo os LEDs de backlight são alimentados pela saída de 5V, através de um resistor.

Para quem não enxergar na figura (mesmo depois de clicar para ampliar) são as seguintes as conexões no display:
  • pino 1 (Vcc) com a saída de 3,3V do Arduino.
  • pino 2 (GND) com o sinal GND do Arduino.
  • pino 3 (SCE) com o Digital 7 do Arduino, através de um divisor 15K / 22K
  • pino 4 (RST) com o Digital 6 do Arduino, através de um divisor 15K / 22K
  • pino 5 (D/C) com o Digital 5 do Arduino, através de um divisor 15K / 22K
  • pino 6 (DIN) com o Digital 4 do Arduino, através de um divisor 15K / 22K
  • pino 7 (SCK) com o Digital 3 do Arduino, através de um divisor 15K / 22K
  • pino 8 (LED) com a saída de 5V do Arduino, através de um resistor de 390R

Software

Vamos usar alguns defines para determinar quais os pinos usados na conexão:
// Conexões do display ao Arduino
#define PIN_SCE   7
#define PIN_RESET 6
#define PIN_DC    5
#define PIN_SDIN  4
#define PIN_SCLK  3
O primeiro passo é termos uma rotina que envie um byte serialmente ao display,  a biblioteca do Arduino possui a função shiftOut que faz o trabalho pesado:
// Seleção de dado ou comando
#define LCD_CMD   LOW
#define LCD_DAT   HIGH

// Envia um byte para o controlador do display
// dc:   LCD_CMD ou LCD_DAT
// data: byte a enviar
void LcdWrite(byte dc, byte data)
{
  digitalWrite(PIN_DC, dc);
  digitalWrite(PIN_SCE, LOW);
  shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data);
  digitalWrite(PIN_SCE, HIGH);
}
Com esta rotina podemos criar uma rotina de iniciação do display, que envia os comandos necessários para a configuração. Os valor usado no comando Set Vop pode precisar ser ajustado para obter um bom contraste.
// Iniciação do display
void LcdInitialise(void)
{
  // define a direção dos pinos de E/S
  pinMode(PIN_SCE, OUTPUT);
  pinMode(PIN_RESET, OUTPUT);
  pinMode(PIN_DC, OUTPUT);
  pinMode(PIN_SDIN, OUTPUT);
  pinMode(PIN_SCLK, OUTPUT);
  
  // executa um reset do controlador
  digitalWrite(PIN_RESET, LOW);
  digitalWrite(PIN_RESET, HIGH);
  
  // envia os comandos de iniciação
  LcdWrite( LCD_CMD, 0x21 ); // LCD Extended Commands.
  LcdWrite( LCD_CMD, 0xaf ); // Set LCD Vop (Contraste)
  LcdWrite( LCD_CMD, 0x04 ); // Set Temp coefficent
  LcdWrite( LCD_CMD, 0x14 ); // LCD bias mode 1:48
  LcdWrite( LCD_CMD, 0x20 ); // LCD Basic Commands.
  LcdWrite( LCD_CMD, 0x0c ); // LCD no modo normal  
}
Podemos agora implementar uma limpeza da tela, que preenche toda a memória com zeros:
// Limpa a tela
// Tamanho da tela
#define LCD_DX    84
#define LCD_DY    48

void LcdClear(void)
{
  // posiciona ponteiro no inicio da memória
  LcdWrite( LCD_CMD, 0x40);  
  LcdWrite( LCD_CMD, 0x80);
  
  // preenche a memória com zeros
  for (int index = 0; index < LCD_DX * LCD_DY / 8; index++)
  {
    LcdWrite(LCD_DAT, 0x00);
  }
}
Vamos implementar agora a escrita de caracteres, usando uma matriz de 7x8. Para simplificar, vamos considerar que os caracteres estão sempre alinhados em um grid alfanumérico de 6 linhas de 12 caracteres. O aspecto dos caracteres é armazenado em uma matriz (copiada do exemplo no site da Sparkfun), vamos deixar uma coluna em branco antes e depois de cada caracter:
// Definição dos caracteres
static const byte ASCII[][5] =
{
 {0x00, 0x00, 0x00, 0x00, 0x00} // 20  
,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 !
,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 "
,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 #
// etc
};
O posicionamento em uma certa linha e coluna é feita através dos comandos Set Address:
// Posiciona em uma determina linha e coluna alfanuméricas
void LcdPos(int lin, int col)
{
  LcdWrite( LCD_CMD, 0x40 + lin);  
  LcdWrite( LCD_CMD, 0x80 + col*7);  
}
A escrita de um caracter consiste simplesmente em copiar para a memória do display os 5 bytes correspondente ao caracter, mais um zero antes e depois:
// Escreve um caracter na posição atual
void LcdWriteChar(char character)
{
  LcdWrite(LCD_DAT, 0x00);
  for (int index = 0; index < 5; index++)
  {
    LcdWrite(LCD_DAT, ASCII[character - 0x20][index]);
  }
  LcdWrite(LCD_DAT, 0x00);
}
Feito tudo isto, é trivial fazer uma rotina para apresentar um string:
// Escreve um string a partir de uma certa
// linha e coluna
void LcdWriteString(char *characters, int lin, int col)
{
  LcdPos (lin, col);  
  while (*characters)
    LcdWriteChar(*characters++);
}

O sketch correspondente (ldc5110_1.zip) pode ser baixado dos arquivos do blog (ver no alto à direita).

Nenhum comentário: