segunda-feira, 20 de fevereiro de 2012

Adaptador para teclado PS/2 - Problemas, considerações e idéias .

Antes de me aventurar num projeto de um adaptador de teclado PS/2 é importante estudar bem as particularidades do hardware do MSX.

O teclado do MSX é uma matriz de 11 linhas por 8 colunas. O MSX seleciona a linha através dos 4 bits menos significativos da porta C da PPI que vão a um decodificador BCD-Decimal (74LS145). A leitura das colunas referentes à linha selecionada é feita através da porta B da PPI.

Normalmente, uma leitura do teclado tem a seguinte estrutura:
A=linha selecionada
OUT (0AAh),A
IN A,(0A9H)
Considerando que as intruções OUT/IN executam em 12 ciclos de máquina (no MSX é inserido um 'wait' de um ciclo de clock cada ciclo M1), e que as instruções são normalmente consecutivas, dá pra se notar que o tempo disponível para a leitura de um teclado é bem estrito.

INSTRUCTION   BYTES   M1         M2        M3  
OUT (n),A     2       OCF(4+1)   OD(3)     PW(4)
IN A,(n)      2       OCF(4+1)   OD(3)     PR(4)


Para ser bem preciso, o tempo disponível entre a escrita na porta 0AAh e a leitura na porta 0A9H vai desde o meio segundo pulso de clock do ciclo M3 da instrução OUT (Port Write) até o meio do quarto pulso de clock do ciclo M3 da instrução IN, onde o Z80 efetivamente lê a porta.


Na ponta do lápis temos:
- 2,5 ciclos de clock restantes do ciclo M3 da instrução Write
- 5 ciclos de clock do 'Opcode Fetch' do ciclo M1 da instrução IN
- 3 ciclos de clock da leitura do operando da instrução IN (ciclo M2)
- 3 ciclos de clock da leitura da porta no ciclo M3 (no 3 1/2 o dado já deve estar pronto para ser lido)

Isso nos dá 13,5 ciclos de clock, o que equivale a 3,77 microssegundos numa máquina rodando a 3,58MHz .

Se quisermos ser mais estritos ainda, devemos considerar que nem todo programa precisa escrever na porta C da PPI antes de ler a porta B, pois basta escrever uma vez e ler quando necessário (por exemplo jogos que usem somente as setas cursoras e a barra de espaço).

Assim, considerando somente a leitura na porta B da PPI (0A9h) temos um tempo que vai desde o meio do segundo pulso ate o final do terceiro pulso de clock do ciclo M3 da instrução IN.
Isso equivale a 2,5 ciclos de clock ou 698 nanosegundos.

Penso em duas abordagens para o problema. A primeira é utilizar um CPLD e um microcontrolador. A CPLD é configurada como uma matriz de 10 linhas com 8 flip flops e mais um shift register capaz de endereçar a entrada de cada uma dessas 10 linhas. O microcontrolador lê o teclado PS/2 e cria internamente uma matriz de 11 bytes representando o estado de cada tecla (para o MSX). De tempos em tempos o microcontrolador desloca sequencialmente para o shift register interno cada um dos 10 bytes e apos transferir cada um dos bytes ele atualiza o estado dos flip flops da linha equivalente. A solução mais simples seria encadear todas as 11 linhas e fazer um shift register de 80 bits, mas isso poderia gerar falsos eventos caso o MSX estivesse lendo uma linha do teclado na hora em que o microcontrolador estivesse atualizando o shift register. Isso ia requerer pelo menos (11x8)+8 macrocélulas de uma CPLD, ou seja 98 macrocélulas. Uma abordagem parecida seria fazer um shift register de 88 bits com um latch de saída, mas isso ia requerer pelo menos 168 macro células.
Uma segunda abordagem utiliza somente o microcontrolador. Mas dados os tempos, qual microcontrolador utilizar? Um AVR rodando a 20MHz roda uma instrução a cada 50 nanossegundos, e pode rodar 14 instruções em 700 nanossegundos. Um PIC com mesmo clock teria uma 3 instruções apenas. Um 8051 a 12MHz nem teria como responder tão rápido. Embora seja possível utilizar microcontroladores mais rápidos, não creio que essa seja uma abordagem elegante, ainda mais que o tempo entre eventos de teclado normalmente é bem longo, da ordem de dezenas de milissegundos.

Pensando novamente, este problema de velocidade para atender a uma requisição do microprocessador não é novo, e a solução também não. O Z80 já tem um recurso para tratar esse problema que é a própria linha WAIT. Essa linha é amostrada no terceiro pulso de clock do ciclo M3 das instruções de I/O. Caso essa linha esteja em nível baixo, o Z80 vai fazendo novas amostragens até que a linha volte a nível alto (contudo deve-se tomar cuidado para não deixar o Z80 muito tempo neste estado, pois durante o "wait state" as memórias dinâmicas não sofrem refresh).

Essa segunda abordagem permite o uso de praticamente qualquer microcontrolador, basta adicionar um flip flop e um decodificador para os sinais /RD, /CS e A0 da própria PPI. O microcontrolador pode trabalhar por polling ou por interrupções, reconhecendo a mudança de estado da saída do flip flop (que vai a zero no momento em que /RD=0; /CS=0; A0=1). O microcontrolador então lê o estado dos bits 0-3 da porta C da PPI, determina qual a linha que o MSX deseja ler. Então o microcontrolador coloca na saída os bits correspondentes à coluna lida e reseta o flip flop, liberando a linha Wait. Se quisermos ser preciosistas, após liberar o Z80 basta aguardar por um tempo equivalente a um ciclo e meio de clock do Z80 e então desativar a porta de saída (bits da coluna).



4 comentários:

Ricardo Bittencourt disse...

Na prática, os jogos e o basic só lêem a matriz de teclado na interrupção de retraço, então se você mantiver uma matriz que é atualizada a cada 15ms já está super bom. Manter o teclado em realtime é overkill.

Danjovic disse...

Responder ao Z80 é uma coisa e manter o teclado é outra. São tarefas diferentes.

A temporização da instrução IN do Z80 deve ser respeitada, caso contrário não tem como o circuito funcionar.

Já a matriz em RAM que reflete a matriz do teclado do MSX só é atualizada quando ocorre um evento no teclado PS/2.

Anônimo disse...

698 nanosegundos?! na minha opinião o tempo disponível para escrita está na casa dos milissegundos, algo em torno de 12 mS e não dos nanossegundos. Como pode ser visto no Tabajara labs o tempo disponível para escrita é o tempo em que as portas ABCD (saída) da PPI permanecem na configuração "1001" (repouso).

Danjovic disse...

Uma coisa é o tempo de resposta e outra coisa completamente diferente é o intervalo entre as leituras.

Imagine que você demora 1 hora pra sair da sua casa e chegar na escola, e que você faz uma prova por semestre.

Então no dia da prova, a partir do momento em que seu despertador tocar você tem somente 1 hora pra chegar na escola, senão você perdeu a prova. Suas respostas de nada valem na prova do próximo semestre.

Entendeu a diferença?