SNSへはこちら

STM32でUSBをベアメタル(10) - HIDコンポジットデバイス完成

ここまで来たら、次なる目標は HID コンポジットデバイスです。有用性とかは置いておいて、取り敢えずマウス + キーボード なデバイスを作ります。

本記事ではそこまで詳しく説明しません。あくまでも備忘録。

キーボードのレポートディスクリプタ

こちらも探していたら見つかった、というか、サンプルをそのまま出力したと言ったほうが適切でしょうか。

実は USB.org が HID Descriptor Tool なるものを配布しているようで、こちらからキーボード用のサンプルファイルを用いました。.h の配列形式で出力してくれるので嬉しいです。そして出力を若干弄りました(アラインメントを)。

const uint8_t __attribute__((aligned(2))) ReportDescriptorForKeyBoard[63] = {
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x08,                    //   REPORT_COUNT (8)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
    0x95, 0x05,                    //   REPORT_COUNT (5)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x05, 0x08,                    //   USAGE_PAGE (LEDs)
    0x19, 0x01,                    //   USAGE_MINIMUM (Num Lock)
    0x29, 0x05,                    //   USAGE_MAXIMUM (Kana)
    0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x03,                    //   REPORT_SIZE (3)
    0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)
    0x95, 0x06,                    //   REPORT_COUNT (6)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0xc0                           // END_COLLECTION
};

コンポジット用のコンフィギュレーションディスクリプタ

bNumInterfaces を 2 にして、あとはずらずらと2つ分のディスクリプタを並べてあげるだけです。マウスは EP1 の Interrupt Transferキーボードは EP2 の Interrupt Transfer としました。

// For USB HID Composite Device
const uint8_t __attribute__((aligned(2))) conf_desc_keyb_mouse[] = {
        // Configuration Descriptor
        CONFIG_DESCRIPTOR_LENGTH,
        CONFIG_DESCRIPTOR_TYPE,
        59 & 0xFF, // .wTotalLength
        59 >> 8, // same above
        2, // .bNumInterfaces
        1, // .bConfigurationValue,
        0, // .iConfiguration
        0x80, // .bmAttributes
        0x32, // MaxPower

        // Interface Descriptor(Mouse)
        INTERFACE_DESCRIPTOR_LENGTH,
        INTERFACE_DESCRIPTOR_TYPE,
        0, // bInterfaceNumber
        0, // bAlternateSetting
        1, // bNumEndpoints
        0x03, // .bInterfaceClass
        0x01, // .bInterfaceSubClass(Boot Interface Subclass)
        0x02, // bInterfaceProtocol(Mouse)
        0, // iInterface

        // HID Class Descriptor
        0x09, // bLength
        0x21, // bDescriptorType
        0x00, // bcdHID
        0x10, // L> 1.00
        0, // bCountryCode
        1, // bNumDescriptors
        0x22, // bDescriptorType
        50, // wDescriptorLength(The length of Report Desc)
        0x00,

        // Endpoint Descriptor
        ENDPOINT_DESCRIPTOR_LENGTH,
        ENDPOINT_DESCRIOTPR_TYPE,
        0x81, // .bEndpointAddress(IN, EP1)
        0x03, // Interrupt Transfer
        64, // .wMacPacketSize
        0,
        0x0A, // bInterval

        // Interface Descriptor(Keyboard)
        INTERFACE_DESCRIPTOR_LENGTH,
        INTERFACE_DESCRIPTOR_TYPE,
        1, // bInterfaceNumber
        0, // bAlternateSetting
        1, // bNumEndpoints
        0x03, // .bInterfaceClass
        0x01, // .bInterfaceSubClass(Boot Interface Subclass)
        0x01, // bInterfaceProtocol(KeyBoard)
        0, // iInterface

        // HID Class Descriptor
        0x09, // bLength
        0x21, // bDescriptorType
        0x00, // bcdHID
        0x10, // L> 1.00
        0, // bCountryCode
        1, // bNumDescriptors
        0x22, // bDescriptorType
        63, // wDescriptorLength(The length of Report Desc)
        0x00,

        // Endpoint Descriptor
        ENDPOINT_DESCRIPTOR_LENGTH,
        ENDPOINT_DESCRIOTPR_TYPE,
        0x82, // .bEndpointAddress(IN, EP2)
        0x03, // Interrupt Transfer
        64, // .wMacPacketSize
        0,
        0x0A // bInterval
};

また、キーボードはレポートに使うキーコードというのがあって、これで押したキーを判別します。これは ASCII 文字コードと全く異なるものなので、逐一対応表を作る必要がある気がします。キーコードはこちらを参照。

動いた

以下の動画みたいな感じです。起動時に素数表を作るシェル芸のコマンドを入力し、その後マウスポインタで星を描きます。何に使うんですかねこれ。

キーボードの実装も出来たので、気分が乗ればフットスイッチやオレオレキーボードの作成したいですね。例えば

  • コピペ用スイッチ
  • メール用ヘッダ入力スイッチ
    • ○○さん\nお世話になっております。M2のxxです。\n\n表題の件につきまして、...
  • 音ゲー用スイッチ
    • 弐寺 Infinitas 用のコントローラとか...?マイクロスイッチが高そう
    • HID ジョイスティックの実装もいいかもね
  • ブログ用定型句入力スイッチ
    • pre ブロック作成
  • 鉄道の発車メロディースイッチ
    • スイッチ入力をマイコンの GPIO に入れて、それを送るのもいいかも

夢があふれますね。僕は怠け者なので多分やりませんが、構想は立てておきましたw

ということで、密かに決めていた第2の目標、コンポジットデバイスの実装があっさりと完了してしまいました!
次は CDC で USB Virtual COM Port... といきたいところですが、研究の進捗がヤヴァいのでまた今度という感じになりますね。