ふたたびASKeyboardをUSB親指シフトキーボードにする

ASKeyboard sono1

概要

4年ほど前、 PC9800シリーズ用のASKeyboard(アスキーボード)を親指シフトのUSBキーボードとして使えるようにした。もともとコントローラーとして入っていたi8051を抜き、その代わりに SparkFun Pro Micro(コピー品)を手作り基板に載せて装着し、hoboNicolaライブラリを使って実現した。

ASkeyboard sono1
ASkeyboard sono1

以前の投稿では、ASKeyboardの中身や紹介についてはその1に、作成した基板の回路などはその2に掲載した。

ASkeyboardをUSBの親指シフトキーボードにする その1

ASkeyboardをUSBの親指シフトキーボードにする その2

4年前に改造したものも問題なく動いていたのだが、hoboNicolaライブラリもいろいろと進歩してきているので新しく作り直すことにし、以下のような作業を行った。

  • マイコンの32ビット化
    Seeed Studio XIAO-m0 (ATSAMD21G-18) を使う。機能や性能だけでなく大きさの面でも適している。
  • プリント基板を作成
    前にやったときはユニバーサル基板を使って手作りしたが、今回はFusion PCBに小さなプリント基板を発注した。回路図などは後ほど。
  • I/Oエクスパンダを利用
    利用できるGPIOが少ないXIAOで、12 x 8 キー構成のスイッチマトリックスをスキャンする必要があるので、SPI接続のI/Oエクスパンダ、MCP23S17MCP23S08を使うことにした。
    XIAOとこれらのデバイスの組み合わせでちゃんとマトリックススキャンができるのか確認することも今回の目的の一つ。
  • LEDを交換
    ASKeyboardにはもともとLEDが5つ入っているが、点灯時の消費電流は1個あたり10mA近い(そのため7407でドライブしていた)。省エネのため1mA前後でも十分明るい新しめのLEDに変更。
    LEDについては当初は考えていなかったのでプリント基板に配線を反映しておらず、後から手張りで対応した。
  • hoboNicolaライブラリ1.6.2に対応
    現在の最新バージョンに対応した。
  • HIDキーボードとして以下のようなレイアウトにする。
ASKeyboard sono1 HID Layout

赤字になっている部分が、ASKeyboardのキーキャップ印字から大きく異なっている部分になる。キーキャップの印字と実際が異なっていても、どうせキーボードはあまり見ないので問題ない。

キースイッチマトリックス

ASKeyboard (sono1)の中身は以下のような回路になっている。スイッチマトリックスやスイッチ自体は1988年の製造時のままである。以前の投稿に載せたものから信号の名称などを若干変更している。

Askeyboard_keymatrix

この回路図は独自研究に基づくものなので、正しい保証はありません。

本体部分(sono1)のキースイッチは、12列×8行のマトリックス構成になっており、ダイオードの向きは行(ROW)から列(COLUMN)。つまり、ある一つの列をLOWとしたとき、その列にオンになっているスイッチがあれば、対応する行がLOW状態になる。他の列はすべてHIGHにしておくので、LOWにした列とLOW状態になっている行の交点が、オン状態のスイッチということになる。なお、右側の点線で囲んだ部分が別体のテンキーパッド(ASKeyboard sono2)になるが、使わないので今回は対象外とした。

スイッチ番号とキーの関係

Keyboard-layout-editor.comでASKeyboard sono1の配列とスイッチ番号の関係を書いてみた。

ASKeyboard sono1

SW41は抜け番になっているが、残りはおおねねレイアウトに基づいたナンバリングになっている。基板上のスイッチの並びとは必ずしも一致していない。

プログラムでは、後述するI/Oエクスパンダによるマトリックススキャンの結果から、変化のあったキーを表すコード(スキャンコード)を生成し、PCに送信するHID Usage IDの表をスキャンコードで参照している。

スキャンコードは、(列番号 – 1) × 8 + 行番号 で算出している。たとえば回路図でC08とR07の交点にある SW42 がオンになっていればスキャンコードは、(8 – 1) × 8 + 7 = 63  ということになる。このスキャンコードで以下のコード表を引くことで、hoboNicolaライブラリに渡すHID Usage ID (この場合は、HID_ENTER) が決まる。

キーボードのスキャンを伴う実装でhoboNicolaライブラリを使う際には、マトリックススキャンして得られるONキーやOFFキーのコード(HID UsageID)を、USBキーボードインタフェース(MAX3421EやCH9350Lなど)から得られるコードと同じようにライブラリの key_event() に渡せばよい。

コード対応表

先に載せたHIDキーボードとしてのレイアウトを実現するため、以下のようなコード表を用いた。スイッチ番号、スキャンコード、HID Usage IDの関係を表している。HIDコード欄のシンボルが表す値は、hoboNicolaライブラリ 1.6.2 が含んでいる hid_keycode.h を参照のこと。

スイッチ
番号
内部
スキャンコード
キーの印字 実際のキー HIDコード 備考
1 1 STOP Pause HID_PAUSE
2 2 COPY 半角/全角 HID_ZENHAN
3 3 F1 HID_F1 Fn (F11)
4 4 F2 HID_F2 Fn (F12)
5 5 F3 HID_F3 Fn (F13)
6 6 F4 HID_F4 Fn (PrtSc)
7 7 F5 HID_F5 Fn (ScrLock)
8 8 F6 HID_F6
9 9 F7 HID_F7
10 10 F8 HID_F8
11 11 F9 HID_F9
12 12 F10 HID_F10
13 13 ESC Escape HID_ESCAPE
14 14 1   ! HID_1
15 15 2   “ HID_2
16 16 3   # HID_3
17 17 4   $ HID_4
18 18 5   % HID_5
19 19 6   & HID_6
20 20 7   ‘ HID_7
21 21 8   ( HID_8
22 22 9   ) HID_9
23 23 0 HID_0
24 24 –   = HID_MINUS
25 25 ^    ^  ~ HID_EQUALS
26 26 ¥  | ¥  | HID_J_BSLASH
27 61 BS HID_BACKSP
28 62 TAB HID_TAB
29 27 Q HID_Q
30 28 W HID_W
31 29 E HID_E
32 30 R HID_R
33 31 T HID_T
34 32 Y HID_Y
35 33 U HID_U
36 34 I HID_I
37 35 O HID_O
38 36 P HID_P
39 37 @   ~ @   ` HID_LBRACK
40 38 [   { HID_RBRACK
42 63 RETURN Enter HID_ENTER
43 64 CTRL Left Ctrl HID_L_CTRL
44 40 A HID_A
45 41 S HID_S
46 42 D HID_D
47 43 F HID_F
48 44 G HID_G
49 45 H HID_H
50 46 J HID_J
51 47 K HID_K
52 48 L HID_L
53 49 ;    + HID_SEMICOLON
54 65 :    * HID_QUOTE NICOLA時
後退(BS)
55 66 ]    } HID_J_RBR_32 NICOLA時
取消(Esc)
56 67 SHIFT Left Shift HID_L_SHIFT
57 50 Z HID_Z
58 51 X HID_X
59 52 C HID_C
60 53 V HID_V
61 54 B HID_B
62 55 N HID_N
63 56 M HID_M
64 57 ,    < HID_COMMA
65 58 .    > HID_PERIOD
66 59 /    ? HID_SLASH
67 60 _ HID_J_UL
68 68 SHIFT Right Shift HID_R_SHIFT
69 69 CAPS 英数 HID_CAPS
70 70 GRPH Left Gui HID_L_GUI Win
71 71 カナ Left Alt HID_L_ALT
72 72 TAB ひらがな HID_HIRAGANA
73 73  親指左 無変換 HID_MUHENKAN ※1
74 74 親指右 変換 HID_HENKAN ※1
75 75 空白 SPC HID_SPACE
76 76 CTRL right Ctrl HID_R_CTRL
77 77 ALT App HID_APP ※2  長押しFn
78 78 無変換 英数 HID_CAPS ※3
79 79 変換 ひらがな HID_HIRAGANA ※3
80 80 ROLL UP Page Up HID_PGUP
81 81 ROLL DN Page Down HID_PGDOWN
82 82 INS Insert HID_INSERT
83 83 HOME Home HID_HOME
84 84 DEL Delete HID_DELETE
85 85 HELP End HID_END
86 89 HID_U_ARROW
87 90 HID_L_ARROW
88 91 HID_R_ARROW
89 92 HID_D_ARROW
※1 親指キーのコード

左右の親指キーには、ふつうの日本語キーボードをhoboNicolaアダプターで使う場合とほぼ同様に、無変換キーと変換キーのコードを割り当てた。日本語入力時に単独打鍵と判断した場合は表内のコードを生成し、同時打鍵時にはコードを出力しない。

ASKeyboardは、写真のように親指キーの下に専用の無変換、変換キーを備えているので、親指キーを内部の同時打鍵専用(コード出力なし)として変換操作や無変換操作は専用のキーに任せることもできる。今回は※3に関係して親指周りで日本語と英数の切替え操作をまかなうため親指キー共有型とした。

※2 Appキーについて

hoboNicolaライブラリでは、指定のキーを 長押しFnキー とすることができる。長押しFnキーを所定の時間(200msec程度)以上押し続けることで、そのキー本来のコードは生成せず、キーボード内部でのみ有効な一種の修飾キーに化ける(単独で押した場合、キーを離した時点でそのキー本来のコードを出力する)。

hoboNicolaライブラリを使った他の実装と同じように、ASKeyboardでも滅多に活躍しないAppキー(ALTキー)を長押しFnキーとして利用している。Fn キーと併用することで、ふつうのUSBキーボードにあってASKeyboardにはないキー(F11, F12, PrtScr, ScrLockなど)やマルチメディア機能の音量変更、システムスリープのためのコード出力を実装。また、hoboNicolaライブラリの設定モードの開始にも用いる (Fn + Right Ctrl + S)。

※3 無変換、変換キーのコード

親指キーの下にあるNFER(無変換)とXFER(変換)は、それぞれ英数キーひらがなキーとした。

日本語入力と英数入力の切り替えについて

hoboNicolaライブラリは、内部がNICOLAモードのときに同時打鍵(NICOLA配列)を有効とするようになっている。NICOLAモードとする/しないを決める方法はいくつかあるのだが、今のところひらがなキーが押されたらNICOLAモード有効、英数キーが押されたらNICOLAモード解除となるように設定している。モード変更はトグルではなく一方通行である。

また日本語入力IMEの文字種の変更操作についても、ひらがなキーを打てば日本語(ひらがなまたはカタカナ)、英数キーを打てばIMEオフとなるよう、IME (Google 日本語入力)をカスタマイズして使っている。これらにより、2つのキーの操作だけでNICOLA配列による日本語入力と英数文字の入力を切り替えることできる。

hoboNicolaアダプターとIME状態通知プログラム(observe_ime.exe) を使っているとき、HTMLフォームのIDやパスワード欄にフォーカスすると、IMEの文字種がWindows APIで正しくとれずパスワードが正しく打てないことがある(Google Chromeを利用)。今回の実装ではそのような状況でも英数キーを打てばNICOLAモードが解除されるので、妙なパスワードを打ってしまうこともない。

ASKeyboard用のhoboNicolaの動作設定

上記のように動作させるため、hoboNicolaライブラリの設定は以下のようにしている。

設定2で右親指キーを空白キーとし、設定Hで変換キーを空白キーに置き換えているのは、IME文字種が英数となっているとき、右親指キーを空白キーとして利用するためである。IMEに対しては、空白キーでも変換キーでも同じような変換動作をするように設定している。

4年前の実装時には、内部がNICOLAモードのとき右親指キーがオンになったら空白キーのスキャンコードを生成するようにしていたが、今回は英数字のときも変換キーの操作で何かやらせたくなるかもしれないので、hoboNicolaの動作設定だけで切り替えられるようにした。

設定0半角/全角キーでNICOLAモード解除 を有効にしているのは、IMEオン(ひらがな)状態で全角英数字を入力するようなときに備えるため。住所を入れるときに全角じゃないと番地を受け付けないとか迷惑なフォームが結構あるから、全角英数字の入力をしやすくした。

IMEの設定について

ついでにIMEの文字種切り替え設定について。この設定は、ASKeyboardにかぎらずhoboNicolaアダプターでふつうの日本語キーボードをつかうときも同じ設定を使っている。IMEは Google日本語入力(GoogleJapaneseInput-2.28.4650.0+24.10.9)

モード 入力キー コマンド
直接入力 Hankaku/Zenkaku 全角英数に入力切替
直接入力 Hiragana ひらがなに入力切替
直接入力 Katakana 全角カタカナに入力切替
入力文字なし Eisu IMEを無効化
入力文字なし Hankaku/Zenkaku 全角英数に入力切替
入力文字なし Hiragana ひらがなに入力切替
入力文字なし Katakana 全角カタカナに入力切替
変換前入力中 Eisu IMEを無効化
変換前入力中 Hankaku/Zenkaku 全角英数に入力切替
変換前入力中 Hiragana ひらがなに入力切替
変換前入力中 Katakana 全角カタカナに入力切替

入力文字種切替(あるいは、IMEのオン/オフ切替)がトグル式になっていると、画面の文字種(あ、A)を見て操作することになるが、切替えが一方通行になっていれば、ひらがなキーや英数キーを叩けば思い通りの文字種に変わるのがいいところ。

LEDまわりの改造

以前やったときには、オリジナルと同様にOCドライバの7407でLEDを駆動していたが、今回は+3.3V電源になったことや消費電流を減らすため7407を外してXIAOのGPIOと(抵抗を介して)直結した。

直列抵抗も220~280Ωのものが使われていたので1kΩに変更した。従来は点灯時にLED1個あたり10mAほど流れていたが、今回は0.8~1.5mAほどになっている(青が0.8mA、赤が1.1mA、緑が1.5mAだった。色ごとに抵抗値変えるのも面倒なので)。

askeyboard LED変更前
askeyboard LED変更後

とりあえず3つもあれば十分なので、回路図でのL1、L2、L3の3つだけ使うことにした。L1(緑)がNICOLAモードLED、L2(赤)がCapsLock LED、L3(青)がScrLock LEDとした。

askeyboard LED変更後

その他の改造点

各行には集合抵抗を使ったプルアップ抵抗が接続されているが、電源電圧が+3.3Vになったことと、 スイッチ・オフ時の立上りを速くするため、もともとは22kΩ x 8だったものを10kΩ x 8に変更した。

ASKeyboard sono1
ASKeyboard sono1

3ポジションある側面のスライドスイッチもマトリックスの一部になっておりスイッチポジションもスキャン時点で検出することができる(今回のプログラムでは特に何もやっていない)。写真右側のコネクタは、テンキーパッドを接続するためのもの。

基板について

以前作った手作り基板は薄いフレキシブルなユニバーサル基板にPro MicroとHC138を2つ(2つで4 to 16のデコーダーとして使用)載せた構成だった。

askeyboard ProMicro board

この基板をキーボードの上部(COPYキーの上あたり)にあるi8051用の40ピンソケットに挿すことで、キーマトリックスのスキャンやLEDのオン/オフを行っていた。キーボードケースの上側(上蓋)を被せたときに当たらないよう、基板上のデバイスは裏側に配置していた(デバイスの高さはソケットの高さの程度なのでケースに干渉しない)。

ASkeyboard用基板
ASkeyboard用基板

今回作成した基板をセットすると以下のようになる。

askb-xiao-rev01 PCB

今回はキーボード基板のソケットを外し、自作基板を直付けした。ソケットを外した分だけ上部に余裕があるので、2つのI/OエクスパンダとXIAO(およびUSBケーブル)はケースに干渉しない。

askb-xiao-rev01 PCB

上から見るとこんな具合でキースイッチ側はギリギリの位置。PCBを設計しているときにはLEDのことを考えていなかったので、キーボードに装着した後で手張りで対処したが、ちょっと残念な感じ。

USBケーブルは、とりあえずケースの裏側まで引っ張り出せる長さのType-Cのオスーメスケーブルを作って接続した。裏側にメスコネクタが出るので、ふつうのType-Cケーブルに接続して使う。最近はダイソーの充電転送ケーブル(USB2.0 Type-C)をよく使っている。

Type-Cケーブル

もともとはちゃんとしたケーブルをケース内まで引き込むつもりでいたが、Type-Cのプラグやレセプタクルが開口部を通過しないし、ケースに穴をあけるのもためらわれるのでこんな具合にした。USB2.0なのでD+、D-、VBUS、GND がつながっていれば問題ない。

作成したPCB

PCB単体(表と裏)は以下のようになった。高さを抑えるため基板の厚さは1mm。

askb-xiao-rev01 PCB

回路図や配線図は以下のようにシンプルである。MCP23S17と23S08 のシンボルやフットプリントはKiCADに含まれていた。

PCB回路図
PCB配線図

MCP23S17と23S08については、面実装タイプを使いたかったのだけど、入手が厄介そうだったので秋月で買えるスルーホールのDIPタイプを使った。

MCP23S17, 23S08ともに、SPI接続ではなくI2C接続のバリエーションもあるのだが、今回は+3.3V動作でも転送速度が速いSPI版を選択した (SPI版は最大10MHz@+2.7~+5.5V。ただし-40~+85℃。I2C版は最大1.7MHz@+5V、400kHz@+3.3V)。面実装タイプについて、I2C版は比較的入手しやすいようだが SPI版は品薄のようである。

I/Oエクスパンダについて

I/Oエクスパンダ MCP23S17, MCP23S08

今回使ったMCP23S17, 23S08はSPI接続なのにスレーブアドレスを送信してアクセスする必要がある。そのため、あるレジスタにデータを書き込むときには、スレーブアドレス、レジスタアドレス、データの3バイトも送信する必要がある。こうなっている理由は、同じバスに接続するデバイスが複数あるとき、それらのスレーブセレクト(SS)を共通として、アドレスを変更してアクセス先を識別するためということのようである。

今回は異なるデバイスを1つずつしか使わないので、両デバイスともにスレーブアドレスの初期値である0x40のままとし、マイコンで制御するスレーブセレクト信号を分けた(23S17の選択はSS_COL、23S08の選択はSS_ROWと表記)。

マトリックススキャンの実際

回路図からわかるように、8ビット必要な行(ROW)側をMCP23S08、12ビット必要な列(COL)側をMCP23S17に接続している。マトリックススキャンは以下のような手順で実行する。

あらかじめ(初期化時に)23S17のGPIOをすべてHIGHにしておく(COL01~COL12はすべてHIGH)。

  1. SS_COL をLOWにして23S17を選択
  2. 23S17のGPIO出力のうち1つだけLOWに (COLn=LOWとする)。
  3. SS_COLをHIGHに戻す。(COLn はLOWを維持)。
  4. SS_ROWをLOWにして23S08を選択
  5. 23S08のGPIOを読み出して、COLnのデータとして保存
  6. SS_ROWをHIGHに戻す
  7. SS_COL をLOWにして23S17を選択
  8. (2) でLOWにしたGPIOをHIGHに (COLn=HIGHとする)。
  9. SS_COLをHIGHに戻す。(COL01~COL12はすべてHIGH)。

すべてのスイッチ状態を読み出すには、上記の1~9を12回繰り返す必要があり、実行時間が気になるところなのでロジアナを使ってモニタしてみた。

1列のスキャン

askb_xiao scan timing

SPI_SCLK, SPI_MOSI, SPI_MISOは23S17と23S08で共通。MOSIはマイコンからの出力なので23S17に対する列選択の指示で、MISOは23S08が出力するGPIOポートの状態(行データ)になる。

上記の番号の処理に要する時間は以下のようになった。

  • 1~3 : COLnの選択に 7.2usec
  • 4~6 : COLnの状態読出しに 7.1usec
  • 7~8 : COLnの選択解除に 7.3usec

つまり、1列だけのスキャンならば約21.5usec程度で実行できることになる。

全列のスキャン

12列すべてをスキャンするのに要する時間は以下のようになった。こちらの場合、実際の全キースキャン時と同じように、COLnの状態を配列に保管する処理や12列処理するためのループなど、プログラム上のオーバーヘッドが加わっている。

askb_xiao scan timing

COL01の立下がりエッジをトリガとし、次の立下がりまでの波形をキャプチャした。最初のCOL01 = LOWから次のCOL01 = LOWまでの期間が433usecなので、12列分の読出しにかかる時間は約400usecといったところだろう(図のSS_ROW == HIGHが37.9usecとなっている部分は次のスキャンまでのオーバーヘッドにあたるので、実使用時には無視できる)。

たとえば2msecごとに全キーの読み取りを行うとすると、処理時間全体の20%を占めることになるが、実用上問題はない。

スキャン用のコード

ある1列の状態を読み出すコードは以下のようにした。

ここに登場しているspi_ss_portというクラスは、SAMD21のポート操作を行うために用意したクラスで中身についてはソースを参照のこと。

プログラム

プログラムの中身については、hoboNicola関係のダウンロードページにある、askb_xiao_hid1.zip を参照のこと。このzipファイルは、Arduino IDEでビルドするメインスケッチ( askb_xiao_hid1.ino )と、ハードウェア周りを担当する askb_xiao.cpp/.h を含んでいる。Arduinoスケッチを置いているフォルダに展開してから開くことで、Arduino IDEに各ファイルのタブができるはず。

ビルドのためには、Adafruit Tiny USB Library(1.14.1) 、hoboNicolaライブラリ1.6.2と、Seeed Stduio XIAO SAMD21 を Adafruit SAMD BSP を使ってビルドするための準備が必要で、手順についてはこちらなどを参照。なお、ビルドには、Arduino IDE 1.8.19を使った。

消費電流など

現在のスケッチを使うと、定常時(通常の入力時、LED全オフ)の平均消費電流は約8mAでスリープ時は0.3mA程度に落ち着いている。スリープ時の最初の30分間は、1秒ごとにスイッチのスキャンを行ってキーによるリジューム要求を確認するようになっているので、もうちょっと大きめの値だろう。消費電流の値は、XIAO-SAMD21のUSB VBUSに流れる電流を測定したもの。スリープ時にUSBを無効化してよいならばもっと小さな値にできるだろう。

なお、XIAO-SAMD21のオンボードのパワーオンLEDは基板から除去している。このLEDのオン/オフはプログラムで制御できず常に1.3mAほど消費する。下の写真の左上がパワーオンLED。他のLEDについては点灯する機会もなさそうなので、そのままにしてある(どうせ見えないが)。

XIAO-SAMD21のオンボードLED

きょうのまとめ

MCP23S17/23S08を使った96キー分のマトリックススキャンは、約400usecに一度の頻度で実行できることがわかった。今回のプログラムではデバウンス処理のために2回のスキャンで1セットとしているが、それでも1msec以内に完結するのでまーまーだろう。デバウンス処理の内容については、4年前の投稿を参照のこと。

このブログの記述はテストも兼ねてすべてASKeyboard を使って行った。こういう文章では、英文と和文の切り替えを頻繁に行う必要があるが、親指キーの周辺で切替え操作を行うことで手指が動く量も減り、結果として誤打鍵も減って楽である。

NICOLAキーボード用のPCBを作るならば親指キーの左右に文字種切替用のキーを配置したいと考えているが、親指を左右に動かすより上下の方が楽かも、とか思ったりする。ただ、変換確定操作のつもりで(従来の無変換キーを押して)英数字にしてしまったりもするので、もうちょっと使い込んでみたい。

親指シフトキーボードとしての動作は自分で使う上で何も問題がない。気になる点は、左側のWindowsキーがちょっと押しにくいこと(Win+EやWin+Dなどはよく使う)。現在は左Altにしている「カナ」キーをWindowsキーとし、その右隣のTAB(NFER)キーをAltにしてもいいかな、とか感じている。文字図形配列以外の部分については、先に載せた表に相当する配列やプログラムコードをちょっと直すだけなので、気が向けばすぐに変更することもできる。こういった表の更新を利用中に行う方法については現在検討中です。

ASKeyboard sono1
ASKeyboard sono1