quinta-feira, 19 de junho de 2014

Emulador de teclado - Protótipo com AVR

Terminei de montar e conferir as ligções do protótipo do adaptador de teclado. Ainda faltam 2 capacitores para soldar e uma modificação para fazer a fim de poder usar o tanto o 74LS365 quanto o 74LS367 como buffer. É que o circuito foi projetado para o LS365 mas não consegui encontrar este chip. 

Emulador montado. Falta uma modificação para poder usar o LS367 no lugar do LS365.

O extensor não é necessário e só foi montado para o protótipo.
O bom de ter o protótipo em mãos é perceber alguns detalhes que devem ser corrigidos ou mesmo melhorados, como a ligação do botão de RESET que deveria ter passado por baixo da placa para não fechar curto com o metal da armação do botão.

Ali na placa, logo abaixo do 74HC74 tem um conector para o adaptador RS232 para testes e debug. O ATMega88 foi gravado com o bootloader do Arduino.

sexta-feira, 13 de junho de 2014

Rotina de interrupção em Assembly dentro do Arduino

A sintaxe do Assembly Inline do GCC é meio confusa, e isso me deu bastante trabalho para conseguir embutir um pouco de código Assembly dentro da IDE do Arduino. Mas finalmente consegui.

Eis o código para tratar a interrupção:

asm volatile ( 
   "in __tmp_reg__,__SREG__ \n\t"   // Salva registrador de Status  
   
   "ldi r26,lo8(mapa) \n\t"         // Ponteiro X = endereço de Keymap
   "ldi r27,hi8(mapa)\n\t"          //
   
   "ldi r18,0x1f \n\t"              // Mascara inicial => bits 0..5 em 1
   
   "in r19,%0 \n\t"                 // Amostra bits PB 0..5 (A8..A13)
   
   "ld r20,X+ \n\t"                 // R20 = Keymap[0] 
   "sbrs R19,0 \n\t"                // Bit [0] = 0 ? 
   "and r18,r20 \n\t"               // sim, copia os bits '0' de Keymap[0] para R18
   
   "ld r20,X+ \n\t"                 // R20 = Keymap[1] 
   "sbrs R19,1 \n\t"                // Bit [1] = 0 ? 
   "and r18,r20 \n\t"               // sim, copia os bits '0' de Keymap[1] para R18   
   
   "ld r20,X+ \n\t"                 // R20 = Keymap[2] 
   "sbrs R19,2 \n\t"                // Bit [2] = 0 ? 
   "and r18,r20 \n\t"               // sim, copia os bits '0' de Keymap[2] para R18
   
   "ld r20,X+ \n\t"                 // R20 = Keymap[3] 
   "sbrs R19,3 \n\t"                // Bit [3] = 0 ? 
   "and r18,r20 \n\t"               // sim, copia os bits '0' de Keymap[3] para R18   

   "ld r20,X+ \n\t"                 // R20 = Keymap[4] 
   "sbrs R19,4 \n\t"                // Bit [4] = 0 ? 
   "and r18,r20 \n\t"               // sim, copia os bits '0' de Keymap[4] para R18   

   "ld r20,X+ \n\t"                 // R20 = Keymap[5] 
   "sbrs R19,5 \n\t"                // Bit [5] = 0 ? 
   "and r18,r20 \n\t"               // sim, copia os bits '0' de Keymap[5] para R18

   "in r19,%1 \n\t"                 // Amostra bits PD 6..7 (A14..A15)

   "ld r20,X+ \n\t"                 // R20 = Keymap[6] 
   "sbrs R19,4 \n\t"                // Bit [6] = 0 ? 
   "and r18,r20 \n\t"               // sim, copia os bits '0' de Keymap[6] para R18   

   "ld r20,X+ \n\t"                 // R20 = Keymap[7] 
   "sbrs R19,5 \n\t"                // Bit [7] = 0 ? 
   "and r18,r20 \n\t"               // sim, copia os bits '0' de Keymap[7] para R18
   
   "out %2,r18 \n\t"                // Escreve resultado nos bits D0-D4 do LS365 e libera Wait State
   "nop \n\t"
   "sbi %2,5 \n\t"                  // libera flip flop
   
   "out __SREG__,__tmp_reg__ \n\t"  // restaura registrador de Status
    
   :: "I" (_SFR_IO_ADDR(PINB)), "I" (_SFR_IO_ADDR(PIND)) , "I" (_SFR_IO_ADDR(PORTC)) );
}


Cabe observar que eu tentei antes incluir uma função externa em assembly e incluir no projeto, mas o Arduino não compila.

Pelas minhas contas são necessários 32 ciclos de clock entre a interrupção e a liberação do Wait, o que significa 1,6us a 20MHz ou 4us a 8MHz (clock interno).

quarta-feira, 11 de junho de 2014

Tabelas de Scancodes para teclados ABNT

Com o auxílio de um Arduino e um pequeno 'sketch' levantei a tabela de scancodes para teclados ABNT pois não havia conseguido encontrar esta tabela em lugar algum.

Aproveitei o embalo e organizei os scancodes em uma tabela com  132 entradas correspondentes aos códigos das teclas entre 0 e 0x83.

O valor _NONE corresponde a um código que não tem ação. Para popular a tabela basta substituir a entrada _NONE pelo código correspondente à posição da tecla na matriz do teclado.

Tomando por exemplo a tecla "Q". Na matriz do TK90 ela corresponde ao bit 2 da linha 0 de varredura (conforme explicado no post anterior).

Assim, a linha da tecla "Q" fica:

0x02,      // 0x14  Q 


Para simplificar mais ainda, basta definir as constantes das teclas:


#define _Q    0x02  // Tecla  Q 

E na tabela usar o valor definido

_Q      // 0x14  Q 

Segue abaixo a tabela 'virgem', ou seja, não preenchida.


const uint8_t PS2Keymap_Normal[] PROGMEM = { 
_NONE,     // 0x00   
_NONE,     // 0x01  F9
_NONE,     // 0x02  
_NONE,     // 0x03  F5
_NONE,     // 0x04  F3
_NONE,     // 0x05  F1
_NONE,     // 0x06  F2
_NONE,     // 0x07  F12           
_NONE,     // 0x08                
_NONE,     // 0x09  F10           
_NONE,     // 0x0A  F8            
_NONE,     // 0x0B  F6            
_NONE,     // 0x0C  F4            
_NONE,     // 0x0D  TAB           
_NONE,     // 0x0E  APOSTROPHE    '
_NONE,     // 0x0F                
_NONE,     // 0x10                
_NONE,     // 0x11  L ALT         
_NONE,     // 0x12  L SHFT        
_NONE,     // 0x13  L CTRL        
_NONE,     // 0x14  Q             
_NONE,     // 0x15  1             
_NONE,     // 0x16                
_NONE,     // 0x17                
_NONE,     // 0x18                
_NONE,     // 0x19                
_NONE,     // 0x1A  Z             
_NONE,     // 0x1B  S             
_NONE,     // 0x1C  A             
_NONE,     // 0x1D  W             
_NONE,     // 0x1E  2             
_NONE,     // 0x1F                
_NONE,     // 0x20                
_NONE,     // 0x21  C             
_NONE,     // 0x22  X             
_NONE,     // 0x23  D             
_NONE,     // 0x24  E             
_NONE,     // 0x25  4             
_NONE,     // 0x26  3             
_NONE,     // 0x27                
_NONE,     // 0x28                
_NONE,     // 0x29  SPACE         
_NONE,     // 0x2A  V             
_NONE,     // 0x2B  F             
_NONE,     // 0x2C  T             
_NONE,     // 0x2D  R             
_NONE,     // 0x2E  5             
_NONE,     // 0x2F                
_NONE,     // 0x30                
_NONE,     // 0x31  N             
_NONE,     // 0x32  B             
_NONE,     // 0x33  H             
_NONE,     // 0x34  G             
_NONE,     // 0x35  Y             
_NONE,     // 0x36  6             
_NONE,     // 0x37                
_NONE,     // 0x38                
_NONE,     // 0x39                
_NONE,     // 0x3A  M             
_NONE,     // 0x3B  J             
_NONE,     // 0x3C  U             
_NONE,     // 0x3D  7             
_NONE,     // 0x3E  8             
_NONE,     // 0x3F                
_NONE,     // 0x40                
_NONE,     // 0x41  COMMA         ,
_NONE,     // 0x42  K             
_NONE,     // 0x43  I             
_NONE,     // 0x44  O             
_NONE,     // 0x45  0             
_NONE,     // 0x46  9             
_NONE,     // 0x47                
_NONE,     // 0x48                
_NONE,     // 0x49  DOT           .
_NONE,     // 0x4A  SEMICOLON     ;
_NONE,     // 0x4B  L             
_NONE,     // 0x4C  CCCEDIL       Ç
_NONE,     // 0x4D  P             
_NONE,     // 0x4E  MINUS         -
_NONE,     // 0x4F  
_NONE,     // 0x50  
_NONE,     // 0x51  SLASH         /
_NONE,     // 0x52  TILDE         ~
_NONE,     // 0x53  
_NONE,     // 0x54  ACUTE         `
_NONE,     // 0x55  EQUAL         =
_NONE,     // 0x56  
_NONE,     // 0x57  
_NONE,     // 0x58  CAPS
_NONE,     // 0x59  R SHFT
_NONE,     // 0x5A  ENTER
_NONE,     // 0x5B  OPENBRACKET   [
_NONE,     // 0x5C  
_NONE,     // 0x5D  CLOSEBRACKET  ]
_NONE,     // 0x5E  
_NONE,     // 0x5F  
_NONE,     // 0x60  
_NONE,     // 0x61  BACKSLASH     \
_NONE,     // 0x62  
_NONE,     // 0x63  
_NONE,     // 0x64  
_NONE,     // 0x65  
_NONE,     // 0x66  BKSP
_NONE,     // 0x67  
_NONE,     // 0x68  
_NONE,     // 0x69  KP1
_NONE,     // 0x6A  
_NONE,     // 0x6B  KP4
_NONE,     // 0x6C  KP7
_NONE,     // 0x6D  KPDOT         .
_NONE,     // 0x6E  
_NONE,     // 0x6F  
_NONE,     // 0x70  KP0
_NONE,     // 0x71  KPCOMMA       ,
_NONE,     // 0x72  KP2
_NONE,     // 0x73  KP5
_NONE,     // 0x74  KP6
_NONE,     // 0x75  KP8
_NONE,     // 0x76  ESC
_NONE,     // 0x77  NUM
_NONE,     // 0x78  F11
_NONE,     // 0x79  KPPLUS        +
_NONE,     // 0x7A  KP3
_NONE,     // 0x7B  KPMINUS       -
_NONE,     // 0x7C  KPTIMES       *
_NONE,     // 0x7D  KP9
_NONE,     // 0x7E  SCROLL
_NONE,     // 0x7F  
_NONE,     // 0x80  
_NONE,     // 0x81  
_NONE,     // 0x82  
_NONE      // 0x83  F7
};  
  

Pode-se replicar esta tabela para as teclas ativadas com a  tecla SHIFT, como por exemplo as teclas sobre os números e as teclas de acentuação, colhetes, etc

Também é possível utilizar uma tabela para as teclas de códigos estendidos (como as setas de cursor), mas como estas teclas não são muitas é mais fácil tratar cada uma caso a caso. O mesmo se aplica à tecla F7. Caso esta tecla seja tratada separadamente dá para deixar a tabela com menos de 128 entradas.

       

domingo, 8 de junho de 2014

Placa para o Emulador de teclado com AVR (atualizado)

Uma vez que o gerador de /WAIT funcionou corretamente, projetei uma placa de circuito impresso para o circuito, com as seguintes funcionalidades:
  1. Botão de Reset (que reseta não só o Z80 mas também o AVR)
  2. Header Serial para a reprogramação do circuito (com suporte a Auto-Programação, como no Arduino Severino)
  3. Montagem em conector EDGE ou com headers para encaixar na Interface M1 do Edu Luccas (assim que pretendo utilizar a minha). Nesse caso tem que serrar a parte de baixo da placa e soldar os headers diretamente nas ilhas.
  4. Jumper para habilitar/desabilitar a interface
  5. Três opções para montagem do conector para o teclado:
  • Mini-DIN 6 pinos
  • USB (alguns teclados revertem para o modo PS/2 quando não conseguem estabelecer comunicação via USB)
  • Header para montagem do conector para painel (esta opção vai ser útil para montagem dentro da M1). 

Eu cheguei a pensar em manter apenas o conector USB pois o conector Mini-DIN é mais caro e mais difícil de ser encontrado. Porém mas acabei deixando desse jeito mesmo.

Também pensei (caso dispensasse o mini-din) foi colocar um bootloader USB e usar um cabo USB A-A para reprogramar o circuito usando um bootloader baseado em V-USB compatível com USBASP, mas acabei deixando a serial mesmo, por conta da facilidade que ela traz na hora de se fazer o Debug do circuito.




A placa ficou com aproximadamente 76 x 55 mm

As fotos abaixo são da placa do protótipo assim que terminou de ser corroída.



Protótipo para o emulador de teclado com AVR

Definidos os componentes básicos desenhei um circuito e comecei a montagem em proto-board para seguir com o desenvolvimento do emulador de teclado.

Montagem inicial em proto-board
Inicialmente conectei apenas a alimentação e os sinais A0, /RD, /IORQ, /WAIT e M1 a fim de testar o gerador de WAIT. Como as linhas de dados ainda não estavam conectadas, o AVR foi programado via SPI.

Protótipo ligado ao TK para monitorar as formas de onda
Com o protótipo montado registrei as formas de onda abaixo:

Em primeiro lugar seguem as formas de onda originais. A ULA do TK gera um sinal de seleção para o 74LS365 que lê o teclado (e o cassete). O atraso na decodificação em relação ao sinal /IORQ é de aproximadamente 40ns

Laranja: Pino 1 do 74LS365 / Azul: /IORQ

  O tempo total entre a decodificação do sinal de leitura e a borda de descida do sinal de clock ao final do tempo T3 é de 600ns.

Laranja: Pino 1 do 74LS365 / Azul: Clock do Z80

Durante o funcionamento normal o TK faz a leitura das 8 linhas do teclado. A linha azul na forma de onda abaixo já é a decodificação externa usando o 74HC138.

Laranja: Pino 1 do 74LS365 / Azul: /ULARD
O tempo gasto na decodificação do sinal /ULARD é de apenas 25ns, um pouco menos do que a ULA.

Laranja: /ULARD / Azul: /IORQ

O tempo que se ganhou na decodificação também pode ser visto na forma de onda abaixo, onde o tempo entre a descida do sinal /ULARD e a borda de descida do ciclo T3 aumentou de 600 para 630ns.
Laranja: Pino /ULARD / Azul: Clock do Z80
Aforma de onda abaixo mostra os tês sinais juntos
Laranja: /ULARD / Azul: /IORQ / Cinza: Clock Z80

O AVR foi programado para atender à interrupção gerada pelo sinal /ULARD e enviar um pulso negativo ao gerador de WAIT. O AVR estava rodando a 20MHz mas por se tratar de prova de conceito nenhuma otimização foi utilizada ainda.

ISR(INT0_vect)
{
release();   
}

A figura abaixo contém a superposição de vários sinais com a mesma referência a fim de resumir o funcionamento do circuito.

Se tomarmos como referência a forma de onda WAIT (OUT) é possível notar que o AVR está demorando por volta de 2,5us para atender a interrupção (5 divisões de 500ns). Como a velocidade de clock é de 20MHz, dá pra se notar que o AVR está executando 50 instruções (50 x 50ns = 2500ns), o que nem é tanto assim. O sinal de WAIT gerado é aplicado ao Z80 através de um diodo (só é possível forçar o nível zero na linha). Por isso a linha demora um pouco a subir. Mas note que quando ela sóbe só acontece mais um ciclo de clock, que é exatamente o ciclo T3. Logo em seguida o Z80 libera as linhas de endereço e de controle e com isso o sinal /ULARD retorna a nível alto e o Z80 já começa a buscar a próxima instrução.

Ciclo de funcionamento do circuito com o gerador de WAIT

A título de comparação eis as mesmas formas de onda, porém sem WAIT gerado pelo circuito.










sábado, 7 de junho de 2014

Escolhendo um bootloader para o ATMega88

Como os pinos de programação do ATMega88 vão estar conectados às linhas de endereço do Z80 fica difícil fazer o desenvolvimento usando o modo de programação ISP do microcontrolador. 

Para contornar este problema e também para ajudar no debug a solução é usar a comunicação serial do microcontrolador tanto para a programação quanto para o Debug. 

Eu escolhi utilizar um bootloader compatível com o Arduino porque assim consigo aproveitar algumas coisas que prototipei num Arduino UNO usando a biblioteca PS/2. Por enquanto vou utilizando esta biblioteca no desenvolvimento.

Como estou utilizando um ATMega88 eu encontrei um bootloader para ele que funcionou direitinho dentro da IDE do Arduino (link).

Emulador de Teclado com AVR para os TKs

Como o gerador de Wait State funcionou bem, já é possível delinear um emulador de teclado para o TK usando apenas 4 chips. 


Se considerarmos que para conectar um um teclado externo no TK já é necessário utilizar um decodificador e um buffer, então dá pra ver que este circuito já está beirando o limite da simplicidade. Talvez seja possível utilizar um PIC que possua uma PSP (como o 18F4520) e deixar de usar o buffer, daí a interface fica com apenas 3 CIs, mas não creio que a diferença de preço compense.

Eu estou considerando usar um ATMega88, porém qualquer microcontrolador com pelo menos 17 pinos consegue dar conta do recado.

Os sinais necessários são:
A[8..15] - (8 pinos) para a seleção de linha
D[0..4] - (5 pinos) para a leitura das teclas de cada linha (via buffer)  
PS2CLK e PS2DAT - (2 pinos) para interface com o teclado PS/2
/ULARD - (1 pino) Interrupção externa.   
RELEASE - (1 pino) para destravar o gerador de Wait State