quarta-feira, fevereiro 06, 2019

Usando um Teclado PS/2 com um Arduino

Um projeto que está na minha lista há algum tempo é um mini terminal baseado no Arduino para uso com os computadores básicos que eu montei com o Z80 e 6502. Para o display vou usar um LCD alfanumérico de 4 linhas de 40 caracteres; para o teclado a ideia é usar um teclado de PC, o que nos traz a este post.


Um Pouco de História

O teclado do PC IBM original utilizou um conector DIN de 5 pinos e uma comunicação serial unidirecional (do teclado para o PC) usando dois sinais (dado e clock). No PC AT o conector foi mantido, porém o protocolo foi alterado para permitir comunicação bidirecional. Por último, nos computadores PS/2 o conector foi trocado por um DIN de 6 pinos de dimensão menor.


Porque Usar Um Teclado PS/2?

A interface e o protocolo são simples (principalmente comparados com USB) e ainda se encontra uma quantidade grande destes teclados em boas condições.  O conector DIN fêmea  pode dar um pouco de trabalho para achar (eu comprei no eBay), mas sempre existe a opção de cortar a ponta do cabo e colocar um conector diferente.

A Interface Elétrica e o Protocolo

Do ponto de vista elétrico temos apenas dois sinais (fora a alimentação e terra): Dado e Clock. Estes sinais são do tipo "coletor aberto", ou seja, normalmente estão flutuando (pullups garantem um nível alto) e podem ser colocados em nível baixo por qualquer um dos lados.

Na situação de repouso os dois sinais estão "soltos" e portanto em nível alto.

Para enviar um byte, o teclado inicia a transmissão conferindo que o clock está no nível alto e colocando o sinal de dados em nível baixo. A partir daí são enviados 11 bits (a começar por este zero); o sinal de dados é posicionado enquanto o clock está alto e depois o teclado força o clock para baixo para indicar que o bit pode ser lido. Os 11 bits correspondem a um start (sempre 0), 8 bits de dados, 1 bit de paridade (impar) e um stop (sempre 1).

O teclado envia, na maioria dos casos, um byte quando uma tecla é apertada (o scancode). Quando a tecla é solta, ele envia dois bytes: F0 e o scancode. Algumas teclas (extended keys) geram dois bytes quando apertadas (E0 scancode) e três quando soltas (E0,F0 scancode).

Para o PC (ou outro host) enviar dados para o teclado, ele deve primeiro segurar o clock em nível baixo, para inibir transmissões pelo teclado. Em seguida o sinal de dados deve ser colocado em nível baixo (start) e o sinal de clock deve ser solto. A partir daí a linha de clock será controlada pelo teclado, que a colocará em nível baixo quando o PC deve controlar a linha de dados. O PC enviará 11 bits (start, dados, paridade, stop) e em seguida aguardar um bit de confirmação do teclado.


Referência (e origem das figuras acima): https://www.avrfreaks.net/sites/default/files/PS2%20Keyboard.pdf

Software para o Arduino

Uma biblioteca simples para  tratar a conexão de um teclado PS/2 ao Arduino é descrita nem https://playground.arduino.cc/Main/PS2Keyboard, e o código mais recente está em https://github.com/PaulStoffregen/PS2Keyboard.

Uma biblioteca mais sofisticada pode ser vista em https://github.com/techpaul/PS2KeyAdvanced.

O princípio básico de ambas é o mesmo: o sinal de clock é conectado a um pino do Arduino capaz de gerar uma interrupção quando o sinal passar do nível alto para o baixo. Na PS2Keyboard é tratado apenas a recepção e a rotina de interrupção lê o sinal de dados e monta o código recebido. A PS2KeyAdavanced suporta comunicação bidirecional. Portanto a rotina de interrupção irá ler ou posicionar o sinal de dados conforme a operação.

Uma vez recebido um código do teclado, é preciso decodificá-lo, tomando o cuidado de manter o estado das teclas modificadoras (shift, caps lock, control e alt).

Para um teste rápido, vamos fazer as seguintes ligações entre um teclado PS/2 e um Arduino Uno ou Nano:
  • Clock do teclado no pino 3 do Arduino
  • Dado do teclado no pino 2 do Arduino
  • +5V do teclado no +5V do Arduino
  • GND do teclado no GND do Arduino
O código abaixo se limita a pegar e mostrar os bytes recebidos:
/*
 * Teste de interface com teclado PS/2
 * 
 * Dados do teclado no pino 2 e clock no pino 3
 */

// fila para os códigos recebidos
const byte tamfila = 16;
byte poe = 0;
byte tira = 0;
byte fila[tamfila];

void setup() {
  // iniciar os pinos
  pinMode (2, INPUT_PULLUP);
  pinMode (3, INPUT_PULLUP);
  pinMode (13, OUTPUT);
  digitalWrite(13, LOW);
  // vamos mostrar os códigos na serial
  Serial.begin(9600);
  Serial.println("Pronto");
  // assumir a interrupção do sinal de clock
  attachInterrupt(digitalPinToInterrupt(3), kbdint, FALLING);
}

void loop() {
  // verifica se tem algum código na fila
  if (poe != tira) {
    // mostra
    Serial.println (fila[tira], HEX);
    // tira da fila
    byte prox = tira+1;
    if (prox == tamfila) {
      tira = 0;
    } else {
      tira = prox;
    }
  }
}

// Trata a interrupção do teclado
void kbdint(void) {
  static byte cod = 0;
  static byte nbit = 0;

  // le o bit de dados
  int dado = digitalRead(2);

  // despreza start, paridade e stop
  if ((nbit > 0) && (nbit < 9)) {
    cod = cod >> 1;
    if (dado == HIGH) {
      cod |= 0x80;
    }
  }
  nbit++;
  if (nbit == 11) {
    // coloca na fila
    fila[poe] = cod;
    byte prox = poe+1;
    if (prox == tamfila) {
      prox = 0;
    }
    if (prox != tira) {
      poe = prox;
    }
    nbit = cod = 0;
  }
}

08/02/20: Corrigida a figura com a pinagem dos conectores.
19/05/21: Corrigida a conexão ao Arduino (dado e clock estavam invertidos)

3 comentários:

Olicheski disse...

Belo post. Mas a busca que estou fazendo e o que me pergunto se é possível, é se, poderíamos fazer o Arduino trabalhar como um teclado. Quero fazer um hack para jogos, onde de tempos em tempos, o Arduino "apertaria" certa tecla do PC. Não muda nada em sua busca,mas sigo na minha. =D Belo blog.

Daniel Quadros disse...

Olicheski, nunca vi isto, mas com certeza dá para simular um teclado PS/2 com um Arduino. Se você quiser simular um teclado USB, de uma procurada por "usb rubber ducky arduino". Eu já fiz uma experiência usando um microcontrolador ATtiny85 para "digitar" as leituras do ADC: http://dqsoft.blogspot.com/2012/08/attiny-4585-ligado-usb.html e http://dqsoft.blogspot.com/2012/09/attiny-4585-ligado-usb-colocando-para.html.

Anônimo disse...

Isso é verdade.