Skip to content

Commit de5353d

Browse files
authored
Merge pull request #355 from adafruit/add-rp2040-max3421e-support
Add rp2040 max3421e support
2 parents e291865 + 31f1edc commit de5353d

File tree

6 files changed

+288
-29
lines changed

6 files changed

+288
-29
lines changed
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/*********************************************************************
2+
Adafruit invests time and resources providing this open source code,
3+
please support Adafruit and open-source hardware by purchasing
4+
products from Adafruit!
5+
6+
MIT license, check LICENSE for more information
7+
Copyright (c) 2019 Ha Thach for Adafruit Industries
8+
All text above, and the splash screen below must be included in
9+
any redistribution
10+
*********************************************************************/
11+
12+
13+
/* This example demonstrates use of both device and host, where
14+
* - Device run on native usb controller (roothub port0)
15+
* - Host run on MAX3421E controller (roothub port1) tested with:
16+
* - SAMD21, SAMD51, nRF52840, ESP32S2, ESP32S3, ESP32
17+
* - RP2040: "pio_usb.h" must not be included, otherwise pio-usb will be used as host controller
18+
*
19+
* Requirements:
20+
* - SPI instance, CS pin, INT pin are correctly configured
21+
*/
22+
23+
/* Host example will get device descriptors of attached devices and print it out via
24+
* device cdc (Serial) as follows:
25+
* Device 1: ID 046d:c52f
26+
Device Descriptor:
27+
bLength 18
28+
bDescriptorType 1
29+
bcdUSB 0200
30+
bDeviceClass 0
31+
bDeviceSubClass 0
32+
bDeviceProtocol 0
33+
bMaxPacketSize0 8
34+
idVendor 0x046d
35+
idProduct 0xc52f
36+
bcdDevice 2200
37+
iManufacturer 1 Logitech
38+
iProduct 2 USB Receiver
39+
iSerialNumber 0
40+
bNumConfigurations 1
41+
*
42+
*/
43+
#include "Adafruit_TinyUSB.h"
44+
45+
// USBHost is defined in usbh_helper.h
46+
// USB Host using MAX3421E: SPI, CS, INT
47+
#include "SPI.h"
48+
49+
#if defined(ARDUINO_METRO_ESP32S2)
50+
Adafruit_USBH_Host USBHost(&SPI, 15, 14);
51+
52+
#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32_V2)
53+
Adafruit_USBH_Host USBHost(&SPI, 27, 33);
54+
55+
#else
56+
57+
// Default CS and INT are pin 10, 9
58+
Adafruit_USBH_Host USBHost(&SPI, 10, 9);
59+
#endif
60+
61+
// Language ID: English
62+
#define LANGUAGE_ID 0x0409
63+
64+
typedef struct {
65+
tusb_desc_device_t desc_device;
66+
uint16_t manufacturer[32];
67+
uint16_t product[48];
68+
uint16_t serial[16];
69+
bool mounted;
70+
} dev_info_t;
71+
72+
// CFG_TUH_DEVICE_MAX is defined by tusb_config header
73+
dev_info_t dev_info[CFG_TUH_DEVICE_MAX] = { 0 };
74+
75+
void setup() {
76+
Serial.begin(115200);
77+
78+
// init host stack on controller (rhport) 1
79+
// For rp2040: this is called in core1's setup1()
80+
USBHost.begin(1);
81+
82+
// while ( !Serial ) delay(10); // wait for native usb
83+
Serial.println("TinyUSB Dual: Device Info Example with MAX3421E");
84+
}
85+
86+
void loop() {
87+
USBHost.task();
88+
Serial.flush();
89+
}
90+
91+
//--------------------------------------------------------------------+
92+
// TinyUSB Host callbacks
93+
//--------------------------------------------------------------------+
94+
void print_device_descriptor(tuh_xfer_t *xfer);
95+
96+
void utf16_to_utf8(uint16_t *temp_buf, size_t buf_len);
97+
98+
void print_lsusb(void) {
99+
bool no_device = true;
100+
for (uint8_t daddr = 1; daddr < CFG_TUH_DEVICE_MAX + 1; daddr++) {
101+
// TODO can use tuh_mounted(daddr), but tinyusb has an bug
102+
// use local connected flag instead
103+
dev_info_t *dev = &dev_info[daddr - 1];
104+
if (dev->mounted) {
105+
Serial.printf("Device %u: ID %04x:%04x %s %s\r\n", daddr,
106+
dev->desc_device.idVendor, dev->desc_device.idProduct,
107+
(char *) dev->manufacturer, (char *) dev->product);
108+
109+
no_device = false;
110+
}
111+
}
112+
113+
if (no_device) {
114+
Serial.println("No device connected (except hub)");
115+
}
116+
}
117+
118+
// Invoked when device is mounted (configured)
119+
void tuh_mount_cb(uint8_t daddr) {
120+
Serial.printf("Device attached, address = %d\r\n", daddr);
121+
122+
dev_info_t *dev = &dev_info[daddr - 1];
123+
dev->mounted = true;
124+
125+
// Get Device Descriptor
126+
tuh_descriptor_get_device(daddr, &dev->desc_device, 18, print_device_descriptor, 0);
127+
}
128+
129+
/// Invoked when device is unmounted (bus reset/unplugged)
130+
void tuh_umount_cb(uint8_t daddr) {
131+
Serial.printf("Device removed, address = %d\r\n", daddr);
132+
dev_info_t *dev = &dev_info[daddr - 1];
133+
dev->mounted = false;
134+
135+
// print device summary
136+
print_lsusb();
137+
}
138+
139+
void print_device_descriptor(tuh_xfer_t *xfer) {
140+
if (XFER_RESULT_SUCCESS != xfer->result) {
141+
Serial.printf("Failed to get device descriptor\r\n");
142+
return;
143+
}
144+
145+
uint8_t const daddr = xfer->daddr;
146+
dev_info_t *dev = &dev_info[daddr - 1];
147+
tusb_desc_device_t *desc = &dev->desc_device;
148+
149+
Serial.printf("Device %u: ID %04x:%04x\r\n", daddr, desc->idVendor, desc->idProduct);
150+
Serial.printf("Device Descriptor:\r\n");
151+
Serial.printf(" bLength %u\r\n" , desc->bLength);
152+
Serial.printf(" bDescriptorType %u\r\n" , desc->bDescriptorType);
153+
Serial.printf(" bcdUSB %04x\r\n" , desc->bcdUSB);
154+
Serial.printf(" bDeviceClass %u\r\n" , desc->bDeviceClass);
155+
Serial.printf(" bDeviceSubClass %u\r\n" , desc->bDeviceSubClass);
156+
Serial.printf(" bDeviceProtocol %u\r\n" , desc->bDeviceProtocol);
157+
Serial.printf(" bMaxPacketSize0 %u\r\n" , desc->bMaxPacketSize0);
158+
Serial.printf(" idVendor 0x%04x\r\n" , desc->idVendor);
159+
Serial.printf(" idProduct 0x%04x\r\n" , desc->idProduct);
160+
Serial.printf(" bcdDevice %04x\r\n" , desc->bcdDevice);
161+
162+
// Get String descriptor using Sync API
163+
Serial.printf(" iManufacturer %u ", desc->iManufacturer);
164+
if (XFER_RESULT_SUCCESS ==
165+
tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, dev->manufacturer, sizeof(dev->manufacturer))) {
166+
utf16_to_utf8(dev->manufacturer, sizeof(dev->manufacturer));
167+
Serial.printf((char *) dev->manufacturer);
168+
}
169+
Serial.printf("\r\n");
170+
171+
Serial.printf(" iProduct %u ", desc->iProduct);
172+
if (XFER_RESULT_SUCCESS ==
173+
tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, dev->product, sizeof(dev->product))) {
174+
utf16_to_utf8(dev->product, sizeof(dev->product));
175+
Serial.printf((char *) dev->product);
176+
}
177+
Serial.printf("\r\n");
178+
179+
Serial.printf(" iSerialNumber %u ", desc->iSerialNumber);
180+
if (XFER_RESULT_SUCCESS ==
181+
tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, dev->serial, sizeof(dev->serial))) {
182+
utf16_to_utf8(dev->serial, sizeof(dev->serial));
183+
Serial.printf((char *) dev->serial);
184+
}
185+
Serial.printf("\r\n");
186+
187+
Serial.printf(" bNumConfigurations %u\r\n", desc->bNumConfigurations);
188+
189+
// print device summary
190+
print_lsusb();
191+
}
192+
193+
//--------------------------------------------------------------------+
194+
// String Descriptor Helper
195+
//--------------------------------------------------------------------+
196+
197+
static void _convert_utf16le_to_utf8(const uint16_t *utf16, size_t utf16_len, uint8_t *utf8, size_t utf8_len) {
198+
// TODO: Check for runover.
199+
(void) utf8_len;
200+
// Get the UTF-16 length out of the data itself.
201+
202+
for (size_t i = 0; i < utf16_len; i++) {
203+
uint16_t chr = utf16[i];
204+
if (chr < 0x80) {
205+
*utf8++ = chr & 0xff;
206+
} else if (chr < 0x800) {
207+
*utf8++ = (uint8_t) (0xC0 | (chr >> 6 & 0x1F));
208+
*utf8++ = (uint8_t) (0x80 | (chr >> 0 & 0x3F));
209+
} else {
210+
// TODO: Verify surrogate.
211+
*utf8++ = (uint8_t) (0xE0 | (chr >> 12 & 0x0F));
212+
*utf8++ = (uint8_t) (0x80 | (chr >> 6 & 0x3F));
213+
*utf8++ = (uint8_t) (0x80 | (chr >> 0 & 0x3F));
214+
}
215+
// TODO: Handle UTF-16 code points that take two entries.
216+
}
217+
}
218+
219+
// Count how many bytes a utf-16-le encoded string will take in utf-8.
220+
static int _count_utf8_bytes(const uint16_t *buf, size_t len) {
221+
size_t total_bytes = 0;
222+
for (size_t i = 0; i < len; i++) {
223+
uint16_t chr = buf[i];
224+
if (chr < 0x80) {
225+
total_bytes += 1;
226+
} else if (chr < 0x800) {
227+
total_bytes += 2;
228+
} else {
229+
total_bytes += 3;
230+
}
231+
// TODO: Handle UTF-16 code points that take two entries.
232+
}
233+
return total_bytes;
234+
}
235+
236+
void utf16_to_utf8(uint16_t *temp_buf, size_t buf_len) {
237+
size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t);
238+
size_t utf8_len = _count_utf8_bytes(temp_buf + 1, utf16_len);
239+
240+
_convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, buf_len);
241+
((uint8_t *) temp_buf)[utf8_len] = '\0';
242+
}

src/arduino/Adafruit_USBH_Host.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,10 @@ TU_ATTR_WEAK void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance,
186186
!defined(PLATFORMIO)
187187
extern "C" void hcd_int_handler_esp32(uint8_t rhport, bool in_isr);
188188
#define tuh_int_handler_esp32 hcd_int_handler_esp32
189+
189190
#else
190191
#define tuh_int_handler_esp32 tuh_int_handler
192+
191193
#endif
192194

193195
static void max3421_intr_task(void *param) {
@@ -321,8 +323,13 @@ void tuh_max3421_int_api(uint8_t rhport, bool enabled) {
321323
} else {
322324
gpio_intr_disable((gpio_num_t)host->_intr);
323325
}
326+
327+
#elif defined(ARDUINO_ARCH_RP2040)
328+
//--- RP2040 ---//
329+
irq_set_enabled(IO_IRQ_BANK0, enabled);
330+
324331
#else
325-
#error "MAX3421e host is not Unsupported by this architecture"
332+
#error "MAX3421e host is not supported by this architecture"
326333
#endif
327334
}
328335

src/arduino/ports/rp2040/tusb_config_rp2040.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,23 @@ extern "C" {
3838
#define CFG_TUD_ENABLED 0
3939
#define CFG_TUH_ENABLED 1
4040
#define CFG_TUH_RPI_PIO_USB 0
41+
4142
#else
4243
// native as device
4344
#define CFG_TUD_ENABLED 1
4445

45-
// Enable host stack with pio-usb if Pico-PIO-USB library is available
4646
#if __has_include("pio_usb.h")
47+
// Enable host stack with pio-usb if Pico-PIO-USB library is available
4748
#define CFG_TUH_ENABLED 1
4849
#define CFG_TUH_RPI_PIO_USB 1
49-
#endif
50-
#endif
50+
51+
#else
52+
// Otherwise enable host controller with MAX3421E
53+
#define CFG_TUH_ENABLED 1
54+
#define CFG_TUH_MAX3421 1
55+
56+
#endif // pio_usb.h
57+
#endif // USE_TINYUSB_HOST
5158

5259
#ifndef CFG_TUSB_MCU
5360
#define CFG_TUSB_MCU OPT_MCU_RP2040

0 commit comments

Comments
 (0)