UART POR SOFTWARE PARA A PLACA Z80 BY ARNE


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.