フォト
2017年9月
          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
無料ブログはココログ

IchigoJam

2017年6月 3日 (土)

IchigoJamに関しての不満

FacebookのIchigoJam-FANにて、
開発者さんが「IchigoJamの拡張版にほしいもの大募集!」とのことで、

RENUMコマンドの不具合の対応をお願いしたのですが、
「ラベルを使え!」との回答。
この回答にはちょと「カチン」ときました。たま吉さん的には..
RENUMコマンドは、行番号の付け替えを行うコマンドで、
N-BASIC、MSX-BASIC等のBASIC言語では古くからあるコマンドです。
IchigoJamのコマンドでは、次のプログラムを

1 CLS
5 A=0
6 PRINT "Hello!"
10 A=A+1
20 IF A<10 GOTO 6
30 END

RENUMコマンドで
RENUM  100, 10

と実行して行番号の付け替えを行うと、
結果は
100 CLS
110 A=0
120 PRINT "Hello!"
130 A=A+1
140 IF A<10 GOTO 6
150 END

となり、GOTO文の飛び先が元のままのため、
プログラムを実行すると動かなくなります。
これは、明らかに問題(欠陥)なのです。

なぜ対応して頂け無いのかを質問すると、
「優先度が低い、実装方式が中間コードを使っていないため、難しい」との回答でした。
さらに、
「ラベルを使わない理由がなんかあるのか!」
とのなんか、角の立つような嫌な回答..
まあ、「つまらんことに突っ込むな」的な意味の回答なんだろうなぁ
不具合の対応よりも、機能拡張の方が優先度が高いらしい..

利用者、お金を払って企業が販売している製品を購入した私からすると、
中身の実装方法なんて全然関係ないし、
昔のBASICのGOTO文といったら、行番号指定が定番、
利用者としては当然、行番号を使おうが、ラベルを使おうが自由ななず..

なんか一気に使う気が無くなった。
荒探しをすると、色々とありそれ含めて見守ってきたですが、
今まで更新のたびに不具合等を見つけて連絡してきたがそれもやめよう..

2017年3月 9日 (木)

IchigoJam用画像コンバータを少々改良

以前作成した「IchigoJam用画像コンバータ」をちょっとだけ改良しました。

  Top

  ・過去の関連記事
    IchigoJamで疑似グラフィックを使った画像の表示
    IchigoJamでQRコードを表示してみる

改良点
・ドラック&ドロップ対応
・画像ファイルパスを直接入力出来るように修正
・Windows 10対応(動作確認)

GitHubにてこの改良プログラムの公開しました。
・Tamakichi/IchigoBmpConv  IchigoJam用画像コンバータ
   https://github.com/Tamakichi/IchigoBmpConv

動作確認にQRコード表示プログラムを作成しました。

09

前回はMS Wordの差し込み印刷機能を使ってブログURLをQRコードに変換したのですが、
Wordのバージョンアップのためか、イチゴジャムの画面解像度64x48ドットに収まるサイズの
画像が作成出来なくなってしまいました。

そこで、下記のサイトを利用させていただいてQRコードを作成しました。
・QRコードBlog
  http://www.qrcodeblog.com/date/qrcgi/

  誤り訂正レベル:Q(25%)
  バージョン:自動
  サイズ:1
を指定して作成しました。この指定で41x41ドットの画像が作成出来ました。
これを元にしてペイントツールで加工して64x48の変換用画像を作成しました。

02

これを作成したツールで変換し、プログラムソースをIchigoJamに転送します。
(転送はこれこれを使って出来ます)

転送したプログラムをIchigoJamで実行

10

スマートフォンでこのQRコードが読めました。

03


2016年11月19日 (土)

IchigoJam用 USBキーボード変換 Bluetooth・USBキーボードの両対応できました

現在取り組んでいる、USB Hostシールドを使った USB - PS/2変換、
Bluetooth版USB HID版が別スケッチだったのですが1つに統合して両対応にしました。

Dscn6165

Bluetoothドングル、有線・無線USBキーボードを自動認識して利用出来ます。
構成的には次のような感じです。

System

更にキーを押し続けた場合のキーリピート機能を追加しました。
これでほぼ完全にPS/2キーボードとして利用出来ます。

USBハブにも対応したのですが、現状は動かない場合がありおまけレベルです。

Arduino Uno環境では、BluetoothドングルとUSB HIDキーボード(有線・無線)が同時利用出来ました。
(同じ種類のデバイスの同時利用は出来ません。 例:USB HIDキーボード2台同時)

Dscn6162

Arduino pro miniではUSBの電流供給能力的に無理ですが..

スケッチ


次のような感じです。まだまだこれから修正を予定しています。
  ダウンロード USBKBD2PS2_v2.zip (5.4K)

最新版はgithubにて公開しています。
       https://github.com/Tamakichi/Arduino_USBToPS2
//
// USBKBD2PS2_v2 USBキーボード PS/2キーボード変換 for IchigoJam v2
// 作成者 たま吉さん
// 作成日 2016/11/11 , 最終修正日 2016/11/18
// 修正 2016/11/18 Bluetooth HIDとUSB HIDのスケッチの統合,キーリピート機能対応
//
// このスケッチの利用には以下のハードウェア(シールド)が必要です.
//  ・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)
//  ・MsTimer2 (http://playground.arduino.cc/Main/MsTimer2)
//

//******* 用途により、下記の定義を設定して下さい ***************************
#define MYDEBUG      0  // 0:デバッグ情報出力なし 1:デバッグ情報出力あり 
#define KB_CLK      A4  // PS/2 CLK  IchigoJamのKBD1に接続
#define KB_DATA     A5  // PS/2 DATA IchigoJamのKBD2に接続
//**************************************************************************

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

#define LOBYTE(x) ((char*)(&(x)))[0]
#define HIBYTE(x) ((char*)(&(x)))[1]

// キーリピートの定義
#define REPEATTIME      5   // キーを押し続けて、REP_INTERVALxREPEATTIMEmsec後にリピート開始
#define EMPTY           0   // リピート管理テーブルが空状態
#define MAXKEYENTRY     6   // リピート管理テーブルサイズ
#define REP_INTERVAL    100 // リピート間隔 150msec

#define MS_SIKIICHI     10

uint8_t keyentry[MAXKEYENTRY];    // リピート管理テーブル
uint8_t repeatWait[MAXKEYENTRY];  // リピート開始待ち管理テーブル
uint8_t enabled =0;               // PS/2 ホスト送信可能状態
PS2dev keyboard(KB_CLK, KB_DATA); // PS/2デバイス

//
// HIDキーボード レポートパーサークラスの定義
//
class KbdRptParser : public KeyboardReportParser {
  protected:
    virtual uint8_t HandleLockingKeys(USBHID *hid, uint8_t key);
    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) {};
};

// HIDマウス レポートパーサークラスの定義
/*
class MouseRptParser : public MouseReportParser {
  protected:
    virtual void OnMouseMove(MOUSEINFO *mi) {};
    virtual void OnLeftButtonUp(MOUSEINFO *mi){};
    virtual void OnLeftButtonDown(MOUSEINFO *mi){};
    virtual void OnRightButtonUp(MOUSEINFO *mi) {};
    virtual void OnRightButtonDown(MOUSEINFO *mi){};
    virtual void OnMiddleButtonUp(MOUSEINFO *mi){};
    virtual void OnMiddleButtonDown(MOUSEINFO *mi){};
};
*/

class MouseRptParser : public MouseReportParser {
  protected:
    void OnMouseMove(MOUSEINFO *mi);
    void OnLeftButtonUp(MOUSEINFO *mi);
    void OnLeftButtonDown(MOUSEINFO *mi);
    void OnRightButtonUp(MOUSEINFO *mi);
    void OnRightButtonDown(MOUSEINFO *mi);
    void OnMiddleButtonUp(MOUSEINFO *mi);
    void OnMiddleButtonDown(MOUSEINFO *mi);
};

void MouseRptParser::OnMouseMove(MOUSEINFO *mi){
/*  
  Serial.print("dx=");
  Serial.print(mi->dX, DEC);
  Serial.print(" dy=");
  Serial.println(mi->dY, DEC);
*/
  if (mi->dX > MS_SIKIICHI) {
    keyboard.write(0xE0);
    keyboard.write(0x74);
    delay(50); 
    keyboard.write(0xE0);
    keyboard.write(0xF0);
    keyboard.write(0x74);
  } else if (mi->dX < -MS_SIKIICHI) {
    keyboard.write(0xE0);
    keyboard.write(0x6B);    
    delay(50); 
    keyboard.write(0xE0);
    keyboard.write(0xF0);
    keyboard.write(0x6B);    
  }
  if (mi->dY > MS_SIKIICHI) {
    keyboard.write(0xE0);
    keyboard.write(0x72);        
    delay(50); 
    keyboard.write(0xE0);
    keyboard.write(0xF0);
    keyboard.write(0x72);        
  } else if (mi->dY < -MS_SIKIICHI) {
    keyboard.write(0xE0);
    keyboard.write(0x75);        
    delay(50); 
    keyboard.write(0xE0);
    keyboard.write(0xF0);
    keyboard.write(0x75);        
  }
}

void MouseRptParser::OnLeftButtonUp  (MOUSEINFO *mi){
  Serial.println("L Butt Up");
}

void MouseRptParser::OnLeftButtonDown (MOUSEINFO *mi){
  Serial.println("L Butt Dn");
}

void MouseRptParser::OnRightButtonUp  (MOUSEINFO *mi){
  Serial.println("R Butt Up");
}

void MouseRptParser::OnRightButtonDown  (MOUSEINFO *mi){
  Serial.println("R Butt Dn");
}

void MouseRptParser::OnMiddleButtonUp (MOUSEINFO *mi){
  Serial.println("M Butt Up");
}

void MouseRptParser::OnMiddleButtonDown (MOUSEINFO *mi){
  Serial.println("M Butt Dn");
}

USB      Usb;
USBHub  Hub1(&Usb);
USBHub  Hub2(&Usb);
USBHub  Hub3(&Usb);
USBHub  Hub4(&Usb);

BTD     Btd(&Usb);
BTHID   bthid(&Btd, PAIR, "0000");
BTHID   hid(&Btd);

KbdRptParser keyboardPrs;
MouseRptParser mousePrs;

HIDBoot<USB_HID_PROTOCOL_KEYBOARD>    HidKeyboard(&Usb);
HIDBoot<USB_HID_PROTOCOL_MOUSE>    HidMouse(&Usb);

uint8_t classType = 0;      
uint8_t subClassType = 0;
uint32_t next_time;

// 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
};

// PS/2 ホストにack送信
void ack() {
  while(keyboard.write(0xFA));
}

// PS/2 ホストから送信されるコマンドの処理
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;
  }
}

// リピート管理テーブルのクリア
void claerKeyEntry() {
 for (uint8_t i=0; i <MAXKEYENTRY; i++)
    keyentry[i] = EMPTY;
}

// リピート管理テーブルにキーを追加
void addKey(uint8_t key) {
 for (uint8_t i=0; i <MAXKEYENTRY; i++) {
  if (keyentry[i] == EMPTY) {
    keyentry[i] = key;  
    repeatWait[i] = REPEATTIME;
    break;
  }
 }
}

// リピート管理テーブルからキーを削除
void delKey(uint8_t key) {
 for (uint8_t i=0; i <MAXKEYENTRY; i++) {
  if (keyentry[i] == key) {
    keyentry[i] = EMPTY;
    break;
  }
 }  
}

//
// PS/2 makeコード送信
// 引数 key(IN) HID Usage ID
//
uint8_t sendKeyMake(uint8_t key) {
  // HID Usage ID から PS/2 スキャンコード に変換
  uint8_t code = 0;
  uint8_t pre = 0xff;

  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(code);
    return 1;
  } else if (pre == 1) {
    keyboard.write(0xE0);
    keyboard.write(code);    
  } else if (pre == 2) { // PrintScreenキー
    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);
  }
  return code;
}

//
// PS/2 breakコード送信
// 引数 key(IN) HID Usage ID
//
uint8_t sendKeyBreak(uint8_t key) {
  // HID Usage ID から PS/2 スキャンコード に変換
  uint8_t code = 0;
  uint8_t pre = 0xff;
 
  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);
  }
  return code;
}

// リピート処理(タイマー割り込み処理から呼ばれる)
void sendRepeat() {
  // HID Usage ID から PS/2 スキャンコード に変換
  uint8_t code = 0;
  uint8_t pre, key;
  
  for (uint8_t i=0; i < MAXKEYENTRY; i++) {
    if (keyentry[i] != EMPTY) {
      key = keyentry[i]; 
      if (repeatWait[i] == 0) {
        sendKeyMake(key);
    } else {
        repeatWait[i]--;          
      }
    }
  }
}

//
// ロックキー(NumLock/CAPSLock/ScrollLock)ハンドラ
//

uint8_t KbdRptParser::HandleLockingKeys(USBHID *hid, uint8_t key) {
  if (classType == USB_CLASS_WIRELESS_CTRL) {
    uint8_t old_keys = kbdLockingKeys.bLeds;  
    switch (key) {
      case UHS_HID_BOOT_KEY_NUM_LOCK:
        kbdLockingKeys.kbdLeds.bmNumLock = ~kbdLockingKeys.kbdLeds.bmNumLock;
        break;
      case UHS_HID_BOOT_KEY_CAPS_LOCK:
        kbdLockingKeys.kbdLeds.bmCapsLock = ~kbdLockingKeys.kbdLeds.bmCapsLock;
        break;
      case UHS_HID_BOOT_KEY_SCROLL_LOCK:
        kbdLockingKeys.kbdLeds.bmScrollLock = ~kbdLockingKeys.kbdLeds.bmScrollLock;
        break;
    }
    if (old_keys != kbdLockingKeys.bLeds && hid) {
      BTHID *pBTHID = reinterpret_cast<BTHID *> (hid); // A cast the other way around is done in BTHID.cpp
      pBTHID->setLeds(kbdLockingKeys.bLeds); // Update the LEDs on the keyboard
    }
  } else {
    return KeyboardReportParser::HandleLockingKeys(hid, key);   
  }
  return 0;
}


//
// キー押しハンドラ
// 引数
//  mod : コントロールキー状態
//  key : HID Usage ID 
//
void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key) {
  MsTimer2::stop();
#if MYDEBUG==1
  Serial.print(F("DN ["));  Serial.print(F("mod="));  Serial.print(mod,HEX);
  Serial.print(F(" key="));  Serial.print(key,HEX);  Serial.println(F("]"));
#endif
  if (sendKeyMake(key))
    addKey(key);
  MsTimer2::start();
}

//
// コントロールキー変更ハンドラ
// 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) {
  MsTimer2::stop();
#if MYDEBUG==1
  Serial.print(F("UP ["));  Serial.print(F("mod="));  Serial.print(mod,HEX);
  Serial.print(F(" key="));  Serial.print(key,HEX);  Serial.println(F("]"));
#endif
  if (sendKeyBreak(key)) // HID Usage ID から PS/2 スキャンコード に変換  
    delKey(key);  
  MsTimer2::start();
}

//
// インターフェースクラスの取得
//
uint8_t getIntClass(byte& intclass, byte& intSubClass ) {
  uint8_t buf[ 256 ];
  uint8_t* buf_ptr = buf;
  byte rcode;
  byte descr_length;
  byte descr_type;
  unsigned int total_length;

  uint8_t flgFound = 0;
  
  //デスクプリタトータルサイズの取得
  rcode = Usb.getConfDescr( 1, 0, 4, 0, buf );
  LOBYTE( total_length ) = buf[ 2 ]; HIBYTE( total_length ) = buf[ 3 ];
  if ( total_length > 256 ) {
    total_length = 256;
  }
  
  rcode = Usb.getConfDescr( 1, 0, total_length, 0, buf ); 
  while ( buf_ptr < buf + total_length ) { 
    descr_length = *( buf_ptr );
    descr_type = *( buf_ptr + 1 );

    if (descr_type == USB_DESCRIPTOR_INTERFACE) {
      // 最初のインタフェースの取得
      USB_INTERFACE_DESCRIPTOR* intf_ptr = ( USB_INTERFACE_DESCRIPTOR* )buf_ptr;  
      intclass = intf_ptr->bInterfaceClass;
      intSubClass = intf_ptr->bInterfaceSubClass;
      flgFound = 1;
      break;
    }
    buf_ptr = ( buf_ptr + descr_length );    //advance buffer pointer
  }
  return ( flgFound );
}

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

  bthid.SetReportParser(KEYBOARD_PARSER_ID, &keyboardPrs);
  bthid.SetReportParser(MOUSE_PARSER_ID, &mousePrs);
  bthid.setProtocolMode(USB_HID_BOOT_PROTOCOL); // Boot Protocol Mode
  bthid.setProtocolMode(HID_RPT_PROTOCOL); // Report Protocol Mode
  //HidKeyboard.SetReportParser(0, &Prs);
  HidKeyboard.SetReportParser(0, &keyboardPrs);
  HidMouse.SetReportParser(0, &mousePrs);

  claerKeyEntry();
  MsTimer2::set(REP_INTERVAL, sendRepeat); 
  MsTimer2::start();
    
  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();
  if ( (classType == 0) && (Usb.getUsbTaskState() == USB_STATE_RUNNING) )  {  
    // デバイスクラス情報の取得
    getIntClass(classType, subClassType) ;
#if MYDEBUG == 1  
    Serial.print(F("class="));  Serial.println(classType, HEX);
    Serial.print(F("subclass="));  Serial.println(subClassType, HEX);
#endif
  }
  
#if MYDEBUG == 1  
  static uint8_t prevSts = 0xFF;
  if (Usb.getUsbTaskState() != prevSts) {
    prevSts = Usb.getUsbTaskState();
    Serial.print(F("sts="));
    Serial.println(prevSts, HEX);
  }
  if (Serial.available()) {
    Serial.read();
    for (uint8_t i=-0; i < MAXKEYENTRY; i++) {
      Serial.print(F("keyentry["));
      Serial.print(i,DEC);
      Serial.print(F("]="));
      Serial.println(keyentry[i],HEX);
    }
  }
#endif
}

現状、試行錯誤中の中途半端なマウス対応のコードを含んでいます。
削除してゲームパッド対応を予定しています。

2016年11月16日 (水)

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

前回からの続きです。

ケースを製作しました。
aitendoのUSBケースは結局、小さすぎて使えませんでした。
事前に作成していたダサいケースを加工して利用することにしました。

とりあえず、表面を平らにして表面に何か貼ろうと構想。

Dscn6138


ホームセンターで購入したコルクシールを貼ることにしました。
はさみで切って貼ることが出来ます。

Dscn6157

貼り付けると、いい感じになりました。
適当なサイズに切って、貼った後にはさみではみ出た部分をカットしました。
中が発泡スチロールボードとはもう分かりません。

Dscn6151

中に入れるモジュール

IchigoJamのUSBポート経由でVCC、GND、KBD1、KBD2を接続します。
電源はUSB 5V(VCC)はArduino pro mini のRAW端子に接続することで3.3Vに変換して
利用しています。

USBケーブルは百円ショップのUSB延長ケーブルを利用しました。
最初太いケーブルをチョイスしたのですが、固くて曲げにくいため細いケーブルに変更しました。

Dscn6150

ケースの中に入れるとこんな感じです。
いい感じ! 上出来です。

Dscn6152


IchogoJamに接続の様子

ケースに使った発泡スチロールボードが5mmと厚いため、一回り大きくなりましたが、
良しとします。

Dscn6156

IchigoJam周辺

邪魔にならず、IchigoJamの全ての端子が利用出来るのでこの形態で正解でした。

Dscn6154


最後に

ケースに収納すると可搬性も良くなり実用性が増しますね。
発泡スチロールボードを使ったケース作成も意外といいです。
カッターナイフでお手軽に作成できます。

IchigoJamのケースも作ってみような?

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


2016年11月10日 (木)

IchigoJamでポケモンキーボードを使ってみる(Bluetooth・PS/2変換)

前回調査したArduino pro mini+USB Hostシールドを使って、
IchigoJamで「ポケモンキーボード(Bluetoothキーボード)」を利用出来るようにしました。

IchigoJamからはPS/2キーボードと認識されて利用出来ます。
ポケモンキーボードの全てのキーが利用出来ます。

Dscn6077

IchigoJamから電源供給しています。
キーボードからの入力情報はPS/2キーボード用の端子 KBD1、KBD2に接続しています。
arduino      IchigoJam
  D2     =>     KBD2
  D3     =>     KBD1
  VCC   =>    VCC
  GND   =>    GND

Dscn6084

Dscn6078

動作の様子


システム構成的な図

If
                          ※図のGND => VCCは記載ミスです GND => GND が正しいです

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

起動後のキーボードのキーを押しまくっているとペアリングが行われます。

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

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

  Dscn6030

 純正品は高いので2つともクローン製品を使っています。
  合計で1000円くらいです。

2)USB Bluetoothドングル
   BT-MicroEDR2X
   Bluetooth Ver.2.1+EDR対応 Micro サイズ USBアダプタ (Class2/10m)

   Dscn6080

   かなり前に買って放置していたものです。

   これ以外にElecom製のLBT-UAN05C2でも動作しました。   

   Dscn6081

スケッチ

スケッチを公開します。
   スケッチのダウンロード btkbd2ps2.zip (3.2K)

   ※ 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

スケッチの全ソース
//
// btkbd2ps2 Bluetoothキーボード PS/2キーボード変換 for IchigoJam
// 作成者 たま吉さん
// 作成日 2016/11/09
//
// このスケッチの利用には以下のハードウェア(シールド)が必要です.
//  ・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 <BTHID.h>
#include <usbhub.h>

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

// HID Usage ID (0x04 - 0x43) => PS/2 スキャンコード 変換テーブル
const uint8_t keytable1[64] PROGMEM = {
  0x1C,0x32,0x21,0x23,0x24,0x2B,0x34,0x33,0x43,0x3B,0x42,0x4B,0x3A,0x31,0x44,0x4D,
  0x15,0x2D,0x1B,0x2C,0x3C,0x2A,0x1D,0x22,0x35,0x1A,0x16,0x1E,0x26,0x25,0x2E,0x36,
  0x3D,0x3E,0x46,0x45,0x5A,0x76,0x66,0x0D,0x29,0x4E,0x55,0x54,0x5B,0x5D,0x5D,0x4C,
  0x52,0x0E,0x41,0x49,0x4A,0x58,0x05,0x06,0x04,0x0C,0x03,0x0B,0x83,0x0A,0x01,0x09,
};

// HID Usage ID (0x4A - 0x52) => PS/2 スキャンコード 変換テーブル
const uint8_t keytable2[9] PROGMEM = {
  0x6C,0x7D,0x71,0x69,0x7A,0x74,0x6B,0x72,0x75,
};

//
// HIDキーボード レポートパーサークラスの定義
//
class KbdRptParser : public KeyboardReportParser {
  protected:
    virtual uint8_t HandleLockingKeys(USBHID *hid, uint8_t key);
    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) {};
};

uint8_t KbdRptParser::HandleLockingKeys(USBHID *hid, uint8_t key) {
  uint8_t old_keys = kbdLockingKeys.bLeds;
  
  switch (key) {
    case UHS_HID_BOOT_KEY_NUM_LOCK:
      kbdLockingKeys.kbdLeds.bmNumLock = ~kbdLockingKeys.kbdLeds.bmNumLock;
      break;
    case UHS_HID_BOOT_KEY_CAPS_LOCK:
      kbdLockingKeys.kbdLeds.bmCapsLock = ~kbdLockingKeys.kbdLeds.bmCapsLock;
      break;
    case UHS_HID_BOOT_KEY_SCROLL_LOCK:
      kbdLockingKeys.kbdLeds.bmScrollLock = ~kbdLockingKeys.kbdLeds.bmScrollLock;
      break;
  }
  if (old_keys != kbdLockingKeys.bLeds && hid) {
    BTHID *pBTHID = reinterpret_cast<BTHID *> (hid); // A cast the other way around is done in BTHID.cpp
    pBTHID->setLeds(kbdLockingKeys.bLeds); // Update the LEDs on the keyboard
  }
  return 0;
}

//
// キー押しハンドラ
// 引数
//  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 = 0;
  if (key >= 0x04 && key <= 0x43) {
    code = pgm_read_byte(keytable1 + key - 0x04);
  } else if (key >= 0x4A && key <= 0x52) {
    code = pgm_read_byte(keytable2 + key - 0x4A);    
    pre = 0xE0;
  } else if (key == 0x87) {
    code = 0x51;    
  } else if (key == 0x89) {
    code = 0x6A;    
  }

  // PS/2キーの発行
  if (pre) keyboard.write(pre);
  keyboard.write(code);
}

//
// コントロールキー変更ハンドラ
// 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 <= 0x43) { // keytable1を使った変換
    code = pgm_read_byte(keytable1 + key - 0x04);
  } else if (key >= 0x4A && key <= 0x52) { // keytable2を使った変換
    code = pgm_read_byte(keytable2 + key - 0x4A);    
    pre = 0xE0;
  } else if (key == 0x87) {
    code = 0x51;    
  } else if (key == 0x89) {
    code = 0x6A;    
  }

  // PS/2キーの発行
  if (pre) keyboard.write(pre);
  keyboard.write(0xF0);  
  keyboard.write(code);
};

// HIDマウス レポートパーサークラスの定義
class MouseRptParser : public MouseReportParser {
  protected:
    virtual void OnMouseMove(MOUSEINFO *mi) {};
    virtual void OnLeftButtonUp(MOUSEINFO *mi){};
    virtual void OnLeftButtonDown(MOUSEINFO *mi){};
    virtual void OnRightButtonUp(MOUSEINFO *mi) {};
    virtual void OnRightButtonDown(MOUSEINFO *mi){};
    virtual void OnMiddleButtonUp(MOUSEINFO *mi){};
    virtual void OnMiddleButtonDown(MOUSEINFO *mi){};
};

USB   Usb;
BTD   Btd(&Usb);
BTHID bthid(&Btd, PAIR, "0000");
BTHID hid(&Btd);

KbdRptParser keyboardPrs;
MouseRptParser mousePrs;
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;
  }
}

void setup() {
  Serial.begin(115200);
  while(keyboard.write(0xAA)!=0);
  
  if (Usb.Init() == -1) {
    Serial.print(F("\r\nOSC did not start"));
    while (1); // Halt
  }

  bthid.SetReportParser(KEYBOARD_PARSER_ID, &keyboardPrs);
  bthid.SetReportParser(MOUSE_PARSER_ID, &mousePrs);
  bthid.setProtocolMode(USB_HID_BOOT_PROTOCOL); // Boot Protocol Mode
  bthid.setProtocolMode(HID_RPT_PROTOCOL); // Report Protocol Mode
  Serial.println(F("Start."));
}

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

ポケモンキーボード以外のBluetoothキーボードも利用出来ると思います。
ただし、現状はポケモンキーボードに無いキーは未対応です。
カタカナ/ひらがなキー、無変換、変換キー、テンキーは未対応です。

サイズ的に中央に配置出来そうです。
最終的にはこの形態で利用出来るよう、やってみます。

Dscn6083

2016/11/10 追記

取りあえず、ブレッドボードを付かない形態に変更しました。
ちょっと使いやすくなりました。

Dscn6093

参考文献

・USB HID to PS/2 Scan Code Translation Table
  http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf
・USBキーボードのキーコード
http://www2d.biglobe.ne.jp/~msyk/keyboard/layout/usbkeycode.html
・Device Class Definition for Human Interface Devices (HID)
  http://www.usb.org/developers/hidpage/HID1_11.pdf
・HID Usage Tables
  http://www.usb.org/developers/hidpage/Hut1_12v2.pdf
・NXP USB and PS/2 Multimedia Keyboard Interface
 http://www.nxp.com/files/microcontrollers/doc/ref_manual/DRM014.pdf

・Sazanami Online PS/2 インターフェイスの研究
  http://hp.vector.co.jp/authors/VA037406/html/ps2interface.htm
・PS/2キーボードインターフェース
  http://www.technoveins.co.jp/technical/keyboard/
・O-Family JIS配列 PS/2 キーボード スキャンコード表
  http://www.ne.jp/asahi/shared/o-family/ElecRoom/AVRMCOM/PS2_RS232C/KeyCordList.pdf


関連記事
IchigoJamでポケモンキーボードを使ってみる (16/11/10) この記事
arduinoでPS/2キーボードのエミュレーションを行う その2 (15/04/23)
arduinoでPS/2キーボードのエミュレーションを行う (15/04/22) 

2016年10月 3日 (月)

1対2のUART接続通信

製作中のIchigoMsg(仮)の動作確認では、パソコン、IchigoJam、IchigoMsg(Arduino pro mini)
の3台を利用しています。3台のUART接続の関係は次のような感じです。

Photo

IchigoJamからIchigoMsgのテストプログラムを実行します。
そのIchigoJam対してテストプログラムをパソコンから転送したり、その逆もします。

IchigoJamのUART端子は1つしかないため、
  IchigoJam - パソコン間
  IchigoJam - IchigoMsg間
一々接続し直していました。ちょと面倒です。

何とかならないかと思い、ジャストタイミングでFacebookのIchigoJam-FANにて
「3台のIchigoJam間でUART通信を行う」話題があり、その方法が投稿されていました。

その方法を参考にして次のような結線を試してみました。

Photo_2

IchigoJam UART(RXD1、TXD1)はパソコン (RXD2、TXD2)、IchigoMsg(RXD3、TXD3)に
対して問題なく送受信出来ました。
(ただし、同時受信は出来ますが、同時送信は出来ません。)

Dscn5787

これで、接続を切り替えることなく、つなげっぱなしでOKとなりました。
送信を行う場合、2台にデータが送られます。

パソコンからIchigoMsgのファームウェア書き込み時は
IchigoJamはUARTコマンドで受信のみをOFFします。

パソコンからIchigoMsgへのプログラム転送、IchigoMsgからパソコンへの
プログラム転送時はIchigoMsgは、意味のないデータをスルーするので問題無しです。




2016年10月 1日 (土)

LEDドットマトリックスを使ったメッセージ表示デバイスを作成中(3)

前回からの続きです。

色々と試行錯誤してIchigoJamから制御出来るようになりました。
とりあえず、IchigoMsg(仮)と命名しています。

プログラム的にはほぼ完成で、現在動作確認中です。
(もうちょっと動作確認してから、プログラムを掲載します^^)

Dscn5786


ドット単位で描画するデモ



IchigoJamでは次のようなプログラムで制御しています。

Program

ドットは @?{XYd} という形式で1点ずつON/OFF出来ます。
@?{001} で 座標 (0,0)のドットをON、@?{000} でOFFに出来ます。

簡単なグラフの描画にも利用出来そうです。


メッセージをスクロール表示するデモ


最初に流れる猫+IchigoMsgのメッセージは起動メッセージです。
その後にRUNして実行しているのが次のプログラムです。

Src

16種類のスクロールパターンで同じメッセージを表示しています。
コマンドとしては "@?{S5}猫ア{U3042}イウエオ{A255}"  みないな形式です。
{S5}でスクロールパターンを指定しています。

20行で"@?{I}{T40}"は、{I}で文字コード系をIchigoJam(ASCIIコード)を指定、
{T40}で1ドット当たりのスクロール速度を指定しています。

{N}でUTF-8コード系にして漢字も利用できるのですが、IchigoJamでは表示出来ない
ため{I}を指定してコード系を切り替える必要があります。
その変わり、{U3042}といった形でUTF16コードにて漢字の表示が出来ます。


このデモプログラムでは利用出来るコマンドの1つ、
@? メッセージ文  (@PRINT メッセージ文の省略形)を使っています。
指定したメッセージ文を即時表示するコマンドです。
このコマンドはおまけで実装したのですが、意外と使えそうです。

2016年9月13日 (火)

IchigoJamのI2Cインタフェースについて

IchigoJamのI2Cインタフェースに関するまとめです。
(対象はファームウェア 1.2.1です)

IchigoJamのI2Cインタフェース利用時の注意点

  1)3.3V対応であること
    I2Cデバイスの中には5V利用が前提で3.3Vに対応していないものがあります。
    ただし、仕様上3.3Vに対応していなくても3.3Vで動くものもあります。
    8x8ドットLEDマトリックスを制御するHT16K33は4.5~5.5Vが利用範囲ですが
    利用出来  ています。

  2)IchigoJam BASICの I2CR、I2CWコマンド仕様で利用可能なデバイスであること
     IchigoJam BASICの I2CR、I2CWコマンドの仕様により、
     一部のI2Cデバイスの制御が出来ません(そう多くないです)。

     I2Cアドレスのみを送信してデータ取得を行うような通信が出来ません。
     (Wiiヌンチャク、タッチセンサーなんかは利用出来ませんでした)

  3)I2Cスレーブアドレスが同時利用するデバイスと同じでないこと
    例:秋月電子 液晶キャラクターディスプレイ(ACM1602NI-FLW-FBW-M01)
        アドレスが0x50なのでEEPROMの0x50と競合して使えないです。

  4)I2Cアドレスは7ビット長

  5)プルアップ抵抗は必須(ボード上の搭載・MPU内部プルアップなし)

      
利用ピン


図の紫色の5番ピンSDA27番ピン SCLをデータの2ピンをデータ通信に利用します。
また必要に応じて、VCC、GNDをIchigoJamに結線します。
5番ピン、純正品のラベルはIN3のみしか書いていないのでちょっと分かりにくいですね。

  Ichigojam121

IchigoJam Tの場合、 マイクロUSBコネクタの近くにある端子の利用も可能です。

 

接続方法

一般的な接続方法

I2Cはバスですので複数のI2Cデバイス(スレーブ)が接続出来ます。
注意点としてSDAとSCLにプルアップ抵抗(2.2kΩ~10kΩ、図では4.7kΩを利用)が必要です。

ただし、I2Cデバイスにあらかじめプルアップ抵抗が搭載されている場合があります。
この場合は、そのプルアップ抵抗を利用するため回路図の抵抗は省略出来ます。
(省略しなくても利用可能)

Photo

「プアップ抵抗って何? 何で必要なの?」、「なんで出力が競合しないん?」的な解説
(内容が間違えているかもしれません)

I2Cバスの電気的性質

(読み返すと若干、内容が怪しいかも..後で修正します)

I2CのSDAとSCLは双方向の信号ラインです。
普通に考えると、それぞれのデバイスで入出力の方向が異なる可能性があり、
なんで上手く動いているのだろう?って疑問に思います。

通常のH(ハイ 3.3V)、L(ロー 0V)の状態を持つ線を共通配線にした場合、
それぞれが出力状態かつ、出力値が異なる場合、厄介なことが発生します。

例えば、SDA接続で1つがH(3.3V)、別の1つがL(0V)を出力するとこれはもう、
ショート状態です。H→L間で大きな電流が流れICが破損する可能性もあります。
また、ショートしなくてもSDAのバス的に信号がHなのかLなのか不定です。

そこでI2CバスではワイヤードAND接続が採用されています。
この場合、SDA、SCL端子はオープンドレインという状態で利用します。

この時の出力の取り得る状態はH(1、ハイ 3.3V)、L(0、ロー 0V)ではなく、
H(Hi-Z:ハイインピータンス=絶縁状態)、L(0、ロー 0V)となります。

ハイインピータンスは高抵抗値を意味し、端子には電流が流れない状態です。
この状態であれば、バス上で各デバイスの出力状態でHとLが重なっても
問題となりません。
絶縁状態の端子に0Vの端子を接続しても電流は流れませんね。
当然、ショートも発生しません。

ただしこの場合、1つ厄介なことが発生します。
下の図は共通配線SDA、各スイッチがI2CデバイスのSDA出力を等価的に表しています。
スイッチOFF時がハイインピータンス(絶縁状態)、スイッチON(接続時)がLです。

   Photo_2

全てのデバイスがH(ハイインピータンス=スイッチが開いている状態)、
共通配線SDAは、電圧が不定て右端のSDA出力端子からは
状態(H? L?どっち?)を読むことが出来ません。

そこで、プルアップ抵抗を入れて
SDAがH(ハイインピータンス)の場合は、プルアップ抵抗経由の電圧が出力され、
ロジックとして"1"をとるようにします。
SDAがL(0V)の場合は、そのまま0Vでロジックとして"0"と判定出来ます。

  __2

もしも、それぞれが好き勝手に出力した場合ですが、
1つでもL(0V)があればSDAの状態はLになります。
上図は全てがHですが、どれか1つスイッチを閉じるとSDAが0Vになるは明白です。

論理的にはANDをとってどれか1つでも0ならばSDAのバスは0、
全部がHなら1となります(= ワイヤードAND (論理積))。
(ちなみにI2Cでは、SDA、SCLともHの場合にバスがフリーな状態と規定しています)


IchigiJamのI2C利用コマンド

例としてRTCモジュール(DS3231搭載)を使って説明します。
RTCモジュールは時計機能を持つモジュールで時刻設定、時刻参照、アラーム設定、
アラーム通知が主な機能です。

 
   (このデバイスはプルアップ抵抗搭載なのでIchigoJamに直結で利用出来ます)

IchigoJamにはデータ送信用とデータ受信用の2つのコマンドが用意されています。

  ・ データ送信
      I2CW(I2Cアドレス、 コマンド、コマンド長、送信データ、データ長)

  ・ データ受信
      I2CR(I2Cアドレス、 コマンド、コマンド長、受信データ、データ長)

引数のI2Cアドレスは利用するI2Cスレーブのアドレスを指定します。
このアドレスの指定方法は7ビット、8ビット、10ビット等がちょっと混乱しすが
IchigoJamでは7ビットアドレスを指定します。

まず、利用するI2CデバイスのI2Cアドレスを知る必要があります。
通常は添付の解説書または、商品情報HPのリンク先の資料に記載されています。
大抵は下図のような通信データのレイアウトが記載されています。

I2c

この図で<SLAVEADDRESS>がI2Cアドレスとなります。
(0と1の羅列が7個あるので7ビットだと分かります。系によっては<R/W>を含めて
8ビットで指定する場合もあります)

2進数表記 1101000  をIchigoJamでそのまま指定する場合は、
`1101000   となります(` はキーボードの[@]キーを[Shift]キーを押しながら入力)。
  メモリー的&読書きにおいて無駄なので16進数に直して#68 で指定することにします。
(IchigoJam上で ?HEX$(`1101000) を実装すれば楽に求められますね)

I2CWI2CRコマンドの引数、
コマンドはI2Cデバイスに送るコマンドの格納アドレス、コマンド長はその長さです。
IchigoJamの変数では可変長のデータを直接扱うことが出来ません。
そのため、アドレスでの指定となります。

ここで、RTCモジュールに時刻設定する場合を考えます。
設定にはI2CWコマンドを使います。

RTCモジュールの場合、引数のコマンドにはRTCモジュール内のレジスタアドレスを指定します。

次の表がレジスタアドレスに割りつけられている機能です。
引数のコマンドにはこの表のアドレスを指定し、書きこみたいアドレスを指定します。

<レジスタアドレス表>
Photo_3

引数の送信データには上記の表に設定する内容を指定します。
ここでは年、月、日、曜日、時、分、秒 の7つの値を1回の送信で設定したいと思います。

2016年9月13日(火) 20時30分00秒 を設定することにします。
この場合、アドレス00h~06hにだけ値を設定します。

まず、設定値は次のようになります。データ長は7バイトです。
アドレス 設定値   補足
00h:   #00         秒
01h:   #30         分
02h:   #20         時
03h:   #03         曜日(01:日、02:月、03:火 ... 06:土)
04h:   #13         日
05h:   #09         月
06h:   #16         西暦年下2桁

16進数BCD表記ではほぼ見た目のままの数字の指定となります。

以上のコマンドと設定データの実際の送信を行うプログラムと実行結果です。

10 POKE #700,#00,#00,#30,#20,#03,#13,#09,#16
20 R=I2CW(#68,#700,1,#701,7)
30 ?R

1

10行のPOKEコマンドでIchigoJamのアドレス#700から#00#00#30..#16
順番に格納しています。最初の#00はコマンドとして送信するデータ1バイト、
次の#00#16が設置値7バイトです。

#700からの領域はPCG定義用の領域です。フォント定義を利用しない場合は、
自由に利用出来ます(256バイト)。

20行ではI2CWコマンドを使って実際にI2Cデバイスのデータを送信しています。
変数Rは送信結果が返されます(0で正常、1で異常)。

#68はI2Cデバイスのアドレス、#700がコマンド格納アドレス、1がコマンド長、
#701が送信データの格納アドレス、7が送信データの長さです。

実行すると次のような感じになります。
正常動作の0が表示されます。

次にI2CWコマンドを使って現在時刻を読みだしてみます。

    I2CR(I2Cアドレス、 コマンド、コマンド長、受信データ、データ長)

やりたいことは、I2Cデバイスのレジスタアドレス表の00h~06hに格納されている、
ここでは年、月、日、曜日、時、分、秒 の7つの値を取得することです。

I2CRの引数のI2CアドレスはI2CRと同じ#68を指定します。
コマンドおよびコマンド長にはレジスタアドレス表の00hを指定します。
受信データおよびデータ長は取得したデータを格納するアドレスとして#710、長さ7を指定します。

以下が実際のプログラムと実行結果です。
10 POKE #700,0
20 R=I2CR(#68,#700,1,#710,7)
30 ?R
40 FOR I=0 TO 6
50 ?HEX$(PEEK(#710+I),2)
60 NEXT

2

10行はコマンドを#700に設定
20行はI2Cアドレス#68、コマンド格納アドレスとして#700、その長さとして1を指定、
受信したデータ格納アドレスとして#710、その長さとして7を指定しています。
30行がI2CRの実行結果の表示
40~60行は#710#706に格納されている取得データを16進数2桁で表示しています。


IchigoJamのI2CW、I2CRのバグ(不具合)について


2つのコマンド、
  I2CW(I2Cアドレス、 コマンド、コマンド長、送信データ、データ長)
  I2CR(I2Cアドレス、 コマンド、コマンド長、受信データ、データ長)

において次のバグ(不具合)があります。
・コマンドにて送信するデータはコマンド長0を指定すると意味不明なデータが
  I2Cデバイスに送信されます。この時、データ長も固定ではないようです。

・コマンドにて5バイト以上のデータを送信するとI2Cデバイスには
  正しくないデータが送信されます。

(本不具合は開発者に連絡済です)
なのでコマンドは1~4バイトの範囲で利用しましょう。

2017/04/03 追記
最新のβファームでは上記不具合は対応済みです。
(facebook IchigoJam-FANにて公開 IchigoJam 1.2 beta 42)

以前試した、Wii用ヌンチャク等も利用可能になりました。

2016年9月11日 (日)

aitendoのこどもパソコン「ai.Jam-T」キット を組み立てました

秋葉原に行ったついでに購入して放置しいた「ai.Jam-T」キットを本日組み立てました。

Aitendo_ichigojam

999円(税別)とリーズナブルな価格です。
基板サイズ・コネクタ・ピン位置は純正と同じです。

中身はこんな感じです。パーツケースが付いてます。
抵抗、コンデンサには丁寧にラベルは貼ってあります。

01

MPU(LPC1114)、抵抗、コンデンサは表面実装タイプです。
表面実装のはんだ付けの練習には良いキットだと思います。
はんだ付け初心者にはちょっと辛いかもしれませんね。

02

表面実装のはんだ付けはフラックスは必須です。

03

説明書等はありません。
商品ページに掲載の図およびパーツリストを参考にして組み立てていきます。

背の低いパーツから部品を取りつていきます。
まずは心臓部のLPC1114FDH28/102を取り付けます。

このLPC1114FDH28/102の足のピッチは0.65mmです。
端子取り付け用の2.54mmピッチの穴と比べるとその狭さが分かります。
これが一番、難易度が高いですが、なんとか取り付けられました。

04

軽いチップ抵抗やチップコンデンサははんだを当てると動いてしまうので、
途中でクリームはんだに切り替えました。

06

取りあえず完成。
やはり表面実装はちょっと時間がかかりますね。

07

純正品と比べると黄色のAV単位のプラスチックが薄くて剛性がなく変形します。
プラグの抜き差しを繰り返すうちにもげてしまうかもしれません。

電源用のスライドスイッチが固くてヘッドのプラスチックが細いのでこちらも
そのうち破損する可能性があります。

タクトスイッチはヘッドが付いており、純正品よりも使いやすいです。

完成後、ファームウェアを書きこんで、画面表示、キーボード入力、LEDが付くことを
確認し特に問題無しでした。

08

はんだ付けに自信がある方は、純正品よりもこちらがおすすめです。

別途半実装タイプもありますが、基板750円(MPU実装済)部品ボックス395円
若干高くなります。この場合のパーツは表面実装ではないようです。

より以前の記事一覧