sábado, 21 de junho de 2014

Emulador de Teclado - fotos e testes com o protótipo

Fiz hoje alguns testes com o emulador de teclado conectado ao TK.


Alguns problemas se apresentaram, a começar pela contenção da linha A13 por parte do AVR (vide post anterior).

Outro problema que eu tive foi com a rotina de interrupção em assembly. Em alguns momentos eu notava que o AVR parava de responder às interrupções e o TK travava com o sinal /wait em nível baixo. Por enquanto retornei o atendimento à interrupção de leitura de teclado para uma interrupção padrão do Arduino e mesmo com o overhead o TK parou de travar.

Ainda estou com dois problemas que tenho que resolver. Um deles é que de vez em quando o TK demora a iniciar ou não inicia, mas isso não é mais problema de contenção. Desconfio que ainda esterja relacionado ao acionamento incorreto do sinal que libera a linha de /WAIT.

Outro problema é que apesar da matriz estar sendo preenchida corretamente quando se pressionam as teclas (o circuito imprime o estado dos bits na serial) nenhum bit muda de estado na entrada do LS367. Vou precisar debugar esta rotina em separado (fora da interrupção) para descobrir o problema.

Por último, pode ser que eu precise colocar uns resistores em série com as linhas D0 a D4 do LS365 interno do TK a fim de evitar a contenção do barramento de dados quando o LS367 do emulador de teclado estiver ativo.




Treta no A13

Como nada nessa vida é fácil, a primeira vez que liguei o circuito do emulador de teclado no TK ele não inicializou.

Passei um tempo conferindo o circuito, retirei o decodificador e o buffer da placa, mas a coisa continuava na mesma.

Daí foi que lembrei que eu não tinha programado o AVR para colocar como entrada os pinos que vão ligados às linhas de endereço A8-A15! Eu comprovei isso retirando o AVR da placa.

Depois de um bom tempo procurando uma fonte de alimentação que servisse no meu adaptador RS232-TTL eu finalmente programei o AVR com o seguinte 'sketch':

void setup() {
  // put your setup code here, to run once:
  DDRB=0x00;
  PORTB=0xff;
  DDRC=0x00;
  PORTC=0x00;
  DDRD=0x00;
  PORTD=0xc0;
}

void loop() {
  // put your main code here, to run repeatedly: 
  
}

Daí o TK bootou!!

Mas como diz o meu amigo Ferraz, "nessa vida tudo tem que ser com dor", só o primeiro boot deu certo. No segundo o TK ficou com a tela cheia de artefatos, no terceiro ficou branca, no quarto ficou ok. Em resumo, a operação estava de forma intermitente!

Tentei em vão descobrir algo de errado com o osciloscópio mas sem muito sucesso. Daí segui um expediente que embora trabalhoso foi o que me levou a descobrir o problema.

Eu retirei todas as conexões das linhas A8-A15 e espetei a placa no TK. Funcionou (óbvio ne?).

Daí eu removi a placa e fiz a ligação do sinal A8. Recoloquei a placa no TK e funcionou.

Fiz o mesmo com as outras linhas até chegar na linha A13. Com a linha A13 no lugar o TK fica funcionandode modo intermitente.

Removi a ligação da linha A13 e continuei o procedimento com as linhas A14 e A15 . Tudo OK.

De fato tinha alguma treta na linha A13! Esta linha está ligada ao pino PB5 do AVR (sinal SCK).


Como o AVR tem um bootloader, desconfiei que alguma coisa poderia estar acontecendo durante o boot. Daí instalei um resistor de 4K7 entre a linha A13 e o pino PB5 do AVR e o TK bootou normalmente (todas as vezes ).

Medindo com o osciloscópio eu verifiquei que o AVR mantém esta linha em nível baixo por um pouco mais de um segundo durante o 'startup' do bootloader. Eu creio que isso seja um erro de implementação do bootloader, mas por enquanto vou deixar o resistor e prosseguir com os testes. Depois eu dou uma olhada no código fonte do bootloader e/ou entro em contato com o desenvolvedor, pois não considero isso um comportamento normal.

Laranja: PB5 do AVR  / Azul: A13 do Z80




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.