基板から自作キーボードを作った！脱Pro Micro！

この記事は[N高等学校 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/n-highschool)の4日目です。

昨日の記事はなかなか渋いサービスを作った@OldBigBuddhaさんの「[六法JSONと言うサービスを作っているお話](https://oldbigbuddha.dev/post/introduce-roppo-json/)」です。合わせてどうぞ。

こんにちは、N高等学校3年生のKakudoです。今年もあと僅かと受験が間近に迫っていますが、[去年](https://qiita.com/kakudo415/items/230a50691dcdcc5b8326)に引き続きアドベントカレンダーを書かせていただきます。

# この記事について
Pro Microに頼らない自作キーボード作りに挑戦しました。
半分以上の地雷を踏み抜いたと思うので参考になれば幸いです。
以前作った[Pro Microを使った自作キーボード](https://qiita.com/kakudo415/items/c22ca2309b2d9e8db6cc)も合わせてご覧ください。

## 環境
- macOS 10.14 Mojave
- KiCAD 5.1.0

## 使ったもの
- ATmega32U4-AU
- USB Type-C レセプタクル (CX70M-24P1)
- 各種抵抗・コンデンサ・水晶発振子
- Gateron 赤軸 (流用)
- キーキャップ (これも流用、自作キーボードはお金がかかります...)
- USB Type-C ケーブル (2.0対応であれば良いので何でもよいはず)

# 作る
今回も詰まって手戻りが発生していますが、間違いも含め時系列順に書きます。

## 設計
大まかなコンセプトを決めてから設計します。

- 一体型
- 省スペース
- 格子配列
- USB Type-C 接続

以上を最初に決めました。ここでキーマップをどうするかをざっくり考えておくと必要なキーの数や配置が見えてくると思います。

### 基板
いつもどおりKiCADで設計します。

まずEeschemaで回路図を書きます。同じマイクロコントローラーを使っているとあまり変化がないですね。[仕様](http://ww1.microchip.com/downloads/en/devicedoc/atmel-7766-8-bit-avr-atmega16u4-32u4_datasheet.pdf)通りに必要箇所を繋ぐだけです。

|![スクリーンショット 2019-11-27 16.36.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271592/a3d64c44-599b-4d39-0949-ae10096c3041.png)|
|:-:|
|回路図を書き終えたところ|

次にPcbnewで配線していきます。回路図と違って実際のキーボードの見た目にも影響しうる場所なのでこだわりました。

|![スクリーンショット 2019-11-27 17.35.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271592/a26d0005-1dd1-16b6-8516-c0878acb5ea2.png)|
|:-:|
|配線し終えたところ|

届くまでに時間がかかりがちなので基板から設計し、注文を早めにしておくと良いと思います。

### ケース
Fusion 360で作成し、3Dプリントを委託しました。極めて単純な形状しか作れないので、デザイン力を上げていきたいところです。

## はんだ付け
細かいパーツが多いので小さい場所をも見通す心の目と気合いが重要です。

|![IMG_1076.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271592/42db6385-b8c5-c2f4-8d31-730f1e17bde1.jpeg)|
|:-:|
|はんだ付けの完成形|

### マイクロコントローラー
ATmega32U4には0.5mm間隔で44本のピンが付いているのでそれを全て正確にランドに固定しなければなりません。
このような細かい間隔で足が並んでいる場合ははんだゴテにはんだを付けてからフラックスを塗った足の上を滑らせるように一気にはんだ付けすると上手くいきます。

### USB Type-C レセプタクル
|![IMG_1072.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271592/9db7f39c-e658-aaf9-e2ff-25e47c692519.jpeg)|
|:-:|
|レセプタクルの表面実装部分。とても細かい...|

**大半のレセプタクルがリフローを前提にした構造になっている**ので、手ではんだ付けする方は必ずデータシートなどをよく読んで手ではんだ付け可能かどうかを調べるようにしてください。
今回使ったCX70M-24P1は0.4mm間隔で18の足が並んでいて中々にはんだ付けが難しかったです。
正直、目では上手くいったか確認不能なので10倍くらいのルーペを用意されたほうが良いです。

### 水晶発振子
Pro Microで使われているからと言う理由でFA-238を選びましたが、人のやる物では無いです。スルーホールをお勧めします。
というのも、置いてしまうと**はんだ付け部が裏側にある**ため一切触れなくなります。
なので、4つのランドにハンダを置いてから、部品全体を熱して同時にハンダを溶かしくっつけました。

### 各種抵抗・コンデンサ
1206サイズを使いましたが十分難しかったです。間違いなくピンセットは必要になると思います。(0603を手でできる人どういう修行を積んでらっしゃるのか...)

### ダイオード
1N4148という表面実装のダイオードを使いましたが、向きを示す線がめちゃくちゃ分かりにくかったので向き間違いに注意してください。

## はんだ失敗！
息をするように失敗したのでまとめます。

### マイクロコントローラー
そもそもの位置合わせに失敗して、正方形のチップの3辺をはんだ付けしたところで最後の1辺がどうしてもずれることに気づいて焦りました。
足が折れたら終わりなので曲げる時は気が気ではありませんでしたが、ずれた11本を少しずつずらして対処しました。

更には4箇所しっかりブリッジしたので、フラックスを付け直しもう一度はんだゴテで撫でるようにして直しました。

### USB Type-C レセプタクル
この部品は6本のスルーホールの足と18本の表面実装の足で構成されているのですが、先にやりやすい方からやって固定したほうが楽かと思ってスルーホールから手を付けましたが、表面実装側がずれてしまいやりなおしました。
**精度がシビアな側(今回なら表面実装)を先に**やるべきですね。

### USBデバイスが無効です
上手くはんだ付けできた！とMacに繋いでみるとこのメッセージが出て、更にはUSB端子付近がだんだん熱くなってきたので慌てて抜きました。
これはVCC-GND間がショートしていることが原因でした(そりゃ熱くなりますね、危ない...)

## PCがキーボードを認識した！
ここまでが長かった...思わず一章を使ってしまうほど長く辛い道のりでした。
ATmega32U4のプログラミングについて調べていると、「認識するとMacのシリアルポートに表示される」と書いてありましたが、少なくとも僕の環境では表示されませんでした。
おかしいと思ってMacのシステムレポートを見てみると`USB 3.0バス`の項に`ATm32U4`の表記があり、認識されていることが分かりました。
何もファームウェアが書き込まれていない時だけかと思いましたが、後述のキーボードのファームウェアを書き込んだあともシリアルポートには表示されませんでした。
なので、はんだ付けしてみてPCとうまく接続できたか確認したい場合はシステムレポートを見ると良さそうです。

## ファームウェアプログラミング
### Cで書いてみよう！(断念)
僕に知見がなく1文字すら送れませんでした...
コンパイル・書き込みコマンドにたどり着くまでにも時間がかかったので残しておきます。

```shell
avr-gcc -g -O2 -mmcu=atmega32u4 -DF_CPU=16000000UL -c -o test.o test.c
avr-gcc -g -O2 -mmcu=atmega32u4 -DF_CPU=16000000UL -o test test.o
avr-objcopy -j .text -j .data -O ihex test test.ihex
dfu-programmer atmega32u4 erase #既存のファームを消す。ファームが書かれてない場合も --force を付けて実行しておくと良さげ
dfu-programmer atmega32u4 flash test.ihex
```

### QMK Firmware に頼る
先人の知恵は借りるに限るもので、とても簡単にファームウェアが書けました。(書くというより設定したというのが近いほど簡便にできました)
https://github.com/qmk/qmk_firmware

#### 初期設定・新しいキーボードを定義

```shell
./util/qmk_install.sh
```

クローンしたら、最初にこのコマンドを実行します。
試しに何かmakeしてみると良いかもしれません。

```shell
make {any_keyboard}:default
```

では、新しいキーボード用のディレクトリを作ります。

```shell
./util/new_keyboard.sh
```

対話形式でキーボードのプロフィールを聞いてくるので、かっこいい名前を付けましょう。実行すると`./keyboards`ディレクトリに命名したキーボードと同じ名前のディレクトリが作られます。

#### 行・列の設定
QMKではキーを行と列で管理しているので、実際のピンと対応させる必要があります。

まず、`config.h`内の下の箇所をキーボードに合わせて書き換えます。

```c
#define MATRIX_ROWS 2
#define MATRIX_COLS 3
```

```c
#define MATRIX_ROWS 4
#define MATRIX_COLS 12
```

今回作ったキーボードは4列12行なのでこのように書き換えました。

次に、下の箇所を書き換えて実際のマイコンのピンと行・列のインデックスを対応させます。

```c
#define MATRIX_ROW_PINS { D0, D5 }
#define MATRIX_COL_PINS { F1, F0, B0 }
```

```c
#define MATRIX_ROW_PINS { B0, B1, B2, B3 }
#define MATRIX_COL_PINS { D5, D3, D2, D1, D0, B7, B6, B5, B4, D7, D6, D4 }
```

これもキーボードに合わせてこのように書き換えました。

#### キーマップの設定
次に行と列で定義した格子状のキー配置と実際に打つキーマップを対応させます。

まずは、すべてのキーに番号を振ります。`キーボード名.h`内のLAYOUTマクロを

```c
#define LAYOUT( \
    k00, k01, k02, \
      k10,  k11   \
) \
{ \
    { k00, k01,   k02 }, \
    { k10, KC_NO, k11 }, \
}
```

```c
#define LAYOUT( \
    k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0a, k0b, \
    k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k1a, k1b, \
    k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k2a, k2b, \
    k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k3a, k3b \
) \
{ \
    { k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0a, k0b }, \
    { k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k1a, k1b }, \
    { k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k2a, k2b }, \
    { KC_NO, KC_NO, KC_NO, k33, k34, k35, k36, k37, k38, KC_NO, KC_NO, KC_NO } \
}
```

このように書き換えました。
LAYOUTマクロついて良く分かっておらず雰囲気でやっているので、ご存じの方がいらっしゃればマサカリを投げていただけると嬉しいです。

最後に、設定したキー番号とキーマップを対応させます。`keymaps/default/keymap.c`内の下の場所を

```c
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  [0] = LAYOUT( /* Base */
    KC_A,  KC_1,  KC_H, \
      KC_TAB,  KC_SPC   \
  ),
};
```

```c
#define ____ KC_TRNS

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  [0] = LAYOUT( /* Base */
    KC_TAB, KC_QUOT, KC_COMM, KC_DOT, KC_P, KC_Y, KC_F, KC_G, KC_C, KC_R, KC_L, KC_SLSH, \
    KC_LCTL, KC_A, KC_O, KC_E, KC_U, KC_I, KC_D, KC_H, KC_T, KC_N, KC_S, KC_MINS, \
    KC_LSFT, KC_SCLN, KC_Q, KC_J, KC_K, KC_X, KC_B, KC_M, KC_W, KC_V, KC_Z, KC_RSFT, \
    KC_NO, KC_NO, KC_NO, KC_LGUI, KC_SPC, MO(1), KC_BSPC, KC_ENT, KC_RALT, KC_NO, KC_NO, KC_NO
  ),
  [1] = LAYOUT(
    KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSLS, \
    KC_LCTL, KC_LBRC, KC_RBRC, KC_GRV, KC_F11, KC_F12, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, KC_EQL, ____, \
    ____, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, ____, \
    KC_NO, KC_NO, KC_NO, KC_LGUI, KC_SPC, KC_NO, KC_BSPC, KC_ENT, KC_RALT, KC_NO, KC_NO, KC_NO
  )
};

```

このように書き換えました。レイヤーを複数定義し切り替えることもできるので小さなキーボードでも不自由しないのがとても便利です。
他にもたくさんキー・キーマクロがあるので公式の[キーコード一覧](https://github.com/qmk/qmk_firmware/blob/master/docs/keycodes.md)をご覧ください。

#### 書き込み
いよいよキーボードに命を吹き込みます！

```shell
make キーボード名:default:dfu
```

このコマンドで書き込めます。
おそらくリセットが求められると思うので、マイコンのRESETピンとGNDをショートさせましょう。**設計段階でリセットスイッチを付けておく**ことをお勧めします(付けていなかったのでケースに穴を空けてショートさせてます...)

|![IMG_1087.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271592/dccb8b6b-9fd0-a4cf-bb41-4e8f7c6a095e.jpeg)|
|:-:|
|完成！|

動いた！！！

もし打てないキーがあったりしたら(上の画像でも左端一列反応してない)十中八九はんだ付けのミスなので、1つ1つテスターを当てるなどして確認しましょう。(最下段の左右3つずつは作っていて使わない気がしたので外しました)

# 終わりに
今回は、1つの目標でもあったPro Microなどのボードに頼らないキーボード作りをしました。デザインにはがっつり難がありますが、使い心地は良いです。今回の内容は動画にもなっているので良かったら合わせてご覧ください。
[自作キーボード Solearn 再生リスト](https://www.youtube.com/watch?v=vvEX2FNAGiY&list=PLycapkeQodvtdzo-tfnxT2YOxEQRUKJUi)

当然のことながら、まだまだ満足できるキーボードでは無いので遥か先のEND GAMEに向かってこれからも作っていきたいと思います(この記事もHHKBで書いてます...)

明日は@kou_nepsonさんの記事です！「3匹目のAI」という謎のワードが興味をそそりますね～お楽しみに！

---
以下ポエムです。

入学してからあっという間に時間が過ぎもう卒業ということに驚いています。
N高ではプログラミングについてたくさん学ばせて頂きました。特にドワンゴに1ヶ月インターンに行ったのは大きな糧となりました。
これは単にN高というプラットフォームだけでなく、吉村先生を始めとした予備校の講師の方々、そしてあらゆる分野で自分の上位互換となり引っ張ってくれた先輩・後輩・同級生のおかげだと思っています。
本当にありがとうございます。