セガサターンのキーボードをPCにつなぐ(3)~完結編 ProMicro登場の巻~

はじめに

前回でセガサターン用キーボードの全貌が明らかになった。

megatsar.hatenablog.com

今回はサターンキーボード用デコーダの作製といっても良い(よくない)。
キーコードの手打ちが厄介だったぞ。
それじゃぁいくぜ!!

前回のおさらい

サターンキーボードのキーを押す/離すとMake/Brakeがその瞬間だけ1になる。
その時キーコードも出てくる。
f:id:MegaTsar:20190623145501p:plain 上図はZを押しながらXを押したときの信号。
キーコードB001001 = 0x22はXのもの。

やったことまとめ

方針
  • サターンからキーボードへの入力(TR,TH)を完コピする。
  • Make/Brakeが1になった時キーコードを読む。
  • ライブラリに頼る。
ピンアサイ

f:id:MegaTsar:20190622204325p:plain ProMicroにはこうつなぐ。 f:id:MegaTsar:20190627082443j:plain
今回THは使用していないのでオープンで良い。
VDDとGNDは勝手につないでください。
キーボードのVDDは5 Vなので3.3 VのProMicroを買っちゃダメよ。

スケッチ

Keyboardライブラリは英字用なので困った。
偉大な人がKeyboard_jpライブラリを作っておられる[1]のでそれを導入した。
ありがとうございます。

#include "Keyboard_jp.h"

//pin assign
const int L = 3;
const int R = 4;
const int TL = 5; // not used in this sketch
const int TR = 6;
const int TH = 7;
const int U = 8;
const int D = 9;

int BK = 0; // 1 at brake moment only
int MK = 0; // 1 at make moment only
byte KC[8];  // 8bit keycode from keyboard(this is address for key map)
byte CKC = B00000000; //combined KC

int i;

//KeyMap[address] output is UsageIndex, address is keycode from SegaSaturn
const byte KM[144] = {
  0x00, 0x39, 0x3e, 0x3c, 0x3a, 0x3b, 0x45, 0x00, 0x43, 0x41, 0x3f, 0x3d, 0x00, 0x2b, 0x35, 0x00,
  0x00, 0xe2, 0xe1, 0x88, 0xe0, 0x14, 0x1e, 0xe6, 0xe4, 0x00, 0x1d, 0x16, 0x04, 0x1a, 0x1f, 0x00,
  0x00, 0x06, 0x1b, 0x07, 0x08, 0x21, 0x20, 0x00, 0x00, 0x2c, 0x19, 0x09, 0x17, 0x15, 0x22, 0x00,
  0x00, 0x11, 0x05, 0x0b, 0x0a, 0x1c, 0x23, 0x00, 0x00, 0x00, 0x10, 0x0d, 0x18, 0x24, 0x25, 0x00,
  0x00, 0x36, 0x0e, 0x0c, 0x12, 0x27, 0x26, 0x00, 0x00, 0x37, 0x38, 0x0f, 0x33, 0x13, 0x2d, 0x00,
  0x00, 0x87, 0x34, 0x00, 0x2f, 0x2e, 0x00, 0x00, 0x39, 0xe5, 0x28, 0x30, 0x00, 0x31, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x2a, 0x8b, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00,
  0x00, 0x49, 0x48, 0x40, 0x46, 0x4c, 0x50, 0x4a, 0x4d, 0x52, 0x51, 0x4b, 0x4e, 0x4f, 0x00, 0x00
};

//function for pulse making
void dWD(int pinout, int HL, int delaytime) {
  if (HL == HIGH) {
    digitalWrite(pinout, HIGH);
  } else {
    digitalWrite(pinout, LOW);
  }
  delayMicroseconds(delaytime);
}

void setup() {
  pinMode(U, INPUT);
  pinMode(D, INPUT);
  pinMode(L, INPUT);
  pinMode(R, INPUT);
  pinMode(TL, OUTPUT);
  pinMode(TR, OUTPUT);
  pinMode(TH, OUTPUT);
  Keyboard.begin();
}

void loop() {
  dWD(TH, LOW, 0);    // #2
  dWD(TR, HIGH, 0);   // #2
  dWD(TR, HIGH, 60);  // #2
  dWD(TR, LOW, 8);    // x
  dWD(TR, LOW, 16);   // #3
  dWD(TR, HIGH, 13);  // x
  dWD(TR, HIGH, 28);  // #4
  dWD(TR, LOW, 9);    // x
  dWD(TR, LOW, 15);   // #5
  dWD(TR, HIGH, 11);  // x
  dWD(TR, HIGH, 54);  // #6
  dWD(TR, LOW, 11);   // x
  dWD(TR, LOW, 13);   // #7
  dWD(TR, HIGH, 13);  // x
  dWD(TR, HIGH, 37);  // #8
  dWD(TR, LOW, 12);   // x
  dWD(TR, LOW, 12);   // #9
  dWD(TR, HIGH, 13);  // x
  dWD(TR, HIGH, 37);  // #10
  BK = digitalRead(U); // break check
  MK = digitalRead(R); // make check
  dWD(TR, LOW, 9);    // x
  dWD(TR, LOW, 15);   // #11
  if (MK == HIGH || BK == HIGH) {
    KC[7] = digitalRead(R); // getting keycode from D7-D4
    KC[6] = digitalRead(L);
    KC[5] = digitalRead(D);
    KC[4] = digitalRead(U);
  }
  dWD(TR, HIGH, 14);  // x
  dWD(TR, HIGH, 41);  // #12
  if (MK == HIGH || BK == HIGH) {
    KC[3] = digitalRead(R); // getting keycode from D3-D0
    KC[2] = digitalRead(L);
    KC[1] = digitalRead(D);
    KC[0] = digitalRead(U);
  }
  dWD(TR, LOW, 13);   // x
  dWD(TR, LOW, 11);   // #13
  dWD(TR, LOW, 14);   // x
  dWD(TR, HIGH, 125); // #14
  dWD(TH, HIGH, 0);   // end & #1

  if (MK == HIGH) {
    for (i = 0; i < 8; i++) {
      CKC += KC[i] << i;    // combining 1bit discrete keycodes to 8bit keycode
    }
    Keyboard.pressRaw(KM[CKC]); //press check

  } else if (BK == HIGH) {
    for (i = 0; i < 8; i++) {
      CKC += KC[i] << i;    // combining 1bit discrete keycodes to 8bit keycode
    }
    Keyboard.releaseRaw(KM[CKC]); // release check
  }
  MK = 0;
  BK = 0;
  CKC = B00000000;
  delay(16);
  delayMicroseconds(600);

}

loop内でTRとTHを変化させて信号を作る。
スケッチ内の#2とかは下の信号番号に対応しているぞ。 f:id:MegaTsar:20190622214637p:plain

#10のR/UがMake(MK)/Brake(BK)に対応してるのでそれを読む。
#11でKeyCode(KC)の頭4ビット
#12でおしり4ビットを読む。

dWD(pinout, HL, delaytime)は見やすさのために作った。
[pinout]を[HL]状態にした後[delaytime] microseconds待つという関数。
これ見やすいか?

loopの終わりでMK or BK = 1の場合のみ何押したか判定を行う。
その際KeyMap(KM[KeyCode])を使う。
サターンからのキーコードをUsageIndexにするためのものです。

Keyboardライブラリを使う場合ASCIIコードでpress/release判定を行う。
今回はKeyboard_jpライブラリでKeyboard.pressRaw/releaseRawを使う。
そのため引数がUsageIndexになっている[1]。注意してください。

最後の16.6 msのdelayはサターン実機がそのくらいだからいれた。
どのくらい早くできるかはわかんないゾ。
暇人はやってみてね。

苦労話

キーコードを1bitでとってきてるので8bitに結合しなきゃいけなかった。
「powでいけるッショ」と思った。
でも誤差が狂ったように出ていけなかった。
何でですかね?
だからビットシフトで2倍にして足したぞ。
ちょっとデジタル知ってる感出してイキりたかったわけではないぞ。

まとめ

サターンのキーボードがPCで動いてみんなうれしいだろう?
普通に打てるから私はビビっておしっこもらしました。
みんなもSEGAのキーボードを机の上においてイキろう!!!!! f:id:MegaTsar:20190627235116j:plain

参考

[1] Arduino Leonardoで\記号を打つ:メガギガテラス:So-netブログ
ありがとうございました。これがないとヤバかったです。

おまけ

keymap用表

キー名の下がUsageIndexになっている。
f:id:MegaTsar:20190627100034j:plain もしくはこれをエクセルかなんかで開こう。

,0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,
00,,F9,F5,F3,F1,F2,F12,,F10,F8,F6,F4,,tab,hankaku,,,,,,,,,,,,,,,,,
,0x00,0x39,0x3e,0x3c,0x3a,0x3b,0x45,0x00,0x43,0x41,0x3f,0x3d,0x00,0x2b,0x35,0x00,
10,,alt,shit,katakana,ctrl,Q,1,alt,ctrl,,Z,S,A,W,2,,,,,,,,,,,,,,,,,
,0x00,0xe2,0xe1,0x88,0xe0,0x14,0x1e,0xe6,0xe4,0x00,0x1d,0x16,0x04,0x1a,0x1f,0x00,
20,,C,X,D,E,4,3,,,space,V,F,T,R,5,,,,,,,,,,,,,,,,,
,0x00,0x06,0x1b,0x07,0x08,0x21,0x20,0x00,0x00,0x2c,0x19,0x09,0x17,0x15,0x22,0x00,
30,,N,B,H,G,Y,6,,,,M,J,U,7,8,,,,,,,,,,,,,,,,,
,0x00,0x11,0x05,0x0b,0x0a,0x1c,0x23,0x00,0x00,0x00,0x10,0x0d,0x18,0x24,0x25,0x00,
40,,<,K,I,O,0,9,,,>,?,L,;,P,-,,,,,,,,,,,,,,,,,
,0x00,0x36,0x0e,0x0c,0x12,0x27,0x26,0x00,0x00,0x37,0x38,0x0f,0x33,0x13,0x2d,0x00,
50,,_,:,,@,^,,,capsl,shift,enter,[,,],,,,,,,,,,,,,,,,,,
,0x00,0x87,0x34,0x00,0x2f,0x2e,0x00,0x00,0x39,0xe5,0x28,0x30,0x00,0x31,0x00,0x00,
60,,,,,henkan,,back,muhenkan,,,bslash,,,,,,,,,,,,,,,,,,,,,
,0x00,0x00,0x00,0x00,0x8a,0x00,0x2a,0x8b,0x00,0x00,0x89,0x00,0x00,0x00,0x00,0x00,
70,,,,,,,esc,,F11,,,,,,scrl,,,,,,,,,,,,,,,,,
,0x00,0x00,0x00,0x00,0x00,0x00,0x29,0x00,0x44,0x00,0x00,0x00,0x00,0x00,0x47,0x00,
80,,ins,pause,F7,print,del,left,home,end,up,down,pup,pdown,right,,,,,,,,,,,,,,,,,,
,0x00,0x49,0x48,0x40,0x46,0x4c,0x50,0x4a,0x4d,0x52,0x51,0x4b,0x4e,0x4f,0x00,0x00,
つなぐヤツの外装

こじゃれた外装でライバルに差をつけろ!!
f:id:MegaTsar:20190627233943j:plain
おうちにある余った箱で激エモなヤツをつくろう!!
出来たらみせてね。 f:id:MegaTsar:20190627233940j:plain
中身

キーボード比較コーナー

f:id:MegaTsar:20190627234459j:plain
SEGAのキーボードとREALFORCEを並べてみた。
外見上の違いはテンキー、Windowsキーアプリケーションキーがないこと。
それ以外は似ている。もうどっちがREALFORCEか私にはわかんないよ。
タイプした感じはSEGAFORCEのほうがデカい音を発する。
スコスコ感があるためタイプ時の爽快感が非常によろしい。

おわり