1.目的
USBキーボードのキー変換をする変換器を作成しました。
(USB Keyboard) → (変換器) → (Host PC)
2.利用する基盤
(1) Pro micro (8MHz, 3.3V) (写真上段:変換器の表側)
(2) mini USB Host Shield(写真下段:変換器の裏側)
(配線/組み立てはこちらを参照にしました)
今回作成した変換器
3.利用するプログラム
プログラミングにはarduino(C/C++)の統合開発基盤を利用しました。
(1) 開発環境
PC:Lenovo IdeaPad Flex10
OS:Linux Ubuntu 20.04.1 TLS
SW:Arduino 1.8.13
(2) ライブラリ
Sketch > Include Library > Manage Library から「HID-project」、「USB
Host Shield Library 2.0」をインストールする。
(3) プログラム概要
Keyboardのキーが押される度にKeyboardReportParser が呼び出される。
*bufに入力されたキー情報をoutbufにCOPYし、oldbufと比較することでキーの
On/Offを判定し、BootKeyboard.add/removeにより出力キーをセットし、
BootKeyboard.sendでHost PCに送信する。
HID-projectによりBIOS設定時でも利用可能でした。
outufにCOPYしたキー情報(USB Scancodeです)
キー情報を単に透過するプログラム
#include <hid-project.h>
#include <hidboot.h>
uint8_t i, outbuf[8], oldbuf[8];
uint8_t modp[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
uint8_t modo[8]={0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7};
class HIDKeyboardParser : public KeyboardReportParser {
virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};
void HIDKeyboardParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {
memcpy(outbuf,buf,8);
/* 必要に応じてoutbufをキー変換する*/
for(i=0;i<8;i++){
if(((outbuf[0]&modp[i])!=0)&&((oldbuf[0]&modp[i])==0)) BootKeyboard.add(KeyboardKeycode(modo[i]));
if(((outbuf[0]&modp[i])==0)&&((oldbuf[0]&modp[i])!=0)) BootKeyboard.remove(KeyboardKeycode(modo[i]));
}
for(i=2;i<8;i++){
if((outbuf[i]!=0)&&(oldbuf[i]==0)) BootKeyboard.add(KeyboardKeycode(outbuf[i]));
if((outbuf[i]==0)&&(oldbuf[i]!=0)) BootKeyboard.remove(KeyboardKeycode(oldbuf[i]));
if((outbuf[i]!=oldbuf[i])&&(outbuf[i]!=0)) BootKeyboard.remove(KeyboardKeycode(oldbuf[i]));
}
BootKeyboard.send();
memcpy(oldbuf,outbuf,8);
}
USB usb;
HIDBoot<hid_protocol_keyboard> HidKeyboard(&usb);
HIDKeyboardParser parser;
void setup() {
Serial.begin(115200);
BootKeyboard.begin();
if (usb.Init() == -1) Serial.println("OSC did not start.");
delay(200);
HidKeyboard.SetReportParser(0, (HIDKeyboardParser*)&parser);
}
void loop() {
usb.Task();
}
【利用例】Sampleプログラムの主な機能を下記に示す。
US配列キーボードのままJapanese Keyboard・US keyboardを切り替えて使います。
(Fn+BackspaceでJP/USを切り替え。)
SpaceキーをTap(Space)、Hold(Fn)にする。
Japanese Keyboardの場合は、CapsLockキーをE/Jキーにする。
(他にShift+CapsLock ⇨ 変換、Fn+CapsLock⇨無変換)
スクリーンセーバー抑止で1分毎に自動キー出力する。
(Windows10連続キー条件からCtrlとShiftを交互に出力する。)
入力されたScancodeで直接デーブルの行検索するため0x00から0x39のキーを定義した。
(Tableにないキーはそのまま入力⇨出力で転送されます)
Layer情報で直接テーブルの列検索をします。
追加のArduinoライブラリ:「MsTimer2」
Sampleのキーマップ(下図の黄色はキー変換した部分)
Keymap (US to JP scancode)
keymap (US to US scancode)
Sampleプログラム
#include <hid-project.h>
#include <hidboot.h>
#include <mstimer2.h>
uint8_t i, t=0, outbuf[8], oldbuf[8], layer=0, atc[58][6];
uint8_t modp[4]={0x11,0x22,0x44,0x88}, modo[4]={0xe0,0xe1,0xe2,0xe3};
static const uint8_t jpc[][6] = { // US Keycap -> JP Scancode
{0,0x00,0,0x00,0,0x00}, // 0x00 dumy
{0,0x00,0,0x00,0,0x00}, // 0x01 dumy
{0,0x00,0,0x00,0,0x00}, // 0x02 dumy
{0,0x00,0,0x00,0,0x00}, // 0x03 dumy layer0 layer2 layer4
{0,0x04,1,0x04,0,0x4A}, // 0x04 'a' -> 'a' , 'A' , 'home'
{0,0x05,1,0x05,0,0x00}, // 0x05 'b' -> 'b' , 'B' , ''
{0,0x06,1,0x06,0,0x00}, // 0x06 'c' -> 'c' , 'C' , ''
{0,0x07,1,0x07,0,0x51}, // 0x07 'd' -> 'd' , 'D' , '↓'
{0,0x08,1,0x08,0,0x52}, // 0x08 'e' -> 'e' , 'E' , '↑'
{0,0x09,1,0x09,0,0x4F}, // 0x09 'f' -> 'f' , 'F' , '→'
{0,0x0A,1,0x0A,0,0x4D}, // 0x0A 'g' -> 'g' , 'G' , 'End'
{0,0x0B,1,0x0B,0,0x2A}, // 0x0B 'h' -> 'h' , 'H' , 'BS'
{0,0x0C,1,0x0C,0,0x00}, // 0x0C 'i' -> 'i' , 'I' , ''
{0,0x0D,1,0x0D,0,0x28}, // 0x0D 'j' -> 'j' , 'J' , 'RT'
{0,0x0E,1,0x0E,0,0x00}, // 0x0E 'k' -> 'k' , 'K' , ''
{0,0x0F,1,0x0F,0,0x00}, // 0x0F 'l' -> 'l' , 'L' , ''
{0,0x10,1,0x10,0,0x00}, // 0x10 'm' -> 'm' , 'M' , ''
{0,0x11,1,0x11,0,0x00}, // 0x11 'n' -> 'n' , 'N' , ''
{0,0x12,1,0x12,0,0x00}, // 0x12 'o' -> 'o' , 'O' , ''
{0,0x13,1,0x13,0,0x00}, // 0x13 'p' -> 'p' , 'P' , ''
{0,0x14,1,0x14,0,0x00}, // 0x14 'q' -> 'q' , 'Q' , ''
{0,0x15,1,0x15,0,0x4E}, // 0x15 'r' -> 'r' , 'R' , 'PgDn'
{0,0x16,1,0x16,0,0x50}, // 0x16 's' -> 's' , 'S' , '←'
{0,0x17,1,0x17,0,0x00}, // 0x17 't' -> 't' , 'T' , ''
{0,0x18,1,0x18,0,0x00}, // 0x18 'u' -> 'u' , 'U' , ''
{0,0x19,1,0x19,0,0x00}, // 0x19 'v' -> 'v' , 'V' , ''
{0,0x1A,1,0x1A,0,0x4B}, // 0x1A 'w' -> 'w' , 'W' , 'PgUp'
{0,0x1B,1,0x1B,0,0x00}, // 0x1B 'x' -> 'x' , 'X' , ''
{0,0x1C,1,0x1C,0,0x00}, // 0x1C 'y' -> 'y' , 'Y' , ''
{0,0x1D,1,0x1D,0,0x00}, // 0x1D 'z' -> 'z' , 'Z' , ''
{0,0x1E,1,0x1E,0,0x3A}, // 0x1E '1' -> '1' , '!' , 'F1'
{0,0x1F,0,0x2F,0,0x3B}, // 0x1F '2' -> '2' , '@' , 'F2'
{0,0x20,1,0x20,0,0x3C}, // 0x20 '3' -> '3' , '#' , 'F3'
{0,0x21,1,0x21,0,0x3D}, // 0x21 '4' -> '4' , '$' , 'F4'
{0,0x22,1,0x22,0,0x3E}, // 0x22 '5' -> '5' , '%' , 'F5'
{0,0x23,0,0x2E,0,0x3F}, // 0x23 '6' -> '6' , '^' , 'F6'
{0,0x24,1,0x23,0,0x40}, // 0x24 '7' -> '7' , '&' , 'F7'
{0,0x25,1,0x34,0,0x41}, // 0x25 '8' -> '8' , '*' , 'F8'
{0,0x26,1,0x25,0,0x42}, // 0x26 '9' -> '9' , '(' , 'F9'
{0,0x27,1,0x26,0,0x43}, // 0x27 '0' -> '0' , ')' , 'F10'
{0,0x28,0,0x00,2,0x28}, // 0x28 'RT' -> 'RT' , '' , ''
{0,0x29,0,0x00,0,0x35}, // 0x29 'esc' -> 'esc', '' , 'E/J'
{0,0x2A,0,0x00,0,0x00}, // 0x2A 'bs' -> 'bs' , '' , 'jp flag'
{0,0x2B,0,0x00,0,0x3B}, // 0x2B 'tab' -> 'tab', '' , 'F2'
{0,0x2C,1,0x2C,0,0x00}, // 0x2C 'spc' -> 'spc', 'spc', ''
{0,0x2D,1,0x87,0,0x44}, // 0x2D '-' -> '-' , '_' , 'F11'
{1,0x2D,1,0x33,0,0x45}, // 0x2E '=' -> '=' , '+' , 'F12'
{0,0x30,1,0x30,0,0x00}, // 0x2F '[' -> '[' , '{' , ''
{0,0x31,1,0x31,0,0x00}, // 0x30 ']' -> ']' , '}' , ''
{0,0x89,1,0x89,0,0x00}, // 0x31 '¥' -> '¥' , '|' , ''
{0,0x00,0,0x00,0,0x00}, // 0x32 '' -> '' , '' , ''
{0,0x33,0,0x34,0,0x00}, // 0x33 ';' -> ';' , ':' , ''
{1,0x24,1,0x1F,0,0x00}, // 0x34 ''' -> ''' , '"' , ''
{0,0x00,1,0x2E,0,0x00}, // 0x35 ''' -> '`' , '~' , '`'
{0,0x36,1,0x36,1,0x2F}, // 0x36 ',' -> ',' , '<' , '`'
{0,0x37,1,0x37,0,0x00}, // 0x37 '.' -> '.' , '>' , ''
{0,0x38,1,0x38,0,0x00}, // 0x38 '/' -> '/' , '?' , ''
{0,0x35,0,0x8A,0,0x8B} // 0x39 'cps' -> 'E/J', 'Cnv', 'NCnv'
};
static const uint8_t usc[][6] = { // US Keycap -> US Scancode
{0,0x00,0,0x00,0,0x00}, // 0x00 dumy
{0,0x00,0,0x00,0,0x00}, // 0x01 dumy
{0,0x00,0,0x00,0,0x00}, // 0x02 dumy
{0,0x00,0,0x00,0,0x00}, // 0x03 dumy layer0 layer2 layer4
{0,0x04,1,0x04,0,0x00}, // 0x04 'a' -> 'a' , 'A' , ''
{0,0x05,1,0x05,0,0x00}, // 0x05 'b' -> 'b' , 'B' , ''
{0,0x06,1,0x06,0,0x00}, // 0x06 'c' -> 'c' , 'C' , ''
{0,0x07,1,0x07,0,0x00}, // 0x07 'd' -> 'd' , 'D' , ''
{0,0x08,1,0x08,0,0x00}, // 0x08 'e' -> 'e' , 'E' , ''
{0,0x09,1,0x09,0,0x00}, // 0x09 'f' -> 'f' , 'F' , ''
{0,0x0A,1,0x0A,0,0x00}, // 0x0A 'g' -> 'g' , 'G' , ''
{0,0x0B,1,0x0B,0,0x2A}, // 0x0B 'h' -> 'h' , 'H' , 'BS'
{0,0x0C,1,0x0C,0,0x00}, // 0x0C 'i' -> 'i' , 'I' , ''
{0,0x0D,1,0x0D,0,0x28}, // 0x0D 'j' -> 'j' , 'J' , 'RT'
{0,0x0E,1,0x0E,0,0x00}, // 0x0E 'k' -> 'k' , 'K' , ''
{0,0x0F,1,0x0F,0,0x00}, // 0x0F 'l' -> 'l' , 'L' , ''
{0,0x10,1,0x10,0,0x00}, // 0x10 'm' -> 'm' , 'M' , ''
{0,0x11,1,0x11,0,0x00}, // 0x11 'n' -> 'n' , 'N' , ''
{0,0x12,1,0x12,0,0x00}, // 0x12 'o' -> 'o' , 'O' , ''
{0,0x13,1,0x13,0,0x00}, // 0x13 'p' -> 'p' , 'P' , ''
{0,0x14,1,0x14,0,0x00}, // 0x14 'q' -> 'q' , 'Q' , ''
{0,0x15,1,0x15,0,0x00}, // 0x15 'r' -> 'r' , 'R' , ''
{0,0x16,1,0x16,0,0x00}, // 0x16 's' -> 's' , 'S' , ''
{0,0x17,1,0x17,0,0x00}, // 0x17 't' -> 't' , 'T' , ''
{0,0x18,1,0x18,0,0x00}, // 0x18 'u' -> 'u' , 'U' , ''
{0,0x19,1,0x19,0,0x00}, // 0x19 'v' -> 'v' , 'V' , ''
{0,0x1A,1,0x1A,0,0x00}, // 0x1A 'w' -> 'w' , 'W' , ''
{0,0x1B,1,0x1B,0,0x00}, // 0x1B 'x' -> 'x' , 'X' , ''
{0,0x1C,1,0x1C,0,0x00}, // 0x1C 'y' -> 'y' , 'Y' , ''
{0,0x1D,1,0x1D,0,0x00}, // 0x1D 'z' -> 'z' , 'Z' , ''
{0,0x1E,1,0x1E,0,0x3A}, // 0x1E '1' -> '1' , '!' , 'F1'
{0,0x1F,1,0x1F,0,0x3B}, // 0x1F '2' -> '2' , '@' , 'F2'
{0,0x20,1,0x20,0,0x3C}, // 0x20 '3' -> '3' , '#' , 'F3'
{0,0x21,1,0x21,0,0x3D}, // 0x21 '4' -> '4' , '$' , 'F4'
{0,0x22,1,0x22,0,0x3E}, // 0x22 '5' -> '5' , '%' , 'F5'
{0,0x23,1,0x23,0,0x3F}, // 0x23 '6' -> '6' , '^' , 'F6'
{0,0x24,1,0x24,0,0x40}, // 0x24 '7' -> '7' , '&' , 'F7'
{0,0x25,1,0x25,0,0x41}, // 0x25 '8' -> '8' , '*' , 'F8'
{0,0x26,1,0x26,0,0x42}, // 0x26 '9' -> '9' , '(' , 'F9'
{0,0x27,1,0x27,0,0x43}, // 0x27 '0' -> '0' , ')' , 'F10'
{0,0x28,0,0x00,0,0x00}, // 0x28 'RT' -> 'RT' , '' , ''
{0,0x29,0,0x00,0,0x35}, // 0x29 'esc' -> 'esc', '' , '`'
{0,0x2A,0,0x00,1,0x00}, // 0x2A 'bs' -> 'bs' , '' , 'us flag'
{0,0x2B,0,0x00,0,0x00}, // 0x2B 'tab' -> 'tab', '' , ''
{0,0x2C,1,0x2C,0,0x00}, // 0x2C 'spc' -> 'spc', 'spc', ''
{0,0x2D,1,0x2D,0,0x44}, // 0x2D '-' -> '-' , '_' , 'F11'
{0,0x2E,1,0x2E,0,0x45}, // 0x2E '=' -> '=' , '+' , 'F12'
{0,0x2F,1,0x2F,0,0x00}, // 0x2F '[' -> '[' , '{' , ''
{0,0x30,1,0x30,0,0x00}, // 0x30 ']' -> ']' , '}' , ''
{0,0x31,1,0x31,0,0x00}, // 0x31 '¥' -> '¥' , '|' , ''
{0,0x32,1,0x32,0,0x00}, // 0x32 '' -> '' , '' , ''
{0,0x33,1,0x33,0,0x2A}, // 0x33 ';' -> ';' , ':' , ''
{0,0x34,1,0x34,0,0x28}, // 0x34 ''' -> ''' , '"' , ''
{0,0x00,1,0x35,0,0x00}, // 0x35 ''' -> '' , '~' , ''
{0,0x36,1,0x36,0,0x00}, // 0x36 ',' -> ',' , '<' , ''
{0,0x37,1,0x37,0,0x00}, // 0x37 '.' -> '.' , '>' , ''
{0,0x38,1,0x38,0,0x00}, // 0x38 '/' -> '/' , '?' , ''
{0,0x39,0,0x00,0,0x00} // 0x39 'cps' -> 'cps', '' , ''
};
class HIDKeyboardParser : public KeyboardReportParser {
virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};
void HIDKeyboardParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {
memcpy(outbuf,buf,8);
switch(layer){
case 0: // Normal layer
if((*buf&0x22)!=0) layer=2; // shift key on to layer 2
if((*(buf+2)==0x2C)&&(*(buf+3)==0x00)){ // space key on to layer 3
layer=3;
return 0;
}
break;
case 2: // shift layer
if((*buf&0x22)==0) layer=0; // shift key off to layer 0
break;
case 3: // space key on
if(*(buf+2)==0x00){ // taped space key off
BootKeyboard.write(KeyboardKeycode(0x2c)); // Push & release space key
layer=0;
return 0;
} else if(*(buf+3)!=0x00) layer=4; // Fanctional space key off
case 4: // Function layer
if((*(buf+2)==0x00)){ // space off to layer 0
layer=0;
return 0;
}
if(*(buf+3)==0x2A){ // Fn + BS
if(atc[0x2A][4]==0) memcpy(atc,usc,348); // to us keycode
else memcpy(atc,jpc,348); // to jp keycode
}
}
for(i=2;i<8;i++){ // change key
if((*(buf+i)>0x03)&&(*(buf+i)<0x40)){ // 0x04('1') <= input key <= 0x39('CapsLock')
if(atc[*(buf+i)][layer]==0) outbuf[0]=outbuf[0]&0xdd; // Set OFF shift key
if(atc[*(buf+i)][layer]==1) outbuf[0]=outbuf[0]|0x02; // Set On shift key
if(atc[*(buf+i)][layer]==2) outbuf[0]=outbuf[0]|0x04; // Set On ALT key
outbuf[i]=atc[*(buf+i)][layer+1]; // Set key changed
}else outbuf[i]=*(buf+i); // Set key over 0x39 (Poker keyboard)
}
for(i=0;i<4;i++){ // Set modifers keys for bootkeyboard
if(((outbuf[0]&modp[i])!=0)&&((oldbuf[0]&modp[i])==0)) BootKeyboard.add(KeyboardKeycode(modo[i]));
if(((outbuf[0]&modp[i])==0)&&((oldbuf[0]&modp[i])!=0)) BootKeyboard.remove(KeyboardKeycode(modo[i]));
}
for(i=2;i<8;i++){ // Set normal kes for bootkeyboard
if((outbuf[i]!=0)&&(oldbuf[i]==0)) BootKeyboard.add(KeyboardKeycode(outbuf[i]));
if((outbuf[i]==0)&&(oldbuf[i]!=0)) BootKeyboard.remove(KeyboardKeycode(oldbuf[i]));
if((outbuf[i]!=oldbuf[i])&&(outbuf[i]!=0)) BootKeyboard.remove(KeyboardKeycode(oldbuf[i]));
}
BootKeyboard.send(); // send krycode to Host PC
memcpy(oldbuf,outbuf,8); // copy to oldbuf from outbuf
t=0; // Timer clear
}
void timerFire() {
if((t%2)==0) BootKeyboard.write(KeyboardKeycode(0xe1)); // Push & release Shift key 60s interval
else BootKeyboard.write(KeyboardKeycode(0xe0)); // Push & release Ctrl key 60s interval
t++;
}
USB usb;
HIDBoot<hid_protocol_keyboard> HidKeyboard(&usb);
HIDKeyboardParser parser;
void setup() {
Serial.begin(115200);
BootKeyboard.begin(); // Init HID on promicro
if (usb.Init() == -1) Serial.println("OSC did not start.");
delay(200);
memcpy(atc,jpc,348); // set JP keycode on default
HidKeyboard.SetReportParser(0, (HIDKeyboardParser*)&parser); // Init host shield
MsTimer2::set(60000, timerFire); // set timer interval 60s
MsTimer2::start();
}
void loop() {
usb.Task();
}
<謝辞>
「東京お気楽カメラ」様 ハードウェア組み立ての情報は大変助かりました。
「森山 将之のホームページへようこそ!」様 Scancode一覧は大変助かりました。
ありがとうございました。



