O programa a
seguir foi utilizado para demonstrar como construir uma
UART por software utilizando a PPI8255.
Código do programa RS232-soft_drZ80.asm
;------------------------------------------------------------------------------
; Projeto - RS232-soft_drZ80
; Arquivo - RS232-soft_drZ80.asm
; Desenv. - Eng. Márcio José Soares
; Versão - 1.0
; Data - 14/03/2023
;
; Permite enviar/receber um caracter via RS232 por software, sem uso do CI8250
; usando a PPI8255
;
; Compilador: pasmo -d -v RS232-soft_drZ80.asm RS232-soft_drZ80.bin > RS232-soft_drZ80.lst
;
; Pinos:
; PPI8255
; ------------------
; TX PA0
; RX PC0
;
; LCD PPI8255
; ------------------
; RS(4) PA3
; EN(6) PB0
; D4(11) PA4
; D5(12) PA5
; D6(13) PA6
; D7(14) PA7
;
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
; Declara endereços dos registradores do 8255
;
_8255_OA equ 0010H ;endereco para entrada/saida no portA do 8255
_8255_OB equ 0011H ;endereco para entrada/saida no portB do 8255
_8255_OC equ 0012H ;endereco para entrada/saida no portC do 8255
;------------------------------------------------------------------------------
; Declara funções necessárias na placa drZ80_uP
;
wrB8255A equ 0085H ; escreve A no portB
wrA8255A equ 0088H ; escreve A no portA
rd8255C equ 008BH ; lê do portC e insere no end de memória 87CEH
t_50us equ 0094H ; temporiza 50us
t_5ms equ 009AH ; temporiza 5ms
t_50ms equ 00A3H ; temporiza 50ms
t_500ms equ 00A9H ; temporiza 500ms
wrzerodisp equ 007CH ; limpa display 7 segmentos - coloca zeros
;-------------------------------------------------------------------------------
; Declara constantes
;
BKS equ 08H ; Back Space
CR equ 0DH ; ENTER
LF equ 0AH ; Line Feed
BR192 equ 1 ; 19200 BPS
BR96 equ 2 ; 9600 BPS
BR48 equ 4 ; 4800 BPS
BR24 equ 9 ; 2400 BPS
BR12 equ 20 ; 1200 BPS
BR06 equ 42 ; 600 BPS
BR03 equ 95 ; 300 BPS
RS_ON equ 08H ; bit de controle RS do LCD
RS_OFF equ 0F7H ; bit de controle RS do LCD invertido
LCDLIN1 equ 80H ; endereço linha 1
LCDLIN2 equ 0C0H ; endereço linha 2
LCDLIN3 equ 94H ; endereço linha 3
LCDLIN4 equ 0D4H ; endereço linha 4
;-------------------------------------------------------------------------------
; Declara constantes
;
org 8000H ; endereço inicial da RAM e programa do usuário
jr main ; desvia para main
myout db 1 ; var de saida para dados
mydata db 1 ; var para dados
myaux3 db 1
bdrate db 1 ; velocidade comunic.
linhaADD db 1 ; endereço da linha do LCD
linha db 1 ; linha
coluna db 1 ; coluna
;-------------------------------------------------------------------------------
; Subrotina principal
;
main: call EN_off
call t_500ms ; aguarda LCD iniciar
call initLCD ; configura LCD
call clearLCD ; apaga LCD
ld hl,msgLCD1 ; envia mensagem linha 1
call sendmsgLCD ; envia para LCD
ld hl,linhaADD ; muda para linha 2
ld (hl),LCDLIN2
call linLCD ; troca linha
ld hl,msgLCD2 ; envia mensagem linha 2
call sendmsgLCD ; envia para LCD
ld hl,linhaADD
ld (hl),LCDLIN3 ; muda para linha 3
call linLCD ; troca
ld hl,msgLCD3 ; envia mensagem linha 3
call sendmsgLCD ; envia para LCD
ld hl,linhaADD ; muda para linha 4
ld (hl),LCDLIN4
call linLCD ; troca
ld hl,msgLCD4 ; envia mensagem para linha 4
call sendmsgLCD ; envia mensagem
ld hl,linhaADD ; zera/inicia vars importantes
ld (hl),LCDLIN1
ld a,00H
ld hl,linha
ld (hl),a
ld hl,coluna
ld (hl),a
call wrzerodisp ; mantem display da placa zerado
ld a,BR96 ; configura baud rate por software a 9600bps
ld hl,bdrate ; pega endereço da var
ld (hl),a ; grava na var
call setport ; mantem o pino TX sempre em nível high
ld hl,msginit ; pega end da mensagem RS232
call sendmsg ; envia
call t_50ms ; aguarda
ld b,06H ; prepara para aguardar 3 segundos
espera: call t_500ms ; aguarda
djnz espera
call clearLCD ; apaga LCD
ld a,0 ; zera linha e coluna
ld hl,linha
ld (hl),a
ld hl,coluna
ld (hl),a
loop: ld e,00H ; zera e
call rx1char ; recebe 1 caracter
ld a,e ; verifica
cp 00H ; se zero, nada recebido
jr z,loop ; aguarda 1 char
ld hl,mydata ; pega end da var
ld (hl),a ; carrega var com char
call sndchar_sf ; envia char de eco
call t_50ms
ld hl,mydata ; pega end da var
ld a,(hl) ; carrega char
testaCR: cp CR ; compara com CR
jr nz,testaBKSPC ; se não é, compara com backspace
ehCR: ld bc,linha ; em bc o end da linha atual
ld a,(bc)
ld d,a ; salva em b
testalinha1: cp 00H ; compara
jr nz,testalinha2 ; desvia se não
inc d ; incrementa linha
ld a,LCDLIN2 ; pega endereço da nova linha
jr fimtesteCR
testalinha2: cp 01H ; compara
jr nz,testalinha3
inc d ; incrementa nr da linha
ld a,LCDLIN3 ; pega end da nova linha
jr fimtesteCR ; desvia para finalizar
testalinha3: cp 02H ; compara
jr nz,testalinha4
inc d ; incrementa nr da linha
ld a,LCDLIN4 ; pega end...
jr fimtesteCR ; ...
testalinha4: cp 03H ; compara
jr nz,loop ; se chegou aqui e mesmo assim não achou, loop!
ld d,00H ; volta nr da linha a 1
ld a,LCDLIN1 ; ...
fimtesteCR: ld hl,linhaADD ; guarda novo endereço
ld (hl),a ; da nova linha
ld hl,mydata ; comandos e dados sempre em myvar
ld (hl),a ; transfere
ld a,d ;
ld (bc),a ; guarda nr da linha
call scomLCD ; envia comando para LCD - nr da nova linha
ld hl,coluna ; zera contador da coluna!!!
ld a,00H
ld (hl),0
jr loop ; fim, desvia
testaBKSPC: cp BKS ; compara
jr nz,asciicod ; se não é, é normal...
ld bc,coluna ; pega nr da coluna
ld a,(bc)
cp 00H ; se está na coluna 0
jr z,loop ; se está na coluna zero não tem o que fazer
dec a ; decrementa em 1
ld (bc),a ; salva
ld hl,linhaADD ; pega endereço da linha atual
add a,(hl) ; soma
ld hl,mydata ; comandos e dados sempre em myvar
ld (hl),a ; transfere
call scomLCD ; envia comando para LCD - nr da nova linha
jr loop ; retorna
asciicod: call schLCD ; envia mydata para o LCD
ld hl,coluna ; verifica posição do caracter
ld a,(hl)
inc a ; incrementa coluna
cp 14H ; compara com 20
jr nz,guardaC ; se não chegou ainda, desvia e guarda
ld a,00H ; se chegou, zera...
ld (hl),a ; guarda...
ld hl,linha ; e prepara para incrementar a linha
ld a,(hl) ;
inc a ; incrementa linha
cp 01H ; estva na linha 1 e vai para a 2
jr nz,complin2 ; se não, testa próxima
ld b,LCDLIN2 ; em b o endereço da linha 2
jr guarda ; apenas guarda
complin2: cp 02H ; estava na linha 2 e vai para 3
jr nz,complin3 ; se não, testa próxima
ld b,LCDLIN3 ; em b o endereço da linha 3
jr guarda ; apenas guarda
complin3: cp 03H ; estava na linha 3 e vai para 4
jr nz,complin4 ; se não, testa próxima
ld b,LCDLIN4 ; em b o endereço da linha 4
jr guarda ; apenas guarda
complin4: cp 04H ; chegou ao fim
jr nz,guarda ; não chegou ainda, desvia e guarda
ld b,LCDLIN1 ; em b o endereço da linha 1
call clearLCD ; apaga e retorna para linha 1
ld a,00H ; linha e coluna voltaram a zero
guarda: ld (hl),a ; apenas guarda a coluna incrementada
ld de,linhaADD ; precisa salvar o endreço da linha
ld a,b
ld (de),a ;
call linLCD ; se mudou linha posiciona cursor
jr guardaCont
;
guardaC: ld (hl),a ; apenas guarda a coluna incrementada
guardaCont: jp loop ; faz eternamente
setport: ld a,01H
call wrA8255A ; levanta pino TX
call t_50ms
ret
;-------------------------------------------------------------------------------
; subrotina para enviar msg RS-232 usando a PPI8255 pino PA0
; ponteiro da mensagem em HL
;
sendmsg: ld a,(hl) ;pega conteudo
ld b,a ;salva em b
cp 00H ;compara com final
ret z ;retorna se chegou ao fim
ld de,mydata ;pega end da var de envio
ld a,b ;restaura a com b
ld (de),a ;coloca conteúdo da mensagem em a
inc hl ;incrementa a posição de hl
call sndchar_sf ;envia char
jr sendmsg ;continua ate o fim
;------------------------------------------------------------------------------
; Subrotina sndchar_sf - envia um caracter via RS232 usando a PPI8255
; UART por software
;
; Caracter a enviar em mydata
;
sndchar_sf: push bc
push de
push hl
ld hl,mydata ; pega o endereço de mydata
ld a,(hl) ; var em a
ld b,a ; em b char a transmitir
ld e,08H ; quantidade de bits a enviar
sndstart: ld a,00H
call wrA8255A
call delay1bit
txbits: bit 0,b ; testa bit 0 - lsb primeiro
jr nz,biteh1 ; envia 1
ld a,00H ; envia 0
jr sndbit
biteh1: ld a,01H
sndbit: call wrA8255A
call delay1bit
dec e ; decrementa contador de bits
jr z,sndstop ; se já foram todos, envia stop bit
rr b ; gira
jr txbits ; continua envio dos bits
sndstop: ld a,01H
call wrA8255A
call delay1bit
pop hl
pop de
pop bc
ret
;------------------------------------------------------------------------------
; Subrotina rx1char - recebe 1 caracter serial via PPI8255
; caracter recebido em E
;
rx1char:
recstartbit: in a,(_8255_OC) ; testa se chegou start bit
bit 0,a ; testa bit zero
jr z,getchar ; se chegou, recebe
ret ; do contrário retorna
getchar: call delayhafbit ; aguara o tempo de 1/2 bit
ld e,00H ;
call delay1bit
ld b,08H ; são 8 bits a receber
rxbits: nop ; aumenta mais um pouco a espera
nop
nop
rr e
in a,(_8255_OC); ; recebe bit
bit 0,a ; testa
jr z,biteh0 ; bit eh zero
set 7,e ; bit eh 1
jr delayBit
biteh0: res 7,e ; zera bit
jr delayBit
delayBit: call delay1bit ; espera tempo de 1 bit
djnz rxbits ; continua
rxend: ret ; se tudo deu certo, byte recebido em E
;------------------------------------------------------------------------------
; Subrotina delay1bit
; tempo de espera de 1 bit para 9600bps - 104us
;
delay1bit:
push de ; salva - 3 ciclos
ld a,(bdrate) ; 2 ciclos
godecp: ld d,4 ; 2 ciclos
godecs: dec d ; 1 ciclo
jr nz,godecs ; 3 ciclos se atendido; 2 ciclos se não atendido
dec a ; 1 ciclo
jr nz,godecp ; 3 ciclos se atendido; 2 ciclos se não atendido
dec e ; 1 ciclo,
dec e
dec e
dec e
dec e
dec e
dec e
dec e
dec e
dec e
dec e
pop de ; 3 ciclos
ret
;------------------------------------------------------------------------------
; Subrotina delayhafbit -
; tempo de espera de 1/2 bit para 9600bps - 50us
;
delayhafbit: push de ; salva - 3 ciclos
ld a,(bdrate) ; 2 ciclos
godecp2: ld d,3 ; 2 ciclos
godecs2: dec d ; 1 ciclo
jr nz,godecs2 ; 3 ciclos se atendido; 2 ciclos se não atendido
dec a ; 1 ciclo
jr nz,godecp2 ; 3 ciclos se atendido; 2 ciclos se não atendido
dec e ; 1 ciclo
dec e
dec e
dec e
dec e
dec e
dec e
dec e
dec e
dec e
dec e
pop de ; 3 ciclos
ret
;------------------------------------------------------------------------------
; Subrotina bin2ascii - converte binario em ascii e envia usando uart por soft
; valor a converter em e
;
bin2ascii: ld hl,mydata
ld b,8 ; 8 bits a converter
bin2a_cont: bit 7,e ; testa bit
jr z,zeroAscii ; eh zero
ld a,'1'
jr sndascii
zeroAscii: ld a,'0'
sndascii: ld (hl),a
call sndchar_sf
rl e
djnz bin2a_cont
ret
;-------------------------------------------------------------------------------
; subrotina para enviar msg para a LCD
; ponteiro da mensagem em HL
;
sendmsgLCD: ld a,(hl) ; pega conteudo
ld b,a ; salva em b
cp 00H ; compara com final
ret z ; retorna se chegou ao fim
cp LF ; compara com LF
jr nz,sndcont ; se nao, continua
call linLCD ; muda linha no LCD
inc hl
jr sndcontLF ; pula para não enviar o LF
sndcont: ld de,mydata ; pega end da var de envio
ld a,b ; restaura a com b
ld (de),a ; coloca conteúdo da mensagem
inc hl ; incrementa a posição de hl
call schLCD ; envia char para LCD
sndcontLF: call t_50ms ; aguarda
jr sendmsgLCD ; continua ate o fim
;------------------------------------------------------------------------------
; Subrotina para mudar de linha no LCD
; endereço em linha
;
linLCD: push hl
push bc
ld bc,linhaADD ; pega endereço da linha
ld a,(linhaADD) ; em b o endreço
ld b,a
ld hl,mydata ; coloca na var o char a ser escrito
ld (hl),b
call scomLCD ;muda de linha no LCD
pop bc
pop hl
ret
;-------------------------------------------------------------------------------
; Subrotina para apagar o LCD
;
clearLCD: push hl ; salva
ld hl,mydata ; coloca na var o char a ser escrito
ld a,01H ; apaga LCD e coloca na coluna 1
ld (hl),a
call scomLCD ; muda de linha no LCD
pop hl ; restaura
ret
;-------------------------------------------------------------------------------
; Envia dois caracteres dummies antes de enviar uma string
;
snd2SPC: ld hl,mydata
ld a,' '
ld (hl),a
call schLCD
call t_50ms
ld hl,mydata
ld a,' '
ld (hl),a
call schLCD
call t_50ms
ret
;-------------------------------------------------------------------------------
; Envia um char para o LCD - usa modo 4 bits
;
schLCD: push hl
push bc
ld hl,mydata ; pega endereco da var
ld a,(hl) ; pega o conteudo
and 0F0H ; limpa a LSb
or RS_ON ; liga o pino de RS
or 01H ; mantem pino TX sempre alto
call wrA8255A ; envia A para portA
call EN_on ; liga o pino EN - pulsa EN no portB
call t_50us ; aguarda
call EN_off ; desliga o pino EN
ld hl,mydata ; volta a pegar o end da var
ld a,(hl) ; pega o conteudo
sla a ; posiciona o LSB no MSB
sla a
sla a
sla a
and 0F0H ; limpa o LSB, mantendo PA0 livre
or RS_ON ; liga o RS
or 01H ; mantem pino TX sempre alto
call wrA8255A ; envia A para portA
call EN_on ; liga o pino EN - pulsa EN
call t_50us ; aguarda
call EN_off ; desliga o pino EN
pop bc
pop hl
ret
;-------------------------------------------------------------------------------
; Envia um comando para o LCD - usa modo 4 bits de dados
;
scomLCD: ld hl,mydata ;pega endereco da var
ld a,(hl) ;pega o conteudo
and 0F0H ;limpa LSB
and RS_OFF ;desliga o pino de RS
or 01H ; mantem pino TX sempre alto
call wrA8255A ;envia A para portA
call EN_on ;liga o pino EN - pulsa EN no portB
call t_50us ;aguarda
call EN_off ;desliga o pino EN
ld hl,mydata ;volta a pegar o end da var
ld a,(hl) ;pega o conteudo
sla a ;posiciona o LSB no MSB
sla a
sla a
sla a
and 0F0H ;limpa o LSB
and RS_OFF ;desliga o RS
or 01H ; mantem pino TX sempre alto
call wrA8255A ;envia A para portA
call EN_on ;liga o pino EN - pulsa EN no portB
call t_50us ;aguarda
call EN_off ;desliga o EN de strobe
ret
;-------------------------------------------------------------------------------
; Subrotina para configurar/iniciar o LCD - RS=0 e WR=0
; Le bytes em _initlcd e envia via porta paralela
; Configuracao adotada: modo 4 bits de dados, 2 linhas, caracter 5x7, cursor piscando
;
initLCD: ld a,00H ;sao 5 bytes a enviar, comecando por zero
ld hl,myaux3 ;carrega endereco
ld (hl),a ;guarda
initLCDloop: ld de,myaux3 ;pega o endereco da var
ld b,00H ;zera a var
ld a,(de) ;pega o conteudo de myaux3
ld c,a ;carrega em c
ld hl,_inilcd ;carrega endereco da var de configuracao
add hl,bc ;posiciona o ponteiro
ld a,(hl) ;pega primeiro byte
ld hl,mydata ;pega endereco da var
ld (hl),a ;coloca valor na var
call scomLCD ;envia o comando para o LCD
ld hl,myaux3 ;carrega endereco
ld a,(hl) ;pega conteudo
inc a ;decrementa a
ld (hl),a ;guarda conteudo
cp 05H ;compara com final - sao 7 comandos agora
jp nz,initLCDloop ;se nao eh ainda o valor
ret ;detectado fim, retorna!
;-------------------------------------------------------------------------------
; Subrotina strobe_on - liga pino de strobe
;
EN_on: ld hl,myEN ;carrega end da var
ld a,01H ;carrega valor ON
jr myEN ;desvia
;-------------------------------------------------------------------------------
; Subrotina strobe_off - desliga pino de strobe
;
EN_off: ld hl,myEN ;carrega end da var
ld a,00H ;carrega valor OFF
;-------------------------------------------------------------------------------
; Subrotina mystrobe - liga/desliga pino de strobe
;
myEN: call wrB8255A ;envia dado para portB
ret ;retorna
;-------------------------------------------------------------------------------
; Dados para LCD - inicializacao
; comandos para inicializar o LCD - modo 4 bits
_inilcd: db 02H, 28H, 06H, 0FH, 01H
;-------------------------------------------------------------------------------
; Mensagem para RS232-soft_drZ80
;
msginit: db CR,LF,'Teste com a placa Z80 1990 by Arne ;)',CR,LF
db 'Comunicacao serial por software com PPI8255',CR,LF
db 'v1.0 - 16/03/2023',CR,LF
db 'Config - 9600,8,n,1',CR,LF,CR,LF
db 'Tecle algo -> ',00H
;-------------------------------------------------------------------------------
; Mensagem para LCD_drZ80
;
msgDummie: db ' ',00H
msgLCD1: db ' Bem-vindo ao ',00H
msgLCD2: db 'Laboratorio do Arne',00H
msgLCD3: db 'Teste placa drZ80_uP',00H
msgLCD4: db ' UART por software',00H
;-------------------------------------------------------------------------------
; Fim do programa
;
end
Copyright deste conteúdo
reservado para Márcio José Soares e protegido pela Lei de
Direitos Autorais LEI N° 9.610, de 19 de Fevereiro de
1998. É estritamente proibida a reprodução total ou
parcial do conteúdo desta página em outros pontos da
internet, livros ou outros tipos de publicações
comerciais ou não, sem a prévia autorização por escrito do
autor.