{"id":50366102,"url":"https://github.com/rh1tech/frank-gamepad","last_synced_at":"2026-05-30T04:04:23.427Z","repository":{"id":357326820,"uuid":"1224504060","full_name":"rh1tech/frank-gamepad","owner":"rh1tech","description":"USB Gamepad Tester for RP2350 Boards","archived":false,"fork":false,"pushed_at":"2026-05-12T08:50:20.000Z","size":740,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-12T10:39:12.104Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rh1tech.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-29T10:50:00.000Z","updated_at":"2026-05-12T08:50:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rh1tech/frank-gamepad","commit_stats":null,"previous_names":["rh1tech/frank-gamepad"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/rh1tech/frank-gamepad","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh1tech%2Ffrank-gamepad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh1tech%2Ffrank-gamepad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh1tech%2Ffrank-gamepad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh1tech%2Ffrank-gamepad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rh1tech","download_url":"https://codeload.github.com/rh1tech/frank-gamepad/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh1tech%2Ffrank-gamepad/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33679310,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-30T04:04:22.962Z","updated_at":"2026-05-30T04:04:23.408Z","avatar_url":"https://github.com/rh1tech.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# frank-gamepad\n\nOfficial page: **[frank.rh1.tech](https://frank.rh1.tech/)** — hub for all FRANK boards and firmware.\n\nA USB gamepad button-mapping tool for the RP2350 (M2 board layout).\n\nPlug any USB HID gamepad — or an Xbox 360 / Xbox One / Original Xbox pad\nover XInput — in and it walks you through pressing each of the 12\nSNES-style buttons. It diffs the raw reports against a quiescent baseline\nto figure out which byte (or bit) changes for each button, and writes the\nresult to the SD card as `gamepad_VID_PID.txt`.\n\nThe reason this exists: every USB pad encodes its buttons differently, and\nthe firmwares that share the Murmulator/FRANK HDMI+USB stack (murmsnes,\nfrank-wolf3d, frank-doom, ...) need a portable way to discover per-pad\nmappings without attaching a PC.\n\n## Supported boards\n\nM2-only. RP2350 boards with integrated HDMI, SD card, and native USB:\n\n- **[FRANK](https://rh1.tech/projects/frank?area=about)** — RP Pico 2\n  dev board with HDMI and extra I/O\n- **[Murmulator](https://murmulator.ru)** — RP Pico 2 board with HDMI and SD\n\nNo extra wiring needed.\n\n## Features\n\n- 640x480 HDMI output (256x224 frame doubled)\n- On-screen SNES-style controller schematic, with the button you should press\n  next highlighted\n- 12-step guided capture\n- Supports generic USB HID gamepads AND XInput controllers\n  (Xbox 360 wired/wireless, Xbox One, Original Xbox). XInput reports are\n  normalised to a canonical 8-byte frame so the output format is identical\n- Writes a plain-text `gamepad_VID_PID.txt` to the SD card root\n- Loops after each capture so you can enumerate several pads in one session\n- HDMI runs on Core 1, USB host and capture state machine on Core 0, so HDMI\n  sync is not disturbed by USB traffic\n- Two build flavors:\n  - `./build.sh ON` — release. Native USB is the HID host; UART0 is the\n    debug console.\n  - `./build.sh` — debug. Native USB is USB-CDC stdio; gamepad capture is\n    compiled out. Handy for checking HDMI and SD without a pad.\n\n## Hardware\n\n- Raspberry Pi Pico 2 (RP2350) or a compatible M2 board\n- HDMI connector wired via 270 Ohm resistors\n- SD card module, SPI mode\n- Any USB HID gamepad\n- USB-to-TTL UART adapter (9600 8-N-1) for the release build's console;\n  optional, connects to GPIO 0 TX / GPIO 1 RX / GND\n\nUnlike murmsnes, PSRAM is not required.\n\n## Pin assignment (M2)\n\n### HDMI\n\nWired via 270 Ohm resistors.\n\n| Signal | GPIO |\n|--------|------|\n| CLK-   | 12   |\n| CLK+   | 13   |\n| D0-    | 14   |\n| D0+    | 15   |\n| D1-    | 16   |\n| D1+    | 17   |\n| D2-    | 18   |\n| D2+    | 19   |\n\n### SD card (SPI)\n\n| Signal  | GPIO |\n|---------|------|\n| CLK     | 6    |\n| CMD     | 7    |\n| DAT0    | 4    |\n| DAT3/CS | 5    |\n\n### UART console (release build)\n\n| Signal | GPIO |\n|--------|------|\n| TX     | 0    |\n| RX     | 1    |\n\nBaud: 9600, 8-N-1. The release build re-runs `uart_init()` at 9600 explicitly\nso the console is readable on a garden-variety USB-TTL adapter.\n\n### USB HID\n\nNative RP2350 USB port. No GPIO assignment — the pad plugs into the board's\nUSB socket.\n\n## How to use\n\n### SD card setup\n\n1. Format an SD card as FAT32.\n2. Put it in the board. Nothing needs to be on the card — the tool writes\n   output files at the root.\n\n### Boot and capture\n\n1. Power on with the gamepad plugged in (hot-plug also works).\n2. The HDMI output shows the controller schematic, the title\n   `FRANK-GAMEPAD CAPTURE`, and a status line.\n3. Once the pad enumerates, the status line becomes\n   `CAPTURING BASELINE... HANDS OFF`. Don't touch the pad for about a second\n   while the tool records the quiescent report.\n4. The tool prompts you to press each button in order:\n   UP, DOWN, LEFT, RIGHT, SELECT, START, Y, B, A, X, L, R.\n   The current button is highlighted amber on the schematic. When you press\n   it, it flashes green and the tool moves on.\n5. After all 12 buttons, the tool writes `gamepad_VID_PID.txt` to the SD\n   card root and goes back to the `PLUG IN A USB GAMEPAD` screen.\n\n### Output format\n\nExample for a DragonRise USB joystick (VID 0x0079, PID 0x0011):\n\n```\n# frank-gamepad capture log\nSource=HID\nVID=0x0079\nPID=0x0011\nManufacturer=(none)\nProduct=(none)\nSerial=(none)\nReportLen=8\nBaseline=01 7F 7F 7F 7F 0F 00 00\n\nUP=byte[4]:-0x7F\n#   raw: 01 7F 7F 7F 00 0F 00 00\nDOWN=byte[4]:+0x7F\n#   raw: 01 7F 7F 7F FF 0F 00 00\nLEFT=byte[3]:-0x7F\n#   raw: 01 7F 7F 00 7F 0F 00 00\nRIGHT=byte[3]:+0x7F\n#   raw: 01 7F 7F FF 7F 0F 00 00\nSELECT=byte[6]:+0x10\n#   raw: 01 7F 7F 7F 7F 0F 10 00\nSTART=byte[6]:+0x20\n...\n```\n\nEach captured line is one of:\n\n- `byte[N]:+0xMM` — bits went from 0 to 1 (button pressed)\n- `byte[N]:-0xMM` — bits went from 1 to 0 (e.g. a D-pad axis resting at 0x7F\n  dropping to 0x00)\n- `byte[N]:=0xVV` — mixed change; raw value is shown\n\nThe `#   raw:` line always shows the full report observed when the button\nwas pressed, so downstream firmware can use whichever form is easier.\n\nFor XInput pads, the captured report is a canonical 8-byte frame rather\nthan the raw USB packet:\n\n```\nbyte[0] = wButtons low  (DPAD U/D/L/R, START, BACK, LS, RS)\nbyte[1] = wButtons high (LB, RB, GUIDE, SHARE, A, B, X, Y)\nbyte[2] = left trigger  (0..255)\nbyte[3] = right trigger (0..255)\nbyte[4] = left stick X, high byte\nbyte[5] = left stick Y, high byte\nbyte[6] = right stick X, high byte\nbyte[7] = right stick Y, high byte\n```\n\nThis keeps the `byte[N]:+0xMM` output format uniform across both transports,\nso downstream firmware doesn't need to know whether the pad was an HID\njoystick or an Xbox controller. A `Source=HID` or `Source=XINPUT` line at\nthe top of the log records which path captured the data.\n\n## Building\n\n### Prerequisites\n\n1. Install the [Pico SDK](https://github.com/raspberrypi/pico-sdk) 2.0 or\n   later.\n2. `export PICO_SDK_PATH=/path/to/pico-sdk`\n3. Install the ARM GCC toolchain.\n\n### Release build (capture tool)\n\n```bash\ngit clone https://github.com/rh1tech/frank-gamepad.git\ncd frank-gamepad\n./build.sh ON          # USB_HID=ON\n```\n\nOutput: `build/frank-gamepad.uf2`.\n\n### Debug build (CDC stdio, no gamepad)\n\n```bash\n./build.sh             # USB_HID=OFF, default\n```\n\nIn this mode the native USB port is a USB-CDC serial device; open it at any\nbaud and you get the boot log. The capture state machine is compiled out\nand the UI idles on the `PLUG IN A USB GAMEPAD` screen. Useful for checking\nHDMI and SD without USB host concerns.\n\n### Flashing\n\nHold BOOTSEL, plug in, copy `build/frank-gamepad.uf2` to the mounted\n`RPI-RP2` drive. Or:\n\n```bash\n./flash.sh\n# equivalent to:\npicotool load -f build/frank-gamepad.uf2 \u0026\u0026 picotool reboot -f\n```\n\n## Troubleshooting\n\n### No HDMI signal\n\n- Make sure you flashed the M2 build. Other board variants use different\n  HDMI GPIOs.\n- The driver is tuned for `sys_clk` = 252 MHz. If you've stripped the clock\n  init, HDMI will stay dark.\n- If the image rolls vertically every few seconds, the pixel clock is\n  drifting. The firmware calls `set_sys_clock_khz(252000, true)` which was\n  stable on the M2 boards I tested.\n\n### Capture hardfaults or HDMI drops partway through a session\n\nThe capture arrays live in `.bss`, not on the stack. An earlier revision\nstack-allocated `button_capture_t caps[12]` — about 4 KB — which overflowed\nthe default 2 KB core stack and trashed the scratch-SRAM-resident HDMI DMA\nstate. That looks like \"HDMI died\" but it's really a hardfault. If you see\nsimilar symptoms after changing the capture logic, check that any new large\narrays are `static`.\n\n### UART console shows garbage\n\nThe release build re-inits UART at 9600 baud. Anything else on the adapter\nside (115200, 57600, ...) will show as line noise.\n\n### Gamepad enumerates but button presses do nothing\n\n- Check that baseline captured cleanly — UART log should have\n  `[cap] baseline settled len=N`.\n- Some pads only emit HID reports on state change. The capture logic\n  handles that, but if the pad is fully silent at rest, wiggle a stick first\n  so it sends at least one report.\n- To double-check the mapping, press the button again and compare raw bytes\n  against the `#   raw:` line in the output file.\n\n## License\n\nCopyright (c) 2026 Mikhail Matveev \u003c\u003cxtreme@rh1.tech\u003e\u003e\n\nfrank-gamepad is released under the GNU General Public License v3.0 or\nlater. See [LICENSE](LICENSE) for full text and for the third-party\ncomponents bundled with this repository.\n\n## Acknowledgments\n\n| Project | Author(s) | License | Used for |\n|---------|-----------|---------|----------|\n| [FatFS](http://elm-chan.org/fsw/ff/) | ChaN | Custom permissive | FAT32 filesystem |\n| [pico_fatfs_test](https://github.com/elehobica/pico_fatfs_test) | Elehobica | BSD-2-Clause | SD card PIO-SPI driver |\n| [TinyUSB](https://github.com/hathach/tinyusb) | Ha Thach | MIT | USB HID host |\n| [tusb_xinput](https://github.com/Ryzee119/tusb_xinput) | Ryan Wendland (usb64) | MIT | TinyUSB XInput class driver (vendored via [SpeccyP](https://github.com/billgilbert7000/SpeccyP)) |\n| [Raspberry Pi Pico SDK](https://github.com/raspberrypi/pico-sdk) | Raspberry Pi Foundation | BSD-3-Clause | Hardware abstraction |\n| [murmsnes](https://github.com/rh1tech/frank-snes) | Mikhail Matveev | GPL-3.0 | HDMI, SD, USB HID drivers |\n\nThanks to the Murmulator community for the HDMI, USB HID, and PSRAM drivers\nthis project reuses.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frh1tech%2Ffrank-gamepad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frh1tech%2Ffrank-gamepad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frh1tech%2Ffrank-gamepad/lists"}