エントリー

夏休みの工作 - PC-6001のキーボードをUSBに! ~ QMK編

20240912142912.JPG

 さて,PC-6001のキーボードをUSBで繋ぐ夏休みの工作,今回は完結編として実際にキーボードをUSBで繋いでみたいと思います。

 分解清掃はいってみれば(ノウハウはあるにせよ)誰でも気軽に出来る仕事です。しかし,USBで繋ぐにはハードとソフトの知識に加え,諦めないという熱意も不可欠だと思います。

 とまあ,気合いを入れて取り組んでみたところ,昨今の自作キーボードの流行を支える便利なシステムの存在がわかり,おかげさまでたった2日,実質2時間ほどで完成してしまいました。

 それはQMKといいます。アマチュアの自作は言うに及ばず,市販品にも使われるファームウェアで,主流だったAVRはもちろん,最近ではRaspberryPi Pico(RP2040)にも実装されていて,様々なMCUで動作します。

 主な機能はもちろん,かなり特殊な機能も実装済みであり,我々はキーボードごとに異なる差分をコンフィギュレーションしていってビルドすれば,信頼性の高いキーボードが完成します。面倒なUSB周りも実装済みなのがうれしいところです。

 以前ADBをUSBに変換する基板を作る時,ATmega32U4を搭載したProMicroを使いましたが,これも定番です。私も当初これを使ってPC-6001のキーボードを繋いでみようと画策しましたが,ちょっとGPIOの数がギリギリすぎ(ローとカラムで18本,LED用の1本が足りない)なので,手持ちのRaspberryPi Pico(以下RPi Pico)を使うことにしました。

 さてこのQMK,実は歴史あるシステムでユーザーも多いのですが,今なお頻繁に変更や修正が加えられている「生きている」システムで,根幹にかかわる部分に半年ごとに手が入るようなムムムな状況です。

 おかげで半年前の先人達のメモが役に立たなかったり,一部読み替えないといけなかったりします。特に私のようなCに慣れた人にとってjsonのデータ構造を見るのはなかなか苦しい物があり,今回はとても良い勉強になりました。

 まずはインストールからです。今回はMacを使ってみましょう。

 Homebrewでインストールしますので,

brew install qmk/qmk/qmk

 としましょう。続けて,

qmk setup

 としてセットアップを済ませてしまいます。これでホームディレクトリにqmkフォルダが作られます。試しに,

qmk compile -kb clueboard/66/rev3 -km default

 としてサンプルをコンパイルし,正しくセットアップが出来ているかどうか確かめておきます。

 ではいよいよ作業開始です。まずは新規のキーボードを設定します。

qmk new-keyboard

 いろいろ聞かれますので,今回の条件に合うように入力していきます。名前とかそんなのはどうでもいいのですが,大切なのはDefault Layoutでnone of the aboveを選ぶ事と,MCUにRP2040を選ぶ事です。

 こうして新規にプロジェクトを作ったら,~/qmk_firmware/keyboards/以下に,自分の命名したキーボードのフォルダが出来ており,この中にkeyboard.jsonとdefault/keymap.cが出来ているはずです。

 この2つを修正するのが今回の作業の中心です。なお,このファイルの構成は時期によって異なっているみたいで,config.hだった時代もあれば,info.jsonだった時代もあるようです。最新の状況は上記のようにkeybord.jsonとkeymap.cですが,これを解説した初心者向けのページはまだ引っかからないようでした。

 個人的には,近い将来keymap.cもcからjsonになるだろうと思うので,今回の情報もいつまで通用するかわかりません。

 では早速keyboard.jsonです。

{
    "manufacturer": "gshoes",
    "keyboard_name": "pc6001_rp2040",
    "maintainer": "gshoes",
    "bootloader": "rp2040",
    "diode_direction": "COL2ROW",
    "features": {
        "bootmagic": true,
        "command": false,
        "console": false,
        "extrakey": true,
        "mousekey": true,
        "nkro": true
    },
    "matrix_pins": {
        "cols": ["GP14","GP15","GP16","GP17","GP18","GP19","GP20","GP21"],
        "rows": ["GP0","GP1","GP2","GP3","GP4","GP5","GP6","GP7","GP8","GP9"]
    },
    "processor": "RP2040",
    "url": "",
    "usb": {
        "device_version": "1.0.0",
        "pid": "0x27DB",
        "vid": "0x16C0"
    },
    "indicators": {
        "caps_lock": "GP22",
        "on_state": 0
    },
    "layouts": {
        "LAYOUT": {
            "layout": [
                {"matrix": [0, 0], "x": 0, "y": 0},
                {"matrix": [0, 1], "x": 1, "y": 0},
                {"matrix": [0, 2], "x": 2, "y": 0},
                {"matrix": [0, 3], "x": 3, "y": 0},
                {"matrix": [0, 4], "x": 4, "y": 0},
                {"matrix": [0, 5], "x": 5, "y": 0},
                {"matrix": [0, 6], "x": 6, "y": 0},
                {"matrix": [0, 7], "x": 7, "y": 0},
                {"matrix": [1, 0], "x": 0, "y": 1},
                {"matrix": [1, 1], "x": 1, "y": 1},
                {"matrix": [1, 2], "x": 2, "y": 1},
                {"matrix": [1, 3], "x": 3, "y": 1},
                {"matrix": [1, 4], "x": 4, "y": 1},
                {"matrix": [1, 5], "x": 5, "y": 1},
                {"matrix": [1, 6], "x": 6, "y": 1},
                {"matrix": [1, 7], "x": 7, "y": 1},
                {"matrix": [2, 0], "x": 0, "y": 2},
                {"matrix": [2, 1], "x": 1, "y": 2},
                {"matrix": [2, 2], "x": 2, "y": 2},
                {"matrix": [2, 3], "x": 3, "y": 2},
                {"matrix": [2, 4], "x": 4, "y": 2},
                {"matrix": [2, 5], "x": 5, "y": 2},
                {"matrix": [2, 6], "x": 6, "y": 2},
                {"matrix": [2, 7], "x": 7, "y": 2},
                {"matrix": [3, 0], "x": 0, "y": 3},
                {"matrix": [3, 1], "x": 1, "y": 3},
                {"matrix": [3, 2], "x": 2, "y": 3},
                {"matrix": [3, 3], "x": 3, "y": 3},
                {"matrix": [3, 4], "x": 4, "y": 3},
                {"matrix": [3, 5], "x": 5, "y": 3},
                {"matrix": [3, 6], "x": 6, "y": 3},
                {"matrix": [3, 7], "x": 7, "y": 3},
                {"matrix": [4, 0], "x": 0, "y": 4},
                {"matrix": [4, 1], "x": 1, "y": 4},
                {"matrix": [4, 2], "x": 2, "y": 4},
                {"matrix": [4, 3], "x": 3, "y": 4},
                {"matrix": [4, 4], "x": 4, "y": 4},
                {"matrix": [4, 5], "x": 5, "y": 4},
                {"matrix": [4, 6], "x": 6, "y": 4},
                {"matrix": [4, 7], "x": 7, "y": 4},
                {"matrix": [5, 0], "x": 0, "y": 5},
                {"matrix": [5, 1], "x": 1, "y": 5},
                {"matrix": [5, 2], "x": 2, "y": 5},
                {"matrix": [5, 3], "x": 3, "y": 5},
                {"matrix": [5, 4], "x": 4, "y": 5},
                {"matrix": [5, 5], "x": 5, "y": 5},
                {"matrix": [5, 6], "x": 6, "y": 5},
                {"matrix": [5, 7], "x": 7, "y": 5},
                {"matrix": [6, 0], "x": 0, "y": 6},
                {"matrix": [6, 1], "x": 1, "y": 6},
                {"matrix": [6, 2], "x": 2, "y": 6},
                {"matrix": [6, 3], "x": 3, "y": 6},
                {"matrix": [6, 4], "x": 4, "y": 6},
                {"matrix": [6, 5], "x": 5, "y": 6},
                {"matrix": [6, 6], "x": 6, "y": 6},
                {"matrix": [6, 7], "x": 7, "y": 6},
                {"matrix": [7, 0], "x": 0, "y": 7},
                {"matrix": [7, 1], "x": 1, "y": 7},
                {"matrix": [7, 2], "x": 2, "y": 7},
                {"matrix": [7, 3], "x": 3, "y": 7},
                {"matrix": [7, 4], "x": 4, "y": 7},
                {"matrix": [7, 5], "x": 5, "y": 7},
                {"matrix": [7, 6], "x": 6, "y": 7},
                {"matrix": [7, 7], "x": 7, "y": 7},
                {"matrix": [8, 0], "x": 0, "y": 8},
                {"matrix": [8, 1], "x": 1, "y": 8},
                {"matrix": [8, 2], "x": 2, "y": 8},
                {"matrix": [8, 3], "x": 3, "y": 8},
                {"matrix": [8, 4], "x": 4, "y": 8},
                {"matrix": [8, 5], "x": 5, "y": 8},
                {"matrix": [8, 6], "x": 6, "y": 8},
                {"matrix": [8, 7], "x": 7, "y": 8},
                {"matrix": [9, 0], "x": 0, "y": 9},
                {"matrix": [9, 1], "x": 1, "y": 9},
                {"matrix": [9, 2], "x": 2, "y": 9},
                {"matrix": [9, 3], "x": 3, "y": 9},
                {"matrix": [9, 4], "x": 4, "y": 9},
                {"matrix": [9, 5], "x": 5, "y": 9},
                {"matrix": [9, 6], "x": 6, "y": 9},
                {"matrix": [9, 7], "x": 7, "y": 9}
            ]
        }
    }
}

 最初に用意されるサンプルコードは4x4のキーパッドのものですのであまり役には立たず,ちょこちょこと修正すればOKというほど甘い物ではありません。

 今回のようにフルキーボードの場合には自分で調べて記述する必要もあり,それがまた楽しい作業だったりしました。

 今回のポイントです。

(1)matrix_pins

 以前はマトリクスの大きさ(4x5とか)を先に指定する必要もあったようですが,今はマトリクスについてはこの記述だけで良いようです。

 ピンの指定にはそれぞれのMCUに指定された端子名を書く必要があるので,RP2040ならGPxxなどの番号を,ATmega32U4ならBxxやCxxという番号で記述します。

 ProMicroだと,基板のを端子名はATmega32U4の端子名と違っているので読み替えないといけないですから,結局回路図が必要だったりするのですが,RP2040の場合RPi Picoの基板に書かれている端子名がRP2040の端子名と一致しているのでそのまま書けば問題ありません。これは楽です。


(2)USBのpidとvid

 アマチュアの工作であれば誰もうるさいことを言わないと思いますが,今回私が使ったIDは条件付きながらアマチュアであれば無償で使っていい物ということで,ありがたく使わせて頂きました。


(3)LED

 LEDについては,jsonでのサンプルコードが見つからず,試行錯誤をしました。まずQMKにはCapsLockなどのLED制御の仕組みは含まれているので,使いますという宣言をするだけで動いてくれます。自分でコードを書く方法もありますが,そこまでする必要は薄いでしょう。

 書き方ですが,indicatorsのcaps_lockにLEDを割り当てた端子名を記述,そしてその論理をon_stateに書きます。負論理だったら0,正論理だったら1です。

 で,この0や1は文字ではなく数値扱いですので,""で囲んでしまったらエラーになります。

 後述しますが私はcaps_lockをかなキーに割り当てました。なぜならかなキーの横にあるLEDを点灯させたいと思ったからで,この記述でCapsLockがロックされるとLEDが点灯するようになりました。ちょっと感動しますよ。


(4)laouts

 ここはおまじないのような感じがします。用意したキーボードのマトリクスの構造を記述します。PC-6001の場合,Xが0から7までの8本,Yが0から9までの10本ですので,順番に書いていくだけです。


 次にkeymap.cです。

// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later

#include QMK_KEYBOARD_H
#include "keymap_japanese.h"

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {

    [0] = LAYOUT(
    KC_NO  ,KC_LCTL,KC_LSFT,KC_LCMD,KC_NO  ,KC_NO  ,KC_NO  ,KC_NO  ,
    JP_1   ,JP_Q   ,JP_A   ,JP_Z   ,JP_K   ,JP_I   ,JP_8   ,JP_COMM,
    JP_2   ,JP_W   ,JP_S   ,JP_X   ,JP_L   ,JP_O   ,JP_9   ,JP_DOT ,
    JP_3   ,JP_E   ,JP_D   ,JP_C   ,JP_SCLN,JP_P   ,KC_F1  ,JP_SLSH,
    JP_4   ,JP_R   ,JP_F   ,JP_V   ,JP_QUOT,JP_AT  ,KC_F2  ,JP_UNDS,
    JP_5   ,JP_T   ,JP_G   ,JP_B   ,JP_RBRC,JP_LBRC,KC_F3  ,KC_SPC ,
    JP_6   ,JP_Y   ,JP_H   ,JP_N   ,JP_MINS,JP_CIRC,KC_F4  ,JP_0   ,
    JP_7   ,JP_U   ,JP_J   ,JP_M   ,KC_NO  ,JP_YEN ,KC_F5  ,KC_NO  ,
    KC_ENT ,KC_STOP,KC_UP  ,KC_DOWN,KC_RGHT,KC_LEFT,KC_TAB ,KC_ESC ,
    JP_CAPS,KC_INS ,KC_DEL ,KC_BSPC,KC_HOME,KC_NO ,KC_NO   ,KC_NO  
    )
};
 
 これは短いのですが,結構重要な記述があります。ファイル名の通りC言語の記述方法ですので,慣れている私は助かりました。

(1)include

 ライブラリやマクロの定義などを読み込ませる記述ですが,サンプルに含まれるQMK_KEYBOARD.Hはそのままに,keymap_japanose.hもincludeしておきます。

 というのは,PC-6001がJIS配列だからです。QMKは標準ではUS配列です。ですのでSHIFT+2では@ではなく"が出ます。さらに;や',[や]の位置も違いますので,これを上手くアサインするのは難しいです。

 私も最初はPC-6001をUS配列でキーをアサインしていたのですが,途中でどうにもならなくなり,調べたところ便利なJIS配列でのアサイン方法が見つかりました。

 通常,アサインにはKC_xxというコードで記述を行うのですが,JIS配列のキーについてはJP_xxで書けば良いです。もちろん,JISとUSで同じキー(例えばカーソルキーなど)はKC_xxのままで構いません。


(2)LAYOUT

 前述の通り,USはKC_xxで,JISはJP_xxでアサインを行っていきます。

 手持ちのPC-6001の資料からキーマトリクスは分かっていますし,コネクタの配列も回路図から判明していますので,その通りに入力していきます。

 これはノウハウというよ私の考え方なのですが,ファンクションキーは無理に拡張せず,F1からF5までとしました。PC-6001ではSHIFT+F1でF6と言う具合にF10まで扱えるようになっていたのですが,今回はそこまではやりませんでした。そもそもファンクションキーなんか使いませんし。

 また,かなキーはCapsLockキーに割り当てました。前述の通りLEDを使いたいからですが。使用頻度が低いので迷いもありました。しかし,PC-6001でもかなキーの使用頻度は低く,同じような物だと思えば気分良く割り切ることができました。

 BackspaceキーはPC-6001にはないキーなのでどこにアサインしようかと考えたところ,位置的にページ切り替えキーが良さそうだったので割り当てました。回っている矢印なので意味は違ってきますが,左向きの矢印も書かれているので良しとしましょう。

 HOME_CLRキーは該当するキーがありませんが,HOMEキーがあるので割り当てました。ほとんど使い道がありませんが,STOPキーに比べればまだましです。そのSTOPキーですが,なんとKC_STOPというキーがアサイン出来るので,割り当てました。Macはもちろん,Windowsでもこんなキーを見た事がないので,きっとザ実在しないキーなのでしょう。

 ただでさえ少ないキーなので使わないキーを割り当てるのはもったいないのですが,もともと実用を考えたキーボードではありませんので,オリジナルの機能を優先することにしました。

 さて最後にGRAPHキーです。グラフィックキャラクタを打ち込むためのキーですが,MacにもWindowsにもそんなものはありません。その上絶妙な位置にあります。ここはcommandキーに割り当てることにします

 WindowsならALTキーになるんだろうと思いますが,これでショートカットも日本語ON/OFFも思いのままです。

 修正が終わったら,以下でコンパイルしましょう。

qmk compile -kb pc6001 -km default

 コンパイルできたら書き込みです。RPi Picoにあるボタンを押しながらUSBを差し込むとフォルダがマウントされますが,これが書き込み可能な状態です。この状態で,

qmk flash -kb pc6001 -km default

 とすれば書き込みが出来ます。終わったら勝手にリセットがかかって今書いたファームが動き出しますので,早速テストしてみましょう。

 っと,その前にハードウェアですね。

 もともと電気屋さんなのに最近はハードウェアの組み立てよりも先にコードを用意するようになり,手を動かすのが億劫になっているんだなあと自覚することもあるのですが,今回は配線数こそ多いものの簡単なので,まずはブレッドボードで作ってみました。

 RPi Picoをブレッドボードにのせて,アサインした端子とキーボードから出ているコネクタを配線していきます。実は最初,コネクタに書かれている番号をそのまま信じて配線したのですが全く動かず,よく調べてみるとこの数字の並びは逆になっていた事が判明し,すべての配線を入れ換える羽目になりました。

 LEDについてはX8がかなキーのLEDでカソードが,X9が電源LED(ESCキーの上)のアノードで電源に,X10が電源LEDのカソードなので抵抗を介してGNDに落とします。

 X9はRPi Picoの3V3OUTに繋げば,キーボードがUSBで繋がった時点でLEDが点灯しますので,本物っぽいですよ。

 PC-6001の回路図によると,このLED用の抵抗は220Ωとかなり小さい値が使われています。ざっと15mA程も流すことになりますが。今どきのLEDはすの数分の一で十分なので,時代を感じます。今回は電源電圧が3.3V程度ですので220Ωでもよいと思いますが,オリジナルの光り方を追求するなら100Ω程度でも良いかもしれないです。


 さて,こうして完成したUSB接続のPC-6001キーボードですが,昨日書いたようになかなか快適なキーボードです。反発力が大きく変化するので,ストロークの割には軽いタッチで入力できますし,底打ちするまで押し込むこともありませんので,メンブレン型の他のキーボードや,RealForceなんかとは別の種類の打ち心地です。

 軸もグラグラせずしっかりとしつつ,引っかかりのないスムーズな打鍵で,キーによる押し心地のムラもありませんので,実に快適です。キートップの真ん中へんが窪んでいるところもポイント高いです。やはりアルプスはいい仕事をしていたんだなあと思います。

 今も日本語をゴリゴリとPC-6001のキーボードで書いているのですが,この新鮮さは何だろうと思って考えてみると,私が触った最古のキーボードである一方で,ローマ字による日本語の入力をほとんど行った事がないことに気が付きました。

 PC-6001のキーボードはもっぱらゲームと,プログラミングに使われるキーボードだったのです。だから,PC-6001でかな漢字変換というのは,今回が初めての体験になるわけです。

 ということで,PC-6001のキーボードをUSBにする夏休みの工作,あっという間に完成してしまいました。強いて言うなら専用の基盤を作ってブレッドボードからおさらばするのが残っていますが,それはもう簡単ですから,後は時間のあるときにやればいいでしょう。

 ここまでくると,PC-6001の筐体に小さいPCでも仕込むか,それこそRaspberryPiでも仕込んで,一体型PCを作ってみても面白いと思いますが,オリジナルの筐体を削るのも気が引けますし,作ったところで使う物でもないので,やめておきます。

 それにしても,ネタで始めた工作ですが,思いのほか収穫がありました。RPi Picoを初めて使ったこともそうですし,QMKを使って自分のやりたいことが実現したこともそうです。

 こうなってくると完全自作キーボードを作る事も気軽な物に見えてきますが,USBで繋ぐというのも今さらな感じがしますし,今のキーボードで満足しているので,変な配列のキーボードを作ろうと思わない限り,作る事はないでしょう。

 ただ,応用としてどんなキーボードでもUSBに出来るとわかった以上は,例えば当時から絶賛されていたFM-7のキーボードやAppleIIのキーボード,ファミリーBASICのキーボードや汎用機の端末やオフコンのキーボード,果てはポケコンのキーボードやM5のゴムキーなどを繋いでみることも可能なわけで,ある特定のキーボードに思い入れのある人には福音となる技術かも知れません。

 個人的にはPC-6001のキーボードの素晴らしさがわかったところで満足です。

 1981年生まれのホビーマシンのくせに,ちゃんとCTRLキーもTABキーもESCキーも正しい位置に備えています(ちなみに兄貴分のPC-8001にはTABキーはありませんでした)。アンダーバーも\もあるし,独立したカーソルキーはとても使いやすいです。

 グレーにオレンジの配色もいいですねえ。こうして現代のマシンに繋いでやりさえすれば,特に違和感も我慢もせずに即戦力になるあたり,大したものだと感心しました。

9月11日追記:
 Aliexpressで,1つ250円(送料込み)のRPi Picoの互換品が売っていたので3つほど試しに買ってみました。早速QMKを書き込んで純正品と交換したところ,ちゃんと動いてくれました。RESETボタンもあるし,USBもTypeCなので,純正よりもむしろ便利なくらいです。

 

ページ移動

ユーティリティ

2024年12月

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 - - - -

検索

エントリー検索フォーム
キーワード

ユーザー

新着画像

過去ログ

Feed