⌨️

60%キーボードの設計〜製作メモ

2022/12/31に公開

1.はじめに

60%キーボードを設計するところから自作したので、完成までの概要を書いてみます。
2回ほど製作しまして、ここで紹介するのは2回めのほうです。
キーボードの名前は、「kplj66r2」になります。

1.1. 作ったもの

  • ごく普通の見た目の、一体型60%キーボードです。家用と職場用で2台作りました。
  • ソケットを使い、日本語配列と英語風配列を付け替えできるようにしました。
  • キーマップは、変換・無変換とレイヤー切り替えを兼用し、vi風カーソルにしました。


(参考までに、初回作の方は日本語配列専用、ソケットなしのはんだ付けタイプで、それ以外は大差ないです。)

1.2. 動機

  • 仕事柄WordやExcelでドキュメントを書くことが多いのですが、ホームポジションを崩してカーソルを押すのがとても煩わしいため、使い慣れたvi風のカーソル移動にしたいと思っていました。
  • 以前は、職場PCへのUSB機器の接続がNGだったため諦めていたのですが、リモートワーク対応PCに更新されたときにUSBキーボードの接続がOKになったので、Keyboard Quantizer や、カスタマイズ機能があるRealforce R3S を使って、キーマップを試行錯誤しました。
  • その結果、変換・無変換キーをFnキー(レイヤー切り替え)に割り当てるのがかなり良いというところにたどり着きました。
  • しかしながら、1つだけ、変換・無変換で日本語入力のON/OFFができなくなってしまうというデメリットがありました。
  • ネットで調べてみると、自作キーボードであれば自由にキーマップを作れることがわかり、ドキュメントも豊富だったので、自作に踏み出すことにしました。

2. 設計方針

POKER,GH60ケース互換の60%キーボードにする。
  • 出張にも持っていきたいので、小さいのがいい。ただし、キーマップ覚え直しの負荷を避けたいので、60%にとどめる。
  • あまり目立ちたくないので、一体型のケース入りで、光らないようにする。
ソケット式にして、日本語配列と英語配列を付け替えできるにする。
  • 英語配列のほうがキーキャップのデザインが豊富で入手性が良いので、英語配列を使ってみたい。
  • でも、馴染めなかった場合には日本語配列に変更できるようにしたい。
5行目(スペースの行)は、日本語配列・英語配列にかかわらず変換・無変換キーを付けて、日本語ON/OFFとレイヤー切り替えに使う。
  • 変換・無変換キーの場所は、FとJの真下から少し外側が良い。
  • 具体的には、Realforce R3S と同じ場所か、それより0.25uずつ内側で、ホームポジションからの距離を左右対称にしたい。
スペースキーを2分割して、右側は右Ctrlキーにする。
  • 3uや3.5uのスペースキーのキャップが入手困難なので。
  • 自分は左手の親指でしかスペースキーを押さないので、小さくても問題ない。
  • 右手の親指にCtrlがあると、Ctrl+z,x,c,v,q,aとかが押しやすい。

3. 事前勉強

書籍やネットの記事で勉強しました。
https://booth.pm/ja/items/1044084
すごく実践的なノウハウが紹介されていて、とても参考になりました。
https://salicylic-acid3.hatenablog.com/
https://docs.qmk.fm/#/ja/

4. 設計

勉強をしつつ、設計を始めました。
概要を掴んだら、あとはやりながら必要なところだけを調べるスタイルです。

4.1. キーのレイアウト検討

keyboard-layout-editor.comでレイアウトを検討し、こんな感じで行くことにしました。

  • Alt+F*やAlt+カーソルを押すときは、親指で右Altと変換(または左Alt+無変換)を2個押しします。
  • 5行目を2種類にしたのは、Altと変換or無変換の同時押しのしやすさと、スペースの押しやすさの兼ね合いがこの時点では判断できなかったためです。
  • SW??1とSW??2は、レイアウトに応じて排他的に使うので、回路は共用します。(SWを並列接続)

4.2. 回路とPCBの設計

KiCadを使って設計しました。
PCBにコントローラチップを直接実装するのは難易度が高いので、ProMicroを使うことにしました。

  • 安く手に入るGH60プラスチックケースを使う前提で、ProMicroが収まりそうな場所は、「I」キーから「;」キーの裏側付近のようなので、そこに配置します。
  • 下の方にあるネジ穴は、今回のレイアウトではどう頑張ってもスイッチのソケットと干渉してしまうので、もぎ取ることにします。

PCBの外形や補助線、ネジ止め用のパッド等のベースとなる部分は、GH60やケース互換のPCBのgithubリポジトリから流用させていただきました。
回路図は、自作キーボード設計入門と、ネット上の方々の記事を参考にして書きました。


ProMicroを「I」キー〜「O」キーの下のPCB裏面に、チップが外側を向くように取り付ける前提で、なるべく配線の取り回しが簡単になるようにrowとcolを割り付けました。
なお、この絵のレイアウトは、英語配列と日本語配列の共用の配線を考えるための便宜上のもので、実際にはこの形では使いません。

ダイオードにつなげるスイッチのピンも、PCBの配線のしやすい方になるようにしました。


配線のしやすさを考えながら回路図を書くので、PCBのフットプリント配置と同時進行でやりました。大まかな流れはこんな感じです。

  1. 回路図エディタで、スイッチとダイオードのシンボルを配置する。
  2. PCBエディタで、スイッチとダイオードのフットプリントを配置する。
  3. PCBエディタで、配線がやりやすいようにスイッチの向きとダイオードの位置を決める。
  4. PCBエディタの配置に合わせて、回路図エディタのシンボルの向きを変更する。
  5. 回路図のシンボルとPCBのフットプリントのリファレンス指定子を合わせる。
  6. 回路図エディタで、シンボルとフットプリントを関連付ける。
  7. 全部のキーについて、上記を繰り返す。
  8. 回路図エディタで配線する。(この付近で、アノテーションも実行した気がしますが、忘れました。)
  9. 回路図エディタでエレクトリカルルールチェッカーを実行し、エラーがあれば直す。
  10. 回路図エディタでネットリストをエクスポートする。
  11. PCBエディタでネットリストをインポートする。
  12. PCBエディタで配線する。
  13. GNDベタを作成する。

PCBエディタでのフットプリント配置と配線はこんな感じです。(GNDベタは非表示)

ProMicroの場所は、ピンヘッダが、ケースの梁、キースイッチ、スイッチソケットのいずれにも干渉しないように調整しました。

USBケーブルは、PCBの左上でMini-Bのソケットで受けて、すぐにリード線に渡してMicro-Bのオス端子につなぐことで考えました。
Mini-Bにしたのは、スルーホールにはんだ付けするタイプの入手性が良さそうだったのと、Type-CやMicro-Bよりは、はんだ付けが簡単そうに思えたためです。

キーのレイアウトを変更できるようにフットプリントを重ねた関係で、デザインルールチェッカーを実行したときに、ドリル穴のクリアランス違反が大量に出ました。
配線未接続などの致命的なものをもれなく修正し、ドリル穴の重なりやクリアランスはそのままにしました。
ソケット付きキースイッチのフットプリント自体でも、クリアランス違反が出るようでした。

3Dビューアーで見るとこんな感じです。

4.3. トッププレートの設計

PCBの上で、キースイッチを固定するためのプレートを設計します。Inkcapeを使ってSVGデータを作成しました。
GH60互換のトッププレートは、ネジでしっかり固定するのではなく、スイッチをはめ込むようにして、すべてのスイッチがお互いに支え合うものが多いようです。設計が簡単そうなので、それを踏襲しました。

ネットで見つけたテンプレ素材を流用させてもらったのですが、ソースの場所がわからなくなってしまいました。(スミマセン…)
keyboard-layout-editor.com で作ったレイアウトのjsonデータを builder.swillkb.com に貼ってベースのSVGデータを作ったあと、PCBマウント用のスタビライザーのスイッチホールを使っているテンプレ素材のパーツに置き換えて作りました。

⇩これは、日本語配列と英語配列の兼用プレートです。

⇩これは、英語配列の専用プレートです。
兼用プレートだと、英語配列の2行目の一番右のバックスラッシュキーがどうもグラつくので、追加で設計し直しました。
バックスペースのところの2u分は、1u×2と2uの兼用のままにすればよかったと、少し後悔しています。

4.4. USB配線の設計

できればType-Cでつなぎたいと思いネットで検索したら、良さそうなパーツがあったので、これを使う方針に転換しました。

  • Type-Cのソケットをケースにビス止めして、リード線4本でMicro-Bの工作用基板につなぎ、ProMicroに差し込むことにします。
  • PCBのフットプリントは、そのまま残して、部品を実装しないようにします。

4.5. 必要な部品・資材まとめ

品目 説明
PCB 4.2で設計したもの
トッププレート 4.3で設計したもの
ダイオード スイッチング用の1N4148
ProMicro ピンヘッダ付き
キースイッチ MX互換。今回はGateron(のはず)の35g荷重
キースイッチ用ソケット MX互換用
スタビライザー PCBマウントのもの
キーキャップ CherryプロファイルまたはOEMプロファイルで、今回の5行目のキーを含めて充足できるもの
ケース プラスチックケース
USB Type-Cソケット基板 Amazonにあったもの
USB Mini-Bオス基板 楽天市場にあったもの
リード線
タクトスイッチ リセット用,1接点,4本足
静音化リング
キースイッチ潤滑剤
その他消耗品 絶縁テープやパテなど

5. 発注

設計に間違いがないか改めて最終確認して、問題がなければ発注します。けっこうテンションが上がって、気づきにくいかもしれませんが。

5.1. PCBの発注

FusionPCBの標準製造サービスを利用しました。
PCBエディタでガーバーファイル、ドリルファイルを出力して、所定のファイル命名ルールに合わせてリネームし、zipにします。
見積もりページにアップロードして、寸法、枚数などを入力して、注文しました。

入力項目 説明
材質 よくわからないので、デフォルトのFR-4 TG130にしました。
層数 表裏だけなので二層です。
寸法 GH60互換なので285mm * 94.6mmです。
製造枚数 最小の5枚にしました。
異種面付けの種類 よくわからないので、デフォルトの1にしました。
板厚 1.6mmにしました。
レジスト色 青にしました。値段が上がる色があるようです。
基板の表面処理 よくわからないので、デフォルトのHASLにしました。
最小ソルダレジストの幅 よくわからないので、デフォルトの0.4mmにしました。
銅箔厚 1oz.にしました。
最小穴径 よくわからないので、デフォルトの0.3mmにしました。
最小パターン幅/パターン間隔 よくわからないので、デフォルトの6/6milにしました。
端面スルーホール 両脇のネジ止めの部分が該当しそうですが、導通部をきれいに仕上げる必要はないので、なしにしました。
インピーダンス制御 よくわからないので、なしにしました。

5枚で47.35ドル+送料18.75ドルで、計66.10ドルでした。支払いはPayPalを使いました。
PCBエディタのデザインルールチェッカーで出たドリル穴の重なりやクリアランス違反については、特に何も指摘されることなく、そのとおりに製造してくれました。

5.2. トッププレートの発注

遊舎工房さんのレーザー加工サービスを利用しました。
https://yushakobo.jp/lasercut/

5.3. その他の部品・資材の調達

Amazon、楽天市場、電子部品ショップなどで調達しました。
ダイオード https://www.amazon.co.jp/gp/product/B06XCBX3B9/
ProMicro https://www.amazon.co.jp/gp/product/B081DY1NWW/
キースイッチ https://www.amazon.co.jp/gp/product/B08DFZLVF7/?th=1
キースイッチ用ソケット https://www.amazon.co.jp/gp/product/B096WZ6TJ5/
スタビライザー https://www.amazon.co.jp/gp/product/B09Y36BJLP/
キーキャップ https://www.amazon.co.jp/gp/product/B0BFVTRSKQ/?th=1 https://www.amazon.co.jp/gp/product/B09D3PZFTW/?th=1
ケース https://www.amazon.co.jp/gp/product/B094NNM9ND/?th=1
USB Type-Cソケット基板 https://www.amazon.co.jp/dp/B09VS7Z86D/
USB Micro-Bオス基板 https://item.rakuten.co.jp/vshopu/219x-1601/?s-id=ph_pc_itemname
タクトスイッチ https://www.marutsu.co.jp/pc/i/310132/

6. 入荷

6.1. PCB

注文後18日で無事に届きました。


ドリル穴が重なっているところも、問題ないようです。

6.2. トッププレート

混んでいたようで、16日くらいで届きました。

6.3. その他の部品

国内から発送されるのものは2〜3日、中国から発送されるものは2週間位かかりました。
中国から発送されたケースとキーキャップは、箱が潰れていましたが、中身は大丈夫でした。

7. qmkファームウェア製作

部品が届くまでの時間を使って、qmkファームウェアを準備しました。
自宅PCのLinux(Mint)環境での紹介となります。

7.1. ビルド環境構築

qmkファームウェアについても、ネット上の記事が豊富なので、見様見真似でやってみました。

  • qmkインストール
user@host:~$ sudo apt install -y git python3-pip
user@host:~$ python3 -m pip install --user qmk
  • qmkのパスを設定
    ~/.bashrcにパスを追記して、ターミナルを開き直します。
if [ -d ${HOME}/.local/bin -a -z "`echo $PATH | grep \"${HOME}/.local/bin\"`" ]; then
    export PATH=$PATH:${HOME}/.local/bin
fi
  • qmkセットアップ
    ~/qmk_firmwareができます。
user@host:~$ qmk setup
  • qmkアップデート
user@host:~$ python3 -m pip install -U qmk

7.2. 今回用のqmkファームウェアを作成

  • 新しいキーボードを作成します。対話形式でいくつか聞かれるので答えます。
user@host:~$ qmk new-keyboard
  • Keyboard Name? と聞かれるので、今回のキーボードの名前の「kplj66r2」を入力します。
  • GitHub Username? は、自分のユーザー名を入れます。
  • Real Name? は、デフォルトのまま(GitHubユーザー名と同じ)にします。
  • Default Layout? は、51の「none of the above」にします。
  • MCU? は、ProMicroを使うので、24の「atmega32u4」にします。
  • 一通り入力すると、~/qmk_firmware/keyboards/kplj66r2が作成されます。

7.3. info.jsonを編集

kplj66r2/info.jsonを編集します。
ここを参考にします。https://github.com/qmk/qmk_firmware/blob/master/docs/reference_info_json.md

  • diode_direction
    ダイオードの向きはcolからrowで設計したので、「COL2LOW」にします。
info.json
   "diode_direction": "COL2ROW",
  • matrix_pins
    PCBの設計でcolとrowに割り当てたピンを、若番から順に記載します。
    ピンの名前は、ProMicroのPortの名前の右側2文字です。(PD3ならD3)
info.json
    "matrix_pins": {
        "cols": ["D3", "D2", "D1", "D0", "D4", "C6", "E6", "D7"],
        "rows": ["B4", "B5", "F4", "F5", "F6", "F7", "B1", "B3", "B2", "B6"]
    },
  • layouts
    キーボードの1行目の左から右へ、2行目の左から右へ、3行目…、4行目…、5行目…の順で、それぞれのキーのピン割付などを記載します。
    ここで記載した順番が、後で編集するkeymap.cのkeymaps配列の並び順になります。
    matrixは、ピンの割付を[row,col]で記載します。
    xはキーの横位置(左が0)、yは縦位置(上が0)、wは幅、hは高さです。
    x,y,w,hは、GUIでファームウェアを編集するツール向けのデータのようです。

    再掲 配線検討用のレイアウト
info.json
    "layouts": {
        "LAYOUT_1": {
            "layout": [
                { "label": "SW1",   "matrix": [2, 0], "x": 0, "y": 0 },
                { "label": "SW2",   "matrix": [2, 1], "x": 1, "y": 0 },
                { "label": "SW3",   "matrix": [2, 2], "x": 2, "y": 0 },
                { "label": "SW4",   "matrix": [2, 3], "x": 3, "y": 0 },
                { "label": "SW5",   "matrix": [2, 4], "x": 4, "y": 0 },
                { "label": "SW6",   "matrix": [2, 5], "x": 5, "y": 0 },
                { "label": "SW7",   "matrix": [2, 6], "x": 6, "y": 0 },
                { "label": "SW8",   "matrix": [2, 7], "x": 7, "y": 0 },
                { "label": "SW9",   "matrix": [0, 7], "x": 8, "y": 0 },
                { "label": "SW10",  "matrix": [0, 6], "x": 9, "y": 0 },
                { "label": "SW11",  "matrix": [0, 5], "x": 10, "y": 0 },
                { "label": "SW12",  "matrix": [0, 4], "x": 11, "y": 0 },
                { "label": "SW13",  "matrix": [0, 3], "x": 12, "y": 0 },
                { "label": "SW14",  "matrix": [0, 2], "x": 13, "y": 0 },
                { "label": "SW15",  "matrix": [1, 2], "x": 14, "y": 0 },
                { "label": "SW16",  "matrix": [3, 0], "x": 0, "y": 1, "w": 1.5 },
                { "label": "SW17",  "matrix": [3, 1], "x": 1.5, "y": 1 },
                { "label": "SW18",  "matrix": [3, 2], "x": 2.5, "y": 1 },
                { "label": "SW19",  "matrix": [3, 3], "x": 3.5, "y": 1 },
                { "label": "SW20",  "matrix": [3, 4], "x": 4.5, "y": 1 },
                { "label": "SW21",  "matrix": [3, 5], "x": 5.5, "y": 1 },
                { "label": "SW22",  "matrix": [3, 6], "x": 6.5, "y": 1 },
                { "label": "SW23",  "matrix": [3, 7], "x": 7.5, "y": 1 },
                { "label": "SW24",  "matrix": [1, 7], "x": 8.5, "y": 1 },
                { "label": "SW25",  "matrix": [1, 6], "x": 9.5, "y": 1 },
                { "label": "SW26",  "matrix": [1, 5], "x": 10.5, "y": 1 },
                { "label": "SW27",  "matrix": [1, 4], "x": 11.5, "y": 1 },
                { "label": "SW28",  "matrix": [1, 3], "x": 12.5, "y": 1 },
                { "label": "SW29",  "matrix": [9, 2], "x": 13.5, "y": 1 },
                { "label": "SW30",  "matrix": [4, 0], "x": 0, "y": 2, "w": 1.75 },
                { "label": "SW31",  "matrix": [4, 1], "x": 1.75, "y": 2 },
                { "label": "SW32",  "matrix": [4, 2], "x": 2.75, "y": 2 },
                { "label": "SW33",  "matrix": [4, 3], "x": 3.75, "y": 2 },
                { "label": "SW34",  "matrix": [4, 4], "x": 4.75, "y": 2 },
                { "label": "SW35",  "matrix": [4, 5], "x": 5.75, "y": 2 },
                { "label": "SW36",  "matrix": [4, 6], "x": 6.75, "y": 2 },
                { "label": "SW37",  "matrix": [4, 7], "x": 7.75, "y": 2 },
                { "label": "SW38",  "matrix": [9, 7], "x": 8.75, "y": 2 },
                { "label": "SW39",  "matrix": [9, 6], "x": 9.75, "y": 2 },
                { "label": "SW40",  "matrix": [9, 5], "x": 10.75, "y": 2 },
                { "label": "SW41",  "matrix": [9, 4], "x": 11.75, "y": 2 },
                { "label": "SW42",  "matrix": [9, 3], "x": 12.75, "y": 2 },
                { "label": "SW43",  "matrix": [8, 2], "x": 13.75, "y": 2, "w": 1.25 },
                { "label": "SW44",  "matrix": [5, 0], "x": 0, "y": 3, "w": 2.25 },
                { "label": "SW45",  "matrix": [5, 1], "x": 2.25, "y": 3 },
                { "label": "SW46",  "matrix": [5, 2], "x": 3.25, "y": 3 },
                { "label": "SW47",  "matrix": [5, 3], "x": 4.25, "y": 3 },
                { "label": "SW48",  "matrix": [5, 4], "x": 5.25, "y": 3 },
                { "label": "SW49",  "matrix": [5, 5], "x": 6.25, "y": 3 },
                { "label": "SW50",  "matrix": [5, 6], "x": 7.25, "y": 3 },
                { "label": "SW51",  "matrix": [5, 7], "x": 8.25, "y": 3 },
                { "label": "SW52",  "matrix": [8, 7], "x": 9.25, "y": 3 },
                { "label": "SW53",  "matrix": [8, 6], "x": 10.25, "y": 3 },
                { "label": "SW54",  "matrix": [8, 5], "x": 11.25, "y": 3 },
                { "label": "SW55",  "matrix": [8, 4], "x": 12.25, "y": 3 },
                { "label": "SW56",  "matrix": [8, 3], "x": 13.25, "y": 3, "w": 1.75 },
                { "label": "SW57",  "matrix": [6, 0], "x": 0, "y": 4, "w": 1.25 },
                { "label": "SW58",  "matrix": [6, 1], "x": 1.25, "y": 4, "w": 1.25 },
                { "label": "SW59",  "matrix": [6, 2], "x": 2.5, "y": 4, "w": 1.25 },
                { "label": "SW60",  "matrix": [6, 3], "x": 3.75, "y": 4, "w": 2.25 },
                { "label": "SW61",  "matrix": [6, 4], "x": 5, "y": 4, "w": 1.25 },
                { "label": "SW62",  "matrix": [6, 6], "x": 7.25, "y": 4, "w": 1.25 },
                { "label": "SW63",  "matrix": [6, 7], "x": 8.5, "y": 4, "w": 1.25 },
                { "label": "SW64",  "matrix": [7, 7], "x": 9.75, "y": 4, "w": 1.25 },
                { "label": "SW65",  "matrix": [7, 6], "x": 11, "y": 4, "w": 1.25 },
                { "label": "SW66",  "matrix": [7, 5], "x": 12.25, "y": 4, "w": 1.25 },
                { "label": "SW67",  "matrix": [7, 4], "x": 13.5, "y": 4, "w": 1.5 }
            ]
        }
    }

7.4. keymap.cを編集

kplj66r2/keymaps/キーマップ名/keymap.cを編集します。
ここを参考にしました。 https://docs.qmk.fm/#/ja/keymap

英語配列用、日本語配列用、OSのキーボード設定を日本語にしたままで英語配列を使う用で、合計3つ作ります。
3つ目は、仕事用PCが権限の関係でキーボードの言語設定を変更できなかったため、仕方なく作ることにしました。
キーマップの名前は、このように使い分けることにしました。

  • 英語配列は、defaultを使う。
  • 日本語配列は、jisにする。(あとで作る)
  • OSのキーボード設定を日本語のままで英語配列用は、ansi_on_jisにする。(あとで作る)

7.4.1. 共通の設定

default/keymap.cを編集します。
後でキーマップを追加するとき、defaultがコピーされるので、最初に書いておきます。
他のキーマップから参照される訳ではないようです。

  • キー配列にかかわらず、変換・無変換・かなキーが欲しいので、JIS配列のキーを使えるようにします。
    keymap_japanese.hをインクルードします。
    (qmkのドキュメントだとkeymap_jp.hだったけど、変わった模様です。)
    使えるキーコードは、 quantum/keymap_extras/keymap_japanese.h に書いてありました。
keymap.c
#include "keymap_japanese.h"
  • レイヤーの番号に別名をつけます。
    たいてい0,1,2… の代わりに別名をつけるようなので、真似します。
    ベースのレイヤー、vi風カーソル他のレイヤー、マウスカーソルのレイヤーの3つを作ることにします。
keymap.c
enum layers {
    _BASE_LAYER = 0,
    _CURSOR_LAYER,
    _MOUSE_LAYER
};
  • 変換キーと無変換キーを_CURSOR_LAYERの切り替えに使えるようにします。
    こちらの記事を参考にさせていただきました。 https://okapies.hateblo.jp/entry/2019/02/02/133953
    • 単体で押したときは変換・無変換として扱います。
    • 他のキーと組合せて押したときは、押している間、_CURSOR_LAYERに切り替えます。
    • そのあと、キーを離したとき、_BASE_LAYERに戻ります。
    • 変換と無変換をラップして押し変えたときには、両方離したタイミングで_BASE_LAYERに戻ります。
    • 移動先のレイヤーでは、変換と無変換の場所はKC_TRNSにしておく必要がありました。
keymap.c
bool process_record_user(uint16_t keycode, keyrecord_t *record){
    static bool b_mhen = false;
    static bool b_henk = false;
    static uint16_t mem_keycode;
    uint16_t prev_keycode = mem_keycode;
    bool is_tapped = ((!record->event.pressed) && (keycode == prev_keycode));
    mem_keycode = keycode;

    switch (keycode) {
        case JP_MHEN: {
            if (record->event.pressed) {
                layer_on(_CURSOR_LAYER);
                b_mhen = true;
            }
            else {
                if (!b_henk) {
                    layer_off(_CURSOR_LAYER);
                }
                b_mhen = false;
                if (is_tapped) {
                    tap_code(keycode);
                }
            }
            return false;
        } break;

        case JP_HENK: {
            if (record->event.pressed) {
                layer_on(_CURSOR_LAYER);
                b_henk = true;
            }
            else {
                if (!b_mhen) {
                    layer_off(_CURSOR_LAYER);
                }
                b_henk = false;
                if (is_tapped) {
                    tap_code(keycode);
                }
            }
            return false;
        } break;

        default: {
        } break;

    }
    return true;
}

7.4.2. 英語配列の設定

英語配列のキーマップは、このようにします。

  • _BASE_LAYER
    主に文字入力用です。

  • _CURSOR_LAYER
    今回の目当てのvi風カーソル用です。
    無変換+右手の中指・薬指でPageDown/UpとHome/Endも、移動しやすくて気に入っています。
    Appメニューは、右下だと押しにくいので、変換+Aにしました。vi風カーソルでファイルを選んでメニューを出すような操作がやりやすいです。
    Enterは、無変換+Mにも割り付けました。こっちのほうが静かです。

  • _MOUSE_LAYER
    せっかくなので作りましたが、マウスは横一列のキーだとぎこちない動きしかできなくて、ほとんど使ってません。

  • default/keymap.cに、英語配列のキーマップを記載します。

default/keymap.c
enum layers {
    _BASE_LAYER = 0,
    _CURSOR_LAYER,
    _MOUSE_LAYER
};

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  /*        SW1       SW2       SW3       SW4       SW5       SW6       SW7       SW8       SW9       SW10      SW11      SW12      SW13      SW14      SW15
   * BASE   `~        1!        2@        3#        4$        5%        6^        7&        8*        9(        0)        -_        =+                  BS
   * CURSOR ESC       F1        F2        F3        F4        F5        F6        F7        F8        F9        F10       F11       F12                 DEL
   * MOUSE  ESC                 BriDnw    BriUp                                             Mute      Vol_Dwn   Vol_Up
   *
   *        SW16      SW17      SW18      SW19      SW20      SW21      SW22      SW23      SW24      SW25      SW26      SW27      SW28      SW29    
   * BASE   Tab       Q         W         E         R         T         Y         U         I         O         P         [{        ]}        \|
   * CURSOR           TO(0)     TO(1)     TO(2)                                             PgUp      PgDwn     PSCREEN   ESC       SCRLLOCK  PAUSE
   * MOUSE            TO(0)     TO(1)     TO(2)                                             MS_WH_Up  MS_WH_Dn            ESC
   *
   *        SW30      SW31      SW32      SW33      SW34      SW35      SW36      SW37      SW38      SW39      SW40      SW41      SW42      SW43
   * BASE   Caps      A         S         D         F         G         H         J         K         L         ;:        ’"                 Enter
   * CURSOR           App                                               Left      Down      Up        Right                                   Enter
   * MOUSE            MS_BTN2             MS_BTN3   MS_BTN1             MS_Left   MS_Down   MS_Up     MS_Right                                Enter
   *
   *        SW44      SW45      SW46      SW47      SW48      SW49      SW50      SW51      SW52      SW53      SW54      SW55      SW56
   * BASE   LShift    Z         X         C         V         B         N         M         ,<        .>        /?                  RShift
   * CURSOR TRNS                                                                  Enter     Home      End                           TRNS
   * MOUSE  TRNS                                                                  Enter                                             TRNS
   *
   *        SW57      SW58      SW59      SW60      SW61      SW62      SW63      SW64      SW65      SW66      SW67
   * BASE   LCtrl     Win       LAlt      Muhen     Space     RCtrl     Henkan    RAlt      Kana      TO(1)     RCtrl
   * CURSOR TRNS                TRNS      TRNS                TRNS      TRNS      TRNS                TO(2)     TRNS
   * MOUSE  TRNS                TRNS      TRNS                TRNS      TRNS      TRNS                TO(0)     TRNS
   */
    [_BASE_LAYER] = LAYOUT_1(
            KC_GRV,   KC_1,     KC_2,     KC_3,     KC_4,     KC_5,     KC_6,     KC_7,     KC_8,     KC_9,     KC_0,     KC_MINS,  KC_EQL,   KC_NO,    KC_BSPC,
            KC_TAB,   KC_Q,     KC_W,     KC_E,     KC_R,     KC_T,     KC_Y,     KC_U,     KC_I,     KC_O,     KC_P,     KC_LBRC,  KC_RBRC,  KC_BSLS,
            KC_CAPS,  KC_A,     KC_S,     KC_D,     KC_F,     KC_G,     KC_H,     KC_J,     KC_K,     KC_L,     KC_SCLN,  KC_QUOT,  KC_NO,    KC_ENT,
            KC_LSFT,  KC_Z,     KC_X,     KC_C,     KC_V,     KC_B,     KC_N,     KC_M,     KC_COMM,  KC_DOT,   KC_SLSH,  KC_NO,    KC_RSFT,
            KC_LCTL,  KC_LGUI,  KC_LALT,  JP_MHEN,  KC_SPC,   KC_RCTL,  JP_HENK,  KC_RALT,  JP_KANA,  TO(1),    KC_RCTL
    ),
    [_CURSOR_LAYER] = LAYOUT_1(
            KC_ESC,   KC_F1,    KC_F2,    KC_F3,    KC_F4,    KC_F5,    KC_F6,    KC_F7,    KC_F8,    KC_F9,    KC_F10,   KC_F11,   KC_F12,   KC_NO,    KC_DEL,
            KC_NO,    TO(0),    TO(1),    TO(2),    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_PGUP,  KC_PGDN,  KC_PSCR,  KC_ESC,   KC_SLCK,  KC_PAUS,
            KC_NO,    KC_APP,   KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_LEFT,  KC_DOWN,  KC_UP,    KC_RGHT,  KC_NO,    KC_NO,    KC_NO,    KC_ENT,
            KC_TRNS,  KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_ENT,   KC_HOME,  KC_END,   KC_NO,    KC_NO,    KC_TRNS,
            KC_TRNS,  KC_NO,    KC_TRNS,  KC_TRNS,  KC_NO,    KC_TRNS,  KC_TRNS,  KC_TRNS,  KC_NO,    TO(2),    KC_TRNS
    ),
    [_MOUSE_LAYER] = LAYOUT_1(
            KC_ESC,   KC_NO,    KC_BRID,  KC_BRIU,  KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_MUTE,  KC_VOLD,  KC_VOLU,  KC_NO,    KC_NO,    KC_NO,    KC_NO,
            KC_NO,    TO(0),    TO(1),    TO(2),    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_WH_U,  KC_WH_D,  KC_NO,    KC_ESC,   KC_NO,    KC_NO,
            KC_NO,    KC_BTN2,  KC_NO,    KC_BTN3,  KC_BTN1,  KC_NO,    KC_MS_L,  KC_MS_D,  KC_MS_U,  KC_MS_R,  KC_NO,    KC_NO,    KC_NO,    KC_ENT,
            KC_TRNS,  KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_ENT,   KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_TRNS,
            KC_TRNS,  KC_NO,    KC_TRNS,  KC_TRNS,  KC_NO,    KC_TRNS,  KC_TRNS,  KC_TRNS,  KC_NO,    TO(0),    KC_TRNS
    )
};

7.4.3. 英語配列のコンパイル

英語配列をコンパイルしてみます。エラーが出たら間違っているところを直します。
~/qmk_firmware/kplj66r2_default.hex ができあがればOKです。

user@host:qmk_firmware$ qmk compile -kb kplj66r2 -km default

7.4.4. 日本語配列の作成

日本語配列のキーマップは、このようにします。

  • _BASE_LAYER

  • _CURSOR_LAYER

  • _MOUSE_LAYER

  • 日本語配列のキーマップを作成します。

user@host:qmk_firmware$ qmk new-keymap -kb kplj66r2 -km jis
  • keymaps/jis/keymap.cが作成されます。
    内容はdefault/keymap.cのコピーなので、日本語配列用に編集します。
jis/keymap.c
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  /*        SW1       SW2       SW3       SW4       SW5       SW6       SW7       SW8       SW9       SW10      SW11      SW12      SW13      SW14      SW15
   * BASE   ESC       1!        2"        3#        4$        5%        6&        7’       8(        9)        0         -=        ^~        \|        BS
   * CURSOR Zen/Han   F1        F2        F3        F4        F5        F6        F7        F8        F9        F10       F11       F12       PAUSE     DEL
   * MOUSE  ESC                 BriDnw    BriUp                                             Mute      Vol_Dwn   Vol_Up
   *
   *        SW16      SW17      SW18      SW19      SW20      SW21      SW22      SW23      SW24      SW25      SW26      SW27      SW28      SW29    
   * BASE   Tab       Q         W         E         R         T         Y         U         I         O         P         @`        [{          
   * CURSOR           TO(0)     TO(1)     TO(2)                                             PgUp      PgDwn     PSCREEN   SCRLLCOK  ESC            
   * MOUSE            TO(0)     TO(1)     TO(2)                                             MS_WH_Up  MS_WH_Dn                      ESC
   *
   *        SW30      SW31      SW32      SW33      SW34      SW35      SW36      SW37      SW38      SW39      SW40      SW41      SW42      SW43
   * BASE   Caps      A         S         D         F         G         H         J         K         L         ;+        :*        ]}        Enter
   * CURSOR           App                                               Left      Down      Up        Right                                   Enter
   * MOUSE            MS_BTN2             KC_BTN3   MS_BTN1             MS_Left   MS_Down   MS_Up     MS_Right                                Enter
   *
   *        SW44      SW45      SW46      SW47      SW48      SW49      SW50      SW51      SW52      SW53      SW54      SW55      SW56
   * BASE   LShift    Z         X         C         V         B         N         M         ,<        .>        /?        \_        RShift
   * CURSOR TRNS                                                                  Enter     Home      End                           TRNS
   * MOUSE  TRNS                                                                  Enter                                             TRNS
   *
   *        SW57      SW58      SW59      SW60      SW61      SW62      SW63      SW64      SW65      SW66      SW67
   * BASE   LCtrl     Win       LAlt      Muhen     Space     RCtrl     Henkan    RAlt      Kana      TO(1)     RCtrl
   * CURSOR TRNS                TRNS      TRNS                TRNS      TRNS      TRNS                TO(2)     TRNS
   * MOUSE  TRNS                TRNS      TRNS                TRNS      TRNS      TRNS                TO(0)     TRNS
   */
    [_BASE_LAYER] = LAYOUT_1(
            KC_ESC,   JP_1,     JP_2,     JP_3,     JP_4,     JP_5,     JP_6,     JP_7,     JP_8,     JP_9,     JP_0,     JP_MINS,  JP_CIRC,  JP_YEN,   KC_BSPC,
            KC_TAB,   JP_Q,     JP_W,     JP_E,     JP_R,     JP_T,     JP_Y,     JP_U,     JP_I,     JP_O,     JP_P,     JP_AT,    JP_LBRC,  KC_NO,
            JP_EISU,  JP_A,     JP_S,     JP_D,     JP_F,     JP_G,     JP_H,     JP_J,     JP_K,     JP_L,     JP_SCLN,  JP_COLN,  JP_RBRC,  KC_ENT,
            KC_LSFT,  JP_Z,     JP_X,     JP_C,     JP_V,     JP_B,     JP_N,     JP_M,     JP_COMM,  JP_DOT,   JP_SLSH,  JP_BSLS,  KC_RSFT,
            KC_LCTL,  KC_LGUI,  KC_LALT,  JP_MHEN,  KC_SPC,   KC_RCTL,  JP_HENK,  KC_RALT,  JP_KANA,  TO(1),    KC_RCTL
    ),
    [_CURSOR_LAYER] = LAYOUT_1(
            JP_ZKHK,  KC_F1,    KC_F2,    KC_F3,    KC_F4,    KC_F5,    KC_F6,    KC_F7,    KC_F8,    KC_F9,    KC_F10,   KC_F11,   KC_F12,   KC_PAUS,  KC_DEL,
            KC_NO,    TO(0),    TO(1),    TO(2),    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_PGUP,  KC_PGDN,  KC_PSCR,  KC_SLCK,  KC_ESC,   KC_NO,
            KC_NO,    KC_APP,   KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_LEFT,  KC_DOWN,  KC_UP,    KC_RGHT,  KC_NO,    KC_NO,    KC_NO,    KC_ENT,
            KC_TRNS,  KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_ENT,   KC_HOME,  KC_END,   KC_NO,    KC_NO,    KC_TRNS,
            KC_TRNS,  KC_NO,    KC_TRNS,  KC_TRNS,  KC_NO,    KC_TRNS,  KC_TRNS,  KC_TRNS,  KC_NO,    TO(2),    KC_TRNS
    ),
    [_MOUSE_LAYER] = LAYOUT_1(
            KC_ESC,   KC_NO,    KC_BRID,  KC_BRIU,  KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_MUTE,  KC_VOLD,  KC_VOLU,  KC_NO,    KC_NO,    KC_NO,    KC_NO,
            KC_NO,    TO(0),    TO(1),    TO(2),    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_WH_U,  KC_WH_D,  KC_NO,    KC_ESC,   KC_NO,    KC_NO,
            KC_NO,    KC_BTN2,  KC_NO,    KC_BTN3,  KC_BTN1,  KC_NO,    KC_MS_L,  KC_MS_D,  KC_MS_U,  KC_MS_R,  KC_NO,    KC_NO,    KC_NO,    KC_ENT,
            KC_TRNS,  KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_ENT,   KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_TRNS,
            KC_TRNS,  KC_NO,    KC_TRNS,  KC_TRNS,  KC_NO,    KC_TRNS,  KC_TRNS,  KC_TRNS,  KC_NO,    TO(0),    KC_TRNS
    )
};

7.4.5. 日本語配列のコンパイル

日本語配列をコンパイルしてみます。エラーが出たら間違っているところを直します。
~/qmk_firmware/kplj66r2_jis.hex ができあがればOKです。

user@host:qmk_firmware$ qmk compile -kb kplj66r2 -km jis

7.4.6. 英語配列_on_JISの作成

OSのキーボード設定を日本語にままで、英語配列を使うためのキーマップです。
qmkはKey Overrides という機能で、押されたキーを置き換えできるようなので、これを使うことにします。
こちらの記事を参考にさせていただきました。 https://qiita.com/shela/items/9e5bd102a95a15d1b00c

  • キーマップを作成します。
    keymaps/ansi_on_jis/keymap.cが作成されます。
user@host:qmk_firmware$ qmk new-keymap -kb kplj66r2 -km ansi_on_jis
  • ansi_on_jis/rules.mkを新規作成し、以下を記載します。
ansi_on_jis/rules.mk
KEY_OVERRIDE_ENABLE = yes
  • ansi_on_jis/keymap.cを編集します。
    • まず、レイヤーの別名定義の下の方に、キーの置き換えの定義を追記します。
ansi_on_jis/keymap.c
#include "keymap_japanese.h"

enum layers {
    _BASE_LAYER = 0,
    _CURSOR_LAYER,
    _MOUSE_LAYER
};

// shift+2  " -> @
const key_override_t kor_at = ko_make_with_layers(MOD_MASK_SHIFT, KC_2, JP_AT, 1);
// shift+6  & -> ^
const key_override_t kor_circ = ko_make_with_layers(MOD_MASK_SHIFT, KC_6, JP_CIRC, 1);
// shift+7  ' -> &
const key_override_t kor_ampr = ko_make_with_layers(MOD_MASK_SHIFT, KC_7, JP_AMPR, 1);
// shift+8  ( -> *
const key_override_t kor_astr = ko_make_with_layers(MOD_MASK_SHIFT, KC_8, JP_ASTR, 1);
// shift+9  ) -> (
const key_override_t kor_lprn = ko_make_with_layers(MOD_MASK_SHIFT, KC_9, JP_LPRN, 1);
// shift+0    -> )
const key_override_t kor_rprn = ko_make_with_layers(MOD_MASK_SHIFT, KC_0, JP_RPRN, 1);
// shift+-  = -> _
const key_override_t kor_unds = ko_make_with_layers(MOD_MASK_SHIFT, KC_MINS, JP_UNDS, 1);
// =        ^ -> =
// shift+=  ~ -> +
const key_override_t kor_eql = ko_make_with_layers_and_negmods(0, JP_CIRC, JP_EQL, 1, MOD_MASK_SHIFT);
const key_override_t kor_plus = ko_make_with_layers(MOD_MASK_SHIFT, JP_CIRC, JP_PLUS, 1);
/* \        ] -> \ */
/* shift+\  } -> | */
const key_override_t kor_bsls = ko_make_with_layers_and_negmods(0, KC_BSLS, JP_BSLS, 1, MOD_MASK_SHIFT);
const key_override_t kor_pipe = ko_make_with_layers(MOD_MASK_SHIFT, KC_BSLS, JP_PIPE, 1);
// [        @ -> [
// shift+[  ` -> {
const key_override_t kor_lbrc = ko_make_with_layers_and_negmods(0, JP_AT, JP_LBRC, 1, MOD_MASK_SHIFT);
const key_override_t kor_lcbr = ko_make_with_layers(MOD_MASK_SHIFT, JP_AT, JP_LCBR, 1);
// ]        [ -> ]
// shift+]  { -> }
const key_override_t kor_rbrc = ko_make_with_layers_and_negmods(0, JP_LBRC, JP_RBRC, 1, MOD_MASK_SHIFT);
const key_override_t kor_rcbr = ko_make_with_layers(MOD_MASK_SHIFT, JP_LBRC, JP_RCBR, 1);
// shift+;  + -> :
const key_override_t kor_coln = ko_make_with_layers(MOD_MASK_SHIFT, KC_SCLN, JP_COLN, 1);
// '        : -> '
// shift+'  * -> "
const key_override_t kor_quot = ko_make_with_layers_and_negmods(0, KC_QUOT, JP_QUOT, 1, MOD_MASK_SHIFT);
const key_override_t kor_dquo = ko_make_with_layers(MOD_MASK_SHIFT, KC_QUOT, JP_DQUO, 1);
// `        全角半角 -> `
// shift+`  shift+全角半角 -> ~
const key_override_t kor_grv = ko_make_with_layers_and_negmods(0, JP_ZKHK, JP_GRV, 1, MOD_MASK_SHIFT);
const key_override_t kor_tild = ko_make_with_layers(MOD_MASK_SHIFT, JP_ZKHK, JP_TILD, 1);
// Caps     英数 -> Caps
const key_override_t kor_caps = ko_make_with_layers_and_negmods(0, JP_EISU, JP_CAPS, 1, MOD_MASK_SHIFT);
  • その下に、置き換えの適用順の定義を追記します。
    CapsLockは、単体押しで英数のままでもいいかなと思い、コメントアウトしました。
ansi_on_jis/keymap.c
const key_override_t **key_overrides = (const key_override_t *[]){
    &kor_at,
    &kor_circ,
    &kor_ampr,
    &kor_astr,
    &kor_lprn,
    &kor_rprn,
    &kor_unds,
    &kor_eql,
    &kor_plus,
    &kor_bsls,
    &kor_pipe,
    &kor_lbrc,
    &kor_lcbr,
    &kor_rbrc,
    &kor_rcbr,
    &kor_coln,
    &kor_quot,
    &kor_dquo,
    &kor_grv,
    &kor_tild,
  /*&kor_caps,*/
    NULL
};

7.4.7. 英語配列_on_JISのコンパイル

コンパイルしてみます。エラーが出たら間違っているところを直します。
~/qmk_firmware/kplj66r2_ansi_on_jis.hex ができあがればOKです。

user@host:qmk_firmware$ qmk compile -kb kplj66r2 -km ansi_on_jis

7.5. コンパイル

試しにコンパイルしてますが、その後でどこか修正したかもしれないので、改めてコンパイルします。

user@host:qmk_firmware$ qmk compile -kb kplj66r2 -km default
user@host:qmk_firmware$ qmk compile -kb kplj66r2 -km jis
user@host:qmk_firmware$ qmk compile -kb kplj66r2 -km ansi_on_jis

7.6. とりあえずProMicorに書き込んでみる

組み立ててから動かないと原因を調べるのが大変そうなので、最初にProMicro単体で動くことを確認してみます。

  • USBケーブルでPCとProMicroをつないで、認識されることを確認します。
user@host1:qmk_firmware$ dmesg 
[    0.000000] Linux version 5.10.0-20-amd64 (debian-kernel@lists.debian.org) (gcc-10 (Debian 10.2.1-6) 10.2.1 20210110, GNU ld (GNU Binutils for Debian) 2.35.2) #1 SMP Debian 5.10.158-2 (2022-12-13)
 … 中略 …
[12437.486370] usb 1-4: USB disconnect, device number 10
[12437.486769] xhci_hcd 0000:02:00.0: WARN Set TR Deq Ptr cmd failed due to incorrect slot or ep state.
  • ProMicroのGNDとRSTをリード線で1秒くらい短絡させて離してリセットし、PCにシリアル接続されたことを確認します。

user@host:qmk_firmware$ dmesg 
[    0.000000] Linux version 5.10.0-20-amd64 (debian-kernel@lists.debian.org) (gcc-10 (Debian 10.2.1-6) 10.2.1 20210110, GNU ld (GNU Binutils for Debian) 2.35.2) #1 SMP Debian 5.10.158-2 (2022-12-13)
 … 中略 …
[12437.486370] usb 1-4: USB disconnect, device number 10
[12437.486769] xhci_hcd 0000:02:00.0: WARN Set TR Deq Ptr cmd failed due to incorrect slot or ep state.
[12442.161887] usb 1-4: new full-speed USB device number 11 using xhci_hcd
[12442.428121] usb 1-4: New USB device found, idVendor=2341, idProduct=0036, bcdDevice= 0.01
[12442.428124] usb 1-4: New USB device strings: Mfr=2, Product=1, SerialNumber=0
[12442.428126] usb 1-4: Product: Arduino Leonardo
[12442.428126] usb 1-4: Manufacturer: Arduino LLC
[12442.448122] cdc_acm 1-4:1.0: ttyACM0: USB ACM device

/dev/ttyACM0に接続されました。

  • ファームウェアを書き込んでみます。下記はansi_on_jisの場合です。
user@host:qmk_firmware$ avrdude -p atmega32u4 -c avr109 -P /dev/ttyACM0 -U flash:w:/home/user/qmk_firmware/kplj66r2_ansi_on_jis.hex 

Connecting to programmer: .
Found programmer: Id = "CATERIN"; type = S
    Software Version = 1.0; No Hardware Version given.
Programmer supports auto addr increment.
Programmer supports buffered memory access with buffersize=128 bytes.

Programmer supports the following devices:
    Device code: 0x44

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e9587 (probably m32u4)
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "/home/user/qmk_firmware/kplj66r2_ansi_on_jis.hex"
avrdude: input file /home/user/qmk_firmware/kplj66r2_ansi_on_jis.hex auto detected as Intel Hex
avrdude: writing flash (16888 bytes):

Writing | ################################################## | 100% 2.03s

avrdude: 16888 bytes of flash written
avrdude: verifying flash memory against /home/user/qmk_firmware/kplj66r2_ansi_on_jis.hex:
avrdude: load data flash data from input file /home/user/qmk_firmware/kplj66r2_ansi_on_jis.hex:
avrdude: input file /home/user/qmk_firmware/kplj66r2_ansi_on_jis.hex auto detected as Intel Hex
avrdude: input file /home/user/qmk_firmware/kplj66r2_ansi_on_jis.hex contains 16888 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.46s

avrdude: verifying ...
avrdude: 16888 bytes of flash verified

avrdude: safemode: Fuses OK (E:CB, H:D8, L:FF)

avrdude done.  Thank you.
  • ProMicroをリセットしてからある程度時間が経つと、シリアル接続が切れてしまい、下記のようになって書き込めなくなります。
    こうなったときは、一旦Ctrl+Cで止めて、ProMicroをもう一回リセットしてやり直します。
user@host:qmk_firmware$ avrdude -p atmega32u4 -c avr109 -P /dev/ttyACM0 -U flash:w:/home/user/qmk_firmware/kplj66r2_ansi_on_jis.hex 

Connecting to programmer: .avrdude: butterfly_recv(): programmer is not responding

avrdude: butterfly_recv(): programmer is not responding
avrdude: butterfly_recv(): programmer is not responding
^C
  • ファームウェアの書き込みに成功したら、リード線で短絡させて、文字が入力されることを確認してみます。
    今回のキーマップだと「」がCol1(ポートD2、印字はRXI)とRow2(ポートF4、印字はA3)なので、これで試してみます。
user@host:qmk_firmware$ 111
  • 無事、入力できました。(ウィンドウにフォーカス必要。)

8. キーボード本体の製作

必要な部品・資材が揃ったところで、本体を製作します。

8.1. PCBのはんだ付け

  • PCBにスイッチソケットをはんだ付けします。

  • つぎに、ダイオードをはんだ付けします。ダイオードは、実装面(足の飛び出し先ではない方)ではんだ付けするほうが簡単でした。(作法的に良いのかはわかりません…)

  • ProMicroをはんだ付けします。向きに気をつけます。

8.2. ケースの加工

  • ケースの使用しないネジ止め用の土台をホットカッターとニッパーでもぎ取ります。

  • また、スペースキーのスタビライザーのネジがケースの梁に干渉するので、元々の6uスペースキー用の切り欠きと同じくらいの高さまで、ホットカッターで切り取ります。

  • USB Type-Cソケット基板は、元々の穴の位置で取り付けるとPCBに干渉するので、ヤスリで少し下の方に穴を広げてます。
    そして、ビス止め用のM2の穴をあけます。

8.3. 組み立て

  • ケースの穴にリード線を通して、USBの基板をはんだ付けします。

  • 一旦ここできちんと動くか、確認してみます。
    いくつかのソケットにスイッチをつけて、PCにつないで打ってみます。
    文字が打てれば、一安心です。

  • USB Type-Cのソケットは、ケースに少し隙間ができたので、シリコーンボンドで埋めました。絶縁性はあるようです。

  • スイッチをあけて潤滑剤を塗ります。
    数が多くて挫けそうになりますが、とりあえず動くことを確認したのが支えになりました。

  • スイッチプレートは2mmのアクリル板で発注したのですが、スタビライザーと干渉したため、ホットカッターで削りました。
    スイッチのプレート取付面からPCBまで5mmのようなのですが、スタビライザーのネジ部分の高さが4mm弱だったため、2mmのプレートではダメでした。
    実は初回作のときも、3mmのアクリルで発注して同じような加工をしていて、今回は「じゃ2mmにしよう」と、ちゃんと測らずに適当に決めたのが浅はかでした。

  • また、見栄えが悪いですが、プレートの脱落防止と静音期待で、隙間テープを貼りました。絶縁性が気になりましたが、今のところ大丈夫です。

  • Enterキー付近は、やむを得ずプレートの一部を切り取りました。

  • ProMicroはUSB Micro-Bのコネクタがもげやすいそうなので、もげ対策をします。
    Micro-Bオス基盤の下に絶縁テープと隙間テープを貼って、端子の振動荷重がProMicroのコネクタに集中しないようにします。
    また、2種類のチューブを混ぜるタイプのパテや接着剤で、コネクタ周りを固めます。
    今回、盛りつけしすぎてケースに干渉したため、少しカッターで削りました。
    固まりきったら、端子周りを更に絶縁テープで貼って固定します。

  • PCBの上にプレートを載せて、スイッチを差し込みます。
    1行目と5行目を差し込むときに、プレートが下に外れるので、PCBをケースにネジ止めする前に付けるほうが良いかもしれません。
    先にPCBをケースに固定した場合は、プレートをキープラーなどで引っ張りながらスイッチを付ければ大丈夫でした。

  • スイッチを取り付けたら、PCにつないで全部のキーが正常に認識されるか確認します。
    私の場合、2台製作して、ソケットの不良が1つ、キースイッチの足の折れが10個くらいありました。
    ソケットの不良は原因不明です。キーを付け替えても認識されず、裏返してソケットのはんだ部分をリード線で短絡したらキー入力されたので、ソケット不良と判断しました。ハンダ吸い取り線で外して、別のソケットを付けたら直りました。
    スイッチの足の折れは、差し込む際の角度が悪かったようです。取り外してペンチで折れた足を伸ばして付けたら直りました。

  • キーキャップと静音化リングを付けて組み立て完了です。
    5行目を内側寄りのレイアウトにする場合は、1.75uのスペースキーのキャップがないので、Shiftキー用のキャップを上下ひっくり返して使います。
    変換・無変換も、高頻度で押すので、親指が痛くならないようにひっくり返します。
    上下の区別がないDSAやXDAは、4行目を押したときに指が踏み外している感じがして、自分には合いませんでした。

8.4. テスト

改めてPCにつないで、全レイヤーの全キーが正しく入力できるか確認します。
ファームウェアのkeymap.cで、キーコードの記入間違いをしていた、というケースもありました。
日本語配列でのテストは、キーとスタビライザーを付け替えるのが面倒になり、まだやっていません。

9. 今回の製作で気づいたこと

  • ソケット式は何度か付け外しすると、ゆるくなる。
    ソケットがゆるくなるのか、スイッチの爪がへたるのか、プレートが変形しているのかはわかりませんが、割とすぐにゆるくなりました。持ち運びの際、キーが取れることがあります。なくさないように気をつけます。
  • キーキャップで静音性がけっこう違う。
    今回2つ製作したので違いがわかりました。上の写真で白黒のほうが、離すときの音が高くて大きい感じがします。とはいえ、なかなか買う前に聴き比べる機会はないので、どうしようもないですが。

10. 感想

先駆者の方々のドキュメントが豊富なので、「昔はんだ付けやったことがある」くらいの経験しかなくても、意外とどうにかなると思いました。
これで一生モノのつもりで作っても、些細なことで改良したくなってしまい、ハマった感じがしますね。

Discussion