diff --git a/README.md b/README.md index 87b69c2..6b210f3 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,29 @@ # Pico-Game-Controller -WIP code for a rhythm game controller using a Raspberry Pi Pico. Intended for SDVX but is capable of handling 11 buttons, 11 LEDs, and 2 encoders. +WIP code for a rhythm game controller using a Raspberry Pi Pico. Intended for SDVX but is capable of handling 11 buttons, 11 LEDs, and 2 encoders. Currently working/fixed: + - Gamepad mode - untested on EAC - default boot mode - Keyboard & Mouse Mode - limited to 6KRO - hold btn-a to enter kb mode - HID LEDs with Reactive LED fallback - untested - sdvx/iidx spoof - untested on EAC - 1000hz polling -- Fix 0-~71% encoder rollover in gamepad mode, uint32 max val isn't divisible evenly by ppr*4 for joystick - thanks friends +- Fix 0-~71% encoder rollover in gamepad mode, uint32 max val isn't divisible evenly by ppr\*4 for joystick - thanks friends - Switch and LED pins are now staggered for easier wiring TODO: + - Store last mode in flash memory https://www.raspberrypi.org/forums/viewtopic.php?t=305570 - nkro - debounce - split input updating into pi pico's second core? + +How to Use: + +- For basic flashing, see README in build_uf2 +- Otherwise, setup the C++ environment for the Pi Pico as per https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf +- Build pico-examples directory once to ensure all the tinyusb and other libraries are there. You might have to move the pico-sdk folder into pico-examples/build for it to play nice. +- Move pico-sdk back outside to the same level directory as Pico-Game-Controller. +- Open Pico-Game-Controller in VSCode(assuming this is setup for the Pi Pico) and see if everything builds. +- Tweakable parameters: Pinout, bindings in pico_game_controller.c, USB device reporting as SDVX/IIDX(con_mode) in usb_descriptors.c diff --git a/build_uf2/Pico_Game_Controller.uf2 b/build_uf2/Pico_Game_Controller.uf2 index 0f2cb55..05ecc1a 100644 Binary files a/build_uf2/Pico_Game_Controller.uf2 and b/build_uf2/Pico_Game_Controller.uf2 differ diff --git a/src/pico_game_controller.c b/src/pico_game_controller.c index 8f93b73..a4be11a 100644 --- a/src/pico_game_controller.c +++ b/src/pico_game_controller.c @@ -1,8 +1,8 @@ /* - * Pico SDVX + * Pico Game Controller * @author SpeedyPotato * - * Based off dev_hid_composite, mdxtinkernick/pico_encoders, and + * Based off tinyusb's hid_composite, mdxtinkernick/pico_encoders, and * Drewol/rp2040-gamecon */ @@ -343,9 +343,11 @@ int main(void) { // Invoked when received GET_REPORT control request // Application must fill buffer report's content and return its length. // Return zero will cause the stack to STALL request -uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, - uint8_t *buffer, uint16_t reqlen) { +uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, + hid_report_type_t report_type, uint8_t* buffer, + uint16_t reqlen) { // TODO not Implemented + (void)itf; (void)report_id; (void)report_type; (void)buffer; @@ -356,8 +358,10 @@ uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, // Invoked when received SET_REPORT control request or // received data on OUT endpoint ( Report ID = 0, Type = 0 ) -void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, - uint8_t const *buffer, uint16_t bufsize) { +void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, + hid_report_type_t report_type, uint8_t const* buffer, + uint16_t bufsize) { + (void)itf; if (report_id == 2 && report_type == HID_REPORT_TYPE_OUTPUT && buffer[0] == 2 && bufsize >= sizeof(lights_report)) // light data { diff --git a/src/tusb_config.h b/src/tusb_config.h index 42d7783..071ae69 100644 --- a/src/tusb_config.h +++ b/src/tusb_config.h @@ -34,21 +34,40 @@ extern "C" { // COMMON CONFIGURATION //-------------------------------------------------------------------- -// defined by compiler flags for flexibility +// defined by board.mk #ifndef CFG_TUSB_MCU #error CFG_TUSB_MCU must be defined #endif -#if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || \ - CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 || \ - CFG_TUSB_MCU == OPT_MCU_CXD56 -#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_DEVICE_RHPORT_NUM +#define BOARD_DEVICE_RHPORT_NUM 0 +#endif + +// RHPort max operational speed can defined by board.mk +// Default to Highspeed for MCU with internal HighSpeed PHY (can be port +// specific), otherwise FullSpeed +#ifndef BOARD_DEVICE_RHPORT_SPEED +#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || \ + CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 || \ + CFG_TUSB_MCU == OPT_MCU_CXD56) +#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED #else -#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE +#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED +#endif +#endif + +// Device mode with rhport and speed defined by board.mk +#if BOARD_DEVICE_RHPORT_NUM == 0 +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#elif BOARD_DEVICE_RHPORT_NUM == 1 +#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#else +#error "Incorrect RHPort configuration" #endif #ifndef CFG_TUSB_OS -#define CFG_TUSB_OS OPT_OS_PICO +#define CFG_TUSB_OS OPT_OS_NONE #endif // CFG_TUSB_DEBUG is defined by compiler in DEBUG build @@ -84,7 +103,7 @@ extern "C" { #define CFG_TUD_VENDOR 0 // HID buffer size Should be sufficient to hold ID (if any) + Data -#define CFG_TUD_HID_BUFSIZE 16 +#define CFG_TUD_HID_EP_BUFSIZE 16 #ifdef __cplusplus } diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c index 3c52b6d..aa96807 100644 --- a/src/usb_descriptors.c +++ b/src/usb_descriptors.c @@ -48,7 +48,6 @@ //--------------------------------------------------------------------+ // Device Descriptors //--------------------------------------------------------------------+ - tusb_desc_device_t const desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, @@ -70,8 +69,8 @@ tusb_desc_device_t const desc_device = { // Invoked when received GET DEVICE DESCRIPTOR // Application return pointer to descriptor -uint8_t const *tud_descriptor_device_cb(void) { - return (uint8_t const *)&desc_device; +uint8_t const* tud_descriptor_device_cb(void) { + return (uint8_t const*)&desc_device; } //--------------------------------------------------------------------+ @@ -87,7 +86,10 @@ uint8_t const desc_hid_report[] = { // Invoked when received GET HID REPORT DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete -uint8_t const *tud_hid_descriptor_report_cb(void) { return desc_hid_report; } +uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf) { + (void)itf; + return desc_hid_report; +} //--------------------------------------------------------------------+ // Configuration Descriptor @@ -105,16 +107,16 @@ uint8_t const desc_configuration[] = { TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), - // Interface number, string index, protocol, report descriptor len, EP In & - // Out address, size & polling interval - TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_PROTOCOL_NONE, - sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_BUFSIZE, - 1)}; + // Interface number, string index, protocol, report descriptor len, EP In + // address, size & polling interval + TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, + sizeof(desc_hid_report), EPNUM_HID, + CFG_TUD_HID_EP_BUFSIZE, 1)}; // Invoked when received GET CONFIGURATION DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete -uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { +uint8_t const* tud_descriptor_configuration_cb(uint8_t index) { (void)index; // for multiple configurations return desc_configuration; } @@ -127,7 +129,7 @@ uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { #define iidx_prod "beatmania IIDX controller premium model" // array of pointer to string descriptors -char const *string_desc_arr[] = { +char const* string_desc_arr[] = { (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) "Konami Amusement", // 1: Manufacturer con_mode ? iidx_prod : sdvx_prod, // 2: Product @@ -139,7 +141,7 @@ static uint16_t _desc_str[32]; // Invoked when received GET STRING DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long // enough for transfer to complete -uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) { (void)langid; uint8_t chr_count; @@ -148,17 +150,19 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { memcpy(&_desc_str[1], string_desc_arr[0], 2); chr_count = 1; } else { - // Convert ASCII string into UTF-16 + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL; - const char *str = string_desc_arr[index]; + const char* str = string_desc_arr[index]; // Cap at max char chr_count = strlen(str); if (chr_count > 31) chr_count = 31; + // Convert ASCII string into UTF-16 for (uint8_t i = 0; i < chr_count; i++) { _desc_str[1 + i] = str[i]; }