ArduinoUNOでPS/2キーボードを使う
昔のPC用のキーボードは同期シリアル通信なのでAVRなら簡単に接続できると思ってやってみた。
接続
普通に同期通信で 8O1 (スタートビット1、データ8、奇数パリティ、ストップビット1
Data,Clockともにオープンコレクタで4.7KΩでプルアップされている
キーボードとArduinoUNOの接続
Data -> PIN0 (RX)
Clock -> PIN4 (XCK)
電源を忘れずに
キーボードからの受信
最初まじめにレジスタを設定したけど、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を無効有効切り替えたりちょっと面倒な手続きが必要。
- クロック、データが共にHIGHになるまで待つ
- USARTを無効にする
- マイコンからClockをLOWにする
- マイコンからDataをLOWにする(start bit)
- マイコンからClockをHIGHにする
- キーボードがClockをLOWにするので順にDataへ書き込む
- 5を8回繰り返したらパリティ
- ストップビット
- キーボードから受信完了としてもう一回クロックが来る
- USARTを有効にしてキーボードからの返信を待つ
キーボードに対するコマンド(一部)
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に一行加えるだけで簡単にできる、読み出しデータがスキャンコードなので少々癖があるがキー数の多い簡単入力デバイスとして、ちょっとした工作や実験には便利。