ゼロから:開発者の夢のキーボードをどのように構築したか

公開: 2022-03-11

2007年8月のある日働いていた私は、通常のPCキーボードが私にできる限り役立たなかったことに気づかずにはいられませんでした。 キーボードのさまざまなブロック間で、1日に数千回とまではいかなくても数百回も手を動かさなければならず、手が不快に接近していました。 もっと良い方法があるに違いないと思いました。

この認識に続いて、開発者向けに最高のキーボードをカスタマイズすることを考えたとき、圧倒的な興奮を感じました。その後、フリーランスの組み込みソフトウェア開発者として、ハードウェアについて無知であることに気づきました。

当時、私は他のプロジェクトでかなり忙しかったのですが、ハッカーキーボードの作成を考えなかった日はありませんでした。 すぐに私は自分の自由な時間をプロジェクトに取り組むことに捧げ始めました。 私はなんとかまったく新しいスキルセットを学び、私の友人である機械エンジニアの並外れたAndras Volgyiを説得してプロジェクトに参加し、キーパーソンを集め、実用的なプロトタイプの作成に十分な時間を費やしました。 今日、究極のハッキングキーボードは現実です。 私たちは日々進歩を遂げており、クラウドファンディングキャンペーンの立ち上げも間近に迫っています。

キーボードのレイアウトを変更する方法を考えることから始めて、これで終わりました!

ソフトウェアのバックグラウンドから、電子機器について何も知らずに、強力で市場性のあるハードウェアデバイスを設計および構築することは、興味深く魅力的な経験です。 この記事では、この電子傑作がどのように機能するかの設計について説明します。 電子回路図の基本的な理解は、あなたが従うのに役立つかもしれません。

どうやってキーボードを作りますか?

私の人生の何千時間もこのトピックに捧げた後、短い答えを出すことは私にとって大きな挑戦ですが、この質問に答える興味深い方法があります。 Arduinoボードのような単純なものから始めて、徐々にそれを構築して究極のハッキングキーボードにするとどうなるでしょうか。 それはより消化しやすいだけでなく、非常に教育的であるべきです。 したがって、キーボードチュートリアルの旅を始めましょう!

ステップ1:キーのないキーボード

まず、1秒に1回の割合でx文字を出力するUSB​​キーボードを作りましょう。 Arduino Micro開発ボードは、ATmega32U4マイクロコントローラー(AVRマイクロクロントロラーとUHKの頭脳であるのと同じプロセッサー)を備えているため、この目的に理想的な候補です。

Arduino Microボードは、開発者向けのキーボードを構築するための基礎でした。

USB対応のAVRマイクロコントローラーに関しては、AVR用の軽量USBフレームワーク(LUFA)が最適なライブラリです。 これにより、これらのプロセッサは、プリンタ、MIDIデバイス、キーボード、またはその他のほとんどすべてのタイプのUSBデバイスの頭脳になることができます。

デバイスをUSBポートに接続する場合、デバイスはUSB記述子と呼ばれるいくつかの特別なデータ構造を転送する必要があります。 これらの記述子は、接続されているデバイスのタイプとプロパティをホストコンピューターに通知し、ツリー構造で表されます。 さらに複雑なことに、デバイスは1つだけでなく複数の機能を実装できます。 UHKの記述子構造を見てみましょう。

  • デバイス記述子
    • 構成記述子
      • インターフェイス記述子0:GenericHID
        • エンドポイント記述子
      • インターフェイス記述子1:キーボード
        • エンドポイント記述子
      • インターフェイス記述子2:マウス
        • エンドポイント記述子

ほとんどの標準キーボードは、単一のキーボードインターフェイス記述子のみを公開します。これは理にかなっています。 ただし、カスタムプログラミングキーボードとして、UHKはマウスインターフェイス記述子も公開します。これは、ユーザーがキーボードの任意のキーをプログラムしてマウスポインターを制御できるため、キーボードをマウスとして使用できるためです。 GenericHIDインターフェースは、キーボードのすべての特別な機能の構成情報を交換するための通信チャネルとして機能します。 LUFAでのUHKのデバイスおよび構成記述子の完全な実装はここで確認できます。

記述子を作成したので、今度は毎秒x文字を送信します。

 uint8_t isSecondElapsed = 0; int main(void) { while (1) { _delay_us(1000); isSecondElapsed = 1; } } bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo, uint8_t* const ReportID, const uint8_t ReportType, void* ReportData, uint16_t* const ReportSize) { USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData; if (isSecondElapsed) { KeyboardReport->KeyCode[0] = HID_KEYBOARD_SC_X; isSecondElapsed = 0; } *ReportSize = sizeof(USB_KeyboardReport_Data_t); return false; }

USBはポーリングされたプロトコルです。つまり、ホストコンピューターは定期的に(通常は1秒間に125回)デバイスにクエリを実行して、送信する新しいデータがあるかどうかを確認します。 関連するコールバックはCALLBACK_HID_Device_CreateHIDReport()関数であり、この場合、 isSecondElapsed変数に1が含まれている場合は常に、 x文字のスキャンコードをホストに送信します。 isSecondElapsedは、メインループから1秒ごとに1に設定され、コールバックから0に設定されます。

ステップ2:4つのキーのキーボード

この時点で、私たちのキーボードはそれほど便利ではありません。 実際に入力できたらいいですね。 しかし、そのためにはキーが必要であり、キーはキーボードマトリックスに配置する必要があります。 フルサイズの104キーキーボードは18行6列である可能性がありますが、起動用の控えめな2x2キーボードマトリックスがあります。 これは概略図です:

ハッカーキーボードをカスタマイズするには、キーマトリックスを慎重に検討する必要があります。

そして、これはブレッドボード上でどのように見えるかです:

ブレッドボードの構成は、開発者向けのキーボードを構築する上で重要なステップです。

ROW1PINA0に、 ROW2PINA1に、 COL1PORTB0に、 COL2PORTB1に接続されているとすると、スキャンコードは次のようになります。

 /* A single pin of the microcontroller to which a row or column is connected. */ typedef struct { volatile uint8_t *Direction; volatile uint8_t *Name; uint8_t Number; } Pin_t; /* This part of the key matrix is stored in the Flash to save SRAM space. */ typedef struct { const uint8_t ColNum; const uint8_t RowNum; const Pin_t *ColPorts; const Pin_t *RowPins; } KeyMatrixInfo_t; /* This Part of the key matrix is stored in the SRAM. */ typedef struct { const __flash KeyMatrixInfo_t *Info; uint8_t *Matrix; } KeyMatrix_t; const __flash KeyMatrixInfo_t KeyMatrix = { .ColNum = 2, .RowNum = 2, .RowPins = (Pin_t[]) { { .Direction=&DDRA, .Name=&PINA, .Number=PINA0 }, { .Direction=&DDRA, .Name=&PINA, .Number=PINA1 } }, .ColPorts = (Pin_t[]) { { .Direction=&DDRB, .Name=&PORTB, .Number=PORTB0 }, { .Direction=&DDRB, .Name=&PORTB, .Number=PORTB1 }, } }; void KeyMatrix_Scan(KeyMatrix_t *KeyMatrix) { for (uint8_t Col=0; Col<KeyMatrix->Info->ColNum; Col++) { const Pin_t *ColPort = KeyMatrix->Info->ColPorts + Col; for (uint8_t Row=0; Row<KeyMatrix->Info->RowNum; Row++) { const Pin_t *RowPin = KeyMatrix->Info->RowPins + Row; uint8_t IsKeyPressed = *RowPin->Name & 1<<RowPin->Number; KeyMatrix_SetElement(KeyMatrix, Row, Col, IsKeyPressed); } } }

コードは一度に1つの列をスキャンし、その列内で個々のキースイッチの状態を読み取ります。 次に、キースイッチの状態がアレイに保存されます。 以前のCALLBACK_HID_Device_CreateHIDReport()関数内で、関連するスキャンコードがその配列の状態に基づいて送信されます。

ステップ3:2つの半分のキーボード

これまで、通常のキーボードの始まりを作成してきました。 しかし、このキーボードチュートリアルでは、高度な人間工学を目指しており、人々が両手を持っていることを考えると、別のキーボードの半分をミックスに追加する方がよいでしょう。

残りの半分は、前のものと同じように機能する別のキーボードマトリックスを備えています。 エキサイティングな新しいことは、キーボードの半分の間の通信です。 電子機器を相互接続するための最も一般的な3つのプロトコルは、SPI、I 2 C、およびUARTです。 この場合、実用的な目的でUARTを使用します。

優れたプログラミングキーボードであるためには、両方の半分の間に恒星の通信がなければなりません。

双方向通信は、上の図に従ってRXを右方向に、TXを左方向に流れます。 電力を転送するには、VCCとGNDが必要です。 UARTは、ピアが同じボーレート、データビット数、およびストップビット数を使用する必要があります。 両方のピアのUARTトランシーバーがセットアップされると、通信が流れ始めることができます。

今のところ、左のキーボードの半分はUARTを介して右のキーボードの半分に1バイトのメッセージを送信し、キーの押下またはキーのリリースイベントを表します。 右のキーボードの半分はこれらのメッセージを処理し、それに応じてメモリ内の完全なキーボードマトリックス配列の状態を操作します。 これは、左キーボードの半分がメッセージを送信する方法です。

 USART_SendByte(IsKeyPressed<<7 | Row*COLS_NUM + Col);

メッセージを受信するためのキーボードの右半分のコードは次のようになります。

 void KeyboardRxCallback(void) { uint8_t Event = USART_ReceiveByte(); if (!MessageBuffer_IsFull(&KeyStateBuffer)) { MessageBuffer_Insert(&KeyStateBuffer, Event); } }

KeyboardRxCallback()割り込みハンドラーは、UARTを介してバイトが受信されるたびにトリガーされます。 割り込みハンドラは可能な限り迅速に実行する必要があるため、受信したメッセージは後で処理するためにリングバッファに入れられます。 リングバッファは最終的にメインループ内から処理され、キーボードマトリックスはメッセージに基づいて更新されます。

上記はこれを実現するための最も簡単な方法ですが、最終的なプロトコルはやや複雑になります。 マルチバイトメッセージを処理する必要があり、CRC-CCITTチェックサムを使用して個々のメッセージの整合性をチェックする必要があります。

この時点で、ブレッドボードのプロトタイプはかなり印象的です。

ブレッドボードのプロトタイプは、開発者向けにカスタマイズされたキーボードの形を取り始めています。

ステップ4:LEDディスプレイに会う

UHKの目標の1つは、ユーザーが複数のアプリケーション固有のキーボードマップを定義して、生産性をさらに高めることができるようにすることでした。 ユーザーは、使用されている実際のキーマップを認識するための何らかの方法が必要であるため、統合されたLEDディスプレイがキーボードに組み込まれています。 これは、すべてのLEDが点灯しているプロトタイプディスプレイです。

LEDディスプレイは、このチュートリアルで開発者に最適なキーボードを構築するための中心的な役割を果たします。

LEDディスプレイは、8x6LEDマトリックスによって実装されます。

ハッカーキーボードは、8x6LEDマトリックスなしでは完成しません。

赤い色のLEDシンボルの2行ごとに、14セグメントLEDディスプレイの1つのセグメントが表されます。 白いLED記号は、追加の3つのステータスインジケータを表します。

LEDに電流を流して点灯させるには、対応する列を高電圧に設定し、対応する行を低電圧に設定します。 このシステムの興味深い結果は、常に1つの列のみを有効にでき(点灯する必要があるその列のすべてのLEDで、対応する行が低電圧に設定されている)、残りの列は無効になっていることです。 。 このシステムではLEDのフルセットを使用できないと思われるかもしれませんが、実際には列と行が非常に迅速に更新されるため、人間の目にはちらつきが見えません。

LEDマトリックスは、2つの集積回路(IC)によって駆動され、1つは行を駆動し、もう1つは列を駆動します。 カラムを駆動するソースICは、PCA9634I2CLEDドライバです。

2つの集積回路がUltimateHackerキーボードのLEDマトリックスを駆動します。

行を駆動するLEDマトリックスシンクICは、TPIC6C595パワーシフトレジスタです。

LEDの列を駆動するICは次のようになります。

関連するコードを見てみましょう:

 uint8_t LedStates[LED_MATRIX_ROWS_NUM]; void LedMatrix_UpdateNextRow(bool IsKeyboardColEnabled) { TPIC6C595_Transmit(LedStates[ActiveLedMatrixRow]); PCA9634_Transmit(1 << ActiveLedMatrixRow); if (++ActiveLedMatrixRow == LED_MATRIX_ROWS_NUM) { ActiveLedMatrixRow = 0; } }

LedMatrix_UpdateNextRow()は、約1ミリ秒ごとに呼び出され、LEDマトリックスの行を更新します。 LedStates配列は、個々のLEDの状態を格納し、キーを押す/キーを離すイベントの場合とほぼ同じ方法で、キーボードの右半分から発信されたメッセージに基づいてUARTを介して更新されます。

大きな絵

これで、カスタムハッカーキーボードに必要なすべてのコンポーネントが徐々に構築され、全体像を確認するときが来ました。 キーボードの内部はミニコンピューターネットワークのようなもので、多くのノードが相互接続されています。 違いは、ノード間の距離がメートルやキロメートルではなくセンチメートルで測定され、ノードが本格的なコンピューターではなく、小さな集積回路であるということです。

チュートリアルキーボードの内部は、相互接続されたノードで構成されています。

これまで、開発者のキーボードのデバイス側の詳細については多くのことが語られてきましたが、ホスト側のソフトウェアであるUHKエージェントについてはあまり語られていません。 その理由は、ハードウェアやファームウェアとは異なり、エージェントは現時点では非常に初歩的なものであるためです。 ただし、エージェントの高レベルのアーキテクチャが決定されているので、共有したいと思います。

UHK Agentは、ユーザーのニーズに合わせてキーボードをカスタマイズできるコンフィギュレーターアプリケーションです。 リッチクライアントであるにもかかわらず、エージェントはWebテクノロジーを使用し、ノードWebキットプラットフォーム上で実行されます。

エージェントは、特別なデバイス固有のUSB制御要求を送信し、その結果を処理することにより、node-usbライブラリを使用してキーボードと通信します。 Express.jsを使用して、サードパーティアプリケーションで使用できるようにRESTAPIを公開します。 また、Angular.jsを使用して、きちんとしたユーザーインターフェイスを提供します。

 var enumerationModes = { 'keyboard' : 0, 'bootloader-right' : 1, 'bootloader-left' : 2 }; function sendReenumerateCommand(enumerationMode, callback) { var AGENT_COMMAND_REENUMERATE = 0; sendAgentCommand(AGENT_COMMAND_REENUMERATE, enumerationMode, callback); } function sendAgentCommand(command, arg, callback) { setReport(new Buffer([command, arg]), callback); } function setReport(message, callback) { device.controlTransfer( 0x21, // bmRequestType (constant for this control request) 0x09, // bmRequest (constant for this control request) 0, // wValue (MSB is report type, LSB is report number) interfaceNumber, // wIndex (interface number) message, // message to be sent callback ); }

すべてのコマンドには、8ビットの識別子とコマンド固有の引数のセットがあります。 現在、re-enumerateコマンドのみが実装されています。 sendReenumerateCommand()は、ファームウェアをアップグレードするために、またはキーボードデバイスとして、デバイスを左側のブートローダーまたは右側のブートローダーとして再列挙します。

このソフトウェアで実現できる高度な機能についてはわからないかもしれないので、いくつか挙げておきます。エージェントは、個々のキーの摩耗を視覚化し、ユーザーに平均余命を通知できるため、ユーザーは次のことができます。差し迫った修理のために、新しいキースイッチをいくつか購入してください。 エージェントは、ハッカーキーボードのさまざまなキーマップとレイヤーを構成するためのユーザーインターフェイスも提供します。 他のuber機能の負荷とともに、マウスポインタの速度と加速度も設定できます。 空は限界です。

プロトタイプの作成

カスタマイズされたキーボードプロトタイプの作成には多くの作業が必要です。 まず、機械設計を完成させる必要があります。これは、それ自体がかなり複雑で、カスタム設計のプラスチック部品、レーザーカットされたステンレス鋼板、精密に粉砕されたスチールガイド、および2つのキーボードの半分をまとめるネオジム磁石が含まれます。 製造が始まる前に、すべてがCADで設計されています。

CAD図面は、開発者にとって適切に機能するキーボードの構築を支援します。

3Dプリントされたキーボードケースは次のようになります。

プログラミングキーボードケースを3Dプリントすることから始めました。

機械設計と回路図に基づいて、プリント回路基板を設計する必要があります。 右側のPCBは、KiCadでは次のようになります。

キーボードのプログラミングは、プリント回路基板の設計から始まります。

次に、PCBが製造され、表面実装コンポーネントを手ではんだ付けする必要があります。

カスタムキーボードコンポーネントをはんだ付けすることで、ケースに入れれば正しく機能するようになります。

最後に、3D印刷、プラスチックパーツの研磨と塗装、すべての組み立てを含むすべてのパーツを製造した後、次のようなハッカーキーボードのプロトタイプが機能するようになります。

結論

開発者のキーボードをミュージシャンの楽器と比較するのが好きです。 あなたがそれについて考えるならば、キーボードはかなり親密なオブジェクトです。 結局のところ、私たちはそれらを1日中使用して、文字ごとに明日のソフトウェアを作成します。

おそらく上記の理由で、私はアルティメットハッキングキーボードの開発を特権と考えています。すべての困難にもかかわらず、それは非常にエキサイティングな旅であり、信じられないほど激しい学習体験でした。

これは幅広いトピックであり、ここでは表面を傷つけることしかできませんでした。 この記事がとても楽しく、興味深い資料でいっぱいだったことを願っています。 ご不明な点がございましたら、コメント欄でお知らせください。

最後に、詳細についてはhttps://ultimatehackingkeyboard.comにアクセスし、そこで購読してキャンペーンの開始に関する通知を受け取ることを歓迎します。