Anda di halaman 1dari 88

Kit de Desenvolvimento

Para Microcontroladores
Modelo MMDB-01

Exemplos de Programas Para MCS-51


ÍNDICE

01 SOFTWARES RECOMENDADOS............................... 02

02. EXEMPLOS DE PROGRAMAS ................................... 04


02.01. Contador Binário Com Saída na Porta P1 ........... 04
02.02. Acendimento Seqüencial de Todos os LED’s ....... 06
02.03. Barra Vertical Móvel nos LED’s ......................... 08
02.04. Cronômetro Binário nos LED’s da Porta P1 ......... 10
02.05. Cronômetro Digital no Display LCD ................... 14
02.06. Mensagem com Deslocamento no Display LCD.... 24
02.07. Relório Calendário Baseado no HT1380 ............. 33
02.08. Relógio Com Alarme ....................................... 48
02.09. Relógio no Display de Sete Segmentos .............. 62
02.10. Exemplo de Uso do Conversor AD ..................... 70
02.11. Rotina de Leitura de Teclado ............................ 80

_______________________________________________
01
01. SOFTWARES RECOMENDADOS
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Existem vários programas de uso livre ou shareware que podem
se constituir em importantes auxiliares no estudo e
desenvo lvi men to de sof twares para apli cações com
microcontroladores.

Dentre estes aplicativos recomendamos alguns que certamente


serão muito úteis ao estudante, hobista ou desenvolvedor.

1. ASM51 Macro Assembler.


Montador para linguagem assembly.
Endereço para download:

http://www.metaice.com/ASM51/ASM51.htm

2. SIM8052.
Si mulador de 8051 para ambiente Windows.
Endereço para download:

http://www.vaultbbs.com/sim8052

3. ConTEXT.
Editor de texto para programadores.
Endereço para download:

http://www.context.cx

4. PICcom.
Ferramenta de desenvolvimento que auxilia na depuração de
programas através da porta serial.
Endereço para download:

http://www.semis.demon.co.uk/PICcom/PICcom.htm

5. MPLAB Integrated Development Environment (IDE).


Conjunto de ferramentas para desenvolvimento de
aplicações com microcontroladores da família PIC.
Endereço para download:

http://www.microchip.com
_______________________________________________
02
6. AVR Studio.
Conjunto de ferramentas para desenvolvimento de
aplicações com microcontroladores da família AVR, incluindo
montador assembly e simulador.
Endereço para download:

http://www.atmel.com

7. FLIP - Flexible In-system Programmer.


Software de programação para AT89C51ED2.
Endereço para download:

http://www.atmel.com

8. SpiPgm.
Programa de gravação serial.
Endereço para download:

http://www.geocities.com/asim1108/spipgm/spipgm.zip

9. Serial Port Monitor.


Programa de monitoração para porta serial.
Endereço para download:

http://www.serial-port-monitor.com/

10. SDCC - Small Device C Compiler


Compilador C para MCS51.
Endereço para download:

http://sdcc.sourceforge.net/

_______________________________________________
03
02. EXEMPLOS DE PROGRAMAS
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Nesta seção são mostrados alguns exemplos de programas
escritos em linguagem assembly para microcontroladores da família
MCS-51. Para todos os exemplos o teclado deverá ser programado
para 4 teclas através do jumper JP16.

02.01. Contador Binário Com Saída na Porta P1:

Esta rotina implementa um contador com saída em sistema


binário nos LED’s da porta P1.

Este exemplo está no CD no arquivo ContBin.asm.

;-------------------------------------------------------------------------------
$MOD89S52
$TITLE(Contador binario mostrado em P1)
;-------------------------------------------------------------------------------
;
; Esta rotina serve para demonstrar que pode ser usada uma posição da
; memória RAM como um contador, no presente caso o resultado da contagem
; vai ser mostrado através dos LEDs da porta P1, e o contador será
; incrementado a cada segundo.
;
;-------------------------------------------------------------------------------
; Segmento de Bits
;
; Segmento da memória RAM, onde se pode trabalhar com um único bit, que são
; usados como sinalizadores (Flags).
; Este segmento começa na posição 020h e vai até 02Fh, totalizando 16 bytes,
; o que permite que o usuário tenha até 128 Flags no seu programa
;-------------------------------------------------------------------------------
BSEG at 0h

; Neste programa não usamos nenhum flag


;-------------------------------------------------------------------------------
; Segmento de Bytes
;
; Segmento da memória RAM, reservada para as variáveis usadas no programas
;-------------------------------------------------------------------------------
DSEG at 22h

ContadorBinario: DS 1
Temporario: DS 3

;-------------------------------------------------------------------------------
FimDosDados EQU $ ; Serve para indicar o fim da memória
; RAM usada para os dados.
;-------------------------------------------------------------------------------
CSEG ; Informa ao montador que a partir daqui
; começa o código fonte do programa

org 0000h ; Endereço de inicialização do


; microcontrolador, o qual vai para este
; quando é resetado
_______________________________________________
;-------------------------------------------------------------------------------

04
IcializaPlaca:
mov P0,#0 ; Coloca "0" em todos os pinos da
; porta 0 para apagar os LEDs
mov P1,#0 ; Coloca "0" em todos os pinos da
; porta 1 para apagar os LEDs
mov P2,#0 ; Coloca "0" em todos os pinos da
; porta 2 para apagar os LEDs
mov P3,#0 ; Coloca "0" em todos os pinos da
; porta 3 para apagar os LEDs

mov ContadorBinario,#0 ; Zera o Contador binário


;-------------------------------------------------------------------------------
call Delay1Seg ; Aguarda 1 segundo para começar
; a contagem
;-------------------------------------------------------------------------------
LoopDeContagem:
inc ContadorBinario ; Incrementa o Contador binário
mov P1,ContadorBinario ; Copia valor do Contador binário em P1
call Delay1Seg ; Aguarda 1 segundo
sjmp LoopDeContagem ; Volta para tornar a executar mais uma
; contagem

;-------------------------------------------------------------------------------
Delay1Seg:
mov Temporario,#84 ; Carrega 3 bytes para contar 921600
mov Temporario+1,#254 ; ciclos de máquina, que é a quantidade
mov Temporario+2,#7 ; de ciclos que

Loop1Segundo: ; Fica neste loop até que os 3 registros


; cheguem a '0', quando então terá
; transcorrido 1 segundo
djnz Temporario,Loop1Segundo
djnz Temporario+1,Loop1Segundo
djnz Temporario+2,Loop1Segundo
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
CodeSize equ $ ; Neste ponto o montador informa
; o tamanho do codigo

MemoriaLivre equ 8192-CodeSize ; Neste ponto o montador calcula e


; informa a memória de programa livre
;-------------------------------------------------------------------------------
; Para visualizar o tamanho do código e a memória de programa livre
; o usuário deve abrir o arquivo ContBin.lst que é gerado pelo montador
; no mesmo subdiretório onde esta o ContBin.asm
;-------------------------------------------------------------------------------
; Sinaliza para o montador o fim do programa
end

_______________________________________________
05
02.02. Acendimento Seqüencial de Todos os LED’s:

Neste programa mostramos como acender todos os LED’s do


MMDB-01 seqüencialmente com intervalo de 1 segundo. Para que o
programa funcione corretamente deverá ser removido o Jumper
JP4.
Este exemplo está no CD no arquivo Sequen.asm.
;-------------------------------------------------------------------------------
$MOD89S52
$TITLE(Sequencial)
;-------------------------------------------------------------------------------
;
; Rotina que acende um LED de cada vez em seqüência em todas as portas com
; intervalo de 1 segundo entre cada acendimento.
;
;-------------------------------------------------------------------------------
; Segmento de Bits
;
; Segmento da memória RAM, onde se pode trabalhar com um único bit, que são
; usados como sinalizadores (Flags).
; Este segmento começa na posição 020h e vai até 02Fh, totalizando 16 bytes,
; o que permite que o usuário tenha até 128 Flags no seu programa
;-------------------------------------------------------------------------------
BSEG at 0h

; Neste programa não usamos nenhum flag


;-------------------------------------------------------------------------------
; Segmento de Bytes
;
; Segmento da memória RAM, reservada para as variáveis usadas no programas
;-------------------------------------------------------------------------------
DSEG at 22h

ByteSequencial: DS 1
Temporario: DS 3

;-------------------------------------------------------------------------------
FimDosDados EQU $ ; Serve para indicar o fim da memória
; RAM usada para os dados.
;-------------------------------------------------------------------------------
CSEG ; Informa ao montador que a partir daqui
; começa o código fonte do programa
org 0000h ; Endereço de inicialização do
; microcontrolador, o qual vai para este
; quando é resetado
;-------------------------------------------------------------------------------
InicializaPlaca: ; Vai inicializar a placa
mov P0,#0 ; Coloca "0" em todos os pinos da
; porta 0 para apagar os LEDs
mov P1,#0 ; Coloca "0" em todos os pinos da
; porta 1 para apagar os LEDs
mov P2,#0 ; Coloca "0" em todos os pinos da
; porta 2 para apagar os LEDs
mov P3,#0 ; Coloca "0" em todos os pinos da
; porta 3 para apagar os LEDs

mov A,#080h ; Liga o bit "7" do acumulador, o qual


; é usado para acender os LEDs das
; portas seqüencialmente
;-------------------------------------------------------------------------------
_______________________________________________
06
call Delay1Seg ; Aguarda 1 segundo para começar a
; seqüência
;-------------------------------------------------------------------------------
LoopSequencialP0:
mov P0,A ; Envia o conteúdo do acumulador para P0
rr A ; Desloca o acumulador para a direita
call Delay1Seg ; Aguarda 1 segundo
cjne A,#080h,LoopSequencialP0 ;
; Quando chegar a 080h é porque já fez
; toda a seqüência em P0
mov P0,#0 ; Apaga todos os LEDs da porta 0

LoopSequencialP1:
mov P1,A ; Envia o conteúdo do acumulador para P1
rr A ; Desloca o acumulador para a direita
call Delay1Seg ; Aguarda 1 segundo
cjne A,#080h,LoopSequencialP1 ;
; Quando chegar a 080h é porque já fez
; toda a seqüência em P1
mov P1,#0 ; Apaga todos os LEDs da porta 1

LoopSequencialP2:
mov P2,A ; Envia o conteúdo do acumulador para P2
rr A ; Desloca o acumulador para a direita
call Delay1Seg ; Aguarda 1 segundo
cjne A,#080h,LoopSequencialP2 ;
; Quando chegar a 080h é porque já fez
; toda a seqüência em P2
mov P2,#0 ; Apaga todos os LEDs da porta 2

LoopSequencialP3:
mov P3,A ; Envia o conteúdo do acumulador para P3
rr A ; Desloca o acumulador para a direita
call Delay1Seg ; Aguarda 1 segundo
cjne A,#080h,LoopSequencialP3 ;
; Quando chegar a 080h é porque já fez
; toda a seqüência em P3
mov P3,#0 ; Apaga todos os LEDs da porta 3

sjmp LoopSequencialP0 ; Volta a fazer a seqüência a partir de


; P0.7
;-------------------------------------------------------------------------------
Delay1Seg:
mov Temporario,#84 ; Carrega 3 bytes para contar 921600
mov Temporario+1,#254 ; ciclos de máquina, que é a quantidade
mov Temporario+2,#7 ; de ciclos que

Loop1Segundo: ; Fica neste loop até que os 3 registros


; cheguem a '0', quando então terá
; transcorrido 1 segundo
djnz Temporario,Loop1Segundo
djnz Temporario+1,Loop1Segundo
djnz Temporario+2,Loop1Segundo
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
CodeSize equ $ ; Neste ponto o montador informa
; o tamanho do código

MemoriaLivre equ 8192-CodeSize ; Neste ponto o montador calcula e


; informa a memória de programa livre
;-------------------------------------------------------------------------------
; Para visualizar o tamanho do código e a memória de programa livre
; o usuário deve abrir o arquivo ContBin.lst que é gerado pelo montador
; no mesmo subdiretório onde esta o ContBin.asm
; Sinaliza para o montador o fim do programa
;-------------------------------------------------------------------------------
end
02.03. Barra Vertical Movel nos LED’s:

Neste programa mostramos como fazer uma barra vertical móvel


nos LED’s do MMDB-01. Para que o programa funcione corretamente
deverá ser removido o Jumper JP4.
Este exemplo está no CD no arquivo Barra.asm.
;-------------------------------------------------------------------------------
$MOD89S52
$TITLE(Barra vertical)
;-------------------------------------------------------------------------------
;
; Esta rotina produz uma barra vertical que se desloca com intervalo de
; 1 segundo
;
;-------------------------------------------------------------------------------
; Segmento de Bits
;
; Segmento da memória RAM, onde se pode trabalhar com um único bit, que são
; usados como sinalizadores (Flags).
; Este segmento começa na posição 020h e vai até 02Fh, totalizando 16 bytes,
; o que permite que o usuário tenha até 128 Flags no seu programa
;-------------------------------------------------------------------------------
BSEG at 0h

; Neste programa não usamos nenhum flag


;-------------------------------------------------------------------------------
; Segmento de Bytes
;
; Segmento da memória RAM, reservada para as variáveis usadas no programas
;-------------------------------------------------------------------------------
DSEG at 22h

ByteDaBarra: DS 1 ; Tempo da barra


Temporario: DS 3

;-------------------------------------------------------------------------------
FimDosDados EQU $ ; Serve para indicar o fim da memória
; RAM usada para os dados.
;-------------------------------------------------------------------------------
CSEG ; Informa ao montador que a partir daqui
; começa o código fonte do programa

org 0000h ; Endereço de inicialização do


; microcontrolador, o qual vai para este
; quando é resetado
;-------------------------------------------------------------------------------
InicializaPlaca: ; Vai inicializar a placa
mov P0,#0 ; Coloca "0" em todos os pinos da
; porta 0 para apagar os LEDs
mov P1,#0 ; Coloca "0" em todos os pinos da
; porta 1 para apagar os LEDs
mov P2,#0 ; Coloca "0" em todos os pinos da
; porta 2 para apagar os LEDs
mov P3,#0 ; Coloca "0" em todos os pinos da
; porta 3 para apagar os LEDs

mov A,#1 ; Liga o bit '0' do acumulador para


; fazer a barra vertical usando P0, P1,
; P2 e P3
;-------------------------------------------------------------------------------
_______________________________________________
08
call Delay1Seg ; Aguarda 1 segundos para começar
; o deslocamento
;-------------------------------------------------------------------------------
LoopDaBarra:
mov P0,A ; Escreve o bit da barra na P0
mov P1,A ; Escreve o bit da barra na P1
mov P2,A ; Escreve o bit da barra na P2
mov P3,A ; Escreve o bit da barra na P3

rl A ; Desloca o bit da barra

call Delay1Seg ; Aguarda 1 segundo


sjmp LoopDaBarra ; Volta para tornar a apresentar a
; próxima barra

;-------------------------------------------------------------------------------
Delay1Seg:
mov Temporario,#84 ; Carrega 3 bytes para contar 921600
mov Temporario+1,#254 ; ciclos de máquina, que é a quantidade
mov Temporario+2,#7 ; de ciclos que

Loop1Segundo: ; Fica neste loop até que os 3 registros


; cheguem a '0', quando então terá
; transcorrido 1 segundo
djnz Temporario,Loop1Segundo
djnz Temporario+1,Loop1Segundo
djnz Temporario+2,Loop1Segundo
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
CodeSize equ $ ; Neste ponto o montador informa
; o tamanho do código

MemoriaLivre equ 8192-CodeSize ; Neste ponto o montador calcula e


; informa a memória de programa livre
;-------------------------------------------------------------------------------
; Para visualizar o tamanho do código e a memória de programa livre
; o usuário deve abrir o arquivo ContBin.lst que é gerado pelo montador
; no mesmo subdiretório onde esta o ContBin.asm
;-------------------------------------------------------------------------------
; Sinaliza para o montador o fim do programa
end

_______________________________________________
09
02.04. Cronômetro Binário nos LED’s da Porta P1:

Este programa implementa um contador de segundos com a


saída mostrada em sistema binário nos LED’s da porta P1.

São usadas as teclas “A” para partir e parar o cronômetro, e “1”


para zerar. A tecla “1” só funciona se o cronômetro estiver parado.

Este exemplo está no CD no arquivo CronoBin.asm.


;-------------------------------------------------------------------------------
$MOD89S52
$TITLE(Cronometro binário mostrado em P1)
;-------------------------------------------------------------------------------
;
; Esta rotina serve para demonstrar que pode ser usada uma posição da
; memória RAM como um contador, no presente caso o resultado da contagem
; vai ser mostrado através dos LEDs da porta P1, e o contador será
; incrementado a cada segundo, acrecido de duas teclas com as funções
; parte/para e zera com o objetivo de fazer um cronometro binário simples.
;
; O teclado deve ser configurado para 4 teclas através de JP16
;
; Função das teclas:
; Tecla 1 - Zera cronômetro
; Tecla A - Parte/para o cronômetro
;
;-------------------------------------------------------------------------------
TempoDoTimer2 EQU 65535-9216 ; Tempo para 10 mS

TeclaPartePara EQU P2.3 ; Tecla 'A'


TeclaZera EQU P2.0 ; Tecla '1'

;-------------------------------------------------------------------------------
; Segmento de Bits
;
; Segmento da memória RAM, onde se pode trabalhar com um único bit, que são
; usados como sinalizadores (Flags).
; Este segmento começa na posição 020h e vai até 02Fh, totalizando 16 bytes,
; o que permite que o usuário tenha até 128 Flags no seu programa
;-------------------------------------------------------------------------------
BSEG at 0h

Passou1Segundo: DBIT 1
PartePara: DBIT 1
Zera: DBIT 1
TeclaValida: DBIT 1

;-------------------------------------------------------------------------------
; Segmento de Bytes
;
; Segmento da memória RAM, reservada para as variáveis usadas no programas
;-------------------------------------------------------------------------------
DSEG at 22h

ContadorBinario: DS 1
Tempo1Segundo: DS 1
Temporario: DS 3
Debounce: DS 1
_______________________________________________
10
;-------------------------------------------------------------------------------
FimDosDados EQU $ ; Serve para indicar o fim da memória
; RAM usada para os dados.
;-------------------------------------------------------------------------------
CSEG ; Informa ao montador que a partir daqui
; começa o código fonte do programa

org 0000h ; Endereço de inicialização do


; microcontrolador, o qual vai para este
; quando é resetado
jmp InicializaPlaca
;-------------------------------------------------------------------------------
org 002Bh ; Vetor da interrupção do Timer 2
Timer2:

clr TF2 ; No caso do timer 2 o flag TF2 tem que


; ser zerado pelo software para que não
; aconteça uma nova interupção logo que
; sair desta interrupção

call TrataTecla ; Vai tratar as teclas

djnz Tempo1Segundo,SaiTimer2 ; Sai da interrupção enquanto não passar


; o tempo de 1 Segundo
setb Passou1Segundo ; Sinaliza que já passou 1 segundo
mov Tempo1Segundo,#100 ; Carrega tempo de 1 S com (100 x 10 mS)

SaiTimer2:
reti ; Saída da interupção do timer 2

;-------------------------------------------------------------------------------
InicializaPlaca: ; Vai inicializar a placa
setb TR2 ; Habilita timer 2
setb PT2 ; Timer 2 com prioridade alta
setb ET2 ; Habilita interrupção do timer 2
setb EA ; Habilita o controlador de interrupt
clr CAP2 ; Coloca o timer 2 para recarregar
; automaticamente

mov RCAP2H,#(high TempoDoTimer2) ;


; Para a recarga da parte alta do
; timer 2
mov RCAP2L,#(low TempoDoTimer2) ;
; Para a recarga da parte baixa do
; timer 2

mov Tempo1Segundo,#100 ; Carrega o contador de 1 segundo com


; (100 x 10 mS = 1 S)

mov P0,#0 ; Coloca "0" em todos os pinos da


; porta 0 para apagar os LEDs
mov P1,#0 ; Coloca "0" em todos os pinos da
; porta 1 para apagar os LEDs
mov P2,#00001001b ; Coloca "0" em parte dos pinos da
; porta 2 para apagar os LEDs
mov P3,#0 ; Coloca "0" em todos os pinos da
; porta 3 para apagar os LEDs

mov ContadorBinario,#0 ; Zera o Contador binário


clr TeclaValida ; Sinaliza que ainda não tem tecla
; válida
clr PartePara ; Para iniciar o cronometro parado
mov Debounce,#10 ; Carrega o contador de debounce das

_______________________________________________
11
; teclas com 10 x 10 mS = 100 mS

Principal:
mov Tempo1Segundo,#100 ; Carrega o contador de 1 segundo com
; (100 x 10 mS = 1 S)
clr Passou1Segundo ; Sinaliza que ainda não passou o
; segundo
orl PCON,#1 ; A CPU se desativa, e só volta a se
; ativar quando ocorrer uma interrupção
jb PartePara,IncCronometro ; Se o flag PartePara estiver ligado,
; vai incrementar o cronometro
jnb Zera,Principal ; Se o flag PartePara estiver desligado
; e o flag Zera também, vai para a
; Principal caso contrario vai zerar o
; cronometro

;-------------------------------------------------------------------------------
ZeraCronometro:
mov ContadorBinario,#0 ; Carrega ContadorBinario com '0'
mov P1,ContadorBinario ; Copia o valor do Contador binário
; em P1
clr Zera ; Desliga o flag Zera, pois já zerou
; o cronômetro
sjmp Principal ; Volta para Principal

;-------------------------------------------------------------------------------
IncCronometro:
mov P1,ContadorBinario ; Copia o valor do Contador binário
; em P1
inc ContadorBinario ; Incrementa o Contador binário
jnb Passou1Segundo,$ ; Espera passar 1 segundo
sjmp Principal ; Volta para Principal

;-------------------------------------------------------------------------------
TrataTecla:
jb TeclaValida,AguardaFimDeTecla ;
; Se tiver tecla apertada, vai aguardar
; que a mesma seja liberada
jb TeclaPartePara,TestaTeclaZera ;
; Se a tecla parte para não estiver
; pressionada, vai testar tecla zera
djnz Debounce,SaiTrataTecla ; Sai enquanto não passar o tempo de
; debounce
setb TeclaValida ; Sinaliza que tem uma tecla válida
cpl PartePara ; Sinaliza que é para partir ou parar
; o cronometro
sjmp SaiTrataTecla ; Sai da rotina

TestaTeclaZera:
jb PartePara,SaiTrataTecla ; Não testa tecla zera se o cronometro
; estiver rodando
jb TeclaZera,SaiTrataTecla ; Sai se a tecla zera não estiver
; pressionada
djnz Debounce,SaiTrataTecla ; Sai enquanto não passar o tempo de
; debounce
setb TeclaValida ; Sinaliza que tem uma tecla válida
setb Zera ; Sinaliza que é para zerar o cronômetro
sjmp SaiTrataTecla ; Sai da rotina

AguardaFimDeTecla:
mov A,P2 ; Le a porta 2 para testar se ainda tem
; tecla apertada
orl A,#11110110b ; Coloca "1" nos bits não usados
cpl A ; Inverte o conteúdo do acumulador

_______________________________________________
12
jnz SaiTrataTecla ; Sai se o acumulador não estiver com
; "0", é sinal de que ainda tem tecla
; apertada
mov Debounce,#10 ; Recarrega o contador de debounce
; com 100 mS
clr TeclaValida ; Sinaliza que não tem mais nenhuma
; tecla apertada

SaiTrataTecla:
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
CodeSize equ $ ; Neste ponto o montador informa
; o tamanho do codigo

MemoriaLivre equ 8192-CodeSize ; Neste ponto o montador calcula e


; informa a memória de programa livre
;-------------------------------------------------------------------------------
; Para visualizar o tamanho do código e a memória de programa livre
; o usuário deve abrir o arquivo ContBin.lst que é gerado pelo montador
; no mesmo subdiretório onde esta o ContBin.asm
;-------------------------------------------------------------------------------
; Sinaliza para o montador o fim do programa
end

_______________________________________________
13
02.05. Cronômetro Digital no Display LCD:

Este programa implementa um cronômetro digital com resolução


de 0,1s e saída no display LCD.

São usadas as teclas “A” para partir e parar o cronômetro, “1”


para zerar e “3” para a função LAP (congela o display sem
interromper a contagem de tempo). A tecla “1” só funciona se o
cronômetro estiver parado.
Este exemplo está no CD no arquivo Cronomet.asm.
;-------------------------------------------------------------------------------
$MOD89S52
$TITLE(Cronometro)
;-------------------------------------------------------------------------------
;
; Esta rotina demonstra uma das formas de implementar um cronômetro
; através de software com display de cristal líquido
;
; O teclado deve ser configurado para 4 teclas através de JP16
;
; Função das teclas:
; Tecla 1 - Zera cronômetro
; Tecla 3 - Entra no modo LAP
; Tecla A - Parte/para o cronômetro
;
;-------------------------------------------------------------------------------

TempoDoTimer2 EQU 65535-9216 ; Tempo para 10 mS

ETX EQU 003h


BarramentoDoDisplay EQU P2 ; Barramento de dados do display

TeclaPartePara EQU P2.3 ; "A"


TeclaLAP EQU P2.2 ; "3"
TeclaZera EQU P2.0 ; "1"

RsLCD EQU P0.2 ; Porta de saída do sinal que informa


; ao display LCD se os dados do
; barramento correspondem a dados ou
; a comandos
EnLCD EQU P0.4 ; Porta de saída do pulso de
; transferência de dados para o display

;-------------------------------------------------------------------------------
; Segmento de Bits
;
; Segmento da memória RAM, onde se pode trabalhar com um único bit, que são
; usados como sinalizadores (Flags).
; Este segmento começa na posição 020h e vai até 02Fh, totalizando 16 bytes,
; o que permite que o usuário tenha até 128 Flags no seu programa
;-------------------------------------------------------------------------------
BSEG at 0h

PartePara: DBIT 1
LAP: DBIT 1
Zera: DBIT 1
TeclaValida: DBIT 1
_______________________________________________
14
;-------------------------------------------------------------------------------
; Segmento de Bytes
;
; Segmento da memória RAM, reservada para as variáveis usadas no programas
;-------------------------------------------------------------------------------
DSEG at 22h

Horas: DS 1
Minutos: DS 1
Segundos: DS 1
CentesimosDeSegundos: DS 1
Tempo1Segundo: DS 1
Debounce: DS 1
ComandoLCD: DS 1 ; Registro destinado ao envio de
; comandos para o display LCD

Temporario: DS 3

DSEG at 40h

Linha1doDisplay: DS 16
Linha2doDisplay: DS 16

;-------------------------------------------------------------------------------
FimDosDados EQU $ ; Serve para indicar o fim da memória
; RAM usada para os dados.
;-------------------------------------------------------------------------------
CSEG ; Informa ao montador que a partir daqui
; começa o código fonte do programa

org 0000h ; Endereço de inicialização do


; microcontrolador, o qual vai para este
; quando é resetado
jmp InicializaPlaca ; Vai inicializar a placa
;-------------------------------------------------------------------------------

org 002Bh ; Vetor da interrupção do Timer 2


Timer2:
clr TF2 ; No caso do timer 2 o flag TF2 tem que
; ser zerado pelo software para que não
; aconteça uma nova interupção logo que
; sair desta interrupção
call TrataTecla ; Vai tratar as teclas
jnb PartePara,SaiTimer2 ; Se o flag PartePara estiver desligado
; sai do interrupt do timer 2 sem
; incrementar o cronometro
call IncrementaCronometro ; Vai incrementar o cronometro
jb LAP,SaiTimer2 ; Se o flag LAP estiver ligado não vai
; reescrever o display
call EscreveCronometroNoLCD ; Vai escrever horas, minutos, segundos
; e centésimos de segundos no buffer do
; display LCD
SaiTimer2:
call EscreveLinha1doDisplay ; Vai escrever o buffer do display na
; linha 1 do mesmo
reti ; Saída da interrupção do timer 2

;-------------------------------------------------------------------------------
InicializaPlaca:

InicializaLCD:
call Retardo4mS
call Retardo4mS
call Retardo4mS
_______________________________________________
call Retardo4mS

15
clr EnLCD
clr RsLCD ; Coloca o display para receber Comandos
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
call Retardo4mS
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
mov A,#020h
call EnviaNibleAltoParaDisplayLCD
mov A,#020h
call EnviaNibleAltoParaDisplayLCD
mov A,#080h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#060h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#0C0h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#010h
call EnviaNibleAltoParaDisplayLCD
setb RsLCD ; Coloca o display para receber Dados
call Retardo4mS

call ApagaDisplay
;-------------------------------------------------------------------------------
; Inicializa timer
;-------------------------------------------------------------------------------
setb TR2 ; Habilita timer 2
setb PT2 ; Timer 2 com prioridade alta
setb ET2 ; Habilita interrupção do timer 2
setb EA ; Habilita o controlador de interrupt
clr CAP2 ; Coloca o timer 2 para recarregar
; automaticamente

mov RCAP2H,#(high TempoDoTimer2) ;


; Para a recarga da parte alta do
; timer 2
mov RCAP2L,#(low TempoDoTimer2) ;
; Para a recarga da parte baixa do
; timer 2

mov Tempo1Segundo,#100 ; Carrega o contador de 1 segundo com


; (100 x 10 mS = 1 S)
;-------------------------------------------------------------------------------

clr PartePara ; Para começar o Cronometro parado


clr LAP ; Começa com LAP desligado
clr Zera ; Sinaliza que não é para zerar o
; cronometro no início

mov CentesimosDeSegundos,#0 ; Carrega o contador de centésimos de


; segundos com '0' para que o cronometro
; comece com '00' centésimos
mov Segundos,#0 ; Carrega o contador de segundos com '0'
; para que o cronômetro comece com
; '00' segundos
mov Minutos,#0 ; Carrega o contador de minutos com '0'
_______________________________________________
16
; para que o cronometro comece com
; '00' minutos
mov Horas,#0 ; Carrega o contador de horas com '0'
; para que o cronometro comece com
; '00' horas
mov Debounce,#10 ; Recarrega o contador de debounce
; com 100 mS
call EscreveCronometroNoLCD ; Vai escrever horas, minutos, segundos
; e centésimos de segundos no buffer do
; display LCD
Principal:
orl PCON,#1 ; A CPU se desativa, e só volta a se
; ativar quando ocorrer uma interrupção
jb PartePara,Principal ; Se estiver contando tempo volta a
; aguardar na Principal
jnb Zera,Principal ; Se o cronometro estiver parado e não
; for apertada a tecla de zerar, volta
; a aguardar na Principal
;-------------------------------------------------------------------------------
ZeraCronometro:
mov CentesimosDeSegundos,#0 ; Carrega contador de Centésimos com "0"
mov Segundos,#0 ; Carrega contador de Segundos com "0"
mov Minutos,#0 ; Carrega contador de Minutos com "0"
mov Horas,#0 ; Carrega contador de Horas com "0"
call EscreveCronometroNoLCD ; Escreve dados acima no buffer do LCD
clr Zera ; Limpa flag para informar que zerou
sjmp Principal ; Volta a aguardar na Principal depois
; de zerar o cronômetro
;-------------------------------------------------------------------------------
TrataTecla:
jb TeclaValida,AguardaFimDeTecla ;
; Vai aguardar finalização de qualquer
; tecla
jb LAP,TestaTeclaLAP ; Não para o cronometro se o mesmo
; estiver em LAP
jb TeclaPartePara,TestaTeclaLAP ;
; Vai testar tecla LAP somente se a
; tecla parte/para não estiver apertada
djnz Debounce,SaiTrataTecla ; Sai da rotina enquanto não vencer o
; tempo de Debounce
setb TeclaValida ; Sinaliza que tem tecla apertada
cpl PartePara ; Parte ou para o cronometro
sjmp SaiTrataTecla ; Sai da rotina de tratar teclas

TestaTeclaLAP:
jnb PartePara,TestaTeclaZera ;
; Vai testar tecla zera somente se o
; cronometro estiver parado
jb TeclaLAP,SaiTrataTecla ; Sai da rotina se a tecla LAP não
; estiver apertada
djnz Debounce,SaiTrataTecla ; Sai da rotina enquanto não vencer o
; tempo de Debounce
setb TeclaValida ; Sinaliza que tem tecla apertada
cpl LAP ; Entra ou sai do modo LAP
sjmp SaiTrataTecla ; Sai da rotina de tratar teclas

TestaTeclaZera:
jb PartePara,SaiTrataTecla ; Não testa tecla zera se o cronometro
; estiver rodando
jb TeclaZera,SaiTrataTecla ; Sai da rotina se a tecla zera não
; estiver apertada
djnz Debounce,SaiTrataTecla ; Sai da rotina enquanto não vencer o
; tempo de Debounce
setb TeclaValida ; Sinaliza que tem tecla apertada
setb Zera ; Sinaliza que é para zerar o cronometro
_______________________________________________
17
sjmp SaiTrataTecla ; Sai da rotina de tratar teclas

AguardaFimDeTecla:
mov A,P2 ; Le a porta 2 para testar se ainda tem
; tecla apertada
orl A,#11110010b ; Coloca "1" nos bits não usados
cpl A ; Inverte o conteúdo do acumulador
jnz SaiTrataTecla ; Sai se o acumulador não estiver com
; '0', sinal de que ainda tem tecla
; apertada
mov Debounce,#10 ; Recarrega o contador de debounce com
; 100 mS
clr TeclaValida ; Sinaliza que não tem nenhuma tecla
; apertada
SaiTrataTecla:
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
IncrementaCronometro:
inc CentesimosDeSegundos ; Incrementa o contador de centésimos
; de segundos
mov A,CentesimosDeSegundos ; Carrega acumulador com o contador
; de centésimos de segundos
cjne A,#100,SaiIncrementaCronometro ;
; Testa se já chegou a 100, se não
; chegou sai da rotina de incrementar
; cronometro
mov CentesimosDeSegundos,#0 ; Carrega o contador de centésimos de
; segundos com '0'
ContaSegundos:
inc Segundos ; Incrementa contador de segundos
mov A,Segundos ; Carrega acumulador com o contador
; de segundos
cjne A,#60,SaiIncrementaCronometro ;
; Testa se já chegou a 60, se não chegou
; sai da rotina de incrementar
; cronometro
mov Segundos,#0 ; Carrega o contador de segundos com '0'

ContaMinutos:
inc Minutos ; Incrementa contador de minutos
mov A,Minutos ; Carrega acumulador com o contador
; de minutos
cjne A,#60,SaiIncrementaCronometro ;
; Testa se já chegou a 60, se não chegou
; sai da rotina de incrementar
; cronometro
mov Minutos,#0 ; Carrega o contador de minutos com '0'

ContaHoras:
inc Horas ; Incrementa contador de horas
mov A,Horas ; Carrega acumulador com o contador
; de horas
cjne A,#100,SaiIncrementaCronometro ;
; Testa se já chegou a 100, se não
; chegou sai da rotina de incrementar
mov Horas,#0 ; Carrega o contador de horas com '0'

SaiIncrementaCronometro:
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
EscreveCronometroNoLCD:
mov A,Horas ; Carrega o acumulador com Horas
mov B,#10 ; Carrega o registrador 'B' com 10
_______________________________________________
18
div AB ; Divide 'A' por 'B', então 'A' sai com
; a dezena de horas e 'B' com a unidade

orl A,#030h ; Converte a dezena de horas para ASCII


mov Linha1doDisplay,A ; Carrega a dezena de horas no buffer
; do display
orl B,#030h ; Converte a unidade de horas para ASCII
mov Linha1doDisplay+1,B ; Carrega a unidade de horas no buffer
; do display

mov Linha1doDisplay+2,#':' ; Carrega ':' no buffer do display

mov A,Minutos ; Carrega o acumulador com Minutos


mov B,#10 ; Carrega o registrador 'B' com 10
div AB ; Divide 'A' por 'B', então 'A' sai com
; a dezena de minutos e 'B' com a
; unidade

orl A,#030h ; Converte a dezena de minutos para


; ASCII
mov Linha1doDisplay+3,A ; Carrega a dezena de minutos no buffer
; do display
orl B,#030h ; Converte a unidade de minutos para
; ASCII
mov Linha1doDisplay+4,B ; Carrega a unidade de minutos no buffer
; do display

mov Linha1doDisplay+5,#':' ; Carrega ':' no buffer do display

mov A,Segundos ; Carrega o acumulador com Segundos


mov B,#10 ; Carrega o registrador 'B' com 10
div AB ; Divide 'A' por 'B', então 'A' sai com
; a dezena de segundos e 'B' com a
; unidade

orl A,#030h ; Converte a dezena de segundos para


; ASCII
mov Linha1doDisplay+6,A ; Carrega a dezena de segundos no buffer
; do display
orl B,#030h ; Converte a unidade de segundos para
; ASCII
mov Linha1doDisplay+7,B ; Carrega a unidade de segundos no
; buffer do display

mov Linha1doDisplay+8,#',' ; Carrega ',' no buffer do display

mov A,CentesimosDeSegundos ; Carrega o acumulador com Centésimos


; de segundos
mov B,#10 ; Carrega o registrador 'B' com 10
div AB ; Divide 'A' por 'B', então 'A' sai com
; a decimo de segundos e 'B' com o
; centésimo de segundos

orl A,#030h ; Converte os décimos de segundos para


; ASCII
mov Linha1doDisplay+9,A ; Carrega os décimos de segundos no
; buffer do display
orl B,#030h ; Converte os centésimos de segundos
; para ASCII
mov Linha1doDisplay+10,B ; Carrega os centésimos de segundos no
; buffer do display

ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
_______________________________________________
19
; Escreve Mensagem no Display LCD
;-------------------------------------------------------------------------------
EscreveMensagem:
clr A ; Zera o acumulador para que na próxima
; instrução só tenha valor o DPTR
movc A,@A+DPTR ; Busca caractere da mensagem na memória
; de programa
cjne A,#ETX,EscreveMensagem1 ; Testa se chegou ao fim da mensagem
call EscreveDisplay ; Vai transferir para o display o
; que escreveu no buffer
ret ; Retorna para onde veio

EscreveMensagem1:
mov @R0,A ; Escreve no buffer o que
; pegou na mensagem
inc R0 ; Incrementa o ponteiro
; do buffer do Display
inc DPTR ; Incrementa o ponteiro da mensagem
jmp EscreveMensagem ; Volta para escrever o próximo
; caractere no display

; Fim EscreveMensagem
;-------------------------------------------------------------------------------
; Apaga Display
;-------------------------------------------------------------------------------
ApagaDisplay:
mov R0,#Linha1doDisplay ; Carrega ponteiro com inicio
; da linha 1 do display
mov R1,#32 ; Carrega tamanho do buffer do Display
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveDisplay ; Vai transferir para o display o
; que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaDisplay
;-------------------------------------------------------------------------------
; Apaga Linha 1 do Display
;-------------------------------------------------------------------------------
ApagaLinha1doDisplay:
mov R0,#Linha1doDisplay ; Carrega ponteiro com inicio
; da linha 1 do display
mov R1,#16 ; Carrega tamanho do buffer da linha 1
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveLinha1doDisplay ; Vai transferir para a linha 1 do
; display o que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaLinha1doDisplay
;-------------------------------------------------------------------------------
; Apaga linha 2 do Display
;-------------------------------------------------------------------------------
ApagaLinha2doDisplay:
mov R0,#Linha2doDisplay ; Carrega ponteiro com inicio
; da linha 2 do display
mov R1,#16 ; Carrega tamanho do buffer da linha 2
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveLinha2doDisplay ; Vai transferir para a linha 2 do
; display o que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaLinha2doDisplay
;-------------------------------------------------------------------------------
; Limpa buffer do Display LCD
; Escreve espaços (brancos) no buffer do display
;-------------------------------------------------------------------------------
_______________________________________________
20
LimpaBufferDisplay:
mov A,#' ' ; Carrega ACC com espaço
CLS1: mov @R0,A ; Escreve espaço na memória de Display
inc R0 ; Incrementa ponteiro da memória
djnz R1,CLS1 ; Testa se ja apagou toda memória
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
; Escreve no display
; Escreve todo o conteúdo do buffer do display na memória do mesmo
;-------------------------------------------------------------------------------
EscreveDisplay:
call EscreveLinha1doDisplay ; Vai escrever a Linha 1 do display
call EscreveLinha2doDisplay ; Vai escrever a Linha 2 do display
ret ; Retorna para onde veio

; Fim EscreveDisplay
;-------------------------------------------------------------------------------
; Escreve linha 1 do display
; Esta rotina escreve o conteudo do buffer
; da linha 1 na memória de dados do display
;-------------------------------------------------------------------------------
EscreveLinha1doDisplay:
; Envia o comando para mudar para o início da linha 1
mov ComandoLCD,#080h ; Comando para mudar para o inicio
; da linha 1 do display
clr RsLCD ; Coloca o display para receber COMANDOS
mov R0,#ComandoLCD ; Carrega R0 com o endereço de onde esta
; o comando a ser enviado para o display
call EnviaByteParaDisplayLCD ; Envia o comando de mudar para
; o inicio da linha 1 do display
setb RsLCD ; Coloca o display para receber DADOS

; Agora vai transferir os dados do buffer da linha 1 para o display


mov R0,#Linha1doDisplay ; Carrega o ponteiro com o endereço
; do buffer da linha 1 do display

LoopLinha1doDisplay:
call EnviaByteParaDisplayLCD ; Vai enviar 1 byte para o display LCD
cjne R0,#Linha1doDisplay+16,LoopLinha1doDisplay ;
; Testa o fim da linha 1
ret ; Retorna para onde veio

; Fim EscreveLinha1doDisplay
;-------------------------------------------------------------------------------
; Escreve linha 2 do display
; Esta rotina escreve o conteudo do buffer
; da linha 2 na memória de dados do display
;-------------------------------------------------------------------------------
EscreveLinha2doDisplay:
; Envia o comando para mudar para o início da linha 2
mov ComandoLCD,#0C0h ; Comando para mudar para o inicio
; da linha 2 do display
clr RsLCD ; Coloca o display para receber COMANDOS
mov R0,#ComandoLCD ; Carrega R0 com o endereço de onde esta
; o comando a ser enviado para o display
call EnviaByteParaDisplayLCD ; Envia o comando de mudar para
; o inicio da linha 2 do display
setb RsLCD ; Coloca o display para receber DADOS

; Agora vai transferir os dados do buffer da linha 2 para o display


mov R0,#Linha2doDisplay ; Carrega o ponteiro com o endereço
; do buffer da linha 2 do display
_______________________________________________
21
LoopLinha2doDisplay:
call EnviaByteParaDisplayLCD ; Vai enviar 1 byte para o display LCD
cjne R0,#Linha2doDisplay+16,LoopLinha2doDisplay ;
; Testa o fim da linha 2
ret ; Retorna para onde veio

; Fim EscreveLinha2doDisplay
;-------------------------------------------------------------------------------
; Envia byte para o display LCD
; Esta rotina envia um byte de comando ou de dado para o display LCD
;-------------------------------------------------------------------------------
EnviaByteParaDisplayLCD:
mov A,@R0 ; Carrega ACC com a memória de Display

; Coloca nible alto no barramento


anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble alto para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 100 uS
mov R7,#50 ; Carrega para 100 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 100 uS

mov A,@R0 ; Carrega ACC com a memória de Display


swap A ; Move o nibble baixo para que
; seja enviado para o display

; Coloca nible baixo no barramento


anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nible alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble baixo para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 100 uS
mov R7,#50 ; Carrega para 100 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 100 uS

inc R0 ; Incrementa ponteiro da


; memória de Display
ret ; Retorna para onde veio

; Fim EnviaByteParaDisplayLCD
;-------------------------------------------------------------------------------
; Envia nibble alto para o display LCD
; Esta rotina é usada apenas na inicialização do display
;-------------------------------------------------------------------------------
EnviaNibleAltoParaDisplayLCD:
; Coloca nible alto no barramento
_______________________________________________
22
anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble alto para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 200 uS
mov R7,#100 ; Carrega para 200 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 200 uS

ret ; Retorna para onde veio

; Fim EnviaNibleAltoParaDisplayLCD
;-------------------------------------------------------------------------------
Retardo100mS:
mov R3,#181 ; Carrega R3 para um retardo de 100 mS,
; com um cristal de 11,0592 MHz
sjmp Retardo4mS1
;-------------------------------------------------------------------------------
Retardo4mS:
mov R3,#8 ; Carrega R3 para um retardo de 4 mS,
; com um cristal de 11,0592 MHz
Retardo4mS1:
mov R4,#39
Retardo4mS2: ; Fica neste loop até que os 2 registros
; cheguem a '0', quando então terá
; transcorrido 4 mS ou 100 mS,
; dependendo do valor de R3
djnz R4,Retardo4mS2
djnz R3,Retardo4mS2
ret ; Retorna para onde veio

; Fim Retardo100mS e Retardo4mS


;-------------------------------------------------------------------------------
CodeSize equ $ ; Neste ponto o montador informa
; o tamanho do código

MemoriaLivre equ 8192-CodeSize ; Neste ponto o montador calcula e


; informa a memória de programa livre
;-------------------------------------------------------------------------------
; Para visualizar o tamanho do código e a memória de programa livre
; o usuário deve abrir o arquivo ContBin.lst que é gerado pelo montador
; no mesmo subdiretório onde esta o ContBin.asm
;-------------------------------------------------------------------------------
; Sinaliza para o montador o fim do programa
end

_______________________________________________
23
02.06. Mensagem com Deslocamento no Display LCD:

Este programa mostra uma mensagem longa (>16 caracteres)


no display LCD.

É usada a tecla “1” para comandar o deslocamento da


mensagem. Para cada toque com duração inferior a 300ms a
mensagem avança 1 caractere. Se a tecla for pressionada por mais
de 300ms o deslocamento da mensagem se faz de modo
automático.

Este exemplo está no CD no arquivo Scrollh.asm.


;-------------------------------------------------------------------------------
$MOD89S52
$TITLE(Deslocamento de menssagem)
;-------------------------------------------------------------------------------
;
; Esta rotina demonstra uma forma de apresentar uma mensagem longa (maior
; que a quantidade de colunas do display) através do deslocamento da mesma
;
;-------------------------------------------------------------------------------

TempoDoTimer2 EQU 65535-9216 ; Tempo para 10 mS

ETX EQU 003h


BarramentoDoDisplay EQU P2 ; Barramento de dados do display

TeclaPasso EQU P2.0

RsLCD EQU P0.2


EnLCD EQU P0.4

;-------------------------------------------------------------------------------
; Segmento de bits
; Segmento da memória RAM, onde se pode trabalhar com um único bit, que são
; usados como sinalizadores (Flags).
; Este segmento começa na posição 020h e vai até 02Fh, totalizando 16 bytes,
; o que permite que o usuário tenha até 128 Flags no seu programa
BSEG at 0h

Passou300mS: DBIT 1
TeclaValida: DBIT 1
PassouSegundos: DBIT 1

;-------------------------------------------------------------------------------
; Segmento da memória RAM
DSEG at 22h

Segundos: DS 1

Tempo1Segundo: DS 1
ComandoLCD: DS 1
Debounce: DS 1
Tempo300mS: DS 1
Temporario: DS 3

DSEG at 40h
_______________________________________________
24
Linha1doDisplay: DS 16
Linha2doDisplay: DS 16

FimDosDados EQU $ ; Serve para indicar o fim da memória RAM usada


; para os dados.
;-------------------------------------------------------------------------------
CSEG ; Informa ao montador a partir daqui começa o código
; fonte do programa

org 0000h ; Endereço de inicialização do microcontrolador,


; o qual vai para este quando é resetado
jmp IcializaPlaca
;-------------------------------------------------------------------------------

org 002Bh ; Vetor da interrupção do Timer 2


Timer2:
clr TF2

call TrataTecla

djnz Tempo1Segundo,SaiTimer2
mov Tempo1Segundo,#100
djnz Segundos,SaiTimer2
setb PassouSegundos

SaiTimer2:
reti

;-------------------------------------------------------------------------------
IcializaPlaca:

InicializaLCD:
call Retardo4mS
call Retardo4mS
call Retardo4mS
call Retardo4mS

clr EnLCD
clr RsLCD ; Coloca o display para receber Comandos
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
call Retardo4mS
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
mov A,#020h
call EnviaNibleAltoParaDisplayLCD
mov A,#020h
call EnviaNibleAltoParaDisplayLCD
mov A,#080h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#060h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#0C0h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD

_______________________________________________
25
mov A,#010h
call EnviaNibleAltoParaDisplayLCD
setb RsLCD ; Coloca o display para receber Dados
call Retardo4mS

call ApagaDisplay
;-------------------------------------------------------------------------------

setb TR2 ; Habilita timer 2


setb PT2 ; Timer 2 com prioridade alta
setb ET2 ; Habilita interrupção do timer 2
setb EA ; Habilita o controlador de interrupt
clr CAP2 ; Coloca o timer 2 para recarregar
; automaticamente

mov RCAP2H,#(high TempoDoTimer2) ;


; Para a recarga da parte alta do
; timer 2
mov RCAP2L,#(low TempoDoTimer2) ;
; Para a recarga da parte baixa do
; timer 2

mov Debounce,#10 ; Recarrega o contador de debounce


; com 100 mS
clr TeclaValida ; Sinaliza que não tem nenhuma tecla
; apertada
clr Passou300mS
mov Tempo300mS,#30

;-------------------------------------------------------------------------------
mov DPTR,#MsgMMDB ; Carrega DPTR com endereço da mensagem
mov R0,#Linha1doDisplay ; Carrega R0 com endereço de início do
; buffer do display
call EscreveMensagem ; Vai escrever a mensagem

mov Tempo1Segundo,#100 ; Carrega o contador de tempo de 1 S


mov Segundos,#2 ; Carrega com 2 para esperar 2 segundos
clr PassouSegundos ; Desliga flag que sinaliza que já
; passou a quantidade de segundos
; carregada anteriormente
jnb PassouSegundos,$ ; Espera passar os 2 segundos

ScrollMensagem:
mov DPTR,#MsgMMDB1 ; Carrega o DPTR com a mensagem a
; deslocar
call ApagaLinha2doDisplay ; Apaga a linha 2 do display

;-------------------------------------------------------------------------------
DeslocaMsg:
call DeslocaLinha2 ; Vai deslocar todo o buffer da linha 2
; uma posição para esquerda
clr A ; Zera o acumulador para usar na próxima
; instrução
movc A,@A+DPTR ; Busca 1 caractere da mensagem
inc DPTR ; Incrementa ponteiro da mensagem para
; usar quando for buscar o próximo
; caractere da mensagem
cjne A,#ETX,DeslocaMsg1 ; Testa caractere para ver se já chegou
; ao fim da mensagem, se não vai
; escrever o caractere na última posição
; do buffer da linha 2
sjmp TerminaDeDeslocar ; Vai deslocar 16 vezes com espaços,
; para terminar o deslocamento

DeslocaMsg1:
_______________________________________________
26
mov Linha2doDisplay+15,A ; Escreve o caractere na última posição
; do buffer da linha 2
call EscreveLinha2doDisplay ; Escreve o conteúdo do buffer da linha
; linha 2 no display
call Retardo200mS ; Espera 200 mS (velocidade de
; deslocamento da mensagem)
sjmp DeslocaMsg ; Volta para deslocar de novo

TerminaDeDeslocar:
mov R3,#16 ; carrega R3 com 16 para terminar o
; deslocamento
TerminaDeDeslocar1:
mov Linha2doDisplay+15,#' ' ; Carrega a última posição do buffer
; com espaço
call DeslocaLinha2 ; Vai deslocar todo o buffer da linha 2
; uma posição para esquerda
call EscreveLinha2doDisplay ; Escreve o conteúdo do buffer da linha
; linha 2 no display
call Retardo200mS ; Espera 200 mS (velocidade de
; deslocamento da mensagem)
djnz R3,TerminaDeDeslocar1 ; Se R3 não chegou a '0' volta para
; deslocar mais uma vez
sjmp ScrollMensagem ; Quando termina o deslocamento volta
; a fazer novamente

; Fim DeslocaMsg
;-------------------------------------------------------------------------------
DeslocaLinha2:
mov R0,#Linha2doDisplay ; Carrega R0 com a primeira posição do
; buffer da linha 2
mov R1,#Linha2doDisplay+1 ; Carrega R1 com a segunda posição do
; buffer da linha 2
DeslocaLinha21:
mov A,@R1 ; Carrega acumulador com o caractere da
; segunda posição
mov @R0,A ; Escreve caractere da segunda posição
; na primeira posição
inc R0 ; Incrementa R0 para usar no próximo
; deslocamento
inc R1 ; Incrementa R1 para usar no próximo
; deslocamento
cjne R0,#Linha2doDisplay+15,DeslocaLinha21 ;
; Testa se já chegou na última posição
; da linha 2
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
Retardo200mS:
jb Passou300mS,Retardo200mS1 ;
; Se flag Passou300mS estiver ligado vai
; aguardar 200 mS e então fazer novo
; deslocamento
jb TeclaValida,Retardo200mS ;
; Enquanto flag TeclaValida estiver
; ligada volta a testar se já passou
; 300 mS
jnb TeclaValida,$ ; Fica aqui aguardando que a tecla de
; passo a passo seja pressionada
sjmp SaiRetardo200mS ; Sai da rotina

Retardo200mS1:
mov R7,#22 ; Carrega R7 para esperar 200 mS

Retardo200mS2:
orl PCON,#1 ; A CPU se desativa, e só volta a se
_______________________________________________
27
; ativar quando ocorrer uma interrupção
djnz R7,Retardo200mS2 ; Volta enquanto R7 não chegar a '0'

SaiRetardo200mS:
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
TrataTecla:
jb TeclaValida,AguardaFimDeTecla ;
; Vai aguardar finalização de qualquer
; tecla
jb TeclaPasso,SaiTrataTecla ;
; Vai testar tecla Alarme somente se a
; tecla parte/para não estiver apertada
djnz Debounce,SaiTrataTecla ; Sai da rotina enquanto não vencer o
; tempo de Debounce
setb TeclaValida ; Sinaliza que tem tecla apertada
clr Passou300mS ; Desliga flag para parar o deslocamento
; automático
sjmp SaiTrataTecla ; Sai da rotina de tratar tecla

AguardaFimDeTecla:
mov A,P2 ; Le a porta 2 para testar se ainda tem
; tecla apertada
orl A,#11111110b ; Coloca "1" nos bits não usados
cpl A ; Inverte o conteúdo do acumulador
jnz AguardaFimDeTecla1 ; Sai se o acumulador não estiver com
; '0', sinal de que ainda tem tecla
; apertada
mov Debounce,#10 ; Recarrega o contador de debounce com
; 100 mS
clr TeclaValida ; Sinaliza que não tem nenhuma tecla
; apertada
mov Tempo300mS,#30 ; Recarrega o contador de tempo
; de 300 mS
AguardaFimDeTecla1:
djnz Tempo300mS,SaiTrataTecla ;
; Decrementa o contador de tempo de
; 300 mS, e sai se ainda não tiver
; passado os 300 mS
setb Passou300mS ; Sinaliza que já passou os 300 mS

SaiTrataTecla:
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
; Escreve Mensagem no Display LCD
;-------------------------------------------------------------------------------
EscreveMensagem:
clr A ; Zera o acumulador para que na próxima
; instrução só tenha valor o DPTR
movc A,@A+DPTR ; Busca caractere da mensagem na memória
; de programa
cjne A,#ETX,EscreveMensagem1 ; Testa se chegou ao fim da mensagem
call EscreveDisplay ; Vai transferir para o display o
; que escreveu no buffer
ret ; Retorna para onde veio

EscreveMensagem1:
mov @R0,A ; Escreve no buffer o que
; pegou na mensagem
inc R0 ; Incrementa o ponteiro
; do buffer do Display
inc DPTR ; Incrementa o ponteiro da mensagem
jmp EscreveMensagem ; Volta para escrever o próximo
_______________________________________________
28
; caractere no display

; Fim EscreveMensagem
;-------------------------------------------------------------------------------
; Apaga Display
;-------------------------------------------------------------------------------
ApagaDisplay:
mov R0,#Linha1doDisplay ; Carrega ponteiro com inicio
; da linha 1 do display
mov R1,#32 ; Carrega tamanho do buffer do Display
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveDisplay ; Vai transferir para o display o
; que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaDisplay
;-------------------------------------------------------------------------------
; Apaga Linha 1 do Display
;-------------------------------------------------------------------------------
ApagaLinha1doDisplay:
mov R0,#Linha1doDisplay ; Carrega ponteiro com inicio
; da linha 1 do display
mov R1,#16 ; Carrega tamanho do buffer da linha 1
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveLinha1doDisplay ; Vai transferir para a linha 1 do
; display o que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaLinha1doDisplay
;-------------------------------------------------------------------------------
; Apaga linha 2 do Display
;-------------------------------------------------------------------------------
ApagaLinha2doDisplay:
mov R0,#Linha2doDisplay ; Carrega ponteiro com inicio
; da linha 2 do display
mov R1,#16 ; Carrega tamanho do buffer da linha 2
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveLinha2doDisplay ; Vai transferir para a linha 2 do
; display o que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaLinha2doDisplay
;-------------------------------------------------------------------------------
; Limpa buffer do Display LCD
; Escreve espaços (brancos) no buffer do display
;-------------------------------------------------------------------------------
LimpaBufferDisplay:
mov A,#' ' ; Carrega ACC com espaço
CLS1: mov @R0,A ; Escreve espaço na memória de Display
inc R0 ; Incrementa ponteiro da memória
djnz R1,CLS1 ; Testa se ja apagou toda memória
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
; Escreve no display
; Escreve todo o conteúdo do buffer do display na memória do mesmo
;-------------------------------------------------------------------------------
EscreveDisplay:
call EscreveLinha1doDisplay ; Vai escrever a Linha 1 do display
call EscreveLinha2doDisplay ; Vai escrever a Linha 2 do display
ret ; Retorna para onde veio

; Fim EscreveDisplay
;-------------------------------------------------------------------------------
_______________________________________________
29
; Escreve linha 1 do display
; Esta rotina escreve o conteúdo do buffer
; da linha 1 na memória de dados do display
;-------------------------------------------------------------------------------
EscreveLinha1doDisplay:
; Envia o comando para mudar para o início da linha 1
mov ComandoLCD,#080h ; Comando para mudar para o inicio
; da linha 1 do display
clr RsLCD ; Coloca o display para receber COMANDOS
mov R0,#ComandoLCD ; Carrega R0 com o endereço de onde esta
; o comando a ser enviado para o display
call EnviaByteParaDisplayLCD ; Envia o comando de mudar para
; o inicio da linha 1 do display
setb RsLCD ; Coloca o display para receber DADOS

; Agora vai transferir os dados do buffer da linha 1 para o display


mov R0,#Linha1doDisplay ; Carrega o ponteiro com o endereço
; do buffer da linha 1 do display

LoopLinha1doDisplay:
call EnviaByteParaDisplayLCD ; Vai enviar 1 byte para o display LCD
cjne R0,#Linha1doDisplay+16,LoopLinha1doDisplay ;
; Testa o fim da linha 1
ret ; Retorna para onde veio

; Fim EscreveLinha1doDisplay
;-------------------------------------------------------------------------------
; Escreve linha 2 do display
; Esta rotina escreve o conteúdo do buffer
; da linha 2 na memória de dados do display
;-------------------------------------------------------------------------------
EscreveLinha2doDisplay:
; Envia o comando para mudar para o início da linha 2
mov ComandoLCD,#0C0h ; Comando para mudar para o inicio
; da linha 2 do display
clr RsLCD ; Coloca o display para receber COMANDOS
mov R0,#ComandoLCD ; Carrega R0 com o endereço de onde esta
; o comando a ser enviado para o display
call EnviaByteParaDisplayLCD ; Envia o comando de mudar para
; o inicio da linha 2 do display
setb RsLCD ; Coloca o display para receber DADOS

; Agora vai transferir os dados do buffer da linha 2 para o display


mov R0,#Linha2doDisplay ; Carrega o pnteiro com o endereço
; do buffer da linha 2 do display

LoopLinha2doDisplay:
call EnviaByteParaDisplayLCD ; Vai enviar 1 byte para o display LCD
cjne R0,#Linha2doDisplay+16,LoopLinha2doDisplay ;
; Testa o fim da linha 2
ret ; Retorna para onde veio

; Fim EscreveLinha2doDisplay
;-------------------------------------------------------------------------------
; Envia byte para o display LCD
; Esta rotina envia um byte de comando ou de dado para o display LCD
;-------------------------------------------------------------------------------
EnviaByteParaDisplayLCD:
mov A,@R0 ; Carrega ACC com a memória de Display

; Coloca nible alto no barramento


anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
_______________________________________________
30
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble alto para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 100 uS
mov R7,#50 ; Carrega para 100 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 100 uS

mov A,@R0 ; Carrega ACC com a memória de Display


swap A ; Move o nibble baixo para que
; seja enviado para o display

; Coloca nible baixo no barramento


anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble baixo para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 100 uS
mov R7,#50 ; Carrega para 100 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 100 uS

inc R0 ; Incrementa ponteiro da


; memória de Display
ret ; Retorna para onde veio

; Fim EnviaByteParaDisplayLCD
;-------------------------------------------------------------------------------
; Envia nible alto para o display LCD
; Esta rotina é usada apenas na inicialização do display
;-------------------------------------------------------------------------------
EnviaNibleAltoParaDisplayLCD:
; Coloca nible alto no barramento
anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble alto para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 200 uS
mov R7,#100 ; Carrega para 200 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 200 uS

ret ; Retorna para onde veio


_______________________________________________
31
; Fim EnviaNibleAltoParaDisplayLCD
;-------------------------------------------------------------------------------
Retardo100mS:
mov R3,#181 ; Carrega R3 para um retardo de 100 mS,
; com um cristal de 11,0592 MHz
sjmp Retardo4mS1
;-------------------------------------------------------------------------------
Retardo4mS:
mov R3,#8 ; Carrega R3 para um retardo de 4 mS,
; com um cristal de 11,0592 MHz
Retardo4mS1:
mov R4,#39
Retardo4mS2: ; Fica neste loop até que os 2 registros
; cheguem a '0', quando então terá
; transcorrido 4 mS ou 100 mS,
; dependendo do valor de R3
djnz R4,Retardo4mS2
djnz R3,Retardo4mS2
ret ; Retorna para onde veio

; Fim Retardo100mS e Retardo4mS


;-------------------------------------------------------------------------------
; Textos
MsgMMDB:
DB ' Scroll '
DB ' horizontal',ETX
;-------------------------------------------------------------------------------
MsgMMDB1:
DB 'Este kit pode ser configurado para trabalhar com as familias'
DB ' MCS51, AVR, PIC16F e PIC18F.',ETX
;-------------------------------------------------------------------------------
CodeSize equ $ ; Neste ponto o montador informa
; o tamanho do código

MemoriaLivre equ 8192-CodeSize ; Neste ponto o montador calcula e


; informa a memória de programa livre
;-------------------------------------------------------------------------------
; Para visualizar o tamanho do código e a memória de programa livre
; o usuário deve abrir o arquivo ContBin.lst que é gerado pelo montador
; no mesmo subdiretório onde esta o ContBin.asm
;-------------------------------------------------------------------------------
; Sinaliza para o montador o fim do programa
end

_______________________________________________
32
02.07. Relógio Calendário Baseado no HT1380:

Este programa mostra como implementar um relógio calendário


baseado no circuito integrado HT1380, com saída no display LCD

É usada a tecla “A” para selecionar o campo a ser acertado, as


teclas de “0” a “9” para entrar com o valor desejado e a tecla “D”
para finalizar o acerto.

Este exemplo está no CD no arquivo HT1380.asm.


;-------------------------------------------------------------------------------
$MOD89S52
$TITLE (Relogio HT1380)
;-------------------------------------------------------------------------------
;
; Esta rotina demonstra o uso do circuito integrado HT1380,
; que é um relógio calendário
;
;-------------------------------------------------------------------------------

TempoDoTimer EQU 65535-9216 ; Tempo de 10 mS


TempoTecla EQU 10 ; 10 * 10 = 100 mS

ETX EQU 003h


BarramentoDoDisplay EQU P2 ; Barramento de dados do display

REST EQU P0.5 ; Sinal de seleção do relógio/calendário


; HT1380
SCLK EQU P1.7 ; Clock para a interface serial do
; HT1380
IO EQU P2.7 ; Linha de entrada e saída para a
; interface serial do HT1380
RsLCD EQU P0.2 ; Porta de saída do sinal que informa
; ao display LCD se os dados do
; barramento correspondem a dados ou
; a comandos
EnLCD EQU P0.4 ; Porta de saída do pulso de
; transferência de dados para o display
;-------------------------------------------------------------------------------
; Segmento de Bits
;
; Segmento da memória RAM, onde se pode trabalhar com um único bit, que são
; usados como sinalizadores (Flags).
; Este segmento começa na posição 020h e vai até 02Fh, totalizando 16 bytes,
; o que permite que o usuário tenha até 128 Flags no seu programa
;-------------------------------------------------------------------------------
BSEG at 0h

Passou250ms: DBIT 1 ; Para sinalizar que passou 250 mS


TeclaValida: DBIT 1 ; Para sinalizar tecla válida
TeclaApertada: DBIT 1 ; Para sinalizar tecla apertada

;-------------------------------------------------------------------------------
; Segmento de Bytes
;
; Segmento da memória RAM, reservada para as variáveis usadas no programas
;-------------------------------------------------------------------------------

_______________________________________________
33
DSEG at 21h

NumeroDeBits: DS 1
; Os registros a seguir devem estar na seqüência que se segue, pois é a que o
; HT1380 recebe e envia
R_Segundo: DS 1 ; Registro de segundos
R_Minuto: DS 1 ; Registro de minutos
R_Hora: DS 1 ; Registro de horas
R_Dia: DS 1 ; Registro de dias
R_Mes: DS 1 ; Registro de mês
R_DiaDaSemana: DS 1 ; Registro de dia da semana
R_Ano: DS 1 ; Registro de anos
; Fim da sequencia
Tempo250ms: DS 1 ; Registro o tempo de 250 mS
TempoDeMenssagem: DS 1 ; Registro o tempo de apresentação de
; mensagem
Linha: DS 1 ; Registro para a linha do teclado
Coluna: DS 1 ; Registro para a coluna do teclado
DebounceDeTecla: DS 1 ; Registro para o contador de debounce
TeclaAtual: DS 1 ; Registro para salvar a tecla
ComandoLCD: DS 1 ; Registro destinado ao envio de
; comandos para o display LCD

Linha1doDisplay: DS 16 ; Buffer da linha 1 do display


Linha2doDisplay: DS 16 ; Buffer da linha 2 do display
;-------------------------------------------------------------------------------
FimDosDados EQU $ ; Serve para indicar o fim da memória
; RAM usada para os dados.
;-------------------------------------------------------------------------------
CSEG ; Informa ao montador que a partir daqui
; começa o código fonte do programa

org 0000h ; Endereço de inicialização do


; microcontrolador, o qual vai para este
; quando é resetado
jmp InicializaPlaca ; Vai inicializar a placa
;-------------------------------------------------------------------------------
ORG 000Bh ; Vetor do interrupt do Timer 0

TrataTimer:
mov TH0,#(high TempoDoTimer) ;#0DBh ; 10 mS a 11,059200 MHz
mov TL0,#(low TempoDoTimer) ;#0FFh

push P2
push PSW ; Salva o PSW na pilha
push ACC ; Salva o ACC na pilha
push B ; Salva o B na pilha
;-------------------------------------------------------------------------------
; | | | | |
; | Col 0 | Col 1 | Col 2 | Col 3 |
; ________|_______|_______|_______|_______|________
; | | | | |
; Linha 0 | 1 | 2 | 3 | A |
; ________|_______|_______|_______|_______|________
; | | | | |
; Linha 1 | 4 | 5 | 6 | B |
; ________|_______|_______|_______|_______|________
; | | | | |
; Linha 2 | 7 | 8 | 9 | C |
; ________|_______|_______|_______|_______|________
; | | | | |
; Linha 3 | * | 0 | # | D |
; ________|_______|_______|_______|_______|________
; | | | | |
; | | | | |
_______________________________________________
34
;-------------------------------------------------------------------------------
Teclado:
jb TeclaValida,DesligaTecla ;
; Se tiver tecla válida, vai esperar
; o fim da tecla
mov DPTR,#ColunaLinha ; Carrega ponteiro com a tabela
; de Coluna/Linha
mov Linha,#0FFh ; Carrega para começar a varrer na Linha
; '0', pois vai incrementar quando for
; varrer o teclado
clr TeclaApertada ; Limpa flag para testar no retorno

VarreTeclado:
inc Linha ; Incrementa contador de Linha
mov A,Linha ; Carrega para converter número da Linha
movc A,@A+DPTR ; Converte número da Linha para fazer
; a varredura
jz DebounceDeTeclado ; Sai se já varreu todas as linhas

VarreLinhas:
swap A ; Para acionar a linha na parte alta da
; porta 2
mov P2,A ; Aciona a saída correspondente a Linha
mov A,P2 ; Lê retorno do teclado
orl A,#0F0h ; Mascara para ignorar a parte alta
cpl A ; Complementa para testar na próxima
; instrução
jz VarreTeclado ; Volta para varrer a próxima linha,
; pois não tinha tecla acionada nesta
; linha
cpl A ; Complementa para voltar ao que era
; antes
mov TeclaAtual,A ; Salva temporariamente a leitura da
; tecla
jb TeclaApertada,SaiTeclado ;
; Sai se já tiver tecla apertada
mov Coluna,#4 ; Carrega contador de colunas com '4'
; porque vai decrementar na rotina
; VarreColunas
VarreColunas:
mov A,Coluna ; Carrega o acumulador com o número da
; coluna
dec A ; Decrementa o acumulador para buscar a
; coluna certa na tabela
movc A,@A+DPTR ; Converte para testar retorno do
; teclado
xrl A,TeclaAtual ; Testa para saber em que coluna esta o
; retorno
jnz VarreColunas1 ; Vai continuar a varredura se não tiver
; tecla apertada na coluna, ou se tiver
; mais de uma tecla apertada
SalvaTecla:
mov A,Linha ; Carrega o acumulador com o número da
; linha
rl A ; Desloca 2 vezes para poder anexar ao
rl A ; número da coluna
dec Coluna ; Para chegar ao número correto da
; coluna e na próxima instrução anexar a
; linha
xrl A,Coluna ; Anexa o número da coluna ao da linha
mov TeclaAtual,A ; Salva valor da tecla em binário
setb TeclaApertada ; Sinaliza que tem apenas uma tecla
; apertada
sjmp VarreTeclado ; Volta para varrer o teclado até o fim

_______________________________________________
35
VarreColunas1:
djnz Coluna,VarreColunas ; Chega a 0 se tiver mais de uma tecla
; apertada na mesma Linha
SaiTeclado:
clr TeclaApertada ; Sinaliza que não tem nenhuma tecla
; apertada
DebounceDeTeclado:
jnb TeclaApertada,DesligaTecla ;
; Se não tiver mais tecla apertada, vai
; fazer o debounce do desligamento
inc DebounceDeTecla ; Incrementa o contador de debounce
mov A,DebounceDeTecla ; Carrega o acumulador para testar na
; próxima instrução
cjne A,#08h,TrataSegundo ; Testa se já passou 80 mS com mesma
; tecla
dec DebounceDeTecla ; Decrementa para não ultrapassar '8'
; no próximo teste
setb TeclaValida ; Sinaliza tecla válida depois de 80 mS

; Converte a tecla para ASCII


mov A,TeclaAtual ; Carrega o acumulador com a leitura do
; teclado em binário
acall ConverteTeclaASCII ; Vai converter tecla binária em ASCII
mov TeclaAtual,A ; O acumulador agora esta com a tecla
; em ASCII
sjmp TrataSegundo ; Vai sair da rotina de teclado

DesligaTecla:
mov P2,#00Fh ; Aciona todas as linhas
mov A,P2 ; Lê retorno do teclado
orl A,#0F0h ; Para garantir que o nibble alto fique
; com 'F'
cpl A ; Complementa para testar na próxima
; instrução
jnz TrataSegundo ; Vai sair da interrupção se o
; acumulador não estiver com '0', pois
; ainda tem tecla apertada
djnz DebounceDeTecla,TrataSegundo ;
; Conta tempo de debounce de desliga
inc DebounceDeTecla ; Para
clr TeclaValida ; Desliga tecla válida depois 80 mS

TrataSegundo:
djnz Tempo250ms,SaiInt
mov Tempo250ms,#25
setb Passou250ms

SaiInt:
pop B ; Recupera B
pop ACC ; Recupera ACC
pop PSW ; Recupera PSW

pop P2

reti ; Sai do interrupt do timer 0

; Fim do interupt do Timer 0


;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
ColunaLinha: ; Tabela de conversão para as colunas
; e linhas
db 0FEh
db 0FDh
db 0FBh

_______________________________________________
36
db 0F7h
db 000h
;-------------------------------------------------------------------------------
ConverteTeclaASCII: ; Rotina para converter o valor binário
; da tecla em ASCII
inc A ; Incrementa para que a instrução 'ret'
; não tenha efeito na tabela
movc A,@A+PC ; O acumulador volta com o caractere
; ASCII correspondente ao valor binário
ret ; Retorna para onde veio

; Vem Volta
; com com
; Hex ASCII
db '1' ; 0 1
db '2' ; 1 2
db '3' ; 2 3
db 'A' ; 3 A
db '4' ; 4 4
db '5' ; 5 5
db '6' ; 6 6
db 'B' ; 7 B
db '7' ; 8 7
db '8' ; 9 8
db '9' ; A 9
db 'C' ; B C
db '*' ; C *
db '0' ; D 0
db '#' ; E #
db 'D' ; F D
;-------------------------------------------------------------------------------
InicializaPlaca:
;-------------------------------------------------------------------------------
; Apaga memoria RAM interna
;-------------------------------------------------------------------------------
mov R0,#0FFh ; Carrega ponteiro com o tamanho
; da memória
ApagaMemoria:
mov @R0,#0 ; Escreve "0" na posição de memória
; apontada por R0
djnz R0,ApagaMemoria ; Decrementa o ponteiro e volta para
; apagar a posição anterior até
; chegar a posição "0"
; Fim apaga memoria
;-------------------------------------------------------------------------------
mov SP,#FimDosDados ; O stack pointer é carregado com o fim
; da memória ocupada para evitar que a
; área de dados seja invadida pela pilha
;-------------------------------------------------------------------------------
; InicializaLCD
;-------------------------------------------------------------------------------
mov P2,#0F0h
InicializaLCD:
call Retardo4mS
call Retardo4mS
call Retardo4mS
call Retardo4mS

clr EnLCD
clr RsLCD ; Coloca o display para receber Comandos
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
call Retardo4mS
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
_______________________________________________
37
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
mov A,#020h
call EnviaNibleAltoParaDisplayLCD
mov A,#020h
call EnviaNibleAltoParaDisplayLCD
mov A,#080h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#060h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#0C0h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#010h
call EnviaNibleAltoParaDisplayLCD
setb RsLCD ; Coloca o display para receber Dados
call Retardo4mS

call ApagaDisplay

; Fim InicializaLCD
;-------------------------------------------------------------------------------
; Inicializa Timer
;-------------------------------------------------------------------------------
InicializaTimer:
mov TH0,#(high TempoDoTimer); Carrega timer 0 com
mov TL0,#(low TempoDoTimer) ; 10 mS a 11,0592 MHz

mov TMOD,#011h ; Programa timer 0 e 1 para o modo 1


; 16 bits
setb ET0 ; Habilita interrupção do timer 0
setb EA ; Habilita interupts
setb TR0 ; Habilita timer 0

; Fim InicializaTimer
;-------------------------------------------------------------------------------

mov DPTR,#MsgHT1380 ; Carrega o DPTR com o endereço da


; mensagem inicial
mov R0,#Linha1doDisplay ; Carrega R0 com endereço de início do
; buffer do display
call EscreveMensagem ; Vai escrever a mensagem

mov Tempo250ms,#25 ; Carrega o contador de tempo de 250 mS


mov TempoDeMenssagem,#8 ; Carrega com 8 para esperar 2 segundos
; 8 vezes 250 mS

AguardaMenssagem:
clr Passou250ms ; Desliga o flag que informa que passou
; 250 mS
jnb Passou250ms,$ ; Espera passar 250 mS
djnz TempoDeMenssagem,AguardaMenssagem ;
; Decrementa 8 vezes para um total
; de 2 segundos (8 x 250 mS)
call ApagaDisplay ; Vai apgar o display

mov DPTR,#MsgHoraInicio
; Carrega o DPTR com o Endereço do
; horário de início do relógio
mov R0,#Linha1doDisplay ; Carrega R0 com endereço de início do
; buffer do display
_______________________________________________
call EscreveMensagem ; Vai escrever o horário de início no

38
; buffer do display
call CarregaHora ; Vai converter o horário que esta no
; buffer do display e enviar para
; o HT1380
Principal:
mov PCON,#1 ; A CPU se desativa, e só volta a se
; ativar quando ocorrer uma interrupção
jb TeclaValida,TestaAcerta ; Se tiver tecla válida, vai testar se é
; para entrar no modo de acerto
jnb Passou250ms,Principal ; Volta para Principal enquanto não
; passar 250 mS
Principal1:
call Relogio ; Vai ler o relógio HT1380 e escrever
; no display
clr Passou250ms ; Desliga o flag Passou250ms, para
; esperar novamente
sjmp Principal ; Volta para Principal para esperar mais
; 250 mS
TestaAcerta:
mov A,TeclaAtual ; Carrega o acumulador com a tecla atual
cjne A,#'A',Principal1 ; Testa se é 'A', se positivo segue e
; entra na rotina de acerto, caso
; contrario vai para Principal1
;-------------------------------------------------------------------------------
mov R1,#Linha1doDisplay ; Carrega R1 com endereço de início do
; buffer do display
AcertaRelogio:
mov PCON,#1 ; A CPU se desativa, e só volta a se
; ativar quando ocorrer uma interrupção
jb TeclaValida,AcertaRelogio ;
; Aguarda o fim da tecla

Testa250ms:
mov B,#' ' ; Carrega B com espaço para piscar o
; digito

Testa250ms1:
mov PCON,#1 ; A CPU se desativa, e só volta a se
; ativar quando ocorrer uma interrupção
jb TeclaValida,TestaTecla ; Se tiver tecla válida, vai testar
; tecla
PiscaDigito:
mov A,B ; Carrega o acumulador com B
xch A,@R1 ; Troca o conteúdo da posição apontada
; por R1 com o acumulador
mov B,A ; Salva em B o que estava na posição
; apontada por R1
jnb Passou250ms,$ ; Espera passar 250 mS
clr Passou250ms ; Desliga o flag Passou250ms, para
; esperar novamente
call EscreveDisplay ; Vai escrever o display
sjmp Testa250ms1 ; Volta para Testa250ms1, e fica neste
; loop, piscando o digito correspondente
; até ter outra tecla válida
TestaTecla:
mov A,TeclaAtual ; Carrega o acumulador com a tecla atual
cjne A,#'D',EscreveDigito ; Testa se é 'D', se positivo segue e
; encerra o acerto
cjne @R1,#' ',TestaTecla1 ; Testa se a posição é igual a espaço,
; se não for pula a próxima instrução
mov @R1,B ; Torna a escrever o valor correto no
; buffer do display antes de converter
; e enviar para o HT1380
TestaTecla1:
call CarregaHora ; Vai converter o horário que esta no
_______________________________________________
; buffer do display e enviar para

39
; o HT1380
sjmp Principal1 ; Volta para Principal1

EscreveDigito:
mov @R1,A ; Carrega a posição do buffer
; correspondente ao digito com o valor
; da tecla atual que esta no acumulador
call EscreveDisplay ; Vai reescrever o display
jb TeclaValida,$ ; Aguarda o fim da tecla
inc R1 ; Incrementa o ponteiro do digito

TestaDoisPontos:
cjne @R1,#':',TestaBarra ; Testa se é um caractere separador para
; pular o separador
inc R1 ; Incrementa o ponteiro para pular o
; separador
sjmp Testa250mS ; Volta para acertar o próximo digito

TestaBarra:
cjne @R1,#'/',TestaPonteiroLinha1 ;
; Testa se é um caractere separador para
; pular o separador
inc R1 ; Incrementa o ponteiro para pular o
; separador
sjmp Testa250mS ; Volta para acertar o próximo digito

TestaPonteiroLinha1:
cjne R1,#Linha1doDisplay+8,TestaPonteiroLinha2 ;
; Testa se já chegou no fim do que esta
; na linha 1
mov R1,#Linha2doDisplay ; Carrega o ponteiro com o início
; da linha 2
sjmp Testa250mS ; Volta para acertar o próximo digito

TestaPonteiroLinha2:
cjne R1,#Linha2doDisplay+8,Testa250mS ;
; Testa se já chegou no fim do que esta
; na linha 2
mov R1,#Linha1doDisplay ; Carrega o ponteiro com o início
; da linha 1
sjmp Testa250mS ; Volta para acertar o próximo digito

;-------------------------------------------------------------------------------
CarregaHora:
mov R0,#Linha1doDisplay ; Carrega R0 com endereço de início do
; buffer do display da linha 1 (Hora)
call ConverteRelogioBCDPack ; Vai converter 'HH' ASCII para BCD Pack
mov R_Hora,A ; Salva horas para depois enviar para o
; HT1380

call ConverteRelogioBCDPack ; Vai converter 'MM' ASCII para BCD Pack


mov R_Minuto,A ; Salva minutos para depois enviar para
; o HT1380

call ConverteRelogioBCDPack ; Vai converter 'SS' ASCII para BCD Pack


mov R_Segundo,A ; Salva segundos para depois enviar para
; o HT1380

mov R0,#Linha2doDisplay ; Carrega R0 com endereço de início do


; buffer do display da linha 2 (Data)
call ConverteRelogioBCDPack ; Vai converter 'DD' ASCII para BCD Pack
mov R_Dia,A ; Salva dias para depois enviar para o
; HT1380

call ConverteRelogioBCDPack ; Vai converter 'MM' ASCII para BCD Pack


_______________________________________________
40
mov R_Mes,A ; Salva mês para depois enviar para o
; HT1380

call ConverteRelogioBCDPack ; Vai converter 'AA' ASCII para BCD Pack


mov R_Ano,A ; Salva anos para depois enviar para o
; HT1380

call HabilitaHT1380 ; Vai habilitar o HT1380 e permitir que


; se escreva no mesmo
call EnviaRelogio ; Vai escrever hora e data no HT1380
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
ConverteRelogioBCDPack:
mov A,@R0 ; Carrega o acumulador com a dezena
anl A,#00Fh ; Converte a dezena em ASCII para BCD
swap A ; Transfere a dezena BCD para o nible
; alto
mov B,A ; Salva a dezena
inc R0 ; Incrementa para buscar a unidade
mov A,@R0 ; Carrega o acumulador com a unidade
anl A,#00Fh ; Converte a unidade em ASCII para BCD
xrl A,B ; Anexa a unidade a dezena
inc R0 ; Incrementa para pular o separador
; (':' ou '/')
inc R0 ; Incrementa para buscar o próximo

ret ; Retorna para onde veio


;-------------------------------------------------------------------------------
MsgHoraInicio:
DB '12:00:00 '
DB '01/10/07',ETX
;-------------------------------------------------------------------------------
MsgHT1380:
DB ' Usando o '
DB ' Relogio HT1380',ETX
;-------------------------------------------------------------------------------
; Rotina para trabalhar com relógio HT1380 ou HT1381 da Holtek
;-------------------------------------------------------------------------------
Relogio:
clr SCLK ; Para garantir que o sinal de clock
; esteja em '0' quando o HT1380 for
; selecionado
setb REST ; Habilita comunicação com HT1380

mov A,#10111111b ; Comando de leitura no modo burst


call EnviaHT1380 ; Vai enviar o comando de leitura

mov R7,#7 ; Para ler 7 bytes


mov R0,#R_Segundo ; O primeiro byte que o HT1380 envia
; corresponde aos segundos

LeRelogio:
call RecebeHT1380 ; Vai receber 1 byte do HT1380
mov @R0,A ; Salva o byte recebido
inc R0 ; Incrementa o ponteiro para salvar o
; próximo byte
djnz R7,LeRelogio ; Volta a receber enquanto não receber
; os 7 bytes
clr REST ; Desabilita comunicação com HT1380

MostraRelogio:
mov R0,#R_Hora
mov R1,#Linha1doDisplay ; Buffer da linha 1 do display

_______________________________________________
41
call EscreveRelogio ; Escreve dezena e unidade de Hora
mov @R1,#':' ; Escreve ":"
inc R1 ; Incrementa para pular o separador
dec R0 ; Decrementa para buscar minutos
call EscreveRelogio ; Escreve dezena e unidade de Minuto
mov @R1,#':' ; Escreve ":"
inc R1 ; Incrementa para pular o separador
dec R0 ; Decrementa para buscar segundos
call EscreveRelogio ; Escreve dezena e unidade de Segundo

mov R0,#R_Dia
mov R1,#Linha2doDisplay ; Buffer da linha 2 do display

call EscreveRelogio ; Escreve dezena e unidade de Dia


mov @R1,#'/' ; Escreve "/"
inc R1 ; Incrementa para pular o separador
inc R0 ; Incecrementa para buscar mês
call EscreveRelogio ; Escreve dezena e unidade de Mes
mov @R1,#'/' ; Escreve "/"
inc R1 ; Incrementa para pular o separador
inc R0 ; Incecrementa para descartar o dia da
; semana
inc R0 ; Incecrementa para buscar ano
call EscreveRelogio ; Escreve dezena e unidade de Ano

call EscreveDisplay ; Vai escrever data e hora no display

ret ; Retorna para onde veio


;-------------------------------------------------------------------------------
EscreveRelogio:
mov A,@R0 ; Carrega o acumulador com o que leu do
; HT1380
swap A ; Coloca a dezena na parte baixa do
; acumulador
anl A,#00Fh ; Apaga a unidade que esta na parte alta
orl A,#'0' ; Converte para ASCII
mov @R1,A ; Envia a dezena ASCII para o buffer do
; display
inc R1 ; Incrementa o ponteiro para quando for
; enviar a unidade para o buffer do
; display
mov A,@R0 ; Carrega o acumulador com o que leu do
; HT1380
anl A,#00Fh ; Apaga a dezena que esta na parte alta
orl A,#'0' ; Converte para ASCII
mov @R1,A ; Envia a unidade ASCII para o buffer do
; display
inc R1 ; Incrementa o ponteiro para quando for
; tratar o próximo byte lido do HT1380

ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
EnviaRelogio:
clr SCLK ; Para garantir que o sinal de clock
; esteja em '0' quando o HT1380 for
; selecionado
setb REST ; Habilita comunicação com HT1380

mov A,#10111110b ; Comando de escrita no modo burst


call EnviaHT1380 ; Envia comando para o HT1380

mov R7,#7 ; Para enviar 7 bytes para o HT1380


mov R0,#R_Segundo ; Primeiro byte a ser enviado (Segundos)

_______________________________________________
42
EnviaRelogio1: ; Loop para enviar 7 bytes
mov A,@R0 ; Carrega o acumulador com o que vai
; enviar
call EnviaHT1380 ; Vai enviar para o HT1380
inc R0 ; Incrementa o ponteiro para quando for
; buscar o próximo byte a enviar
djnz R7,EnviaRelogio1 ; Fim do loop para enviar 7 bytes

mov A,#080h ; Para proteção de escrita


call EnviaHT1380 ; Envia a proteção de escrita
clr REST ; Desabilita comunicação com HT1380

ret ; Retorna para onde veio

; Fim de EnviaRelogio
;-------------------------------------------------------------------------------
HabilitaHT1380:
clr REST ; Coloca o REST em '0' para garantir a
; transição de '0' para '1' na primeira
; vez que enviar dados para o HT1380
clr SCLK ; Inicializa o sinal de clock com '0'

setb REST ; Habilita comunicação com HT1380


mov A,#08Eh ; Habilita escrita no HT1380
call EnviaHT1380 ; Envia 8Eh para o HT1380
clr A ; Zera o acumulador para enviar para o
HT1380
call EnviaHT1380 ; Envia 00h para o HT1380
clr REST ; Desabilita comunicação com HT1380

setb REST ; Habilita comunicação com HT1380


mov A,#080h ; Ativa o oscilador do HT1380
call EnviaHT1380 ; Envia 80h para o HT1380
clr A ; Zera o acumulador para enviar para o
HT1380
call EnviaHT1380 ; Envia 00h para o HT1380
clr REST ; Desabilita comunicação com HT1380

ret ; Retorna para onde veio

; Fim de HabilitaHT1380
;-------------------------------------------------------------------------------
EnviaHT1380:
mov NumeroDeBits,#8 ; Carrega para enviar 8 bits

EnviaHT1380_1:
rrc A ; Transfere o bit menos significativo do
; acumulador para o carry
mov IO,C ; Transfere o carry para a linha de IO
; para enviar ao HT1380
setb SCLK ; Início do pulso de clock
clr SCLK ; Fim do pulso de clock
djnz NumeroDeBits,EnviaHT1380_1 ;
; Enquanto não chegar a zero é porque
; ainda tem bits a enviar
setb IO ; Coloca a linha de IO em '1' para
; quando for receber dados do HT1380

ret ; Retorna para onde veio

; Fim de EnviaHT1380
;-------------------------------------------------------------------------------
RecebeHT1380:
setb IO ; Para garantir que a linha de IO esteja

_______________________________________________
43
; para receber dados do HT1380
mov NumeroDeBits,#8 ; Carrega para receber 8 bits

RecebeHT1380_1:
mov C,IO ; Carrega o carry com o que esta na
; linha de IO
rrc A ; Transfere o carry para o bit mais
; significativo do acumulador
setb SCLK ; Início do pulso de clock
clr SCLK ; Fim do pulso de clock
djnz NumeroDeBits,RecebeHT1380_1 ;
; Enquanto não chegar a zero é porque
; ainda tem bits a receber

ret ; Retorna para onde veio

; Fim de RecebeHT1380
;-------------------------------------------------------------------------------
; Escreve Mensagem no Display LCD
;-------------------------------------------------------------------------------
EscreveMensagem:
clr A ; Zera o acumulador para que na próxima
; instrução só tenha valor o DPTR
movc A,@A+DPTR ; Busca caractere da mensagem na memória
; de programa
cjne A,#ETX,EscreveMensagem1 ; Testa se chegou ao fim da mensagem
call EscreveDisplay ; Vai transferir para o display o
; que escreveu no buffer
ret ; Retorna para onde veio

EscreveMensagem1:
mov @R0,A ; Escreve no buffer o que
; pegou na mensagem
inc R0 ; Incrementa o ponteiro
; do buffer do Display
inc DPTR ; Incrementa o ponteiro da mensagem
jmp EscreveMensagem ; Volta para escrever o próximo
; caractere no display

; Fim EscreveMensagem
;-------------------------------------------------------------------------------
; Apaga Display
;-------------------------------------------------------------------------------
ApagaDisplay:
mov R0,#Linha1doDisplay ; Carrega ponteiro com inicio
; da linha 1 do display
mov R1,#32 ; Carrega tamanho do buffer do Display
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveDisplay ; Vai transferir para o display o
; que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaDisplay
;-------------------------------------------------------------------------------
; Apaga Linha 1 do Display
;-------------------------------------------------------------------------------
ApagaLinha1doDisplay:
mov R0,#Linha1doDisplay ; Carrega ponteiro com inicio
; da linha 1 do display
mov R1,#16 ; Carrega tamanho do buffer da linha 1
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveLinha1doDisplay ; Vai transferir para a linha 1 do
; display o que escreveu no buffer
ret ; Retorna para onde veio

_______________________________________________
; Fim ApagaLinha1doDisplay

44
;-------------------------------------------------------------------------------
; Apaga linha 2 do Display
;-------------------------------------------------------------------------------
ApagaLinha2doDisplay:
mov R0,#Linha2doDisplay ; Carrega ponteiro com inicio
; da linha 2 do display
mov R1,#16 ; Carrega tamanho do buffer da linha 2
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveLinha2doDisplay ; Vai transferir para a linha 2 do
; display o que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaLinha2doDisplay
;-------------------------------------------------------------------------------
; Limpa buffer do Display LCD
; Escreve espaços (brancos) no buffer do display
;-------------------------------------------------------------------------------
LimpaBufferDisplay:
mov A,#' ' ; Carrega ACC com espaço
CLS1: mov @R0,A ; Escreve espaço na memória de Display
inc R0 ; Incrementa ponteiro da memória
djnz R1,CLS1 ; Testa se já apagou toda memória
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
; Escreve no display
; Escreve todo o conteúdo do buffer do display na memória do mesmo
;-------------------------------------------------------------------------------
EscreveDisplay:
call EscreveLinha1doDisplay ; Vai escrever a Linha 1 do display
call EscreveLinha2doDisplay ; Vai escrever a Linha 2 do display
ret ; Retorna para onde veio

; Fim EscreveDisplay
;-------------------------------------------------------------------------------
; Escreve linha 1 do display
; Esta rotina escreve o conteúdo do buffer
; da linha 1 na memória de dados do display
;-------------------------------------------------------------------------------
EscreveLinha1doDisplay:
; Envia o comando para mudar para o início da linha 1
mov ComandoLCD,#080h ; Comando para mudar para o inicio
; da linha 1 do display
clr RsLCD ; Coloca o display para receber COMANDOS
mov R0,#ComandoLCD ; Carrega R0 com o endereço de onde esta
; o comando a ser enviado para o display
call EnviaByteParaDisplayLCD ; Envia o comando de mudar para
; o inicio da linha 1 do display
setb RsLCD ; Coloca o display para receber DADOS

; Agora vai transferir os dados do buffer da linha 1 para o display


mov R0,#Linha1doDisplay ; Carrega o ponteiro com o endereço
; do buffer da linha 1 do display

LoopLinha1doDisplay:
call EnviaByteParaDisplayLCD ; Vai enviar 1 byte para o display LCD
cjne R0,#Linha1doDisplay+16,LoopLinha1doDisplay ;
; Testa o fim da linha 1
ret ; Retorna para onde veio

; Fim EscreveLinha1doDisplay
;-------------------------------------------------------------------------------
; Escreve linha 2 do display
; Esta rotina escreve o conteúdo do buffer
_______________________________________________
45
; da linha 2 na memória de dados do display
;-------------------------------------------------------------------------------
EscreveLinha2doDisplay:
; Envia o comando para mudar para o início da linha 2
mov ComandoLCD,#0C0h ; Comando para mudar para o inicio
; da linha 2 do display
clr RsLCD ; Coloca o display para receber COMANDOS
mov R0,#ComandoLCD ; Carrega R0 com o endereço de onde esta
; o comando a ser enviado para o display
call EnviaByteParaDisplayLCD ; Envia o comando de mudar para
; o inicio da linha 2 do display
setb RsLCD ; Coloca o display para receber DADOS

; Agora vai transferir os dados do buffer da linha 2 para o display


mov R0,#Linha2doDisplay ; Carrega o ponteiro com o endereço
; do buffer da linha 2 do display

LoopLinha2doDisplay:
call EnviaByteParaDisplayLCD ; Vai enviar 1 byte para o display LCD
cjne R0,#Linha2doDisplay+16,LoopLinha2doDisplay ;
; Testa o fim da linha 2
ret ; Retorna para onde veio

; Fim EscreveLinha2doDisplay
;-------------------------------------------------------------------------------
; Envia byte para o display LCD
; Esta rotina envia um byte de comando ou de dado para o display LCD
;-------------------------------------------------------------------------------
EnviaByteParaDisplayLCD:
mov A,@R0 ; Carrega ACC com a memória de Display

; Coloca nible alto no barramento


anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble alto para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 100 uS
mov R7,#50 ; Carrega para 100 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 100 uS

mov A,@R0 ; Carrega ACC com a memória de Display


swap A ; Move o nible baixo para que
; seja enviado para o display

; Coloca nible baixo no barramento


anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble baixo para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita
_______________________________________________
46
; Retardo de 100 uS
mov R7,#50 ; Carrega para 100 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 100 uS

inc R0 ; Incrementa ponteiro da


; memória de Display
ret ; Retorna para onde veio

; Fim EnviaByteParaDisplayLCD
;-------------------------------------------------------------------------------
; Envia nible alto para o display LCD
; Esta rotina é usada apenas na inicialização do display
;-------------------------------------------------------------------------------
EnviaNibleAltoParaDisplayLCD:
; Coloca nible alto no barramento
anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble alto para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 200 uS
mov R7,#100 ; Carrega para 200 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 200 uS

ret ; Retorna para onde veio

; Fim EnviaNibleAltoParaDisplayLCD
;-------------------------------------------------------------------------------
Retardo4mS:
mov R3,#8 ; Carrega R3 para um retardo de 4 mS,
; com um cristal de 11,0592 MHz
mov R4,#39
Retardo4mS1: ; Fica neste loop até que os 2 registros
; cheguem a '0', quando então terá
; transcorrido 4 mS ou 100 mS,
; dependendo do valor de R3
djnz R4,Retardo4mS1
djnz R3,Retardo4mS1
ret ; Retorna para onde veio

; Fim Retardo4mS
;-------------------------------------------------------------------------------
CodeSize equ $ ; Neste ponto o montador informa
; o tamanho do codigo

MemoriaLivre equ 8192-CodeSize ; Neste ponto o montador calcula e


; informa a memória de programa livre
;-------------------------------------------------------------------------------
; Para visualizar o tamanho do código e a memória de programa livre
; o usuário deve abrir o arquivo ContBin.lst que é gerado pelo montador
; no mesmo subdiretório onde esta o ContBin.asm
;-------------------------------------------------------------------------------
; Sinaliza para o montador o fim do programa
end

_______________________________________________
47
02.08. Relógio Com Alarme:

Este programa mostra como implementar um relógio com alarme


no display LCD.

São usadas as teclas: “A” para selecionar o modo de acerto de


hora, “1” para incrementar as horas, “2” para incrementar os
minutos, “3” para selecionar o modo de acerto do alarme e a tecla
“A” para ligar e desligar o alarme.

Foram usadas as linhas P1.0 e P1.1 para a saída do alarme.


Quando alarma, o microcontrolador coloca um sinal complementar
de aproximadamente 4kHz nestas duas linhas.

Para que a saída de alarme possa funcionar, é preciso que seja


montado na placa-padrão o circuito complementar mostrado na
Figura 13.01.
VCC

R1 R2
4K7 4K7
C1
220nF,100V
P1.0

LS1

P1.1
Buzzer Piezoeletrico
Figura 13.01

Este exemplo está no CD no arquivo Relogio.asm.


;-------------------------------------------------------------------------------
$MOD89S52
$TITLE(Relógio despertador)
;-------------------------------------------------------------------------------
;
; Esta rotina demonstra uma das formas de implementar um relógio
; despertador através de software com display de cristal líquido.
;
; O teclado deve ser configurado para 4 teclas através de JP16
;
; Função das teclas:
; Tecla 1 - Incrementa Hora
; Tecla 2 - Incrementa Minuto
; Tecla 3 - Entra e sai do modo de ajustar despertador
; Tecla A - Entra e sai do modo de ajustar relógio, tendo também a função
; de ligar e desligar o alarme quando esta no modo de ajustar despertador
;
;-------------------------------------------------------------------------------

_______________________________________________
48
TempoDoTimer2 EQU 65535-9216 ; Tempo para 10 mS

ETX EQU 003h


BarramentoDoDisplay EQU P2 ; Barramento de dados do display

TeclaIncrementaHora EQU P2.0 ; Porta de entrada da tecla Incrementa


; hora
TeclaIncrementaMinuto EQU P2.1 ; Porta de entrada da tecla Incrementa
; minuto
TeclaAlarme EQU P2.2 ; Porta de entrada da tecla Alarme
TeclaAcerta EQU P2.3 ; Porta de entrada da tecla Acerta

Buzina0 EQU P1.0 ; Porta destinada a gerar sinal do


; alarme
Buzina1 EQU P1.1 ; Porta destinada a gerar sinal do
; alarme complementado, para uso com
; uma buzinz piezoelétrica

RsLCD EQU P0.2 ; Porta de saída do sinal que informa


; ao display LCD se os dados do
; barramento correspondem a dados ou
; a comandos
EnLCD EQU P0.4 ; Porta de saída do pulso de
; transferência de dados para o display
;-------------------------------------------------------------------------------
; Segmento de Bits
;
; Segmento da memória RAM, onde se pode trabalhar com um único bit, que são
; usados como sinalizadores (Flags).
; Este segmento começa na posição 020h e vai até 02Fh, totalizando 16 bytes,
; o que permite que o usuário tenha até 128 Flags no seu programa
;-------------------------------------------------------------------------------
BSEG at 0h

Acerta: DBIT 1 ; Sinaliza o modo de acerta relógio


Alarme: DBIT 1 ; Sinaliza o modo de ajusta alarme
IncrementaMinuto: DBIT 1 ; Sinaliza que é para incrementar minuto
IncrementaHora: DBIT 1 ; Sinaliza que é para incrementar hora
TeclaValida: DBIT 1 ; Sinaliza que tem uma tecla válida
IncrementaMinutoAlarme: DBIT 1 ; Sinaliza que é para incrementar minuto
; do alarme
IncrementaHoraAlarme: DBIT 1 ; Sinaliza que é para incrementar hora
; do alarme
Passou300mS: DBIT 1 ; Sinaliza que já passou 300 mS
AlarmeLigado: DBIT 1 ; Sinaliza que o alarme esta ligado
TocaAlarme: DBIT 1 ; Sinaliza que é para tocar a buzina
;-------------------------------------------------------------------------------
; Segmento de Bytes
;
; Segmento da memória RAM, reservada para as variáveis usadas no programas
;-------------------------------------------------------------------------------
- DSEG at 22h

Horas: DS 1 ; Contador de Horas


Minutos: DS 1 ; Contador de Minutos
Segundos: DS 1 ; Contador de Segundos
Debounce: DS 1 ; Contador de Debounce de tecla
Tempo300mS: DS 1 ; Contador de Tempo de 300 mS
Tempo1Segundo: DS 1 ; Contador de Tempo de 1 Segundo

AlarmeHoras: DS 1 ; Registro para hora de alarme


AlarmeMinutos: DS 1 ; Registro para minuto de alarme

ComandoLCD: DS 1 ; Registro destinado ao envio de


_______________________________________________
49
; comandos para o display LCD

DSEG at 40h

Linha1doDisplay: DS 16 ; Buffer da linha 1 do display LCD


Linha2doDisplay: DS 16 ; Buffer da linha 2 do display LCD

;-------------------------------------------------------------------------------
FimDosDados EQU $ ; Serve para indicar o fim da memória
; RAM usada para os dados.
;-------------------------------------------------------------------------------
CSEG ; Informa ao montador que a partir daqui
; começa o código fonte do programa

org 0000h ; Endereço de inicialização do


; microcontrolador, o qual vai para este
; quando é resetado
jmp InicializaPlaca ; Vai inicializar a placa
;-------------------------------------------------------------------------------

org 002Bh ; Vetor da interrupção do Timer 2


Timer2:
push ACC
push B
push PSW
clr TF2 ; No caso do timer 2 o flag TF2 tem que
; ser zerado pelo software para que não
; aconteça uma nova interrupção logo que
; sair desta interrupção

call TrataTecla ; Vai tratar as teclas

jb Acerta,SaiTimer2 ; Se estiver no modo de acerto, sai da


; interrupção

djnz Tempo1Segundo,SaiTimer2 ; Sai da interrupção enquanto não passar


; o tempo de 1 Segundo
mov Tempo1Segundo,#100 ; Carrega tempo de 1 S com (100 x 10 mS)

call Relogio ; Vai incrementar o relógio

jb Alarme,SaiTimer2 ; Se tiver no modo de ajustar o alarme,


; sai da interrupção

call EscreveRelogioNoLCD ; Vai escrever horas, minutos e segundos


; no buffer do display LCD

jnb AlarmeLigado,SaiTimer2 ; Se o alarme estiver desligado, sai sem


; comparar hora do alarme
call ComparaHora ; Vai comparar hora do alarme

SaiTimer2:
call EscreveLinha1doDisplay ; Vai escrever o buffer do display na
; linha 1 do mesmo
pop PSW
pop B
pop ACC
reti ; Saída da interrupção do timer 2

;-------------------------------------------------------------------------------
InicializaPlaca:

InicializaLCD:
call Retardo4mS
call Retardo4mS
_______________________________________________
50
call Retardo4mS
call Retardo4mS

clr EnLCD
clr RsLCD ; Coloca o display para receber Comandos
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
call Retardo4mS
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
mov A,#020h
call EnviaNibleAltoParaDisplayLCD
mov A,#020h
call EnviaNibleAltoParaDisplayLCD
mov A,#080h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#060h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#0C0h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#010h
call EnviaNibleAltoParaDisplayLCD
setb RsLCD ; Coloca o display para receber Dados
call Retardo4mS

call ApagaDisplay
;-------------------------------------------------------------------------------
; Inicializa timer
;-------------------------------------------------------------------------------
setb TR2 ; Habilita timer 2
setb PT2 ; Timer 2 com prioridade alta
setb ET2 ; Habilita interrupção do timer 2
setb EA ; Habilita o controlador de interrupt
clr CAP2 ; Coloca o timer 2 para recarregar
; automaticamente

mov RCAP2H,#(high TempoDoTimer2) ;


; Para a recarga da parte alta do
; timer 2
mov RCAP2L,#(low TempoDoTimer2) ;
; Para a recarga da parte baixa do
; timer 2

mov Tempo1Segundo,#100 ; Carrega o contador de 1 segundo com


; (100 x 10 mS = 1 S)
;-------------------------------------------------------------------------------

mov P1,#11111100b ; Zera P1.0 e P1.1 destinadas a saída


; de alarme
mov Segundos,#0 ; Carrega o contador de segundos com '0'
; para que o relógio comece com
; '00' segundos
mov Minutos,#0 ; Carrega o contador de minutos com '0'
; para que o relógio comece com
; '00' minutos
mov Horas,#0 ; Carrega o contador de horas com '0'
; para que o relógio comece com
_______________________________________________
51
; '00' horas
mov AlarmeHoras,#0 ; Carrega o registro de hora de alarme
; com '00' horas
mov AlarmeMinutos,#0 ; Carrega o registro de minuto de alarme
; com '00' minutos

mov Debounce,#10 ; Recarrega o contador de debounce


; com 100 mS
clr TeclaValida ; Sinaliza que não tem nenhuma
; tecla apertada
clr Acerta ; Zera flag Acerta
clr Alarme ; Zera flag Alarme
clr IncrementaHora ; Zera flag IncrementaHora
clr IncrementaMinuto ; Zera flag IncrementaMinuto

clr AlarmeLigado ; Zera flag AlarmeLigado


clr TocaAlarme ; Zera flag TocaAlarme

clr Passou300mS ; Zera flag Passou300mS


mov Tempo300mS,#30 ; Carrega o contador de
; 300 mS (30 x 10 mS = 300 mS)

Principal:
mov PCON,#1 ; A CPU se desativa, e só volta a se
; ativar quando ocorrer uma interrupção

jnb TocaAlarme,Principal1 ; Se não for para tocar o alarme, vai


; testar se é para acertar hora ou
; ajustar alarme
jmp Buzina ; Vai tocar a buzina

Principal1:
jb Alarme,AjustaAlarme ; Se estiver no modo de ajustar alarme,
; vai para AjustaAlarme
jnb Acerta,Principal ; Se não estiver no modo de acerta hora
; volta a aguardar na Principal

;-------------------------------------------------------------------------------
; Rotina de acertar o relógio
;-------------------------------------------------------------------------------
AcertaRelogio:
mov Segundos,#0 ; Carrega o contador de segundos
; com '0'
jb IncrementaHora,AcertaHora ;
; Vai incrementar as horas se o flag
; IncrementaHora estiver ligado
jnb IncrementaMinuto,Principal ;
; Volta para Principal se o flag
; IncrementaMinuto estiver desligado,
; caso contrario vai incrementar minutos
AcertaMinuto:
inc Minutos ; Incrementa o contador de minutos
mov A,Minutos ; Carrega o acumulador com minutos
cjne A,#60,AtualizaRelogio ; Testa se minutos esta igual a '60', se
; estiver igual, zera minutos na
; instrução a seguir
mov Minutos,#0 ; Carrega o contador de minutos
; com '0'
jmp AtualizaRelogio

AcertaHora:
inc Horas ; Incrementa o contador de horas
mov A,Horas ; Carrega o acumulador com horas
cjne A,#24,AtualizaRelogio
; Testa se horas esta igual a '24', se
; estiver igual, zera horas na
_______________________________________________
; instrução a seguir

52
mov Horas,#0 ; Carrega o contador de horas
; com '0'

AtualizaRelogio:
call EscreveRelogioNoLCD ; Vai escrever relógio no buffer do
; display
jmp IncrementaRapido ; Vai testar se é para incrementar
; rápido

;-------------------------------------------------------------------------------
; Rotina de ajustar o alarme
;-------------------------------------------------------------------------------
AjustaAlarme:
jb IncrementaHora,AjustaHoraAlarme ;
; Vai incrementar horas do alarme se o
; flag IncrementaHora estiver ligado
jnb IncrementaMinuto,Principal ;
; Volta para a Principal se o flag
; IncrementaMinuto estiver desligado,
; caso contrario vai incrementar minutos
; do alarme
AjustaMinutoAlarme:
inc AlarmeMinutos ; Incrementa o registro dos minutos do
; alarme
mov A,AlarmeMinutos ; Carrega o acumulador com minutos do
; alarme
cjne A,#60,AtualizaAlarme ; Testa se minutos do alarme esta igual
; a '60', se estiver igual, zera minutos
; do alarme na instrução a seguir
mov AlarmeMinutos,#0 ; Carrega o registro de minutos do
; alarme com '0'
jmp AtualizaAlarme ; Vai atualizar display do alarme

AjustaHoraAlarme:
inc AlarmeHoras ; Incrementa o registro das horas do
; alarme
mov A,AlarmeHoras ; Carrega o acumulador com horas do
; alarme
cjne A,#24,AtualizaAlarme ; Testa se horas do alarme esta igual
; a '24', se estiver igual, zera horas
; do alarme na instrução a seguir
mov AlarmeHoras,#0 ; Carrega o registro de horas do alarme
; com '0'
AtualizaAlarme:
call EscreveAlarmeNoLCD ; Escreve horas e minutos do alarme no
; buffer do display LCD

;-------------------------------------------------------------------------------
IncrementaRapido:
call Retardo100mS ; Espera 100 mS

IncrementaRapido1:
jnb TeclaValida,Principal ; Se não tiver tecla válida volta para
; Principal
jnb Passou300mS,IncrementaRapido1 ;
; Volta a testar tecla válida se não
; tiver passado 300 mS
jb Acerta,AcertaRelogio ; Se tiver passado 300 mS e o flag
; Acerta ligado vai incrementar horas ou
; minutos de modo rápido
jb Alarme,AjustaAlarme ; Se tiver passado 300 mS e o flag
; Alarme ligado vai incrementar horas ou
; minutos do alarme de modo rápido
jmp Principal ; Volta para Principal se não estiver
; no modo acerta relógio ou ajusta
; alarme
_______________________________________________
53
;-------------------------------------------------------------------------------
TrataTecla:
jb TeclaValida,AguardaFimDeTecla ;
; Vai aguardar finalização de qualquer
; tecla
jb TocaAlarme,TestaQualquerTecla ;
; Se o alarme estiver tocando, vai
; aceitar qualquer tecla para desligar
; o alarme

jb TeclaAcerta,TestaTeclaAlarme ;
; Vai testar tecla Alarme somente se a
; tecla parte/para não estiver apertada
djnz Debounce,SaiTrataTecla ; Sai da rotina enquanto não vencer o
; tempo de Debounce
setb TeclaValida ; Sinaliza que tem tecla apertada
jb Alarme,LigaDesligaAlarme ;
; Se estiver no modo ajusta alarme, a
; tecla acerta serve para ligar ou
; desligar o alarme
cpl Acerta ; Entra ou sai do modo acerta relógio
jnb Acerta,SaiTrataTecla ; Se o flag acerta estiver em '0' é
; porque saiu do modo acerta relógio

; Se o flag Acerta estiver ligado neste ponto,


; o programa entra no modo acerta relógio
mov Segundos,#0 ; Zera o contador de segundos
call EscreveRelogioNoLCD ; Vai escrever horas, minutos e segundos
; no buffer do display LCD
sjmp SaiTrataTecla ; Sai da rotina de tratar teclas

LigaDesligaAlarme:
cpl AlarmeLigado ; Liga ou desliga o alarme
mov Linha1doDisplay+9,#'*' ; Coloca '*' na coluna 10 da linha 1 do
; buffer do display
jb AlarmeLigado,SaiTrataTecla ;
; Sai da rotina de tratar teclas se o
; flag AlarmeLigado estiver ligado
mov Linha1doDisplay+9,#' ' ; Coloca espaço na coluna 10 da linha 1
; do buffer do display
sjmp SaiTrataTecla ; Sai da rotina de tratar teclas

TestaTeclaAlarme:
jb Acerta,TestaTeclaIncrementaHora
jb TeclaAlarme,TestaTeclaIncrementaHora
djnz Debounce,SaiTrataTecla ; Sai da rotina enquanto não vencer
; o tempo de Debounce
setb TeclaValida ; Sinaliza que tem tecla apertada
cpl Alarme ; Se ligado, sinaliza que é para ajustar
; horário do alarme
jnb Alarme,SaiTrataTecla ; Se desligado sai da rotina de tratar
; teclas
call EscreveAlarmeNoLCD ; Vai escrever horário do alarme no
; buffer do display
sjmp SaiTrataTecla ; Sai da rotina de tratar teclas

TestaTeclaIncrementaHora:
jb TeclaIncrementaHora,TestaTeclaIncrementaMinuto
djnz Debounce,SaiTrataTecla ; Sai da rotina enquanto não vencer
; o tempo de Debounce
setb TeclaValida ; Sinaliza que tem tecla apertada
setb IncrementaHora ; Sinaliza que é para incrementar hora
sjmp SaiTrataTecla ; Sai da rotina de tratar teclas

_______________________________________________
54
TestaTeclaIncrementaMinuto:
jb TeclaIncrementaMinuto,SaiTrataTecla
djnz Debounce,SaiTrataTecla ; Sai da rotina enquanto não vencer
; o tempo de Debounce
setb TeclaValida ; Sinaliza que tem tecla apertada
setb IncrementaMinuto ; Sinaliza que é para incrementar minuto
sjmp SaiTrataTecla ; Sai da rotina de tratar teclas

TestaQualquerTecla:
mov A,P2 ; Lê a porta 2 para testar
orl A,#11110000b ; Coloca "1" nos bits não usados
cpl A ; Complementa o acumulador para testar
; se o mesmo chegou aqui com '0FFh'
jz SaiTrataTecla ; Sai da rotina se não tiver nenhuma
; tecla apertada
djnz Debounce,SaiTrataTecla ; Sai da rotina enquanto não vencer
; o tempo de Debounce
setb TeclaValida ; Sinaliza que tem tecla apertada
clr TocaAlarme ; Desliga o flag TocaAlarme para parar
; de tocar o alarme
sjmp SaiTrataTecla ; Sai da rotina de tratar teclas

AguardaFimDeTecla:
mov A,P2 ; Le a porta 2 para testar se ainda tem
; tecla apertada
orl A,#11110000b ; Coloca "1" nos bits não usados
cpl A ; Inverte o conteúdo do acumulador
jnz AguardaFimDeTecla1 ; Sai se o acumulador não estiver com
; '0', sinal de que ainda tem tecla
; apertada
mov Debounce,#10 ; Recarrega o contador de debounce com
; 100 mS
clr TeclaValida ; Sinaliza que não tem nenhuma tecla
; apertada
clr IncrementaHora ; Desliga flag que sinaliza que é para
; incrementar o contador de horas
clr IncrementaMinuto ; Desliga flag que sinaliza que é para
; incrementar o contador de minutos
clr Passou300mS ; Desliga flag que sinaliza que já
; passou mais de 300 mS com uma mesma
; tecla apertada
mov Tempo300mS,#30 ; Recarrega o contador de tempo
; de 300 mS
AguardaFimDeTecla1:
djnz Tempo300mS,SaiTrataTecla ;
; Decrementa o contador de tempo de
; 300 mS, e sai se ainda não tiver
; passado os 300 mS
setb Passou300mS ; Sinaliza que já passou os 300 mS

SaiTrataTecla:
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
Relogio:
inc Segundos ; Incrementa contador de segundos
mov A,Segundos ; Carrega acumulador com o contador
; de segundos
cjne A,#60,SaiRelogio ; Testa se já chegou a 60, se não chegou
; sai da rotina de relógio
mov Segundos,#0 ; Carrega o contador de segundos com '0'

ContaMinutos:
inc Minutos ; Incrementa contador de minutos
mov A,Minutos ; Carrega acumulador com o contador
; de minutos
_______________________________________________
55
cjne A,#60,SaiRelogio ; Testa se já chegou a 60, se não chegou
; sai da rotina de relógio
mov Minutos,#0 ; Carrega o contador de minutos com '0'

ContaHoras:
inc Horas ; Incrementa contador de horas
mov A,Horas ; Carrega acumulador com o contador
; de horas
cjne A,#24,SaiRelogio ; Testa se já chegou a 24, se não chegou
; sai da rotina de relógio
mov Horas,#0 ; Carrega o contador de horas com '0'

SaiRelogio:
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
EscreveRelogioNoLCD:
mov A,Horas ; Carrega o acumulador com Horas
mov B,#10 ; Carrega o registrador 'B' com 10
div AB ; Divide 'A' por 'B', então 'A' sai com
; a dezena de horas e 'B' com a unidade

orl A,#030h ; Converte a dezena de horas para ASCII


mov Linha1doDisplay,A ; Carrega a dezena de horas no buffer
; do display
orl B,#030h ; Converte a unidade de horas para ASCII
mov Linha1doDisplay+1,B ; Carrega a unidade de horas no buffer
; do display

mov Linha1doDisplay+2,#':' ; Carrega ':' no buffer do display

mov A,Minutos ; Carrega o acumulador com Minutos


mov B,#10 ; Carrega o registrador 'B' com 10
div AB ; Divide 'A' por 'B', então 'A' sai com
; a dezena de minutos e 'B' com a
; unidade

orl A,#030h ; Converte a dezena de minutos para


; ASCII
mov Linha1doDisplay+3,A ; Carrega a dezena de minutos no buffer
; do display
orl B,#030h ; Converte a unidade de minutos para
; ASCII
mov Linha1doDisplay+4,B ; Carrega a unidade de minutos no buffer
; do display

mov Linha1doDisplay+5,#':' ; Carrega ':' no buffer do display

mov A,Segundos ; Carrega o acumulador com Segundos


mov B,#10 ; Carrega o registrador 'B' com 10
div AB ; Divide 'A' por 'B', então 'A' sai com
; a dezena de segundos e 'B' com a
; unidade

orl A,#030h ; Converte a dezena de segundos para


; ASCII
mov Linha1doDisplay+6,A ; Carrega a dezena de segundos no buffer
; do display
orl B,#030h ; Converte a unidade de segundos para
; ASCII
mov Linha1doDisplay+7,B ; Carrega a unidade de segundos no
; buffer do display

ret ; Retorna para onde veio

_______________________________________________
56
;-------------------------------------------------------------------------------
EscreveAlarmeNoLCD:
mov A,AlarmeHoras ; Carrega o acumulador com AlarmeHoras
mov B,#10 ; Carrega o registrador 'B' com 10
div AB ; Divide 'A' por 'B', então 'A' sai com
; a dezena de horas e 'B' com a unidade

orl A,#030h ; Converte a dezena de horas para ASCII


mov Linha1doDisplay,A ; Carrega a dezena de horas no buffer
; do display
orl B,#030h ; Converte a unidade de horas para ASCII
mov Linha1doDisplay+1,B ; Carrega a unidade de horas no buffer
; do display

mov Linha1doDisplay+2,#':' ; Carrega ':' no buffer do display

mov A,AlarmeMinutos ; Carrega o acumulador com AlarmeMinutos


mov B,#10 ; Carrega o registrador 'B' com 10
div AB ; Divide 'A' por 'B', então 'A' sai com
; a dezena de minutos e 'B' com a
; unidade

orl A,#030h ; Converte a dezena de minutos para


; ASCII
mov Linha1doDisplay+3,A ; Carrega a dezena de minutos no buffer
; do display
orl B,#030h ; Converte a unidade de minutos para
; ASCII
mov Linha1doDisplay+4,B ; Carrega a unidade de minutos no buffer
; do display

mov Linha1doDisplay+5,#' ' ; Apaga separador (':') de minutos e


; segundos
mov Linha1doDisplay+6,#' ' ; Apaga dezena de segundos
mov Linha1doDisplay+7,#' ' ; Apaga unidade de segundos

ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
ComparaHora:
mov A,Horas ; Carrega acumulador com a hora atual
cjne A,AlarmeHoras,SaiComparaHora ;
; Compara a hora atual com a hora de
; alarme, se diferente sai da comparação
mov A,Minutos ; Carrega acumulador com o minuto atual
cjne A,AlarmeMinutos,SaiComparaHora ;
; Compara o minuto atual com o minuto de
; alarme, se diferente sai da comparação
mov A,Segundos ; Carrega acumulador com o segundo atual
jnz SaiComparaHora ; Se o segundo atual não for '00' sai
; para não voltar a tocar o alarme, caso
; o mesmo tenha sido desligado dentro do
; mesmo minuto
setb TocaAlarme ; Liga o flag que sinaliza que é para
; tocar o alarme
SaiComparaHora:
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
Buzina:
setb Buzina0 ; Liga saída '0' da buzina
clr Buzina1 ; Desliga saída '1' da buzina
mov R7,#54 ; Para um retardo aproximado de 125 uS
djnz R7,$ ; Quando R7 chegar a '0' é porque passou
; os 125 uS
_______________________________________________
57
setb Buzina1 ; Liga saída '1' da buzina
clr Buzina0 ; Desiga saída '0' da buzina
mov R7,#54 ; Para um retardo aproximado de 125 uS
djnz R7,$ ; Quando R7 chegar a '0' é porque passou
; os 125 uS
jb TocaAlarme,Buzina
clr Buzina0 ; Desliga saída '0' da buzina
clr Buzina1 ; Desliga saída '1' da buzina

jmp Principal ; Volta para a Principal quando o alarme


; for desligado

;-------------------------------------------------------------------------------
; Escreve Mensagem no Display LCD
;-------------------------------------------------------------------------------
EscreveMensagem:
clr A ; Zera o acumulador para que na próxima
; instrução só tenha valor o DPTR
movc A,@A+DPTR ; Busca caractere da mensagem na memória
; de programa
cjne A,#ETX,EscreveMensagem1 ; Testa se chegou ao fim da mensagem
call EscreveDisplay ; Vai transferir para o display o
; que escreveu no buffer
ret ; Retorna para onde veio

EscreveMensagem1:
mov @R0,A ; Escreve no buffer o que
; pegou na mensagem
inc R0 ; Incrementa o ponteiro
; do buffer do Display
inc DPTR ; Incrementa o ponteiro da mensagem
jmp EscreveMensagem ; Volta para escrever o próximo
; caractere no display

; Fim EscreveMensagem
;-------------------------------------------------------------------------------
; Apaga Display
;-------------------------------------------------------------------------------
ApagaDisplay:
mov R0,#Linha1doDisplay ; Carrega ponteiro com inicio
; da linha 1 do display
mov R1,#32 ; Carrega tamanho do buffer do Display
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveDisplay ; Vai transferir para o display o
; que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaDisplay
;-------------------------------------------------------------------------------
; Apaga Linha 1 do Display
;-------------------------------------------------------------------------------
ApagaLinha1doDisplay:
mov R0,#Linha1doDisplay ; Carrega ponteiro com inicio
; da linha 1 do display
mov R1,#16 ; Carrega tamanho do buffer da linha 1
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveLinha1doDisplay ; Vai transferir para a linha 1 do
; display o que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaLinha1doDisplay
;-------------------------------------------------------------------------------
; Apaga linha 2 do Display
;-------------------------------------------------------------------------------
ApagaLinha2doDisplay:
_______________________________________________
mov R0,#Linha2doDisplay ; Carrega ponteiro com inicio

58
; da linha 2 do display
mov R1,#16 ; Carrega tamanho do buffer da linha 2
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveLinha2doDisplay ; Vai transferir para a linha 2 do
; display o que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaLinha2doDisplay
;-------------------------------------------------------------------------------
; Limpa buffer do Display LCD
; Escreve espaços (brancos) no buffer do display
;-------------------------------------------------------------------------------
LimpaBufferDisplay:
mov A,#' ' ; Carrega ACC com espaço
CLS1: mov @R0,A ; Escreve espaço na memória de Display
inc R0 ; Incrementa ponteiro da memória
djnz R1,CLS1 ; Testa se já apagou toda memória
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
; Escreve no display
; Escreve todo o conteúdo do buffer do display na memória do mesmo
;-------------------------------------------------------------------------------
EscreveDisplay:
call EscreveLinha1doDisplay ; Vai escrever a Linha 1 do display
call EscreveLinha2doDisplay ; Vai escrever a Linha 2 do display
ret ; Retorna para onde veio

; Fim EscreveDisplay
;-------------------------------------------------------------------------------
; Escreve linha 1 do display
; Esta rotina escreve o conteudo do buffer
; da linha 1 na memória de dados do display
;-------------------------------------------------------------------------------
EscreveLinha1doDisplay:
; Envia o comando para mudar para o início da linha 1
mov ComandoLCD,#080h ; Comando para mudar para o inicio
; da linha 1 do display
clr RsLCD ; Coloca o display para receber COMANDOS
mov R0,#ComandoLCD ; Carrega R0 com o endereço de onde esta
; o comando a ser enviado para o display
call EnviaByteParaDisplayLCD ; Envia o comando de mudar para
; o inicio da linha 1 do display
setb RsLCD ; Coloca o display para receber DADOS

; Agora vai transferir os dados do buffer da linha 1 para o display


mov R0,#Linha1doDisplay ; Carrega o ponteiro com o endereço
; do buffer da linha 1 do display

LoopLinha1doDisplay:
call EnviaByteParaDisplayLCD ; Vai enviar 1 byte para o display LCD
cjne R0,#Linha1doDisplay+16,LoopLinha1doDisplay ;
; Testa o fim da linha 1
ret ; Retorna para onde veio

; Fim EscreveLinha1doDisplay
;-------------------------------------------------------------------------------
; Escreve linha 2 do display
; Esta rotina escreve o conteúdo do buffer
; da linha 2 na memória de dados do display
;-------------------------------------------------------------------------------
EscreveLinha2doDisplay:
; Envia o comando para mudar para o início da linha 2
_______________________________________________
mov ComandoLCD,#0C0h ; Comando para mudar para o inicio

59
; da linha 2 do display
clr RsLCD ; Coloca o display para receber COMANDOS
mov R0,#ComandoLCD ; Carrega R0 com o endereço de onde esta
; o comando a ser enviado para o display
call EnviaByteParaDisplayLCD ; Envia o comando de mudar para
; o inicio da linha 2 do display
setb RsLCD ; Coloca o display para receber DADOS

; Agora vai transferir os dados do buffer da linha 2 para o display


mov R0,#Linha2doDisplay ; Carrega o ponteiro com o endereço
; do buffer da linha 2 do display

LoopLinha2doDisplay:
call EnviaByteParaDisplayLCD ; Vai enviar 1 byte para o display LCD
cjne R0,#Linha2doDisplay+16,LoopLinha2doDisplay ;
; Testa o fim da linha 2
ret ; Retorna para onde veio
; Fim EscreveLinha2doDisplay
;-------------------------------------------------------------------------------
; Envia byte para o display LCD
; Esta rotina envia um byte de comando ou de dado para o display LCD
;-------------------------------------------------------------------------------
EnviaByteParaDisplayLCD:
mov A,@R0 ; Carrega ACC com a memória de Display

; Coloca nible alto no barramento


anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble alto para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 100 uS
mov R7,#50 ; Carrega para 100 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 100 uS

mov A,@R0 ; Carrega ACC com a memória de Display


swap A ; Move o nibble baixo para que
; seja enviado para o display
; Coloca nible baixo no barramento
anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble baixo para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 100 uS
mov R7,#50 ; Carrega para 100 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 100 uS

inc R0 ; Incrementa ponteiro da


; memória de Display
_______________________________________________
60
ret ; Retorna para onde veio

; Fim EnviaByteParaDisplayLCD
;-------------------------------------------------------------------------------
; Envia nibble alto para o display LCD
; Esta rotina é usada apenas na inicialização do display
;-------------------------------------------------------------------------------
EnviaNibleAltoParaDisplayLCD:
; Coloca nible alto no barramento
anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble alto para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 200 uS
mov R7,#100 ; Carrega para 200 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 200 uS

ret ; Retorna para onde veio

; Fim EnviaNibleAltoParaDisplayLCD
;-------------------------------------------------------------------------------
Retardo100mS:
mov R3,#181 ; Carrega R3 para um retardo de 100 mS,
; com um cristal de 11,0592 MHz
sjmp Retardo4mS1
;-------------------------------------------------------------------------------
Retardo4mS:
mov R3,#8 ; Carrega R3 para um retardo de 4 mS,
; com um cristal de 11,0592 MHz
Retardo4mS1:
mov R4,#39
Retardo4mS2: ; Fica neste loop até que os 2 registros
; cheguem a '0', quando então terá
; transcorrido 4 mS ou 100 mS,
; dependendo do valor de R3
djnz R4,Retardo4mS2
djnz R3,Retardo4mS2
ret ; Retorna para onde veio

; Fim Retardo100mS e Retardo4mS


;-------------------------------------------------------------------------------
CodeSize equ $ ; Neste ponto o montador informa
; o tamanho do código

MemoriaLivre equ 8192-CodeSize ; Neste ponto o montador calcula e


; informa a memória de programa livre
;-------------------------------------------------------------------------------
; Para visualizar o tamanho do código e a memória de programa livre
; o usuário deve abrir o arquivo ContBin.lst que é gerado pelo montador
; no mesmo subdiretório onde esta o ContBin.asm
;-------------------------------------------------------------------------------
; Sinaliza para o montador o fim do programa
end

_______________________________________________
61
02.09. Relógio no Display de Sete Segmentos:

Este programa mostra como implementar um relógio com


indicação de horas e minutos no display de sete segmentos.

São usadas as teclas: “A” para selecionar o modo de acerto, “1”


para incrementar as horas, “2” para incrementar os minutos e “A”
para sair do modo de acerto.

Este exemplo está no CD no arquivo Rel7seg.asm.

;-------------------------------------------------------------------------------
$MOD89S52
$TITLE (Relogio com Display de LED de 7 segmentos)
;-------------------------------------------------------------------------------
;
; Esta rotina demonstra uma das formas de implementar um relógio
; através de software com display de LED de 7 segmentos
;
; O teclado deve ser configurado para 16 teclas, pois temos que controlar
; o mesmo para evitar conflito com a rotina de multiplexar display que
; usa todos os bits do Barramento de dados
;
; Função das teclas:
; Tecla 1 - Incrementa Hora
; Tecla 2 - Incrementa Minuto
; Tecla A - Entra e sai do modo de ajustar relógio
;-------------------------------------------------------------------------------

TempoDoTimer2 EQU 65535-2304 ; Tempo para 2,5 mS

ETX EQU 003h


BarramentoDoDisplay EQU P2 ; Barramento de dados do display

TeclaIncrementaHora EQU P2.0 ; Porta de entrada da tecla Incrementa


; hora
TeclaIncrementaMinuto EQU P2.1 ; Porta de entrada da tecla Incrementa
; minuto
TeclaAcerta EQU P2.3 ; Porta de entrada da tecla Acerta

Clk7Seg EQU P0.4 ; Porta de saída do clock de


; transferência de dados para o display
; de sete segmentos
HabilitaTeclado EQU P1.4 ; Porta de saída do sínal que habilita
; o teclado
;-------------------------------------------------------------------------------
; Segmento de Bits
;
; Segmento da memória RAM, onde se pode trabalhar com um único bit, que são
; usados como sinalizadores (Flags).
; Este segmento começa na posição 020h e vai até 02Fh, totalizando 16 bytes,
; o que permite que o usuário tenha até 128 Flags no seu programa
;-------------------------------------------------------------------------------
BSEG at 0h

Acerta: DBIT 1 ; Sinaliza o modo de acerta relógio


IncrementaMinuto: DBIT 1 ; Sinaliza que é para incrementar minuto
IncrementaHora: DBIT 1 ; Sinaliza que é para incrementar hora
TeclaValida: DBIT 1 ; Sinaliza que tem uma tecla válida
_______________________________________________
62
Passou300mS: DBIT 1 ; Sinaliza que já passou 300 mS
Traco: DBIT 1 ; Sinaliza que é para acender o traço
; que separa horas de minutos

;-------------------------------------------------------------------------------
; Segmento de Bytes
;
; Segmento da memória RAM, reservada para as variáveis usadas no programas
;-------------------------------------------------------------------------------
DSEG at 22h

Horas: DS 1 ; Contador de Horas


Minutos: DS 1 ; Contador de Minutos
Segundos: DS 1 ; Contador de Segundos
Debounce: DS 1 ; Contador de Debounce de tecla
Tempo10mS: DS 1 ; Contador de Tempo de 10 mS
Tempo300mS: DS 1 ; Contador de Tempo de 300 mS
Tempo500mS: DS 1 ; Contador de Tempo de 500 mS
Posicao7Seg: DS 1 ; Registro da posição do digito

BufferDoDisplay: DS 16

;-------------------------------------------------------------------------------
FimDosDados EQU $ ; Serve para indicar o fim da memória
; RAM usada para os dados.
;-------------------------------------------------------------------------------
CSEG ; Informa ao montador que a partir daqui
; começa o código fonte do programa

org 0000h ; Endereço de inicialização do


; microcontrolador, o qual vai para este
; quando é resetado
jmp InicializaPlaca ; Vai inicializar a placa
;-------------------------------------------------------------------------------
org 002Bh ; Vetor da interrupção do Timer 2

TrataTimer2:
clr TF2 ; No caso do timer 2 o flag TF2 tem que
; ser zerado pelo software para que não
; aconteça uma nova interupção logo que
; sair desta interupção

call MultiplexaDisplay ; Vai executar a multiplexação


; do display

djnz Tempo10mS,SaiTimer2 ; Sai da interrupção enquanto não passar


; o tempo de 10 mS
mov Tempo10mS,#4 ; Carrega tempo de 10 mS (4 x 2,5 mS)

; Passa por aqui a cada 10 mS


call TrataTecla ; Vai tratar as teclas

jb Acerta,SaiTimer2 ; Se estiver no modo de acerto, sai da


; interrupção

djnz Tempo500mS,SaiTimer2 ; Sai da interrupção enquanto não passar


; o tempo de 500 mS
mov Tempo500mS,#50 ; Carrega tempo de 500 mS (50 x 10 mS)

cpl Traco ; Para piscar o traço que separa


; horas de minutos

mov BufferDoDisplay+3,#10 ; Para acender o traço


jb Traco,TrataTimer21 ; Se o flag Traco estiver ligado,
; vai para TrataTimer21
_______________________________________________
63
mov BufferDoDisplay+3,#11 ; Para apagar o traço, se o flag Traco
; estiver com '0'

TrataTimer21:
jb Traco,SaiTimer2 ; Se o flag Traco estiver ligado, sai
; da interrupção e não incrementa
; o relógio

call Relogio ; Vai incrementar o relógio

call EscreveRelogio ; Vai escrever horas e minutos no


; buffer do display
SaiTimer2:
reti ; Saída da interrupção do timer 2

;-------------------------------------------------------------------------------
InicializaPlaca:
;-------------------------------------------------------------------------------
; Apaga memoria RAM interna
;-------------------------------------------------------------------------------
mov R0,#0FFh ; Carrega ponteiro com o tamanho
; da memória
ApagaMemoria:
mov @R0,#0 ; Escreve "0" na posição de memória
; apontada por R0
djnz R0,ApagaMemoria ; Decrementa o ponteiro e volta para
; apagar a posição anterior até
; chegar a posição "0"
; Fim apaga memoria
;-------------------------------------------------------------------------------

setb TR2 ; Habilita timer 2


setb PT2 ; Timer 2 com prioridade alta
setb ET2 ; Habilita interrupção do timer 2
setb EA ; Habilita o controlador de interrupt
clr CAP2 ; Coloca o timer 2 para recarregar
; automaticamente

mov RCAP2H,#(high TempoDoTimer2) ;


; Para a recarga da parte alta do
; timer 2
mov RCAP2L,#(low TempoDoTimer2) ;
; Para a recarga da parte baixa do
; timer 2

mov Tempo500mS,#50 ; Carrega o contador de 500 mS com


; (50 x 10 mS = 500 mS)

mov Segundos,#0 ; Carrega o contador de segundos com '0'


; para que o relógio comece com
; '00' segundos
mov Minutos,#0 ; Carrega o contador de minutos com '0'
; para que o relógio comece com
; '00' minutos
mov Horas,#0 ; Carrega o contador de horas com '0'
; para que o relógio comece com
; '00' horas
mov BufferDoDisplay,#11 ; Para manter o primeiro digito do
; display apagado
mov BufferDoDisplay+3,#10 ; Para acender o segmento "G" (traço)
; que separa horas e minutos
mov BufferDoDisplay+6,#11 ; Para manter o último digito do
; display apagado

mov Debounce,#10 ; Carrega o contador de debounce


_______________________________________________
64
; com 100 mS
clr TeclaValida ; Sinaliza que não tem nenhuma
; tecla apertada
clr Acerta ; Zera flag Acerta
clr TeclaValida ; Zera flag TeclaValida
clr IncrementaHora ; Zera flag IncrementaHora
clr IncrementaMinuto ; Zera flag IncrementaMinuto

clr Passou300mS ; Zera flag Passou300mS


mov Tempo300mS,#30 ; Carrega o contador de
; 300 mS (30 x 10 mS = 300 mS)

Principal:
mov PCON,#1 ; A CPU se desativa, e só volta a se
; ativar quando ocorrer uma interrupção

Principal1:
jnb Acerta,Principal ; Se não estiver no modo de acerto
; volta a aguardar na Principal

;-------------------------------------------------------------------------------
AcertaRelogio:
jb IncrementaHora,AcertaHora ;
; Se o flag IncrementaHora estiver
; ligado vai incrementar o contador de
; horas
jnb IncrementaMinuto,Principal ;
; Se chegar aqui e o flag
; IncrementaMinuto estiver ligado, vai
; incrementar o contador de minutos,
; caso contrário volta a esperar na
; Principal
AcertaMinuto:
inc Minutos ; Incrementa o contador de minutos
mov A,Minutos ; Carrega o acumulador com o contador
; de minutos
cjne A,#60,AtualizaDisplay ; Teste se o contador de minutos chegou
; a '60', se não vai atualizar o display
mov Minutos,#0 ; Zera o contador de minutos
jmp AtualizaDisplay ; Vai atualizar o display

AcertaHora:
inc Horas ; Incrementa o contador de horas
mov A,Horas ; Carrega o acumulador com o contador
; de horas
cjne A,#24,AtualizaDisplay ; Teste se o contador de horas chegou
; a '24', se não chegou
mov Horas,#0 ; Zera o contador de horas e atualiza
; o display

AtualizaDisplay:
call EscreveRelogio ; Vai escrever Horas e Minutos no buffer
; do display
call Retardo100mS ; Aguarda 100 mS para fazer novo
; incremento de hora ou minuto se tiver
; com a tecla Incrementa hora ou minuto
; apertada por mais de 300 mS

AtualizaDisplay1:
jnb TeclaValida,Principal ; Volta para Principal enquanto não
; tiver uma tecla válida
jnb Passou300mS,AtualizaDisplay1 ;
; Testa se já passou 300 mS com uma
; tecla válida
_______________________________________________
65
jb Acerta,AcertaRelogio ; Se ainda estiver no modo de acertar
; relógio, volta para a rotina
; AcertaRelogio
sjmp Principal ; Volta para a Principal, se a rotina de
; tratar teclas informar que já saiu do
; modo acerta

;-------------------------------------------------------------------------------
TrataTecla:
setb HabilitaTeclado ; Habilita leitura do teclado
mov P2,#11101111b ; Habilita linha 1 do teclado (1,2,3,A)

jb TeclaValida,AguardaFimDeTecla ;
; Vai aguardar finalização de qualquer
; tecla

jb TeclaAcerta,TestaTeclaIncrementaHora ;
; Vai testar tecla Incrementa hora,
; se a tecla Acerta não estiver apertada
djnz Debounce,SaiTrataTecla ; Sai da rotina enquanto não vencer
; o tempo de Debounce
setb TeclaValida ; Sinaliza que tem tecla apertada
cpl Acerta ; Entra ou sai do modo acerta relógio
jnb Acerta,SaiTrataTecla ; Se o flag acerta estiver em '0' é
; porque saiu do modo acerta relógio

; Se o flag Acerta estiver ligado neste ponto,


; o programa entra no modo acerta relógio
mov Segundos,#0 ; Zera o contador de segundos
sjmp SaiTrataTecla ; Sai da rotina de tratar teclas

TestaTeclaIncrementaHora:
jnb Acerta,SaiTrataTecla ; Sai da rotina de tratar teclas se não
; estiver no modo de acerta relógio
jb TeclaIncrementaHora,TestaTeclaIncrementaMinuto ;
; Vai testar a tecla Incrementa minuto,
; se a tecla Incrementa hora não
; estiver apertada
djnz Debounce,SaiTrataTecla ; Sai da rotina enquanto não vencer
; o tempo de Debounce
setb TeclaValida ; Sinaliza que tem tecla
; apertada
setb IncrementaHora ; Sinaliza para rotina de acerta relógio
; que é para incrementar o contador
; de horas
sjmp SaiTrataTecla ; Sai da rotina de tratar teclas

TestaTeclaIncrementaMinuto:
jb TeclaIncrementaMinuto,SaiTrataTecla ;
; Sai da rotina de tratar teclas se não
; tiver nenhuma tecla apertada
djnz Debounce,SaiTrataTecla ; Sai da rotina enquanto não vencer
; o tempo de Debounce
setb TeclaValida ; Sinaliza que tem tecla
; apertada
setb IncrementaMinuto ; Sinaliza para rotina de acerta relógio
; que é para incrementar o contador
; de minutos
sjmp SaiTrataTecla ; Sai da rotina de tratar teclas

AguardaFimDeTecla:
mov A,P2 ; Lê a porta 2 para testar se ainda tem
; tecla apertada
orl A,#11110010b ; Coloca "1" nos bits não usados
cpl A ; Inverte o conteúdo do acumulador
_______________________________________________
66
jnz AguardaFimDeTecla1 ; Sai se o acumulador não estiver com
; "0", é sinal de que ainda tem tecla
; apertada
mov Debounce,#10 ; Recarrega o contador de debounce
; com 100 mS
clr TeclaValida ; Sinaliza que não tem nenhuma tecla
; apertada
clr IncrementaHora ; Desliga flag que sinaliza que é para
; incrementar o contador de horas
clr IncrementaMinuto ; Desliga flag que sinaliza que é para
; incrementar o contador de minutos
clr Passou300mS ; Desliga flag que sinaliza que já
; passou mais de 300 mS com uma mesma
; tecla apertada
mov Tempo300mS,#30 ; Recarrega o contador de tempo
; de 300 mS

AguardaFimDeTecla1:
djnz Tempo300mS,SaiTrataTecla ;
; Decrementa o contador de tempo de
; 300 mS, e sai se ainda não tiver
; passado os 300 mS
setb Passou300mS ; Sinaliza que já passou os 300 mS

SaiTrataTecla:
clr HabilitaTeclado ; Desabilita leitura do teclado

ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
Relogio:
inc Segundos ; Incrementa contador de segundos
mov A,Segundos ; Carrega acumulador com o contador
; de segundos
cjne A,#60,SaiRelogio ; Testa se já chegou a 60, se não chegou
; sai da rotina de relógio
mov Segundos,#0 ; Carrega o contador de segundos com '0'

ContaMinutos:
inc Minutos ; Incrementa contador de minutos
mov A,Minutos ; Carrega acumulador com o contador
; de minutos
cjne A,#60,SaiRelogio ; Testa se já chegou a 60, se não chegou
; sai da rotina de relógio
mov Minutos,#0 ; Carrega o contador de minutos com '0'

ContaHoras:
inc Horas ; Incrementa contador de horas
mov A,Horas ; Carrega acumulador com o contador
; de horas
cjne A,#24,SaiRelogio ; Testa se já chegou a 24, se não chegou
; sai da rotina de relógio
mov Horas,#0 ; Carrega o contador de horas com '0'

SaiRelogio:
ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
EscreveRelogio:
mov A,Horas ; Carrega acumulador com byte Horas
mov B,#10 ; Carrega registrador 'B' com 10 para
; executar a próxima instrução
div AB ; Divide o acumulador p/ 10 para separar
; dezena de horas de unidade de horas

_______________________________________________
67
mov BufferDoDisplay+1,A ; Transfere dezena de horas para o
; buffer do display
mov BufferDoDisplay+2,B ; Transfere unidade de horas para
; o buffer do display

mov A,Minutos ; Carrega acumulador com byte Minutos


mov B,#10 ; Carrega registrador 'B' com 10 para
; executar a próxima instrução
div AB ; Divide o acumulador p/ 10 para separar
; dezena de min. de unidade de minutos

mov BufferDoDisplay+4,A ; Transfere dezena de minutos para


; o buffer do display
mov BufferDoDisplay+5,B ; Transfere unidade de minutos para
; o buffer do display

ret ; Retorna para onde veio

;-------------------------------------------------------------------------------
MultiplexaDisplay:
mov BarramentoDoDisplay,#0 ; Coloca dado no barramento para apagar
; o display que estiver aceso, para
; evitar fantasma
clr Clk7Seg ; Baixa o sinal de clock, para permitir
; a transferência do dado na subida
setb Clk7Seg ; Transfere dado do barramento para
; latch (74HC374) do display

inc Posicao7Seg ; Incrementa o contador da posição do


; digito de sete segmentos
mov A,Posicao7Seg ; Carrega o acumulador com a posição do
; digito de sete segmentos
cjne A,#7,Mux ; Testa se a posição já chegou a 7, se
; não chegou vai aceder o digito
; correspondente
mov Posicao7Seg,#0 ; Zera o contador da posição do
; digito de sete segmentos
mov A,Posicao7Seg ; Carrega o acumulador com a posição do
; digito de sete segmentos
Mux:
anl P0,#11111000b ; Prepara os 3 primeiros bits da porta
; P0 para receber o endereço da posição
; do digito de sete segmentos
orl P0,A ; Coloca o endereço na porta sem
; perturbar a parte alta da mesma
mov R0,#BufferDoDisplay ; Carrega ponteiro com o endereço do
; inicio do buffer do display
mov A,Posicao7Seg ; Carrega o acumulador com a posição do
; digito de sete segmentos
add A,R0 ; Soma o ponteiro do inicio do buffer
; do display com a posição do digito de
; sete segmentos
mov R0,A ; Carrega o ponteiro com o resultado da
; soma para buscar o número
; correspondente

mov A,@R0 ; Carrega 'A' com o número


mov DPTR,#Converte7Seg ; Carrega DPTR com tabela de conversão
movc A,@A+DPTR ; Converte o número para sete segmentos

mov BarramentoDoDisplay,A ; Coloca dado no barramento para acender


; o display que estiver selecionado
clr Clk7Seg ; Baixa o sinal de clock, para permitir
; a transferência do dado na subida
setb Clk7Seg ; Levanta o sinal de clock para
_______________________________________________
68
; transferir o dado do barramento para
; o latch (74HC374) do display

mov BarramentoDoDisplay,#0FFh ;
; Libera o barramento para que outras
; rotinas possam usar o mesmo

ret ; Retorna para onde veio


;-------------------------------------------------------------------------------
Converte7Seg:
db 00111111b ; 0
db 00000110b ; 1
db 01011011b ; 2
db 01001111b ; 3
db 01100110b ; 4
db 01101101b ; 5
db 01111101b ; 6
db 00000111b ; 7
db 01111111b ; 8
db 01101111b ; 9
db 01000000b ; "-"
db 00000000b ; " "

;-------------------------------------------------------------------------------
Retardo100mS:
mov R3,#181 ; Carrega R3 para um retardo de 100 mS,
; com um cristal de 11,0592 MHz
mov R4,#39
Retardo4mS1: ; Fica neste loop até que os 2 registros
; cheguem a '0', quando então terá
; transcorrido 4 mS ou 100 mS,
; dependendo do valor de R3
djnz R4,Retardo4mS1
djnz R3,Retardo4mS1
ret ; Retorna para onde veio

; Fim Retardo100mS
;-------------------------------------------------------------------------------
CodeSize equ $ ; Neste ponto o montador informa
; o tamanho do código

MemoriaLivre equ 8192-CodeSize ; Neste ponto o montador calcula e


; informa a memória de programa livre
;-------------------------------------------------------------------------------
; Para visualizar o tamanho do código e a memória de programa livre
; o usuário deve abrir o arquivo ContBin.lst que é gerado pelo montador
; no mesmo subdiretório onde esta o ContBin.asm
;-------------------------------------------------------------------------------
; Sinaliza para o montador o fim do programa
end

_______________________________________________
69
02.10. Exemplo de Uso do Conversor AD:

Este programa exemplifica como usar o conversor AD. Para efeito


de teste deve ser conectado na placa padrão um potenciômetro de
10 KOhms com os extremos no Terra e no +5V e o cursor ligado na
entrada AD5.

O conversor AD é configurado para o modo de entrada individual.


O valor digital da conversão realizada pelo AD é apresentado em
hexadecimal no display LCD.

Este exemplo está no CD no arquivo ConvAD.asm.

;-------------------------------------------------------------------------------
$MOD89S52
$TITLE (Rotina para conversor AD MCP3004/8 ou MCP3204/8 da Microchip)
;-------------------------------------------------------------------------------
;
; Esta rotina serve para demonstrar o funcionamento dos conversores
; AD tipo MCP3004/8 ou MCP3204/8 da Microchip, sendo que o resultado da
; conversão é apresentado no formato hexadecimal, correspondente ao número
; de degraus do sinal convertido, este valor depende da tensão de
; referência e do tipo do conversor AD (10 ou 12 bits)
;
; Neste programa tem 8 entradas para a leitura do conversor AD denominadas
; LeConversorAD0 a LeConversorAD7, neste exemplo usamos LeConversorAD5, e
; ligamos o cursor de um potenciometro na entrada 5 do AD e os estremos do
; potenciometro é ligado ao GND e ao VCC.
;-------------------------------------------------------------------------------

ETX EQU 003h


BarramentoDoDisplay EQU P2 ; Barramento de dados do display

CSAD EQU P0.6 ; Chip select do conversor AD


Dout EQU P0.7 ; Saída de dados serial do conversor AD
CLK EQU P1.7 ; Clock para a comunicação serial com o
; conversor AD
Din EQU P1.5 ; Entrada de dados serial do conversor
; AD

RsLCD EQU P0.2 ; Porta de saída do sinal que informa


; ao display LCD se os dados do
; barramento correspondem a dados ou
; a comandos
EnLCD EQU P0.4 ; Porta de saída do pulso de
; transferência de dados para o display

;-------------------------------------------------------------------------------
; Segmento de Bits
;
; Segmento da memória RAM, onde se pode trabalhar com um único bit, que são
; usados como sinalizadores (Flags).
; Este segmento começa na posição 020h e vai até 02Fh, totalizando 16 bytes,
; o que permite que o usuário tenha até 128 Flags no seu programa
;-------------------------------------------------------------------------------
BSEG at 0

_______________________________________________
70
Passou1Segundo: dbit 1 ; Para sinalizar que passou 1 segundo
PassouSegundos: dbit 1 ; Para sinalizar que passou segundos

;-------------------------------------------------------------------------------
; Segmento de Bytes
;
; Segmento da memória RAM, reservada para as variáveis usadas no programas
;-------------------------------------------------------------------------------
DSEG at 23h

Tempo1Segundo: ds 1 ; Registro para contar o tempo


; de 1 segundo
Segundos: ds 1 ; Registro para contar segundos
NumeroDeBits: ds 1 ; Registro usado na comunicação serial
; com o conversor AD
ByteADH: ds 1 ; Registro destinado ao byte alto do
; conversor AD
ByteADL: ds 1 ; Registro destinado ao byte baixo do
; conversor AD
ComandoLCD: DS 1 ; Registro destinado ao envio de
; comandos para o display LCD
Linha1doDisplay: ds 16 ; Buffer da linha 1 do display LCD
Linha2doDisplay: ds 16 ; Buffer da linha 2 do display LCD
;-------------------------------------------------------------------------------
FimDosDados EQU $ ; Serve para indicar o fim da memória
; RAM usada para os dados.
;-------------------------------------------------------------------------------
CSEG ; Informa ao montador que a partir daqui
; começa o código fonte do programa

org 0000h ; Endereço de inicialização do


; microcontrolador, o qual vai para este
; quando é resetado
jmp InicializaPlaca ; Vai inicializar a placa
;-------------------------------------------------------------------------------
ORG 000Bh ; Vetor do interrupt do Timer 0

TrataTimer0:
mov TH0,#0DBh ; Recarrega timer 0 com
mov TL0,#0FFh ; 10 mS a 11,0592 MHz

TrataSegundo:
djnz Tempo1Segundo,SaiInt ; Sai enquanto Tempo1Segundo não chegar
; a '0'
mov Tempo1Segundo,#100 ; Recarrega contador para contar mais
; 1 segundo
setb Passou1Segundo ; Sinaliza que já passou 1 segundo
djnz Segundos,SaiInt ; Sai enquanto Segundos não chegar a '0'
setb PassouSegundos ; Sinaliza que já passou segundos

SaiInt:
reti ; Saída da interrupção do timer 0

; Fim TrataTimer0
;-------------------------------------------------------------------------------
InicializaPlaca:
;-------------------------------------------------------------------------------
; Apaga memoria RAM interna
;-------------------------------------------------------------------------------
mov R0,#0FFh ; Carrega ponteiro com o tamanho
; da memória
ApagaMemoria:
mov @R0,#0 ; Escreve "0" na posição de memória
; apontada por R0
djnz R0,ApagaMemoria ; Decrementa o ponteiro e volta para
_______________________________________________
71
; apagar a posição anterior até
; chegar a posição "0"
; Fim apaga memoria
;-------------------------------------------------------------------------------
; InicializaLCD
;-------------------------------------------------------------------------------
InicializaLCD:
call Retardo4mS ; Espera 16 mS
call Retardo4mS
call Retardo4mS
call Retardo4mS

clr EnLCD
clr RsLCD ; Coloca o display para receber Comandos
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
call Retardo4mS
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
mov A,#030h
call EnviaNibleAltoParaDisplayLCD
mov A,#020h
call EnviaNibleAltoParaDisplayLCD
mov A,#020h
call EnviaNibleAltoParaDisplayLCD
mov A,#080h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#060h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#0C0h
call EnviaNibleAltoParaDisplayLCD
mov A,#000h
call EnviaNibleAltoParaDisplayLCD
mov A,#010h
call EnviaNibleAltoParaDisplayLCD
setb RsLCD ; Coloca o display para receber Dados
call Retardo4mS

call ApagaDisplay

; Fim InicializaLCD
;-------------------------------------------------------------------------------
; Inicializa Timer
;-------------------------------------------------------------------------------
InicializaTimer:
mov TH0,#0DBh ; Carrega timer 0 com
mov TL0,#0FFh ; 10 mS a 11,0592 MHz
mov TMOD,#011h ; Programa timer 0 e 1 para o modo 1
; 16 bits
setb ET0 ; Habilita interrupção do timer 0
setb EA ; Habilita interrupts
setb TR0 ; Habilita timer 0

; Fim InicializaTimer
;-------------------------------------------------------------------------------
MenssagemInicial:
mov DPTR,#MsgMMDB ; Carrega DPTR com endereço da mensagem
mov R0,#Linha1doDisplay ; Carrega R0 com endereço de início do
; buffer do display
call EscreveMensagem ; Vai escrever a mensagem

_______________________________________________
72
mov Tempo1Segundo,#100 ; Carrega o contador de tempo de 1 S
mov Segundos,#2 ; Carrega com 2 para esperar 2 segundos
clr PassouSegundos ; Desliga flag que sinaliza que já
; passou a quantidade de segundos
; carregada anteriormente
jnb PassouSegundos,$ ; Espera passar os 2 segundos exibindo
; a mensagem inicial
; Fim MenssagemInicial
;-------------------------------------------------------------------------------
call ApagaLinha2doDisplay ; Vai apagar a linha 2 para mostrar
; a leitura do conversor AD nesta linha
TrataConversor:
mov PCON,#1 ; A CPU se desativa, e só volta a se
; ativar quando ocorrer uma interrupção
call LeConversorAD5 ; Lê a entrada 5 do conversor AD

mov R0,#ByteADH ; Endereço do primeiro byte a ser


; mostrado
mov R1,#Linha2doDisplay ; Posição do display onde vai exibir a
; leitura do conversor AD
mov R7,#2 ; Número de bytes a exibir
call MostraHex ; Vai converte a leitura do conversor AD
; e colocar no buffer da linha 2 do
; display
call EscreveLinha2doDisplay ; Vai escrever a linha 2 do display

sjmp TrataConversor ; Volta para fazer nova leitura do AD

; Fim TrataConversor
;-------------------------------------------------------------------------------
; Le os canais 0 a 7 do conversor AD
;-------------------------------------------------------------------------------
LeConversorAD0:
mov A,#11000111b ; Carrega o acumulador com Comando do AD
sjmp LeAD ; Vai executar a leitura da entrada do
; conversor AD correspondente ao
; endereço contido no byte de comando

LeConversorAD1:
mov A,#11001111b ; Carrega o acumulador com Comando do AD
sjmp LeAD ; Vai executar a leitura da entrada do
; conversor AD correspondente ao
; endereço contido no byte de comando

LeConversorAD2:
mov A,#11010111b ; Carrega o acumulador com Comando do AD
sjmp LeAD ; Vai executar a leitura da entrada do
; conversor AD correspondente ao
; endereço contido no byte de comando

LeConversorAD3:
mov A,#11011111b ; Carrega o acumulador com Comando do AD
sjmp LeAD ; Vai executar a leitura da entrada do
; conversor AD correspondente ao
; endereço contido no byte de comando

LeConversorAD4:
mov A,#11100111b ; Carrega o acumulador com Comando do AD
sjmp LeAD ; Vai executar a leitura da entrada do
; conversor AD correspondente ao
; endereço contido no byte de comando

LeConversorAD5:
mov A,#11101111b
; Carrega o acumulador com Comando do AD
sjmp LeAD ; Vai executar a leitura da entrada do
_______________________________________________
; conversor AD correspondente ao

73
; endereço contido no byte de comando

LeConversorAD6:
mov A,#11110111b ; Carrega o acumulador com Comando do AD
sjmp LeAD ; Vai executar a leitura da entrada do
; conversor AD correspondente ao
; endereço contido no byte de comando

LeConversorAD7:
mov A,#11111111b ; Carrega o acumulador com Comando do AD
; sjmp LeAD ; Vai executar a leitura da entrada do
; conversor AD correspondente ao
; endereço contido no byte de comando

LeAD:
mov NumeroDeBits,#6 ; 1 (bit 7) Start
; 1 (bit 6) Modo
; 3 (bits 5,4 e 3) Endereço
; 1 (bit 2) Vago para o tempo de
; amostragem do sinal de entrada do AD

setb Dout ; Coloca a linha saida do AD em '1'


clr CSAD ; Habilita a comunicação com o AD

EnviaComandoParaAD:
clr CLK ; Inicializa o pulso de deslocamento
; de dados
rlc A ; Transfere para o carry o bit 7
; do acumulador
mov Din,C ; Coloca na entrada do AD o bit
; contido no carry
setb CLK ; Transfere para o AD o sinal que
; esta em Din do AD e finaliza
; o pulso de deslocamento
djnz NumeroDeBits,EnviaComandoParaAD ;
; Volta para EnviaComandoParaAD,
; até enviar todos os bits previstos
setb Din ; Coloca e entrada de em '1'

mov NumeroDeBits,#3 ; 3 para MCP3008 e 5 para MCP3208

RecebeADH:
clr CLK ; Disponibiliza 1 bit de dado na saída
; do AD e inicializa o pulso
; de deslocamento
mov C,Dout ; Coloca no carry o sinal de saída do AD
rlc A ; Transfere para o bit 0 do acumulador
; o carry
setb CLK ; Desloca dados no interior do AD
; e finaliza o pulso de deslocamento
djnz NumeroDeBits,RecebeADH ; Volta para RecebeADH, até receber
; todos os bits previstos
mov ByteADH,A ; Transfere para ByteADH o que
; recebeu no acumulador
anl ByteADH,#00001111b ; Zera bits não usados do byte ADH
; 00000011b para MCP3008
; 00001111b para MCP3208

mov NumeroDeBits,#8 ; Carrega para receber 8 bits no


; próximo loop
RecebeADL:
clr CLK ; Disponibiliza 1 bit de dado na saída
; do AD e inicializa o pulso
; de deslocamento
mov C,Dout ; Coloca no carry o sinal de saída do AD

_______________________________________________
74
rlc A ; Transfere para o bit 0 do acumulador
; o carry
setb CLK ; Desloca dados no interior do AD
; e finaliza o pulso de deslocamento
djnz NumeroDeBits,RecebeADL ; Volta para RecebeADL, até receber
; todos os bits previstos
mov ByteADL,A ; Transfere para ByteADL o que
; recebeu no acumulador

setb CSAD ; Desabilita a comunicação com o AD

ret ; Retorna para onde veio

; Fim LeConversorAD
;-------------------------------------------------------------------------------
; Rotina de Mostrar em Hexadecimal no Disply LCD
; R0 vem com o endereço do que vai ser mostrado (byte mais significativo)
; R1 com a posição que deve ser mostrado
; R7 com o número de bytes
;-------------------------------------------------------------------------------
MostraHex:

LoopMostraHex:
mov A,@R0 ; Carrega acumulador com
; o byte a converter
; Coverte a parte alta
swap A ; Troca nibles para tratar a parte alta
anl A,#00Fh ; Limpa parte alta do acumulador
; e fica somente com o nible alto
; na parte baixa do byte
cjne A,#00Ah,NibleAlto ; Compara com 00Ah para testar se esta
; abaixo ou acima de 9
NibleAlto:
jnc NibleAltoDeAaF ; Se o carry for '0' significa
; que o nibble é maior que '9'
NibleAltoDe0a9:
add A,#030h ; Converte nibble alto para ASCII(0 a 9)
sjmp NibleAltoDe0aF ; Vai salvar o resultado da conversão

NibleAltoDeAaF:
add A,#037h ; Converte nibble alto para ASCII(A a F)
NibleAltoDe0aF:
mov @R1,A ; Salva o resultado da conversão
; no buffer de saída
inc R1 ; Incrementa ponteiro do buffer de saída

mov A,@R0 ; Carrega acumulador com


; o byte a converter
; Coverte a parte baixa
anl A,#00Fh ; Limpa parte alta do acumulador
; e fica somente com o nibble bixo
; na parte baixa do byte
cjne A,#00Ah,NibleBaixo ; Compara com 00Ah para testar se esta
; abaixo ou acima de 9
NibleBaixo:
jnc NibleBaixoDeAaF ; Se o carry for 0 significa que o
; nibble esta com valor de A a F
NibleBaixoDe0a9:
add A,#030h ; Converte nibble baixo p/ ASCII(0 a 9)
sjmp NibleBaixoDe0aF ; Vai salvar o resultado da conversão

NibleBaixoDeAaF:
add A,#037h ; Converte nibble baixo para ASCII (A a
F)

_______________________________________________
75
NibleBaixoDe0aF:
mov @R1,A ; Salva o resultado da conversão
; no buffer de saída
inc R1 ; Incrementa ponteiro do buffer de saída
inc R0 ; Incrementa ponteiro do buffer
; de entrada
djnz R7,LoopMostraHex ; Volta para LoopMostraHex até completar
; o número de bytes a converter

ret ; Retorna para onde veio

; Fim MostraHex
;-------------------------------------------------------------------------------
; Escreve Mensagem no Display LCD
;-------------------------------------------------------------------------------
EscreveMensagem:
clr A ; Zera o acumulador para que na próxima
; instrução só tenha valor o DPTR
movc A,@A+DPTR ; Busca caractere da mensagem na memória
; de programa
cjne A,#ETX,EscreveMensagem1 ; Testa se chegou ao fim da mensagem
call EscreveDisplay ; Vai transferir para o display o
; que escreveu no buffer
ret ; Retorna para onde veio

EscreveMensagem1:
mov @R0,A ; Escreve no buffer o que
; pegou na mensagem
inc R0 ; Incrementa o ponteiro
; do buffer do Display
inc DPTR ; Incrementa o ponteiro da mensagem
jmp EscreveMensagem ; Volta para escrever o próximo
; caractere no display

; Fim EscreveMensagem
;-------------------------------------------------------------------------------
; Apaga Display
;-------------------------------------------------------------------------------
ApagaDisplay:
mov R0,#Linha1doDisplay ; Carrega ponteiro com inicio
; da linha 1 do display
mov R1,#32 ; Carrega tamanho do buffer do Display
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveDisplay ; Vai transferir para o display o
; que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaDisplay
;-------------------------------------------------------------------------------
; Apaga Linha 1 do Display
;-------------------------------------------------------------------------------
ApagaLinha1doDisplay:
mov R0,#Linha1doDisplay ; Carrega ponteiro com inicio
; da linha 1 do display
mov R1,#16 ; Carrega tamanho do buffer da linha 1
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveLinha1doDisplay ; Vai transferir para a linha 1 do
; display o que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaLinha1doDisplay
;-------------------------------------------------------------------------------
; Apaga linha 2 do Display
;-------------------------------------------------------------------------------
ApagaLinha2doDisplay:
_______________________________________________
76
mov R0,#Linha2doDisplay ; Carrega ponteiro com inicio
; da linha 2 do display
mov R1,#16 ; Carrega tamanho do buffer da linha 2
call LimpaBufferDisplay ; Vai carregar o buffer com espaços
call EscreveLinha2doDisplay ; Vai transferir para a linha 2 do
; display o que escreveu no buffer
ret ; Retorna para onde veio

; Fim ApagaLinha2doDisplay
;-------------------------------------------------------------------------------
; Limpa buffer do Display LCD
; Escreve espaços (brancos) no buffer do display
;-------------------------------------------------------------------------------
LimpaBufferDisplay:
mov A,#' ' ; Carrega ACC com espaço
CLS1: mov @R0,A ; Escreve espaço na memória de Display
inc R0 ; Incrementa ponteiro da memória
djnz R1,CLS1 ; Testa se ja apagou toda memória
ret ; Retorna para onde veio

; Fim LimpaBufferDisplay
;-------------------------------------------------------------------------------
; Escreve no display
; Escreve todo o conteúdo do buffer do display na memória do mesmo
;-------------------------------------------------------------------------------
EscreveDisplay:
call EscreveLinha1doDisplay ; Vai escrever a Linha 1 do display
call EscreveLinha2doDisplay ; Vai escrever a Linha 2 do display
ret ; Retorna para onde veio

; Fim EscreveDisplay
;-------------------------------------------------------------------------------
; Escreve linha 1 do display
; Esta rotina escreve o conteudo do buffer
; da linha 1 na memória de dados do display
;-------------------------------------------------------------------------------
EscreveLinha1doDisplay:
; Envia o comando para mudar para o início da linha 1
mov ComandoLCD,#080h ; Comando para mudar para o inicio
; da linha 1 do display
clr RsLCD ; Coloca o display para receber COMANDOS
mov R0,#ComandoLCD ; Carrega R0 com o endereço de onde esta
; o comando a ser enviado para o display
call EnviaByteParaDisplayLCD ; Envia o comando de mudar para
; o inicio da linha 1 do display
setb RsLCD ; Coloca o display para receber DADOS

; Agora vai transferir os dados do buffer da linha 1 para o display


mov R0,#Linha1doDisplay ; Carrega o ponteiro com o endereço
; do buffer da linha 1 do display

LoopLinha1doDisplay:
call EnviaByteParaDisplayLCD ; Vai enviar 1 byte para o display LCD
cjne R0,#Linha1doDisplay+16,LoopLinha1doDisplay ;
; Testa o fim da linha 1
ret ; Retorna para onde veio

; Fim EscreveLinha1doDisplay
;-------------------------------------------------------------------------------
; Escreve linha 2 do display
; Esta rotina escreve o conteudo do buffer
; da linha 2 na memória de dados do display
;-------------------------------------------------------------------------------
_______________________________________________
77
EscreveLinha2doDisplay:
; Envia o comando para mudar para o início da linha 2
mov ComandoLCD,#0C0h ; Comando para mudar para o inicio
; da linha 2 do display
clr RsLCD ; Coloca o display para receber COMANDOS
mov R0,#ComandoLCD ; Carrega R0 com o endereço de onde esta
; o comando a ser enviado para o display
call EnviaByteParaDisplayLCD ; Envia o comando de mudar para
; o inicio da linha 2 do display
setb RsLCD ; Coloca o display para receber DADOS

; Agora vai transferir os dados do buffer da linha 2 para o display


mov R0,#Linha2doDisplay ; Carrega o ponteiro com o endereço
; do buffer da linha 2 do display

LoopLinha2doDisplay:
call EnviaByteParaDisplayLCD ; Vai enviar 1 byte para o display LCD
cjne R0,#Linha2doDisplay+16,LoopLinha2doDisplay ;
; Testa o fim da linha 2
ret ; Retorna para onde veio

; Fim EscreveLinha2doDisplay
;-------------------------------------------------------------------------------
; Envia byte para o display LCD
; Esta rotina envia um byte de comando ou de dado para o display LCD
;-------------------------------------------------------------------------------
EnviaByteParaDisplayLCD:
mov A,@R0 ; Carrega ACC com a memória de Display

; Coloca nible alto no barramento


anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble alto para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 100 uS
mov R7,#50 ; Carrega para 100 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 100 uS

mov A,@R0 ; Carrega ACC com a memória de Display


swap A ; Move o nibble baixo para que
; seja enviado para o display

; Coloca nible baixo no barramento


anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble baixo para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 100 uS
_______________________________________________
78
mov R7,#50 ; Carrega para 100 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 100 uS

inc R0 ; Incrementa ponteiro da


; memória de Display
ret ; Retorna para onde veio

; Fim EnviaByteParaDisplayLCD
;-------------------------------------------------------------------------------
; Envia nible alto para o display LCD
; Esta rotina é usada apenas na inicialização do display
;-------------------------------------------------------------------------------
EnviaNibleAltoParaDisplayLCD:
; Coloca nible alto no barramento
anl A,#0F0h ; Mascara para não perturbar a parte
; baixa da Porta 2
anl BarramentoDoDisplay,#00Fh ;
; Zera o nibble alto para que a próxima
; instrução funcione corretamente
orl BarramentoDoDisplay,A ; Envia nibble alto para o Display

; Pulso de escrita
setb EnLCD ; Início do pulso de escrita
nop ; Prolonga o pulso de escrita
clr EnLCD ; Fim do pulso de escrita

; Retardo de 200 uS
mov R7,#100 ; Carrega para 200 uS com clock de
; 11,059200 MHz
djnz R7,$ ; Aguarda 200 uS

ret ; Retorna para onde veio

; Fim EnviaNibleAltoParaDisplayLCD
;-------------------------------------------------------------------------------
Retardo4mS:
mov R3,#8 ; Carrega R3 para um retardo de 4 mS,
; com um cristal de 11,0592 MHz
mov R4,#39
Retardo4mS1: ; Fica neste loop até que os 2 registros
; cheguem a '0', quando então terá
; transcorrido 4 mS ou 100 mS,
; dependendo do valor de R3
djnz R4,Retardo4mS1
djnz R3,Retardo4mS1
ret ; Retorna para onde veio

; Fim Retardo4mS
;-------------------------------------------------------------------------------
; Textos
MsgMMDB:
DB ' MMDB-01 '
DB ' Kit Didatico',ETX
;-------------------------------------------------------------------------------
CodeSize equ $ ; Neste ponto o montador informa
; o tamanho do código

MemoriaLivre equ 8192-CodeSize ; Neste ponto o montador calcula e


; informa a memória de programa livre
;-------------------------------------------------------------------------------
; Para visualizar o tamanho do código e a memória de programa livre
; o usuário deve abrir o arquivo ContBin.lst que é gerado pelo montador
; no mesmo subdiretório onde esta o ContBin.asm
;-------------------------------------------------------------------------------
_______________________________________________
79
02.11. Rotina de Leitura de Teclado:

Este programa mostra como usar o teclado matricial de 4 x 4


teclas. O resultado correspondente à tecla pressionada é colocado
em hexadecimal na porta P1 e em ASCII na porta P3. O bit
correspondente à porta P0.0 é usado para sinalizar a existência de
tecla válida.

Este exemplo está no CD no arquivo Teclado.asm.


;-------------------------------------------------------------------------------
$MOD89S52
$TITLE(Rotina de teclado)
;-------------------------------------------------------------------------------
;
; Esta rotina serve para ler o teclado, sendo que as instruções comentadas
; como teste servem apenas para demonstrar o funcionamento da mesma, e não
; devem ser usadas quando esta rotina for usada em uma aplicação qualquer.
;
; Na porta P1 é mostrado o valor da tecla em hexadecimal ('0' a 'F') antes
; de ser convertido para ASCII
;
; Na porta P3 é mostrado o valor da tecla já convertida para ASCII
; ('0' a '9','*','#' e 'A' a 'D')
;
; Na porta P0.0 é mostrado o flag de sinalização de tecla válida
;
;-------------------------------------------------------------------------------
; Segmento de Bits
;
; Segmento da memória RAM, onde se pode trabalhar com um único bit, que são
; usados como sinalizadores (Flags).
; Este segmento começa na posição 020h e vai até 02Fh, totalizando 16 bytes,
; o que permite que o usuário tenha até 128 Flags no seu programa
;-------------------------------------------------------------------------------
BSEG at 0h

TeclaValida: DBIT 1 ; Para sinalizar tecla válida


TeclaApertada: DBIT 1 ; Para sinalizar tecla apertada

;-------------------------------------------------------------------------------
; Segmento de Bytes
;
; Segmento da memória RAM, reservada para as variáveis usadas no programas
;-------------------------------------------------------------------------------
DSEG at 22h

Linha: DS 1 ; Registro para a linha do teclado


Coluna: DS 1 ; Registro para a coluna do teclado
DebounceDeTecla: DS 1 ; Registro para o contador de debounce
TeclaAtual: DS 1 ; Registro para salvar a tecla

;-------------------------------------------------------------------------------
FimDosDados EQU $ ; Serve para indicar o fim da memória
; RAM usada para os dados.
;-------------------------------------------------------------------------------
CSEG ; Informa ao montador que a partir daqui
; começa o código fonte do programa
_______________________________________________
80
org 0000h ; Endereço de inicialização do
; microcontrolador, o qual vai para este
; quando é resetado
;-------------------------------------------------------------------------------
jmp IcializaPlaca ; Vai inicializar a placa

TIMER:
ORG 000BH ; Vetor do Interrupt 0
mov TH0,#0DBh ; Recarrega timer 0 com
mov TL0,#0FFh ; 10 mS a 11,0592 MHz

;-------------------------------------------------------------------------------
; | | | | |
; | Col 0 | Col 1 | Col 2 | Col 3 |
; ________|_______|_______|_______|_______|________
; | | | | |
; Linha 0 | 1 | 2 | 3 | A |
; ________|_______|_______|_______|_______|________
; | | | | |
; Linha 1 | 4 | 5 | 6 | B |
; ________|_______|_______|_______|_______|________
; | | | | |
; Linha 2 | 7 | 8 | 9 | C |
; ________|_______|_______|_______|_______|________
; | | | | |
; Linha 3 | * | 0 | # | D |
; ________|_______|_______|_______|_______|________
; | | | | |
; | | | | |
;-------------------------------------------------------------------------------
Teclado:
jb TeclaValida,DesligaTecla ;
; Se tiver tecla válida, vai esperar
; o fim da tecla
mov DPTR,#ColunaLinha ; Carrega ponteiro com a tabela
; de Coluna/Linha
mov Linha,#0FFh ; Carrega para começar a varrer na Linha
; '0', pois vai incrementar quando for
; varrer o teclado
clr TeclaApertada ; Limpa flag para testar no retorno

VarreTeclado:
inc Linha ; Incrementa contador de Linha
mov A,Linha ; Carrega para converter número da Linha
movc A,@A+DPTR ; Converte número da Linha para fazer
; a varredura
jz DebounceDeTeclado ; Sai se já varreu todas as linhas

VarreLinhas:
swap A ; Para acionar a linha na parte alta da
; porta 2
mov P2,A ; Aciona a saída correspondente a Linha
mov A,P2 ; Lê retorno do teclado
orl A,#0F0h ; Mascara para ignorar a parte alta
cpl A ; Complementa para testar na próxima
; instrução
jz VarreTeclado ; Volta para varrer a próxima linha,
; pois não tinha tecla acionada nesta
; linha
cpl A ; Complementa para voltar ao que era
; antes
mov TeclaAtual,A ; Salva temporariamente a leitura da
; tecla
jb TeclaApertada,SaiTeclado ;
_______________________________________________
81
; Sai se já tiver tecla apertada
mov Coluna,#4 ; Carrega contador de colunas com '4'
; porque vai decrementar na rotina
; VarreColunas
VarreColunas:
mov A,Coluna ; Carrega o acumulador com o número da
; coluna
dec A ; Decrementa o acumulador para buscar a
; coluna certa na tabela
movc A,@A+DPTR ; Converte para testar retorno do
; teclado
xrl A,TeclaAtual ; Testa para saber em que coluna esta o
; retorno
jnz VarreColunas1 ; Vai continuar a varredura se não tiver
; tecla apertada na coluna, ou se tiver
; mais de uma tecla apertada
SalvaTecla:
mov A,Linha ; Carrega o acumulador com o número da
; linha
rl A ; Desloca 2 vezes para poder anexar ao
rl A ; número da coluna
dec Coluna ; Para chegar ao número correto da
; coluna e na próxima instrução anexar a
; linha
xrl A,Coluna ; Anexa o número da coluna ao da linha
mov TeclaAtual,A ; Salva valor da tecla em binário
setb TeclaApertada ; Sinaliza que tem apenas uma tecla
; apertada
sjmp VarreTeclado ; Volta para varrer o teclado até o fim

VarreColunas1:
djnz Coluna,VarreColunas ; Chega a 0 se tiver mais de uma tecla
; apertada na mesma Linha
SaiTeclado:
clr TeclaApertada ; Sinaliza que não tem nenhuma tecla
; apertada
DebounceDeTeclado:
jnb TeclaApertada,DesligaTecla ;
; Se não tiver mais tecla apertada, vai
; fazer o debounce do desligamento
inc DebounceDeTecla ; Incrementa o contador de debounce
mov A,DebounceDeTecla ; Carrega o acumulador para testar na
; próxima instrução
cjne A,#08h,SaiInt ; Testa se já passou 80 mS com mesma
; tecla
dec DebounceDeTecla ; Decrementa para não ultrapassar '8'
; no próximo teste
setb TeclaValida ; Sinaliza tecla válida depois de 80 mS

;*******************************************************************************
mov P1, TeclaAtual ; Teste
;*******************************************************************************

; Converte a tecla para ASCII


mov A,TeclaAtual ; Carrega o acumulador com a leitura do
; teclado em binário
acall ConverteTeclaASCII ; Vai converter tecla binária em ASCII
mov TeclaAtual,A ; O acumulador agora esta com a tecla
; em ASCII
;*******************************************************************************
mov P3, TeclaAtual ; Teste
;*******************************************************************************

sjmp SaiInt ; Vai sair da rotina de teclado

_______________________________________________
82
DesligaTecla:
mov P2,#00Fh ; Aciona todas as linhas
mov A,P2 ; Le retorno do teclado
orl A,#0F0h ; Para garantir que o nibble alto fique
; com 'F'
cpl A ; Complementa para testar na próxima
; instrução
jnz SaiInt ; Vai sair da interrupção se o
; acumulador não estiver com '0', pois
; ainda tem tecla apertada
djnz DebounceDeTecla,SaiInt ; Conta tempo de debounce de desliga
inc DebounceDeTecla ; Para
clr TeclaValida ; Desliga tecla válida depois 80 mS

SaiInt:
mov P2,#0FFh ; Desliga acionamento de Linhas

;*******************************************************************************
mov C,TeclaValida ; Teste
mov P0.0,C ; Teste
;*******************************************************************************

reti ; Saída da interrupção do timer 0


;-------------------------------------------------------------------------------
ColunaLinha: ; Tabela de conversão para as colunas
; e linhas
db 0FEh
db 0FDh
db 0FBh
db 0F7h
db 000h
;-------------------------------------------------------------------------------
ConverteTeclaASCII: ; Rotina para converter o valor binário
; da tecla em ASCII
inc A ; Incrementa para que a instrução 'ret'
; não tenha efeito na tabela
movc A,@A+PC ; O acumulador volta com o caractere
; ASCII correspondente ao valor binário
ret ; Retorna

; Vem Volta
; com com
; Hex ASCII
db '1' ; 0 1
db '2' ; 1 2
db '3' ; 2 3
db 'A' ; 3 A
db '4' ; 4 4
db '5' ; 5 5
db '6' ; 6 6
db 'B' ; 7 B
db '7' ; 8 7
db '8' ; 9 8
db '9' ; A 9
db 'C' ; B C
db '*' ; C *
db '0' ; D 0
db '#' ; E #
db 'D' ; F D
;-------------------------------------------------------------------------------

IcializaPlaca:
;-------------------------------------------------------------------------------
; Inicializa Timer
;-------------------------------------------------------------------------------
_______________________________________________
83
InicializaTimer:
mov TH0,#0DBh ; Carrega timer 0 com
mov TL0,#0FFh ; 10 mS a 11,0592 MHz
mov TMOD,#011h ; Programa timer 0 e 1 para o modo 1
; 16 bits
setb ET0 ; Habilita interrupção do timer 0
setb EA ; Habilita interupts
setb TR0 ; Habilita timer 0

; Fim InicializaTimer
;-------------------------------------------------------------------------------

clr TeclaValida ; Zera flag de tecla válida


mov P0,#0 ; Escreve '0' em todos os pinos de P0
;-------------------------------------------------------------------------------
Dorme:
mov PCON,#1 ; A CPU se desativa, e só volta a se
; ativar quando ocorrer uma interrupção
sjmp Dorme ; Volta a aguardar

;-------------------------------------------------------------------------------
CodeSize equ $ ; Neste ponto o montador informa
; o tamanho do código

MemoriaLivre equ 8192-CodeSize ; Neste ponto o montador calcula e


; informa a memória de programa livre
;-------------------------------------------------------------------------------
; Para visualizar o tamanho do código e a memória de programa livre
; o usuário deve abrir o arquivo ContBin.lst que é gerado pelo montador
; no mesmo subdiretório onde esta o ContBin.asm
;-------------------------------------------------------------------------------
; Sinaliza para o montador o fim do programa
end

_______________________________________________
84
ANOTAÇÕES
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

______________________
______________________
______________________
______________________
______________________
______________________
______________________
______________________
______________________
______________________
______________________
______________________
______________________
______________________
______________________
______________________
______________________
_______________________________________________
85
CI - Circuitos Inteligentes Ltda.
SIA Quadra 5C, Área Especial 36, Sobreloja 01.
71.200-055 Brasília - DF.
Fone: 61 3362-9101 Fax: 61 3362-9102
www.circuitosinteligentes.com.br

Anda mungkin juga menyukai