Skip to content

Commit 5fe76b9

Browse files
authored
Merge pull request #3024 from FoamyGuy/usbhost_snespad_arduino_2
add support for Metro rp2350 USB SNES-like controller
2 parents 3f0ed86 + c056b4e commit 5fe76b9

File tree

6 files changed

+332
-34
lines changed

6 files changed

+332
-34
lines changed

Diff for: USB_SNES_Gamepad/Arduino_Feather_RP2040_USB_Host/snes_gamepad_simpletest/snes_gamepad_simpletest.ino

+8-33
Original file line numberDiff line numberDiff line change
@@ -48,49 +48,25 @@ hid_gamepad_report_t gp;
4848
bool printed_blank = false;
4949

5050
void setup() {
51-
if (!TinyUSBDevice.isInitialized()) {
52-
TinyUSBDevice.begin(0);
53-
}
5451
Serial.begin(115200);
55-
// Setup HID
56-
usb_hid.setPollInterval(2);
57-
usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
58-
usb_hid.begin();
59-
60-
// If already enumerated, additional class driver begin() e.g msc, hid, midi won't take effect until re-enumeration
61-
if (TinyUSBDevice.mounted()) {
62-
TinyUSBDevice.detach();
63-
delay(10);
64-
TinyUSBDevice.attach();
65-
}
66-
}
67-
68-
#if defined(ARDUINO_ARCH_RP2040)
69-
//--------------------------------------------------------------------+
70-
// For RP2040 use both core0 for device stack, core1 for host stack
71-
//--------------------------------------------------------------------//
72-
73-
//------------- Core0 -------------//
74-
void loop() {
75-
}
7652

77-
//------------- Core1 -------------//
78-
void setup1() {
7953
// configure pio-usb: defined in usbh_helper.h
8054
rp2040_configure_pio_usb();
8155

8256
// run host stack on controller (rhport) 1
83-
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
84-
// host bit-banging processing works done in core1 to free up core0 for other works
8557
USBHost.begin(1);
58+
delay(3000);
59+
Serial.print("USB D+ Pin:");
60+
Serial.println(PIN_USB_HOST_DP);
61+
Serial.print("USB 5V Pin:");
62+
Serial.println(PIN_5V_EN);
8663
}
8764

88-
void loop1() {
65+
void loop() {
8966
USBHost.task();
9067
Serial.flush();
9168

9269
}
93-
#endif
9470

9571
//--------------------------------------------------------------------+
9672
// HID Host Callback Functions
@@ -112,16 +88,15 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
11288
Serial.printf("HID device unmounted (address %d, instance %d)\n", dev_addr, instance);
11389
}
11490

115-
11691
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) {
11792

11893
if (report[BYTE_DPAD_LEFT_RIGHT] != DPAD_NEUTRAL ||
11994
report[BYTE_DPAD_UP_DOWN] != DPAD_NEUTRAL ||
12095
report[BYTE_ABXY_BUTTONS] != BUTTON_NEUTRAL ||
12196
report[BYTE_OTHER_BUTTONS] != BUTTON_MISC_NEUTRAL){
122-
97+
12398
printed_blank = false;
124-
99+
125100
//debug print report data
126101
// Serial.print("Report data: ");
127102
// for (int i = 0; i < len; i++) {

Diff for: USB_SNES_Gamepad/Arduino_Feather_RP2040_USB_Host/snes_gamepad_simpletest/usbh_helper.h

+21-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,26 @@
3535
#endif
3636
#endif // ARDUINO_ARCH_RP2040
3737

38+
#ifdef ARDUINO_ARCH_RP2350
39+
40+
// pio-usb is required for rp2040 host
41+
#include "pio_usb.h"
42+
43+
// Pin D+ for host, D- = D+ + 1
44+
#ifndef PIN_USB_HOST_DP
45+
#define PIN_USB_HOST_DP 32
46+
#endif
47+
48+
// Pin for enabling Host VBUS. comment out if not used
49+
#ifndef PIN_5V_EN
50+
#define PIN_5V_EN 29
51+
#endif
52+
53+
#ifndef PIN_5V_EN_STATE
54+
#define PIN_5V_EN_STATE 1
55+
#endif
56+
#endif // ARDUINO_ARCH_RP2350
57+
3858
#include "Adafruit_TinyUSB.h"
3959

4060
#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
@@ -61,7 +81,7 @@
6181
#ifdef ARDUINO_ARCH_RP2040
6282
static void rp2040_configure_pio_usb(void) {
6383
//while ( !Serial ) delay(10); // wait for native usb
64-
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
84+
Serial.println("Setup to run TinyUSB host with pio-usb");
6585

6686
#ifdef PIN_5V_EN
6787
pinMode(PIN_5V_EN, OUTPUT);

Diff for: USB_SNES_Gamepad/Arduino_Metro_RP2350_USB_Host/snes_gamepad_simpletest/.metro_rp2350_tinyusb.test.only

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
// HID reports for USB SNES-like controller
6+
7+
// Byte indices for the gamepad report
8+
#define BYTE_DPAD_LEFT_RIGHT 0 // D-Pad left and right
9+
#define BYTE_DPAD_UP_DOWN 1 // D-Pad up and down
10+
// bytes 2,3,4 unused
11+
#define BYTE_ABXY_BUTTONS 5 // A, B, X, Y
12+
#define BYTE_OTHER_BUTTONS 6 // Shoulders, start, and select
13+
14+
15+
#define DPAD_NEUTRAL 0x7F
16+
// D-Pad directions
17+
#define DPAD_UP 0x00
18+
#define DPAD_RIGHT 0xFF
19+
#define DPAD_DOWN 0xFF
20+
#define DPAD_LEFT 0x00
21+
22+
23+
// Face buttons (Byte[5])
24+
#define BUTTON_NEUTRAL 0x0F
25+
#define BUTTON_X 0x1F
26+
#define BUTTON_A 0x2F
27+
#define BUTTON_B 0x4F
28+
#define BUTTON_Y 0x8F
29+
30+
31+
// Miscellaneous buttons (Byte[6])
32+
#define BUTTON_MISC_NEUTRAL 0x00
33+
#define BUTTON_LEFT_SHOULDER 0x01
34+
#define BUTTON_RIGHT_SHOULDER 0x02
35+
#define BUTTON_SELECT 0x10
36+
#define BUTTON_START 0x20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
/*********************************************************************
6+
Adafruit invests time and resources providing this open source code,
7+
please support Adafruit and open-source hardware by purchasing
8+
products from Adafruit!
9+
10+
MIT license, check LICENSE for more information
11+
Copyright (c) 2019 Ha Thach for Adafruit Industries
12+
All text above, and the splash screen below must be included in
13+
any redistribution
14+
*********************************************************************/
15+
16+
/* This example demonstrates use of usb host with a SNES-like game controller
17+
* - Host depends on MCU:
18+
* - rp2040: bit-banging 2 GPIOs with Pico-PIO-USB library (roothub port1)
19+
*
20+
* Requirements:
21+
* - For rp2040:
22+
* - Pico-PIO-USB library
23+
* - 2 consecutive GPIOs: D+ is defined by PIN_USB_HOST_DP, D- = D+ +1
24+
* - Provide VBus (5v) and GND for peripheral
25+
* - CPU Speed must be either 120 or 240 MHz. Selected via "Menu -> CPU Speed"
26+
*/
27+
28+
// USBHost is defined in usbh_helper.h
29+
#include "usbh_helper.h"
30+
#include "tusb.h"
31+
#include "Adafruit_TinyUSB.h"
32+
#include "gamepad_reports.h"
33+
34+
// HID report descriptor using TinyUSB's template
35+
// Single Report (no ID) descriptor
36+
uint8_t const desc_hid_report[] = {
37+
TUD_HID_REPORT_DESC_GAMEPAD()
38+
};
39+
40+
// USB HID object
41+
Adafruit_USBD_HID usb_hid;
42+
43+
// Report payload defined in src/class/hid/hid.h
44+
// - For Gamepad Button Bit Mask see hid_gamepad_button_bm_t
45+
// - For Gamepad Hat Bit Mask see hid_gamepad_hat_t
46+
hid_gamepad_report_t gp;
47+
48+
bool printed_blank = false;
49+
50+
void setup() {
51+
Serial.begin(115200);
52+
53+
// configure pio-usb: defined in usbh_helper.h
54+
rp2040_configure_pio_usb();
55+
56+
// run host stack on controller (rhport) 1
57+
USBHost.begin(1);
58+
delay(3000);
59+
Serial.print("USB D+ Pin:");
60+
Serial.println(PIN_USB_HOST_DP);
61+
Serial.print("USB 5V Pin:");
62+
Serial.println(PIN_5V_EN);
63+
}
64+
65+
void loop() {
66+
USBHost.task();
67+
Serial.flush();
68+
69+
}
70+
71+
//--------------------------------------------------------------------+
72+
// HID Host Callback Functions
73+
//--------------------------------------------------------------------+
74+
75+
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
76+
{
77+
Serial.printf("HID device mounted (address %d, instance %d)\n", dev_addr, instance);
78+
79+
// Start receiving HID reports
80+
if (!tuh_hid_receive_report(dev_addr, instance))
81+
{
82+
Serial.printf("Error: cannot request to receive report\n");
83+
}
84+
}
85+
86+
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
87+
{
88+
Serial.printf("HID device unmounted (address %d, instance %d)\n", dev_addr, instance);
89+
}
90+
91+
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) {
92+
93+
if (report[BYTE_DPAD_LEFT_RIGHT] != DPAD_NEUTRAL ||
94+
report[BYTE_DPAD_UP_DOWN] != DPAD_NEUTRAL ||
95+
report[BYTE_ABXY_BUTTONS] != BUTTON_NEUTRAL ||
96+
report[BYTE_OTHER_BUTTONS] != BUTTON_MISC_NEUTRAL){
97+
98+
printed_blank = false;
99+
100+
//debug print report data
101+
// Serial.print("Report data: ");
102+
// for (int i = 0; i < len; i++) {
103+
// Serial.print(report[i], HEX);
104+
// Serial.print(" ");
105+
// }
106+
// Serial.println();
107+
108+
if (report[BYTE_DPAD_LEFT_RIGHT] == DPAD_LEFT){
109+
Serial.print("Left ");
110+
}else if (report[BYTE_DPAD_LEFT_RIGHT] == DPAD_RIGHT){
111+
Serial.print("Right ");
112+
}
113+
114+
if (report[BYTE_DPAD_UP_DOWN] == DPAD_UP){
115+
Serial.print("Up ");
116+
}else if (report[BYTE_DPAD_UP_DOWN] == DPAD_DOWN){
117+
Serial.print("Down ");
118+
}
119+
120+
if ((report[BYTE_ABXY_BUTTONS] & BUTTON_A) == BUTTON_A){
121+
Serial.print("A ");
122+
}
123+
if ((report[BYTE_ABXY_BUTTONS] & BUTTON_B) == BUTTON_B){
124+
Serial.print("B ");
125+
}
126+
if ((report[BYTE_ABXY_BUTTONS] & BUTTON_X) == BUTTON_X){
127+
Serial.print("X ");
128+
}
129+
if ((report[BYTE_ABXY_BUTTONS] & BUTTON_Y) == BUTTON_Y){
130+
Serial.print("Y ");
131+
}
132+
133+
if ((report[BYTE_OTHER_BUTTONS] & BUTTON_LEFT_SHOULDER) == BUTTON_LEFT_SHOULDER){
134+
Serial.print("Left Shoulder ");
135+
}
136+
if ((report[BYTE_OTHER_BUTTONS] & BUTTON_RIGHT_SHOULDER) == BUTTON_RIGHT_SHOULDER){
137+
Serial.print("Right Shoulder ");
138+
}
139+
if ((report[BYTE_OTHER_BUTTONS] & BUTTON_START) == BUTTON_START){
140+
Serial.print("Start ");
141+
}
142+
if ((report[BYTE_OTHER_BUTTONS] & BUTTON_SELECT) == BUTTON_SELECT){
143+
Serial.print("Select ");
144+
}
145+
Serial.println();
146+
} else {
147+
if (! printed_blank){
148+
Serial.println("NEUTRAL");
149+
printed_blank = true;
150+
}
151+
}
152+
153+
// Continue to receive the next report
154+
if (!tuh_hid_receive_report(dev_addr, instance)) {
155+
Serial.println("Error: cannot request to receive report");
156+
}
157+
}

0 commit comments

Comments
 (0)