Help us understand the problem. What is going on with this article?

ArduinoUNOでPS/2キーボードを使ったメモ

ArduinoUNOでPS/2キーボードを使う

昔のPC用のキーボードは同期シリアル通信なのでAVRなら簡単に接続できると思ってやってみた。

接続

普通に同期通信で 8O1 (スタートビット1、データ8、奇数パリティ、ストップビット1
Data,Clockともにオープンコレクタで4.7KΩでプルアップされている
キーボードとArduinoUNOの接続
Data -> PIN0 (RX)
Clock -> PIN4 (XCK)
電源を忘れずに

SANY0339-2.jpg

キーボードからの受信

最初まじめにレジスタを設定したけど、Serial.beginで初期設定後に同期通信に変更するだけで受信割り込みも含めて使用できる。

  Serial.begin(9600, SERIAL_8O1); //初期設定はおまかせして
  UCSR0C |= (1 << UMSEL00);   //同期通信モードへ変更

/*
 ワイヤードOR接続のおまじない、Input時はハイインピーダンス
  Output時はLOWになる 
*/
  pinMode(0, INPUT);
  pinMode(4, INPUT);

  digitalWrite(0, LOW);
  digitalWrite(4, LOW);  

読み出しは普通にSerial.Read()が使える、データはキートップに印字された文字では無くスキャンコードと呼ばれるデータ、用途によっては変換が必要

uint8_t  get_usart() {
  uint8_t c;
  while (Serial.available() == 0)
    ;
  c = (0x00ff & Serial.read());
  return c;

}

キーボードへの送信

パワーオンリセット以外でリセットする場合や、キーボードのLEDをコントロールする場合はUSARTを無効有効切り替えたりちょっと面倒な手続きが必要。

  1. クロック、データが共にHIGHになるまで待つ
  2. USARTを無効にする
  3. マイコンからClockをLOWにする
  4. マイコンからDataをLOWにする(start bit)
  5. マイコンからClockをHIGHにする
  6. キーボードがClockをLOWにするので順にDataへ書き込む
  7. 5を8回繰り返したらパリティ
  8. ストップビット
  9. キーボードから受信完了としてもう一回クロックが来る
  10. USARTを有効にしてキーボードからの返信を待つ

image.png

キーボードに対するコマンド(一部)

1 2 3
Reset 0xFF リセット
LED 0xED 次データでLEDの転倒消灯bit2=CapsLock,bit1=NumLock,bit0=ScrollLock

主な返信コード

1 2 3
Ack 0xFA 正常受信
Resend 0xFE データ再送要求
#define CLK_LO  pinMode(4,OUTPUT);
#define CLK_HI  pinMode(4,INPUT);
#define DATA_LO pinMode(0,OUTPUT);
#define DATA_HI pinMode(0,INPUT);
#define WAIT_CLK_LO while(digitalRead(4) == HIGH);
#define WAIT_CLK_HI while(digitalRead(4) == LOW);

#define DELAY 50


uint8_t send_data(uint8_t data) {
  int shift = 1;
  uint8_t parity = 0;
  uint8_t rcv;


  WAIT_CLK_HI;   //busy?
  delayMicroseconds(DELAY);

  TIMSK1 &= ~(1 << OCIE1A); //timer off
  UCSR0B &= ~(1 << RXEN0); //usart off
  UCSR0C &= ~(1 << UMSEL00);

  CLK_LO;                 //RTS
  delayMicroseconds(DELAY);

  DATA_LO; //start bit
  delayMicroseconds(DELAY);
  CLK_HI;

  WAIT_CLK_LO;          //キーボード発生のクロック待ち
  delayMicroseconds(DELAY / 2);

  while (shift != 0x100) {

    if (data & shift) {
      DATA_HI;  //bit = 1
      parity++;
    } else {
      DATA_LO  //bit = 0
    }
    WAIT_CLK_LO;
    WAIT_CLK_HI;
    delayMicroseconds(DELAY / 2);
    shift <<= 1;
  }
  //parity
  if (parity & 0x01) {
    DATA_LO;  //bit = 1
  } else {
    DATA_HI;  //bit = 0
  }
  WAIT_CLK_LO;
  WAIT_CLK_HI;
  delayMicroseconds(DELAY/2);

  DATA_HI; //stop bit
  WAIT_CLK_LO;
  WAIT_CLK_HI;
  WAIT_CLK_LO;
  WAIT_CLK_HI;

  UCSR0B &= ~(1 << RXCIE0); // intrrupt off
  UCSR0B |= (1 << RXEN0); //usart on
  UCSR0C |= (1 << UMSEL00);
  while (!(UCSR0A & (1 << RXC0))); //キーボードから応答待ち
  rcv = UDR0;
  UCSR0B |= (1 << RXCIE0); // intrrupt on

  TIMSK1 |= (1 << OCIE1A);
  return rcv;
}

まとめ

受信だけならSerial.beginに一行加えるだけで簡単にできる、読み出しデータがスキャンコードなので少々癖があるがキー数の多い簡単入力デバイスとして、ちょっとした工作や実験には便利。

参考にしたサイト
http://ioiodesu.web.fc2.com/PS2/PS2.HTML

Shigosen
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away