フォト
2017年3月
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
無料ブログはココログ

« IchigoJamでポケモンキーボードを使ってみる(Bluetooth・PS/2変換) | トップページ | IchigoJamでLogicool製ワイヤレスキーボードを利用する(USB・PS/2変換) その2 »

2016年11月13日 (日)

IchigoJamでLogicool製ワイヤレスキーボードを利用する(USB・PS/2変換)

前回のポケモンキーボードのスケッチを修正して一般的なUSBキーボードに対応しました。
USBワイヤレスキーボードをPS/2インターフェースに変換します。


利用したキーボードはLogicool  K270です。
以前 amazonで1,290円で購入したのですが、今は値上がりしてますね。

  Dscn6124

IchigoJamはaitendoのai.Jam(ファームウェア V1.2.1)を使っています。
純正品IchigoJam Tと互換性があります。

IchigoJamとの接続はArduino pro miniのGND、VCCの他、
A4、A5ピンをPS/2用の通信に利用し、IchigoJamのKBD1、KBD2に接続しています。

Arduino pro miniではA4、A5端子がボード上の変なところにあります。
これはこれで、場合によっては接続しやすいです。

  Dscn6121

動いている様子

 

システム構成的な図

Photo

USB HIDに対応しています。有線キーボードも利用出来ます。

処理としては、図のようなハードウェア&ソフトウェア構成となっています。
USB HIDのキーボード入力データをPS/2キーボードデータと信号に変換して
IchigoJamのPS/2キーボードとして利用出来るようにしています。


利用しているハードウェア

1)Arduino pro mini 3.3V 8MHz版(上)とUSB Hostシールド

  Dscn6127

VCCに3.3Vを供給するのであれば、5V 16MHz版のArduino pro mini も利用出来ます。

私は最終的にIchigoJamのUSBコネクタに繋げる予定で、5V=>3.3Vの電圧変換が出来る
3.3V版を利用しています。

制約事項
・USBポートからの電源供給は3.3Vで電流もあまり流せません。
 動かないキーボードもあると思います。
・現状、マルチメディアキーには対応していません。
・起動直後、なぜか、IchigoJamに"@"文字が送られていまいます。
  IchigoJamとのPS/2キーボード接続のイニシャル処理でIchigoJamがコマンドではなく
  文字として認識しているようです。
 接続完了の目印になるので、これは放置しておきます。
 

スケッチ

  スケッチを公開します。
  ダウンロード USBKBD2PS2.zip (3.2K) (2016/11/19 バグがあり差し換えました)

  ※ 2016/11/18 追記 最新版はgithubにて公開しています。
       https://github.com/Tamakichi/Arduino_USBToPS2

コンパイルはArduino IDE 1.6.12を利用しています。

本スケッチの他に別途、下記のライブラリが必要です。

  ・PS2 mouse interface for  Arduino
     http://playground.arduino.cc/ComponentLib/Ps2mouse
  ・USB_Host_Shield_2.0
     https://github.com/felis/USB_Host_Shield_2.0

スケッチの全ソース
//
// USBKBD2PS2 USBキーボード PS/2キーボード変換 for IchigoJam
// 作成者 たま吉さん
// 作成日 2016/11/11
// 修正日 2016/11/19 OnKeyUp()のバグ修正 
//
// このスケッチの利用には以下のハードウェア(シールド)が必要です.
//  ・USB Host Shield
// 
// このスケッチのコンパイルには以下のライブラリが必要です.
//  ・PS/2デバイス ps2dev(ps2dev.zip) - an interface library for ps2 host 
//    PS2 mouse interface for Arduino(http://playground.arduino.cc/ComponentLib/Ps2mouse)
//  ・USB_Host_Shield_2.0 (https://github.com/felis/USB_Host_Shield_2.0)
//

#include <ps2dev.h>
#include <hidboot.h>
#include <usbhub.h>

#define KB_CLK    A4  // IchigoJamのKBD1に接続
#define KB_DATA   A5  // IchigoJamのKBD2に接続

PS2dev keyboard(KB_CLK, KB_DATA);   // PS/2デバイス

// HID Usage ID (0x04 - 0x67) => PS/2 スキャンコード 変換テーブル
const uint8_t keytable1[] PROGMEM = {
  0x1C,0, 0x32,0, 0x21,0, 0x23,0, 0x24,0, 0x2B,0, 0x34,0, 0x33,0, // 0x04 - 0x0B
  0x43,0, 0x3B,0, 0x42,0, 0x4B,0, 0x3A,0, 0x31,0, 0x44,0, 0x4D,0, // 0x0C - 0x13
  0x15,0, 0x2D,0, 0x1B,0, 0x2C,0, 0x3C,0, 0x2A,0, 0x1D,0, 0x22,0, // 0x14 - 0x1B
  0x35,0, 0x1A,0, 0x16,0, 0x1E,0, 0x26,0, 0x25,0, 0x2E,0, 0x36,0, // 0x1C - 0x23
  0x3D,0, 0x3E,0, 0x46,0, 0x45,0, 0x5A,0, 0x76,0, 0x66,0, 0x0D,0, // 0x24 - 0x2B
  0x29,0, 0x4E,0, 0x55,0, 0x54,0, 0x5B,0, 0x5D,0, 0x5D,0, 0x4C,0, // 0x2C - 0x33
  0x52,0, 0x0E,0, 0x41,0, 0x49,0, 0x4A,0, 0x58,0, 0x05,0, 0x06,0, // 0x34 - 0x3B
  0x04,0, 0x0C,0, 0x03,0, 0x0B,0, 0x83,0, 0x0A,0, 0x01,0, 0x09,0, // 0x3C - 0x43
  0x78,0, 0x07,0, 0x00,2, 0x7E,0, 0x00,3, 0x70,1 ,0x6C,1, 0x7D,1, // 0x44 - 0x4B
  0x71,1, 0x69,1, 0x7A,1, 0x74,1, 0x6B,1, 0x72,1 ,0x75,1, 0x77,0, // 0x4C - 0x53
  0x4A,1, 0x7C,0, 0x7B,0, 0x79,0, 0x5A,1, 0x69,0, 0x72,0, 0x7A,0, // 0x54 - 0x5B
  0x6B,0, 0x73,0, 0x74,0, 0x6C,0, 0x75,0, 0x7D,0, 0x70,0, 0x71,0, // 0x5C - 0x63
  0x61,0, 0x2F,1, 0x37,1, 0x0F,0,                                 // 0x64 - 0x67
};

// HID Usage ID (0x87 - 0x94) => PS/2 スキャンコード 変換テーブル
const uint8_t keytable2[] PROGMEM = {
  0x51,0, 0x13,0, 0x6A,0, 0x64,0, 0x67,0, 0x27,0, 0x00,0, 0x00,0, // 0x87 - 0x8E
  0x00,0, 0xF2,0, 0xF1,0, 0x63,0, 0x62,0, 0x5F,0,                 // 0x8F - 0x94
};

//
// HIDキーボード レポートパーサークラスの定義
//
class KbdRptParser : public KeyboardReportParser {
  protected:
    virtual void OnControlKeysChanged(uint8_t before, uint8_t after);
    virtual void OnKeyDown(uint8_t mod, uint8_t key);
    virtual void OnKeyUp(uint8_t mod, uint8_t key);
    virtual void OnKeyPressed(uint8_t key) {};
};

//
// キー押しハンドラ
// 引数
//  mod : コントロールキー状態
//  key : HID Usage ID 
//
void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key) {

  Serial.print(F("DN ["));  Serial.print(F("mod="));  Serial.print(mod,HEX);
  Serial.print(F(" key="));  Serial.print(key,HEX);  Serial.println(F("]"));

  // HID Usage ID から PS/2 スキャンコード に変換
  uint8_t code = 0;
  uint8_t pre;
 
  if (key >= 0x04 && key <= 0x67) {
    code = pgm_read_byte(keytable1 + (key - 0x04)*2);
    pre = pgm_read_byte(keytable1 + (key - 0x04)*2 + 1);
  } else if (key >= 0x87 && key <= 0x94) {
    code = pgm_read_byte(keytable2 + (key - 0x87)*2);    
    pre = pgm_read_byte(keytable2 + (key - 0x87)*2 + 1);    
  }

  //Serial.print(F("PS2 ["));  Serial.print(F("code="));  Serial.print(code,HEX);
  //Serial.print(F(" pre="));  Serial.print(pre,DEC);  Serial.println(F("]"));
  
  // PS/2キーの発行
  if (pre == 0) {
    keyboard.write(code);    
  } else if (pre == 1) {
    keyboard.write(0xE0);
    keyboard.write(code);    
  } else if (pre == 2) { // Breakキー
    keyboard.write(0xE0);
    keyboard.write(0x12);
    keyboard.write(0xE0);
    keyboard.write(0x7C);
  } else if (pre == 3) { // Pauseキー
    keyboard.write(0xE1);
    keyboard.write(0x14);
    keyboard.write(0x77);
    keyboard.write(0xE1);
    keyboard.write(0xF0);    
    keyboard.write(0x14);
    keyboard.write(0xF0);    
    keyboard.write(0x77);
  }
}

//
// コントロールキー変更ハンドラ
// SHIFT、CTRL、ALT、GUI(Win)キーの処理を行う
// 引数 before : 変化前のコード USB Keyboard Reportの1バイト目
//      after  : 変化後のコード USB Keyboard Reportの1バイト目
//
void KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) {
  MODIFIERKEYS beforeMod;
  *((uint8_t*)&beforeMod) = before;

  MODIFIERKEYS afterMod;
  *((uint8_t*)&afterMod) = after;

  // 左Ctrlキー
  if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl) {
    if (afterMod.bmLeftCtrl) {
      keyboard.write(0x14);  // 左Ctrlキーを押した
    } else {
      keyboard.write(0xF0);  // 左Ctrltキーを離した       
      keyboard.write(0x14);
    } 
  }

  // 左Shiftキー
  if (beforeMod.bmLeftShift != afterMod.bmLeftShift) {
    if (afterMod.bmLeftShift) {
      keyboard.write(0x12);  // 左Shiftキーを押した
    } else {
      keyboard.write(0xF0);  // 左Shiftキーを離した       
      keyboard.write(0x12);  // 
    }
  }

  // 左Altキー
  if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt) {
    if (afterMod.bmLeftAlt) {
      keyboard.write(0x11);  // 左Altキーを押した
    } else {
      keyboard.write(0xF0);  // 左Altキーを離した       
      keyboard.write(0x11);  // 
    }
  }

  // 左GUIキー(Winキー)
  if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI) {
    if (afterMod.bmLeftGUI) {
      keyboard.write(0xE0);  // 左GUIキーを押した
      keyboard.write(0x1F);
    } else {
      keyboard.write(0xE0);  // 左GUIキーを離した       
      keyboard.write(0xF0); 
      keyboard.write(0x1F); 
    }
  }

  // 右Ctrlキー
  if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl) {
    if (afterMod.bmRightCtrl) {
      keyboard.write(0xE0);  // 右Ctrlキーを押した
      keyboard.write(0x14);  
    } else {
      keyboard.write(0xE0);  // 右Ctrlキーを離した 
      keyboard.write(0xF0);       
      keyboard.write(0x14);
    }
  }

  // 右Shiftキー
  if (beforeMod.bmRightShift != afterMod.bmRightShift) {
    if (afterMod.bmRightShift) {
      keyboard.write(0x59);  // 右Shiftキーを押した
    } else {
      keyboard.write(0xF0);  // 右Shiftキーを離した       
      keyboard.write(0x59); 
    }
  }

  // 右Altキー
  if (beforeMod.bmRightAlt != afterMod.bmRightAlt) {
    if (afterMod.bmRightAlt) {
      keyboard.write(0xE0);  // 右Altキーを押した
      keyboard.write(0x11); 
    } else {
      keyboard.write(0xE0);  // 右Altキーを離した       
      keyboard.write(0xF0);
      keyboard.write(0x11); 
    };
  }

  // 右GUIキー
  if (beforeMod.bmRightGUI != afterMod.bmRightGUI) {
    if (afterMod.bmRightGUI) {
      keyboard.write(0xE0);  // 右GUIキーを押した
      keyboard.write(0x27);
    } else {
      keyboard.write(0xE0);  // 右GUIキーを離した       
      keyboard.write(0xF0); 
      keyboard.write(0x27); 
    }
  }
}

//
// キー離し ハンドラ
// 引数
//  mod : コントロールキー状態
//  key : HID Usage ID 
//
void KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key) {
  // HID Usage ID から PS/2 スキャンコード に変換
  uint8_t code = 0;
  uint8_t pre = 0;
 
  if (key >= 0x04 && key <= 0x67) {
    code = pgm_read_byte(keytable1 + (key - 0x04)*2);
    pre = pgm_read_byte(keytable1 + (key - 0x04)*2 + 1);
  } else if (key >= 0x87 && key <= 0x94) {
    code = pgm_read_byte(keytable2 + (key - 0x87)*2);    
    pre = pgm_read_byte(keytable2 + (key - 0x87)*2 + 1);    
  }

  // PS/2キーの発行
  if (pre == 0) {
    keyboard.write(0xF0);
    keyboard.write(code);    
  } else if (pre == 1) {
    keyboard.write(0xE0);
    keyboard.write(0xF0);
    keyboard.write(code);    
  } else if (pre == 2) {
    keyboard.write(0xE0);
    keyboard.write(0xF0);
    keyboard.write(0x7C);
    keyboard.write(0xE0);
    keyboard.write(0xF0);
    keyboard.write(0x12);
  }
}

int enabled =0; 

void ack() {
  while(keyboard.write(0xFA));
}

// ホストから送信されるコマンドの処理
int keyboardcommand(int command) {
  unsigned char val;
  switch (command) {
  case 0xFF:  ack();// Reset: キーボードリセットコマンド。正しく受け取った場合ACKを返す。
    while(keyboard.write(0xAA)!=0);
    break;
  case 0xFE: // 再送要求
    ack();
    break;
  case 0xF6: // 起動時の状態へ戻す
    //enter stream mode
    ack();
    break;
  case 0xF5: //起動時の状態へ戻し、キースキャンを停止する
    //FM
    enabled = 0;
    ack();
    break;
  case 0xF4: //キースキャンを開始する
    //FM
    enabled = 1;
    ack();
    break;
  case 0xF3: //set typematic rate/delay : 
    ack();
    keyboard.read(&val); //do nothing with the rate
    ack();
    break;
  case 0xF2: //get device id : 
    ack();
    keyboard.write(0xAB);
    keyboard.write(0x83);
    break;
  case 0xF0: //set scan code set
    ack();
    keyboard.read(&val); //do nothing with the rate
    ack();
    break;
  case 0xEE: //echo :キーボードが接続されている場合、キーボードはパソコンへ応答(ECHO Responce)を返す。
    //ack();
    keyboard.write(0xEE);
    break;
  case 0xED: //set/reset LEDs :キーボードのLEDの点灯/消灯要求。これに続くオプションバイトでLEDを指定する。 
    ack();
    keyboard.read(&val); //do nothing with the rate
    ack();
    break;
  }
}

USB     Usb;
//USBHub     Hub(&Usb);
HIDBoot<USB_HID_PROTOCOL_KEYBOARD>    HidKeyboard(&Usb);
uint32_t next_time;
KbdRptParser Prs;

void setup() {
  Serial.begin( 115200 );
  while(keyboard.write(0xAA)!=0);
  
  if (Usb.Init() == -1)
    Serial.println(F("OSC did not start."));

  // USBデバイス情報の取得
  delay(200);

  while(1) {
  Usb.Task();
    if ( Usb.getUsbTaskState() == USB_STATE_RUNNING )  {
      USB_DEVICE_DESCRIPTOR buf;
      byte rcode;
      rcode = Usb.getDevDescr( 1 , 0, 0x12, ( uint8_t *)&buf );
      if (rcode) {
        Serial.println(F("USB Devide Error."));
      } else {
        Serial.print(F("class:")); Serial.println(buf.bDeviceClass, HEX);
        Serial.print(F("sub class:")); Serial.println(buf.bDeviceSubClass, HEX);
      }
      break;
    }
  }
  //delay( 200 );
  next_time = millis() + 5000;
  HidKeyboard.SetReportParser(0, &Prs);
  Serial.println(F("Start."));
}

void loop() {
  unsigned char c;  // ホストからの送信データ
  if( (digitalRead(KB_CLK)==LOW) || (digitalRead(KB_DATA) == LOW)) {
    while(keyboard.read(&c)) ;
    keyboardcommand(c);
  }  
  Usb.Task();
}

スケッチ的には実は大したことやってないです。ライブラリが大半の処理をやってくれます。
2つのプロトコル(USB HIDとPS/2)を繋げただけです。
USB HIDインターフェスのキー入力情報(HID Usage ID)をPS/2のキー入力に変換する
部分が本スケッチのかなめです。

この変換にはテーブル(keytable1[]、keytable2[])を用いて行っています。
ただし、CTRL、Shift、ALT、GUIキーはHID Usage IDではなくビット(ON/OFF)情報での
取得となるので、KbdRptParser::OnControlKeysChanged()で別処理を行っています。

スケッチはBluetooth版と類似点が多いです。なんとか統合したいと思います。
最終的にはケースに収納して、USBコネクタに接続して使いたいのですが..

とりあえず、手持ちの発泡スチロールボードで作って見たけど...
厚さが5mmあるのでデカくなった.. ダサい.. Otz  3Dプリンター欲しいですね。

Dscn6129

aitendoの「プラケースwith基板 [2P-E44.5]」が使えるかもしれないので注文しました。
(厚さがダメかも...)

Photo_2



スケッチはArduino UNO+USB HOSTシールドでも動くことを確認しました。
IchigoJamの入力端子KBD1、KBD2には5Vが入力されますが、IchigoJam(LPC1114)は、
5Vトレラントなので問題なしです。

比べるとArduino UNOだとデカいですね。

Dscn6132


« IchigoJamでポケモンキーボードを使ってみる(Bluetooth・PS/2変換) | トップページ | IchigoJamでLogicool製ワイヤレスキーボードを利用する(USB・PS/2変換) その2 »

arduino」カテゴリの記事

IchigoJam」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/571408/64485610

この記事へのトラックバック一覧です: IchigoJamでLogicool製ワイヤレスキーボードを利用する(USB・PS/2変換):

« IchigoJamでポケモンキーボードを使ってみる(Bluetooth・PS/2変換) | トップページ | IchigoJamでLogicool製ワイヤレスキーボードを利用する(USB・PS/2変換) その2 »