sábado, 23 de fevereiro de 2008

Biblioteca SPI para porta de Joystick

A porta de Joystick pode ser utilizada como uma porta SPI, permitindo assim ligar um grande número de dispositivos, como memórias flash seriais, relógios RTC, conversores A/D e D/A, displays de celulares, cartões MMC/SD, etc.

Eis abaixo um código básico para comandar um dispositivo na porta B do joystick.

Uma desvantagem do protocolo SPI é necessitar de um sinal de Chip Select para cada dispositivo. Mas isso pode ser contornado, utilizando um duplo Flip-Flop, permitindo assim que uma cadeia de dispositivos seja conectada simultaneamente.

A velocidade de transmissão deve ser algo em torno de 1,7KBytes/segundo.

; Biblioteca SPI para a porta de Joystick do MSX
; Danjovic 2008 - danjovic@vespanet.com.br
; http://hotbit.blogspot.com
;
; Version: 0.1 : 18/02/2008
; Version: 0.2 : 22/02/2008 - Added 8 clock pulses after release of CS line.

; Caracteristicas:
; Modo 0 ->; CPOL=0, CPHA=0
; Sample in Rise, shift in Fall
;
; Pinout:
; MISO =>; 1 (UP)
; MOSI =>; 6 (TRGA)
; MSSL =>; 7 (TRGB)
; MSCK =>; 8 (PULSE)

;SPIUP - Init SPI port B
;SPIDW - Release SPI port
;SPITX - Transmit a byte
;SPIRX - Receive a byte
;SPIXF - Transfer a byte



; Bits do Registro 14 do PSG
;
BMISO EQU 1 ; UP
MSKSO EQU 0FEH

; Bits do Registro 15 do PSG
;
BMOSI EQU 2 ; TRG1
BMSSL EQU 3 ; TRG2
BMSCK EQU 5 ; PULSE
ABSEL EQU 6 ; A/B SELECT

; Registros do PSG
;
PSGAD EQU 0A0H
PSGWR EQU 0A1H
PSGRD EQU 0A2H


SPIUP: ; Initialize SPI Port
; Inputs: None
; Outputs: A - Reg 15 state
; Changes: A,EI
DI
LD A,15
OUT [PSGAD],A
IN A,[PSGRD]
LD [PSGSAV],A
SET ABSEL,A
RES BMSSL,A ; Chip Select low
OUT [PSGWR],A
RET

SPIDW: ; Release SPI Port
; Inputs: A - Reg 15 State
; Outputs: None
; Changes: A,EI
LD B,A ; Save Reg 15 State
; Release /CS line off
LD A,15
OUT [PSGAD],A
LD A,B
SET BMSSL,A
OUT [PSGWR],A

; Dummy RX - 8 clock pulses
SCF
CALL SPIRX

; Restore PSG reg 15 State
LD A,[PSGSAV]
OUT [PSGWR],A
EI
RET

SPITX: ; Transmit a byte
; Inputs: A - Reg 15 State
; C - Byte to transmit
; PSG Reg 15 already selected
; Outputs: None
; Changes: AF,B,C
LD B,8
TXBIT: RL C ; MSB -> CY Flag
SET BMOSI,A
JR C,TPUTB
RES BMOSI,A
TPUTB: OUT [PSGWR],A
; CLOCK()
SET BMSCK,A
OUT [PSGWR],A
RES BMSCK,A
OUT [PSGWR],A
;
DJNZ TXBIT


SPIRX: ; Receive a byte
; Inputs: A - Reg 15 State
; CY - MOSI line state while receiving
; PSG Reg 15 already selected
; Outputs: C - Byte received
; Changes: AF,B,C,D

; Set MOSI line state
SET BMOSI,A
JR C,RXHIGH
RES BMOSI,A
RXHIGH: OUT [PSGWR],A

LD B,8
RXBIT: ; CLOCK HI
SET BMSCK,A
OUT [PSGWR],A
LD D,A ; Save Reg 15 State
; Read bit
LD A,14
OUT [PSGAD],A
IN A,[PSGRD]
AND MSKSO
NEG ; Bit MSKSO->CY
RL C ; LSB<-CY
; CLOCK LO
LD A,15
OUT [PSGAD],A
LD A,D ; Restore Reg15 state
RES BMSCK,A
OUT [PSGWR],A
;
DJNZ RXBIT


SPIXF: ; Transfer a byte
; Inputs: A - Reg 15 State
; C - Byte to transmit
; PSG Reg 15 already selected
; Outputs: E - Byte received
; Changes: AF,B,C,D,E

LD B,8

XFBIT: ; Write bit
RL C ; MSB -> CY Flag
SET BMOSI,A
JR C,XFPUTB
RES BMOSI,A
XFPUTB: OUT [PSGWR],A
; CLOCK HI
SET BMSCK,A
OUT [PSGWR],A
LD D,A ; Save Reg 15 State
; Read bit
LD A,14
OUT [PSGAD],A
IN A,[PSGRD]
AND MSKSO
NEG ; Bit MSKSO->CY
RL E ; LSB<-CY
; CLOCK LO
LD A,15
OUT [PSGAD],A
LD A,D ; Restore Reg15 state
RES BMSCK,A
OUT [PSGWR],A
;
DJNZ XFBIT

terça-feira, 19 de fevereiro de 2008

RX232 - Primeira versão compilável

Integrei hoje a rotina de recepção de um único byte na rotina de recepçao de buffer, e já tenho uma estimativa do tamanho do 'driver' de recepção após compilado: 122 bytes.
Agora tenho que passar um pente fino na lógica, no uso dos registradores e da pilha. Mas eventuais correções não vão alterar sensivelmente o tamanho do código gerado.

Tenho ainda que construir o adaptador RS232 (com um MAX232) para poder testar o hardware.

Segue abaixo o código:
;
; Recepcao serial via
; porta de Joystick do MSX
;
; danjovic@hotmail.com
; http://hotbit.blogspot.com
;
; Versao 0.1 18/02/2008
;
; Licenca de uso: GNU GPL
;
;
; Compilado no ASMSX
; http://www.robsy.net/asmsx.htm

ORG 0E000H

.BASIC
.START RX232

;
; Bits do Registro 14 do PSG
;
BRTS EQU 0 ; UP
BRXD EQU 1 ; DOWN
MRXD EQU 0FDH ; mask for bit BRXD
;
; Bits do Registro 15 do PSG
;
BTXD EQU 2 ; TRG1
BCTS EQU 5 ; TRG2
ABSEL EQU 6 ; A/B Port select

;
; Registros do PSG
;
PSGAD EQU 0A0H
PSGWR EQU 0A1H
PSGRD EQU 0A2H

; Variaveis em RAM
PSGSAV EQU 0F974H ; RS232 putback
; Not used in MSX
;
; Constantes

; BaudRates
;RBAUD EQU 6 ; 19200 Bauds, (6)
;RBAUD EQU 10 ; 14400 Bauds, (10)
RBAUD EQU 18 ; 9600 Bauds, (17-19)
;RBAUD EQU 42 ; 4800 Bauds, (41-43)
;RBAUD EQU 92 ; 2400 Bauds, (90-93)
;RBAUD EQU 190 ; 1200 Bauds, (188-191)

ERFUL EQU 0FFH ; Buffer cheito
ERFRM EQU 0FEH ; Framing error
ERBRK EQU 0FDH ; RTS off antes startbit


RX232:
; Entradas: HL = Ender Buffer em RAM
; C = Tam maximo do buffer
; Saicas : D = Bytes recebidos
; CY = 0, OK
; CY = 1, Error; A=Codigo erro
;
LD A,15
OUT [PSGAD],A
IN A,[PSGRD]
LD [PSGSAV],A
SET ABSEL,A
LD E,A
LD D,0
NEWBYTE: LD A,14
OUT [PSGAD],A
IN A,[PSGRD]
AND A ; Clear carry
BIT BRTS,A
LD A,D
JR NZ,EXIT ; RTS off

LD A,D
CP C
LD A,ERFUL
JR C,EXIT ; Buffer full

PUSH BC
PUSH HL
CALL RECEIVE
POP HL
POP BC
JR C,EXIT ; Error

LD [HL],A ; Save byte
INC HL
INC D ; Incr counter
JR NEWBYTE ; Next byte

EXIT: PUSH AF
LD A,15
OUT [PSGAD],A
LD A,[PSGSAV]
OUT [PSGWR],A
POP AF
RET


RECEIVE:
; Recebe um único caractere
; E - State of PSG register 15
; Currently selected PSG rgister 14
; Return Byte received in Register A

; init local variables
LD B,10 ; bits to receive
LD H,RBAUD ; Baud rate delay
LD L,H
AND A ; Clear CY
RR L ; L= L\2, Half bit timer
SET BCTS,E ; CTS ON

; CTS=ON
; Set PSG register 15
LD A,15
OUT [PSGAD],A
LD A,E
RES BCTS,E ; Reg 15 with CTS OFF
OUT [PSGWR],A

; Set PSG register 14
LD A,14
OUT [PSGAD],A

WAITSTRT: ; Awaits Start Bit
IN A,[PSGRD]
BIT BRXD,A
JR Z,MEIOBIT ;
BIT BRTS,A
JR NZ,WAITSTRT

;Break: RTS went off before
; Start bit
LD A,ERBRK
SCF
RET

; Receive 10 bits
; First is discarded,
; last is the Stop Bit
RXBIT: LD L,H ; Reload delay time

; Delay for bit time
MEIOBIT: DEC L
JR NZ,MEIOBIT

RR C
IN A,[PSGRD]
AND MRXD ; Mask RXD bit
NEG ; RXD to Carry Flag
DJNZ RXBIT ;
; Here Carry contains Stop Bit

; CTS=OFF
; Set PSG register 15
LD A,15
OUT [PSGAD],A
LD A,E ; Reg E has bit CTS off
OUT [PSGWR],A

LD A,ERFRM
CCF ; invert CY to indicate
; Framing error
RET C

LD A,C ; Se sucesso, retorna
RET ; NC, A=byte recebido

segunda-feira, 18 de fevereiro de 2008

Código base RS232

Eis o código da rotina de recepção de "buffer". Agora é preciso adaptar o código da rotina anterior para criar a rotina RXBYTE. Também é preciso acrescentar o código que chaveia do registro 14 para o registro 15 do PSG. Essa operação é necessária devido à arquitetura do PSG

RX232:
; Entradas: HL = Ender Buffer em RAM
; C = Tam maximo do buffer
; Saicas : D = Bytes recebidos
; CY = 0, OK
; CY = 1, Error, A=Codigo erro
;
LD A,15
OUT [PSGAC],A
IN A,[PSGRD]
LD [PSGSAV],A
SET ABSEL,A
LD E,A
LD D,0
NEWBYTE: LD A,14
OUT [PSGAD],A
IN A,[PSGRD]
AND A ; Clear carry
BIT RTS,A
LD A,D
JR NZ,EXIT ; RTS off

LDA A,D
CP C
LD A,ERFUL
JR C,EXIT ; Buffer full

PUSH BC
PUSH HL
CALL RXBYTE
POP HL
POP BC
JR C,EXIT ; Error

LD [HL],A ; Save byte
INC HL
INC D ; Incr counter
JR NEWBYTE ; Next byte

EXIT: PUSH AF
LD A,15
OUT [PSGAD],A
LD A,[PSGSAV]
OUT [PSGWR],A
POP AF
RET

Delineando a rotina de recepção

Após alguns tropeços enquanto escrevia a rotina de recepção de um "buffer", resolvi definir melhor o funcionamento do código que chama a rotina de recepção de dados, do post anterior.

Eis abaixo o fluxograma da rotina, que deve ser chamada com 2 parâmetros:
- Endereço do buffer que vai receber os dados
- Tamanho do buffer de recepção

Uma simplificação importante que fiz, foi que considerar que o buffer está vazio na inícion da chamada da rotina. Isso evita a necessidade de uma chamada de inicialização do buffer e simplifica o tratamento de erros.

Outra vantagem é não depender diretamente de um buffer em RAM para receber temporariamente os caracteres da RS232, o que gerava a necessidade de um LDIR para o destino dos bytes. Com o esquema atual, basta apontar para a região de memória que deve receber os dados e dizer qual o tamanho máximo do buffer de dados.

Caso tudo a recepção tenha se processado corretamente, a quantidade de bytes recebidos é retornada. Caso contrário, um dos códigos de erro é retornado:
  • ERFUL - Buffer Cheiro
  • ERFRA - "Framing error", ou stop bit recebido incorretamente
  • ERBRK - Sinal RTS foi desativado pelo PC antes que o "Start" Bit fosse enviado.


sexta-feira, 15 de fevereiro de 2008

Código para a recepção RS232

Estive trabalhando no código para a recepção RS232 na porta de joystick. .

Eis um trecho da rotina de recepção de dados, que aguarda pelo Start Bit, tão logo ele seja detectado, recebe os 10 bits da palavra de dados, dentro de um só loop, para simplificar a temporização. Ao final da rotina, flag Carry contém o estado do Stop bit. Note que start bit é descartado, pois ele já havia sido detectado.
Outro detalhe importante é que o primeiro delay é de meio bit, e os subsequentes de 1 bit inteiro. Isso faz com que a amostragem seja feita no meio do tempo de bit, diminuindo assim a taxa de erro. Após o stop bit ser amostrado, ainda temos meio tempo de bit disponível, o que é importante, pois é necessário mudar registro do PSG antes de levantar novamente o sinal CTS, para fazer o PC esperar o MSX processar o dado antes de enviar outro.

;...
WAITSTART: IN A,(PSGRD)
BIT RXD,A ; Start bit?
JR Z,MEIOBIT ;yes, wait 1/2 bit before start
; to acquire samples
BIT RTS,A ; PC stopped TX ?
JR Z,WAITSTART ; No, sample again
JP BREAKERROR

MEIOBIT: LD L,BITTIME ;
SRL L ; divide by two
LD B,10 ; Ten bits.
PROXBIT: CALL DELAYBIT ; Wait Bit Time
RR C ; CY->7...0->CY
IN A,(PSGRD) ; Sample bit
AND RXD
NEG ; Bit RXD->CARRY
LD L,BITTIME ; Reload delay preset
DJNZ PROXBIT ; Wait full bit time before
; get another sample
LD A,(15)
OUT (PSGAD),A ; Select Register 15
LD A,E ; Previous state of reg 15
SET RTS,A
OUT (PSGWR),A ; deassert RTS
LD E,A ; Save state of reg15

JR NC,FRAMERR ; If stop bit=0 an framing
; error has occoured
;...
PROCESSABIT:

quinta-feira, 14 de fevereiro de 2008

Recepção serial na porta de Joystick

Fiz uns testes para determinar se o controle de fluxo por RTS/CTS poderia ser utilizado para fazer o PC esperar o MSX atender o pedido de solicitação de transmissão, e também para ver se a transmissão pode ser interrompida a qualquer momento. Os resultados do teste demonstraram que sim.

A correspondência entre os pinos da porta de joystick do MSX e a porta de PC pode ser vista na figura abaixo. Entre os dois deve ser utilizado um conversor de nível, tipo um MAX232.

MSX – Porta Joystick Dir PC – Porta Serial
Função Pino RS232 - Pino RS232
DOWN 2 RxD <- 3 TxD
UP 1 RTS <- 7 RTS
TRGA 6 TxD -> 2 RxD
TRGB 7 CTS -> 8 CTS
GND 9 GND - 5 GND

WiznetX (correção do circuito)

Algumas pessoas já me questionaram sobre como fazer a ligação de um módulo Wiznet WIZ810MJ no MSX.



Este módulo de rede é baseado no chip WM5100 e tem dois modos de interligação: SPI e memória. No modo memória, o chipa aparece para o 'host' como uma área endereçável de 32Kbytes, de acordo com o mapa abaixo (vide data sheet):




O WM5100 é alimentado por uma tensão de 3,3Volts, mas tem tolerância a sinais TTL, o que torna bem fácil a conexão ao MSX. Além disso, os sinais de controle têem a mesma polaridade (nível ativo) do Z80.

Então, todo o hardware necessário para conectar este módulo ao MSX se resume a um decodificador e um regulador de tensão LDO, que possa fornecer ao menos 183mA.

O circuito abaixo é de uma placa de protótipo. O decodificador é um LS138, que permite mapear a placa no intervalo 0000-7FFF. A temporização dos sinais /RD e /WR teve que ser adaptada para o chip W5100 pois difere em alguns pontos do Z80, em especial o sinal /WR, que fica ativo 1 ciclo de clock após o sinal MREQ do Z80. Segundo o "data sheet" do W5100 os sinais /RD e /WR têm que acontecer simultaneamente ao sinal /CS. Eu fiz umas perguntas sobre a temporização destes sinais no site do fabricante do CHIP, pois achei meio crótica a temporização "oficial" que diz que o sinal de /RD ou de /WR deve acontecer em, no máximo 1ns após o sinal de /CS, e deve retornar a nível alto 1ns antes do sinal de /CS. Mas caso seja assim mesmo, um latch controlado pelo clock do Z80 deve resolver a situação.

O regulador é o TC1108, que tem capacidade de fornecer até 300mA.




Abaixo está uma sugestão de placa para o protótipo. O módulo Wiznet deve ser montado do lado de baixo da placa.



Como eu não tenho um módulo desses, não devo montar uma placa dessas tão cedo, mas quem se aventurar a montar, seguem dois conselhos:

-O data sheet do WM5100 estipula um tempo máximo de 1ns entre o sinal de Chip Select e o READ ou Write, o que para mim significa que os sinais têem que ser simultâneos. O decodificador do meu circuito induz um certo atraso entre o sinal SLCTSLT e o CS, que eu estimo entre 30 a 40ns. Para a leitura isso não deve ser problema, pois já que o Data sheet não estipula tempo mínimo eu suponho que o sinal de RD Possa iniciar antes do sinal CS. Já para a escrita, o Z80 atrasa em 1 ciclo de clock (280ns) o sinal de WR em relaçao ao sinal MREQ. Pode ser que isso não traga problema algum, mas sempre é bom ficar e olho.

-Quando o circuito endereça na faixa de 4000-BFFF, o pino A14 está em nível alto entre - 4000-7FFF e em nível baixo entre 8000-BFFF. Isso significa que o mapa de memória vai aparecer trocado, ou seja, as áreas do TX buffer e RXbuffer vêm primeiro, entre 4000-7FFF, e os registros vêm depois, na faixa de 8000-BFFF.