僕の住んでいるウンコ・寮ではゲーム機の持ち込みはおろかゲームコントローラの持ち込みすら禁止されています。携帯ゲームがある今その寮則は機能しているのだろうか….
そんな中カービィ64がやりたくなってしまったのでどうしたもんかと考えた結果、自分で作ってゲームコントローラではなくただのマイコンとボタンだと言い張ろうということになりました(アホくさ)
使用するマイコンはもちろんSTM32です。使い慣れてるというのもありますがいつの間にか大量に買い込んだBluePillがあります…
まず、レポートディスクリプタを作成します。
これを作るためにはコントローラの仕様(軸とかボタン数とか)を決める必要があります。なので今のうちに仕様を固めておきましょう。
まあハードウェアを作っていないならいつでも変更は効きますが面倒です…
で、レポートディスクリプタを手で書くのは大変なので作ってくれるツールがUSB.orgよりリリースされています。
約20年前の古臭いツールでよく落ちるので気をつけてください。落ちる頻度は昔のOfficeぐらいです(
作り方はここでは触れませんが、完成したらSave asからHeader Fileで出力します。
参考まで、僕のカービィコントローラのディスクリプタは
こんな感じです。下の方のsize1,count2,INPUT(cnst,ary,abs)はパディングです。
レポートのサイズが8ビット区切りになっていないとうまく認識されないので注意してください。
できたらプロジェクトに流し込みます。
CubeMXで適当にFull-SpeedのUSBを設定し、MiddlewareからUSB_DEVICEを開きます。
Human Interface DeviceとかいうHIDデバイスになれそうな項目がありますが、それにするとマウスになってしまっていじるのが面倒です。
なのでプレーンなHIDデバイスになるCustom Human Interface Deviceを選択します。
そして忘れずにClass Parametersのレポートディスクリプタのサイズとレポートのサイズを変更します。
レポートのサイズは最大ぽいので大きめに取ればいいと思うのですが、現状16にしてても2バイト以上送ると失敗するのでよくわかりません…
いじり終わったらプロジェクトを生産して先程作ったディスクリプタを
usbd_custom_hid_if.cに書きます。
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 32 |
/** Usb HID report descriptor. */ __ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END = { /* USER CODE BEGIN 0 */ 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x04, // USAGE (Joystick) 0xa1, 0x01, // COLLECTION (Application) 0x05, 0x09, // USAGE_PAGE (Button) 0x19, 0x01, // USAGE_MINIMUM (Button 1) 0x29, 0x0a, // USAGE_MAXIMUM (Button 10) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x95, 0x0a, // REPORT_COUNT (10) 0x75, 0x01, // REPORT_SIZE (1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x01, // USAGE (Pointer) 0xa1, 0x00, // COLLECTION (Physical) 0x15, 0xff, // LOGICAL_MINIMUM (-1) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x75, 0x02, // REPORT_SIZE (2) 0x95, 0x02, // REPORT_COUNT (2) 0x09, 0x30, // USAGE (X) 0x09, 0x31, // USAGE (Y) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x75, 0x01, // REPORT_SIZE (1) 0x95, 0x02, // REPORT_COUNT (2) 0x81, 0x01, // INPUT (Cnst,Ary,Abs) 0xc0, // END_COLLECTION /* USER CODE END 0 */ 0xC0 /* END_COLLECTION */ }; |
https://github.com/Haruroid/f1_kirby64/blob/master/Src/usbd_custom_hid_if.c#L95
みたいな感じに、
/* USER CODE END 0 */
のあとに
0xC0 /* END_COLLECTION */
があるので注意してください(消してもいいけど生産のたびに復活して面倒)
まあ大体のディスクリプタはC0で終わるのでそんなに問題はないと思います。
あとusbd_conf.cにmallocが定義されてなくてエラーになるのでmallocとついでにfreeを定義します。生産のたびに消えるので注意(バグやろ…)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/** * @brief Static single allocation. * @param size: Size of allocated memory * @retval None */ void *USBD_static_malloc(uint32_t size) { return malloc(size); } /** * @brief Dummy memory free * @param p: Pointer to allocated memory address * @retval None */ void USBD_static_free(void *p) { free(p); } |
https://github.com/Haruroid/f1_kirby64/blob/master/Src/usbd_conf.c#L585
ここまでくればUSBを刺したときにHIDデバイスとして表示されるはずです。
ディスクリプタ通りにボタンなどが認識されてるか確認してみてください。
認識されていたらあとは自分で書いたディスクリプタに従ってレポートを送るだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void send_GP_Report(){ uint8_t report[4] = {0,0,0,0}; report[0] |= !(bool)(B_GPIO_Port->IDR & B_Pin) << 0; report[0] |= !(bool)(A_GPIO_Port->IDR & A_Pin) << 1; report[0] |= !(bool)(C_LEFT_GPIO_Port->IDR & C_LEFT_Pin) << 2; report[0] |= !(bool)(C_DOWN_GPIO_Port->IDR & C_DOWN_Pin) << 3; report[0] |= !(bool)(C_RIGHT_GPIO_Port->IDR & C_RIGHT_Pin) << 4; report[0] |= !(bool)(C_UP_GPIO_Port->IDR & C_UP_Pin) << 5; report[0] |= !(bool)(L_GPIO_Port->IDR & L_Pin) << 6; report[0] |= !(bool)(R_GPIO_Port->IDR & R_Pin) << 7; report[1] |= !(bool)(START_GPIO_Port->IDR & START_Pin) << 0; report[1] |= !(bool)(SELECT_GPIO_Port->IDR & SELECT_Pin) << 1; bool d_up = !(D_UP_GPIO_Port->IDR & D_UP_Pin); bool d_down = !(D_DOWN_GPIO_Port->IDR & D_DOWN_Pin); bool d_right = !(D_RIGHT_GPIO_Port->IDR & D_RIGHT_Pin); bool d_left = !(D_LEFT_GPIO_Port->IDR & D_LEFT_Pin); report[1] |= (d_right | d_left) << 2; report[1] |= d_left << 3; report[1] |= (d_up | d_down) <<4; report[1] |= d_up <<5; USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS,report,2); } |
https://github.com/Haruroid/f1_kirby64/blob/master/Src/main.c#L65
ただ、今の所何故か送るたびに2ms待たないとWindowsがエラーを吐くというのとレポートを2バイト以上送れないという問題があります。
これはバグなのか俺がミスってるのか…
というわけで、度々参照してましたが今回作ったプロジェクトは
https://github.com/Haruroid/f1_kirby64
にあります。
形はこんな感じになりました。カービィ64以外にもDSとかスーファミとかもできるような配置になっています😏