TUTORIAL

PROGRAMAÇÃO DE LCD (Liquid Crystal Display) COM O 80C51

Freqüentemente, um programa em microprocessadores/ microcontroladores deve interagir com o meio externo usando dispositivos de entrada e saída (I/O) para permitir a comunicação diretamente com o ser humano.
Um dos dispositivos mais comuns de acoplamento é o LCD. Alguns dos LCDs mais utilizados são os displays de 16x2 e 20x2. Isto significa 16 e 20 caracteres em cada uma das duas linhas do display respectivamente.
Felizmente, o HD44780U, que é o controlador padrão mais popular utilizado pelos fabricantes, permite fazer uma comunicação de forma simples com a maioria dos LCDs.
HD44780 - OPERAÇÃO E COMANDOS
O HD44780 requer 3 linhas de controle e também 4 ou 8 linhas de I/O para o bus de dados. O usuário deve selecionar operação com 4 ou 8 bits no bus de dados. Se o usuário selecionar 4 bits, o LCD necessitará de 7 linhas ( 3 para controle e 4 para dados ). Se selecionar 8 bits, serão 11 linhas ( 3 para controle e 8 para dados ).
As três linhas de controle são denominadas EN, RS, RW, ou variações próximas como E, R/S e R/W.
 A linha EN é chamada “Enable” . É usada para mostrar ao LCD que dados estão sendo enviados. Para enviá-los, seu programa deverá “setar” o EN e depois “setar” as duas outras linhas de comando e/ou colocar os dados no bus de dados. Quando as duas outras linhas estiverem definidas, leve o EN para nível baixo novamente. A transição 1-0 diz ao HD44780 para capturar os dados nas duas outras linhas de controle, no bus de dados e tratar isso como um comando.
 A linha RS é chamada de “Register Select”. Quando estiver em nível baixo, o dado é tratado como um comando ou uma instrução especial ( como limpar tela, posicionar cursor, etc...). Quando estiver em nível alto o dado que está sendo enviado é do tipo texto. Por exemplo, para mostrar a letra “T” no LCD você deverá “setar” a linha RS.
 A linha RW é o “Read/Write”. Quando RW estiver em nível baixo, a informação que está no bus de dados é de escrita no LCD. Quando estiver em nível alto, a informação é de leitura. Apenas uma instrução ( “Get LCD status” ) é um comando de leitura. Todos os outros comandos são de escrita, assim a linha RW deverá quase sempre estar em nível baixo.
Finalmente, o bus de dados consiste de 4 ou 8 linhas ( depende do modo de operação selecionado pelo usuário ). No caso de 8 bits, as linhas de referência são como DB0, DB1, DB2, DB3, DB4, DB5, DB6 e DB7.


EXEMPLO DE CONFIGURAÇÃO DE HARDWARE

Como mencionado anteriormente, o LCD requer 8 ou 11 linhas I/O para comunicação.
Aqui usaremos 8 bits para o bus de dados, assim estaremos usando 11 pinos I/O do 8051 para fazer a interface com o LCD.
Vamos desenhar um simples exemplo de como o LCD irá conectar-se com o 8051:
No programa para acessar o LCD, serão usadas constantes para as portas do 8051, para que possam ser referenciadas pelos nomes das linhas do HD44780 ao invés de P0.1, P0.2, etc. 
 DB0    EQU    P1.0
 DB1    EQU    P1.1
 DB2    EQU   P1.2
 DB3    EQU   P1.3
 DB4    EQU   P1.4
 DB5    EQU   P1.5
 DB6   EQU   P1.6
 DB7    EQU   P1.7
 EN      EQU   P3.7
 RS      EQU   P3.6 
 RW    EQU   P3.5
 DATA   EQU   P1
Tendo feito o relacionamento acima, as linhas I/O podem ser referenciadas pelos nomes das portas do HD44780. Por exemplo, para “setar” a linha RW, basta executar o seguinte comando:
SETB RW

MANIPULANDO A LINHA EN

Como mencionado acima, a EN é usada para dizer ao LCD para capturar as informações colocadas no bus de dados e nas duas outras linhas de controle. Note que a EN deve estar em nível alto/baixo antes/depois de cada instrução enviada ao LCD, dessa forma o comando é de leitura ou escrita, tipo texto ou comando. Em resumo, deve-se manipular a EN a cada comando enviado ao LCD. Se não for realizada a transição 1-0 (nível alto-baixo), o LCD não será capaz de realizar a captura das informações nas duas outras linhas de controle.
Portanto, antes de interagir com o LCD, uma opção é levar a EN para o nível alto com a seguinte instrução:
SETB EN
Depois de colocadas as informações nas duas outras linhas e no bus de dados, deve-se “zerar” a EN novamente, assim:
CLR EN
Nota: O LCD interpreta e executa comandos no instante em que a linha EN é colocada em nível baixo e isso requer uma certa quantidade de tempo para a execução dos comandos. O tempo solicitado depende da instrução e da velocidade que o cristal está conectado ao HD44780.


VERIFICANDO TAREFAS NO LCD

Como mencionado acima, o tempo de execução de cada instrução depende do tipo e da velocidade de comunicação do LCD com o HD44780.
Há um método quando colocamos um código de escrita que exige um determinado tempo de espera para o LCD executar as instruções, esse método não é flexível. Se a freqüência do cristal é modificada, o software precisará mudá-lo. Adicionalmente, se trocarmos o LCD por outro, embora com o HD44780 compatível, que necessite de mais tempo para executar operações, o programa não funcionará até que o tempo não seja ajustado de forma correta.
O método mais utilizado para saber se o LCD ainda está executando a ultima instrução recebida é usar o comando “get LCD status”.
O comando “get LCD status” retornará dois tipos de informação, e  a informação que é útil agora está no bit DB7. Em resumo, quando usamos o comando “get LCD status” o LCD imediatamente seta o bit DB7 se ele ainda está executando algum comando ou baixa o mesmo para indicar que não está executando nada. Assim o programa pode manter comunicação com o LCD sempre que o bit DB7 estija em nível baixo. A partir desse ponto pode-se enviar o próximo comando.
Pode-se usar esse procedimento sempre que enviarmos alguma instrução ao LCD.
A subrotina abaixo executa a tarefa descrita anteriormente:

           WAIT_LCD:

  SETB EN                   ; inicia os comandos no LCD
  CLR RS                     ;
  SETB RW                  ; comando de leitura
  MOV DATA,#0FFh  ; seta todos os pinos para FF
LENDO:       MOV A,DATA          ; le e retorna valor
 JB ACC.7,LENDO     ; se o bit 7 estiver em alto, 
                                     ;o LCD está ;executando a 
                                     ;última tarefa
 CLR EN                     ;finaliza os comandos
 CLR RW                      ; coloca o RW para receber 
                                     ;futuros comandos
  RET                            ; 
Assim, o procedimento padrão será enviar uma instrução ao LCD e depois chamar a subrotina ( WAIT_LCD ) para aguardar até que a última instrução seja executada. Garante-se assim que o programa dará ao LCD o tempo necessário para executar instruções e também tornar o programa compatível com qualquer cristal.

Nota: a rotina acima faz o trabalho de espera do LCD, porém se o LCD não ficar “desocupado” o programa poderá travar esperando o bit DB7 assumir nível baixo. Isto nunca deverá acontecer desde que o hardware esteja trabalhando corretamente. Como uma solução, seria interessante colocar algum tempo limite na espera - por exemplo, um máximo de 256 tentativas para o que sinal de “ocupado” entre em nível baixo. Isso garantiria até mesmo que o hardware falhe e o programa não trave.


INICIALIZANDO O LCD

Antes de usar o LCD, deve-se inicializá-lo e configurá-lo. Isso é feito com o envio de um número de instruções de inicialização.

A primeira instrução que deve ser enviada diz respeito ao modo de operação do bus de dados: 4 ou 8 bits. Seleciona-se também a fonte de caracteres de 5x8. Essas duas opções são selecionadas enviando o comando 38h ao LCD com o RS em baixo (tipo instrução). Deve-se chamar a sub-rotina de espera WAIT_LCD. Assim, para enviar o comando 38h deve-se executar as seguintes instruções:

 SETB    EN
 CLR      RS
 MOV    DATA,#38h
 CLR      EN
 LCALL WAIT_LCD
 Nota: o comando do LCD 38h é na verdade a soma de um número de opções. O código de instrução puro é 20h ( “Function set” ). Entretanto, agrega-se o valor 10h para indicar o valor de 8 bits no bus de dados mais o 08h para indicar que o display é de duas linhas.

O segundo byte a ser enviado é o 0Eh. Assim deve-se repetir o código de inicialização acima:

 SETB      EN
 CLR        RS
 MOV      DATA,#0Eh
 CLR       EN
 LCALL WAIT_LCD
 Nota: o comando 0Eh é na verdade um código de instrução 08h mais  04h para ligar o LCD. Para isso um 02h adicional é agregado para ligar o cursor.

O último byte necessário é usado para parâmetros adicionais de configuração. Deve-se enviar o código de instrução 06h.

 SETB     EN
 CLR       RS
 MOV     DATA,#06h
 CLR      EN
 LCALL WAIT_LCD
 Nota: o comando 06h é a instrução 04h mais 02h para dizer ao LCD que a todo tempo será enviado um caracter e o cursor automaticamente se moverá para a direita.

Assim, a rotina de inicialização completa será a seguinte:

                                             Init_LCD:

SETB EN
CLR RS
MOV DATA,#38h
CLR EN
LCALL WAIT_LCD
SETB EN
CLR RS
MOV DATA,#0Eh
CLR EN
LCALL WAIT_LCD
SETB EN
CLR RS
MOV DATA,#06h
CLR EN
LCALL WAIT_LCD
RET
Tendo executado esse código o LCD estará completamente inicializado para receber outros dados e  os apresentar em  sua tela.


LIMPANDO A TELA ( CLEAR SCREEN )

Quando o LCD é inicializado pela primeira vez, a tela deverá ser limpa automaticamente pelo HD44780. Entretanto, não é má a idéia de limpar a tela em muitas das operações depois que o LCD foi inicializado.

O código de comando para executar essa função é o 01h. Limpar a  tela é uma função usada normalmente mais de uma vez, portanto uma boa idéia é construir uma sub-rotina para isso:


                                                CLEAR_LCD:

  SETB    EN
  CLR      RS
  MOV    DATA,#01h
  CLR      EN
  LCALL WAIT_LCD
  RET
 Nota: executando a função “Clear Screen” a posição do cursor irá automaticamente para a parte superior esquerda.


ESCREVENDO TEXTO NO LCD

A rotina WRITE_LCD abaixo enviará o caracter do acumulador para o LCD que o colocará no display:


                                        WRITE_TEXT:

SETB     EN
SETB     RS
MOV     DATA, A
CLR      EN
LCALL WAIT_LCD
RET


UM EXEMPLO DE PROGRAMA

Com todas as sub-rotinas necessárias escritas, o programa abaixo escreverá o texto “HELLO WORLD” no LCD:
 LCALL INIT_LCD
 LCALL CLEAR_LCD
 MOV A,#’H’
 LCALL WRITE_TEXT
 MOV A,#’E’
 LCALL WRITE_TEXT
 MOV A,#’L’
 LCALL WRITE_TEXT
 MOV A,#’L’
 LCALL WRITE_TEXT
 MOV A,#’O’
 LCALL WRITE_TEXT
 MOV A,#’W’
 LCALL WRITE_TEXT
 MOV A,#’O’
 LCALL WRITE_TEXT
 MOV A,#’R’
 LCALL WRITE_TEXT
 MOV A,#’L’
 LCALL WRITE_TEXT
 MOV A,#’D’
 LCALL WRITE_TEXT
O programa acima deverá, quando executado, inicializar, limpar, e mostrar “HELLO WORLD” no LCD começando do canto esquerdo superior do cristal.


POSICIONANDO O CURSOR

O programa acima é simples, pois apenas imprime o texto na primeira linha começando do lado esquerdo. Mas e se for necessário que a palavra “HELLO” apareça na linha de cima, no canto esquerdo e a palavra “WORLD” na linha de baixo e no caracter de número 10? Isto parece simples, e na verdade é. Entretando, isso requer um maior entendimento do design do LCD.

O HD44780 contém uma certa quantidade de memória que é dedicada ao display. Todo o texto escrito é armazenado nessa memória, que é lida pelo HD 44780 para a apresentação do texto no cristal. Essa memória pode ser representada no seguinte mapa:

No mapa de memória acima, a área em verde é a parte visível do display. Como pode-se observar, isso representa 16 caracteres em cada uma das duas linhas. Os números em cada caixinha são os endereços de memória que correspondem a uma determinada posição na tela.

Assim, o primeiro caracter na primeira linha, canto esquerdo, é o de endereço 00h. A seguinte posição é a de endereço 01h, etc... E isso continua até chegarmos ao caracter de número 16 na primeira linha que corresponde ao endereço 0Fh.

Entretanto, o primeiro caracter da segunda linha, como é mostrado no mapa, possui o endereço 40h. Isso significa que se escrevermos um caracter na última posição da primeira linha e depois escrever o próximo, o segundo caracter não aparecerá na segunda linha. Isso acontece porque esse segundo caracter será escrito no endereço 10h, mas a segunda linha começa pelo 40h.

Assim é necessário enviar um comando ao LCD que posiciona o cursor na segunda linha. O código de instrução “Set Cursor Position” é 80h. Para isso deve-se juntar a essa instrução o endereço de localização onde deseja-se posicionar o cursor. No exemplo, a palavra “WORLD” será apresentada na segunda linha e no décimo caracter.

Fazendo referência novamente ao mapa, pode-se ver que a posição do décimo caracter da segunda linha corresponde ao endereço 4Ah. Antes de escrever a palavra “WORLD”, deve-se enviar a instrução de posicionamento do cursor; o valor desse comando será: 80h ( instrução de posicionamento de cursor ) acompanhado do endereço 4Ah, ou seja, 80h + 4Ah = C4h. Portanto, enviando o comando C4h, o LCD posicionará o cursor na segunda linha e no décimo caracter:

 SETB     EN
 CLR       RS
 MOV     DATA,#0C4h
 CLR       EN
 LCALL WAIT_LCD
Para apresentar a palavra “HELLO” a partir do canto esquerdo superior com a palavra “WORLD” na segunda linha e no décimo caracter basta inserir os comandos escritos acima no programa anterior. Assim, o programa ficaria:
 LCALL INIT_LCD
 LCALL CLEAR_LCD
 MOV A,#’H’
 LCALL WRITE_TEXT
 MOV A,#’E’
 LCALL WRITE_TEXT
 MOV A,#’L’
 LCALL WRITE_TEXT
 MOV A,#’L’
 LCALL WRITE_TEXT
 MOV A,#’O’
 LCALL WRITE_TEXT
 SETB EN
 CLR RS
 MOV DATA,#0C4h
 CLR EN
 LCALL WAIT_LCD
 MOV A,#’W’
 LCALL WRITE_TEXT
 MOV A,#’O’
 LCALL WRITE_TEXT
 MOV A,#’R’
 LCALL WRITE_TEXT
 MOV A,#’L’
 LCALL WRITE_TEXT
 MOV A,#’D’
 LCALL WRITE_TEXT


RESUMO

Esse tutorial apresentou conceitos básicos de programação no LCD. Obviamente, não possui todas as funções do display. O controlador HD44780  oferece muitas outras funções que são passadas usando outros comandos, e alguns dos comandos já apresentados incluem outras opções que não foram discutidas aqui.
ContribuiçãoFernando Pegoraro Silva
Hosted by www.Geocities.ws

1