Switch to tinyUsb stack

This commit is contained in:
Frederik Walk 2023-04-22 17:44:38 +02:00
parent cf799f5253
commit 50486c3f20
9 changed files with 521 additions and 15 deletions

View File

@ -13,7 +13,7 @@ add_compile_options(-Wall -Wextra -Werror)
add_subdirectory(libs)
file(GLOB ${PROJECT_NAME}_SOURCES src/*.cpp src/utils/*.cpp
file(GLOB ${PROJECT_NAME}_SOURCES src/*.cpp src/usb/*.c src/utils/*.cpp
src/peripherals/*.cpp)
add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_SOURCES})
@ -21,8 +21,9 @@ add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_SOURCES})
target_include_directories(${PROJECT_NAME}
PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(${PROJECT_NAME} PUBLIC pico_stdlib pico_multicore
pio_ws2812)
target_link_libraries(
${PROJECT_NAME} PUBLIC tinyusb_device tinyusb_board pico_stdlib
pico_multicore pio_ws2812)
pico_enable_stdio_usb(${PROJECT_NAME} 1)
pico_enable_stdio_uart(${PROJECT_NAME} 0)

64
include/tusb_config.h Normal file
View File

@ -0,0 +1,64 @@
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT (0)
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG (0)
#endif
// Enable Device stack
#define CFG_TUD_ENABLED (1)
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE (64)
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC (1)
#define CFG_TUD_MSC (0)
#define CFG_TUD_MIDI (0)
#define CFG_TUD_HID (0)
#define CFG_TUD_VENDOR (0)
// Device class buffer sizes
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_HID_EP_BUFSIZE (64)
#ifdef __cplusplus
}
#endif
#endif // _TUSB_CONFIG_H_

View File

@ -0,0 +1,27 @@
#ifndef _USB_DEBUG_DRIVER_H_
#define _USB_DEBUG_DRIVER_H_
#include "usb/usb_driver.h"
#include "device/usbd_pvt.h"
#include <stdint.h>
#define USBD_DEBUG_CDC_NAME "Serial Debug"
#define USBD_DEBUG_RESET_NAME "Picotool Reset"
#ifdef __cplusplus
extern "C" {
#endif
bool send_debug_report(usb_report_t report);
extern const tusb_desc_device_t debug_desc_device;
extern const uint8_t debug_desc_cfg[];
extern const usbd_class_driver_t debug_app_driver;
#ifdef __cplusplus
}
#endif
#endif // _USB_DEBUG_DRIVER_H_

57
include/usb/usb_driver.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef _USB_USB_DRIVER_H_
#define _USB_USB_DRIVER_H_
#include "tusb.h"
#include <stdint.h>
#define USBD_MANUFACTURER "DonCon"
#define USBD_PRODUCT "DonCon rev1"
#define USBD_MAX_POWER_MAX (500)
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
// USB_MODE_SWITCH_TATACON,
// USB_MODE_SWITCH_HORIPAD,
// USB_MODE_DUALSHOCK3,
// USB_MODE_DUALSHOCK4,
// USB_MODE_XBOX360,
USB_MODE_DEBUG,
} usb_mode_t;
enum {
USBD_STR_LANGUAGE,
USBD_STR_MANUFACTURER,
USBD_STR_PRODUCT,
USBD_STR_SERIAL,
USBD_STR_CDC,
USBD_STR_SWITCH,
USBD_STR_PS3,
USBD_STR_PS4,
USBD_STR_XINPUT,
USBD_STR_RPI_RESET,
};
typedef struct {
uint8_t *data;
uint16_t size;
} usb_report_t;
extern char *const usbd_desc_str[];
void usb_driver_init(usb_mode_t mode);
void usb_driver_task();
usb_mode_t usb_driver_get_mode();
void usb_driver_send_and_receive_report(usb_report_t report);
#ifdef __cplusplus
}
#endif
#endif // _USB_USB_DRIVER_H_

View File

@ -1,7 +1,10 @@
#ifndef _UTILS_INPUTSTATE_H_
#define _UTILS_INPUTSTATE_H_
#include "usb/usb_driver.h"
#include <stdint.h>
#include <string>
namespace Doncon::Utils {
@ -14,8 +17,15 @@ struct InputState {
public:
Drum drum;
private:
std::string m_debug_report;
usb_report_t getDebugReport();
public:
InputState();
usb_report_t getReport(usb_mode_t mode);
};
} // namespace Doncon::Utils

View File

@ -1,5 +1,6 @@
#include "peripherals/Drum.h"
#include "peripherals/StatusLed.h"
#include "usb/usb_driver.h"
#include "GlobalConfiguration.h"
@ -35,6 +36,9 @@ int main() {
Utils::InputState input_state;
Peripherals::Drum drum(Config::Default::drum_config);
usb_mode_t mode = USB_MODE_DEBUG;
usb_driver_init(mode);
stdio_init_all();
@ -43,18 +47,8 @@ int main() {
while (true) {
drum.updateInputState(input_state);
if (input_state.drum.don_left) {
printf("DON LEFT\n");
}
if (input_state.drum.don_right) {
printf("DON RIGHT\n");
}
if (input_state.drum.ka_left) {
printf("KA LEFT\n");
}
if (input_state.drum.ka_right) {
printf("KA RIGHT\n");
}
usb_driver_send_and_receive_report(input_state.getReport(mode));
usb_driver_task();
queue_try_add(&input_queue, &input_state);
}

157
src/usb/debug_driver.c Normal file
View File

@ -0,0 +1,157 @@
#include "usb/debug_driver.h"
#include "usb/usb_driver.h"
#include "pico/bootrom.h"
#include "pico/stdio_usb.h"
#include "pico/usb_reset_interface.h"
#include "tusb.h"
#define USBD_CDC_EP_CMD (0x81)
#define USBD_CDC_EP_OUT (0x02)
#define USBD_CDC_EP_IN (0x82)
#define USBD_CDC_CMD_MAX_SIZE (8)
#define USBD_CDC_IN_OUT_MAX_SIZE (64)
const tusb_desc_device_t debug_desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x2E8A, // Raspberry Pi
.idProduct = 0x000A, // stdio_usb to allow reset via picotool
.bcdDevice = 0x0100,
.iManufacturer = USBD_STR_MANUFACTURER,
.iProduct = USBD_STR_PRODUCT,
.iSerialNumber = USBD_STR_SERIAL,
.bNumConfigurations = 1,
};
enum {
USBD_ITF_CDC,
USBD_ITF_CDC_DATA,
USBD_ITF_RPI_RESET,
USBD_ITF_MAX,
};
bool send_debug_report(usb_report_t report) {
printf((char *)report.data);
return true;
}
#if !PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
#else
#define TUD_RPI_RESET_DESC_LEN 9
#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_RPI_RESET_DESC_LEN)
#define TUD_RPI_RESET_DESCRIPTOR(_itfnum, _stridx) \
9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_VENDOR_SPECIFIC, RESET_INTERFACE_SUBCLASS, \
RESET_INTERFACE_PROTOCOL, _stridx
#endif
const uint8_t debug_desc_cfg[USBD_DESC_LEN] = {
TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_LANGUAGE, USBD_DESC_LEN, 0, USBD_MAX_POWER_MAX),
TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT,
USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE),
#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
TUD_RPI_RESET_DESCRIPTOR(USBD_ITF_RPI_RESET, USBD_STR_RPI_RESET),
#endif
};
#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE && !(PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL || \
PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT)
#warning PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE has been selected but neither PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL nor PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT have been selected.
#endif
#if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE
#include "device/usbd_pvt.h"
#include "hardware/watchdog.h"
#include "pico/stdio_usb/reset_interface.h"
static uint8_t itf_num;
static void resetd_init(void) {}
static void resetd_reset(uint8_t __unused rhport) { itf_num = 0; }
static uint16_t resetd_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass &&
RESET_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass &&
RESET_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol,
0);
uint16_t const drv_len = sizeof(tusb_desc_interface_t);
TU_VERIFY(max_len >= drv_len, 0);
itf_num = itf_desc->bInterfaceNumber;
return drv_len;
}
// Support for parameterized reset via vendor interface control request
static bool resetd_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const *request) {
// nothing to do with DATA & ACK stage
if (stage != CONTROL_STAGE_SETUP)
return true;
if (request->wIndex == itf_num) {
#if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL
if (request->bRequest == RESET_REQUEST_BOOTSEL) {
#ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED
uint gpio_mask = 1u << PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED;
#else
uint gpio_mask = 0u;
#endif
#if !PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED
if (request->wValue & 0x100) {
gpio_mask = 1u << (request->wValue >> 9u);
}
#endif
reset_usb_boot(gpio_mask, (request->wValue & 0x7f) | PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK);
// does not return, otherwise we'd return true
}
#endif
#if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT
if (request->bRequest == RESET_REQUEST_FLASH) {
watchdog_reboot(0, 0, PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS);
return true;
}
#endif
}
return false;
}
static bool resetd_xfer_cb(uint8_t __unused rhport, uint8_t __unused ep_addr, xfer_result_t __unused result,
uint32_t __unused xferred_bytes) {
return true;
}
usbd_class_driver_t const debug_app_driver = {
#if CFG_TUSB_DEBUG >= 2
.name = "RESET",
#endif
.init = resetd_init,
.reset = resetd_reset,
.open = resetd_open,
.control_xfer_cb = resetd_control_xfer_cb,
.xfer_cb = resetd_xfer_cb,
.sof = NULL};
#endif
#if PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE
// Support for default BOOTSEL reset by changing baud rate
void tud_cdc_line_coding_cb(__unused uint8_t itf, cdc_line_coding_t const *p_line_coding) {
if (p_line_coding->bit_rate == PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE) {
#ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED
const uint gpio_mask = 1u << PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED;
#else
const uint gpio_mask = 0u;
#endif
reset_usb_boot(gpio_mask, PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK);
}
}
#endif

162
src/usb/usb_driver.c Normal file
View File

@ -0,0 +1,162 @@
#include "usb/usb_driver.h"
#include "usb/debug_driver.h"
// #include "usb/hid_driver.h"
// #include "usb/xinput_driver.h"
#include "bsp/board.h"
#include "pico/unique_id.h"
static usb_mode_t usbd_mode = USB_MODE_DEBUG;
static const tusb_desc_device_t *usbd_desc_device = NULL;
static const uint8_t *usbd_desc_cfg = NULL;
static const uint8_t *usbd_desc_hid_report = NULL;
static const usbd_class_driver_t *usbd_app_driver = NULL;
static bool (*usbd_send_report)(usb_report_t report) = NULL;
static bool (*usbd_receive_report)() = NULL;
#define USBD_SERIAL_STR_SIZE (PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2 + 1 + 3)
static char usbd_serial_str[USBD_SERIAL_STR_SIZE] = {};
char *const usbd_desc_str[] = {
[USBD_STR_MANUFACTURER] = USBD_MANUFACTURER, //
[USBD_STR_PRODUCT] = USBD_PRODUCT, //
[USBD_STR_SERIAL] = usbd_serial_str, //
// [USBD_STR_SWITCH] = USBD_SWITCH_NAME, //
// [USBD_STR_PS3] = USBD_PS3_NAME, //
// [USBD_STR_PS4] = USBD_PS4_NAME, //
// [USBD_STR_XINPUT] = USBD_XINPUT_NAME, //
[USBD_STR_CDC] = USBD_DEBUG_CDC_NAME, //
[USBD_STR_RPI_RESET] = USBD_DEBUG_RESET_NAME, //
};
void usb_driver_init(usb_mode_t mode) {
usbd_mode = mode;
switch (mode) {
// case USB_MODE_SWITCH_TATACON:
// usbd_desc_device = &switch_tatacon_desc_device;
// usbd_desc_cfg = switch_desc_cfg;
// usbd_desc_hid_report = switch_desc_hid_report;
// usbd_app_driver = &hid_app_driver;
// usbd_send_report = send_hid_switch_report;
// usbd_receive_report = NULL;
// break;
// case USB_MODE_SWITCH_HORIPAD:
// usbd_desc_device = &switch_horipad_desc_device;
// usbd_desc_cfg = switch_desc_cfg;
// usbd_desc_hid_report = switch_desc_hid_report;
// usbd_app_driver = &hid_app_driver;
// usbd_send_report = send_hid_switch_report;
// usbd_receive_report = NULL;
// break;
// case USB_MODE_DUALSHOCK3:
// usbd_desc_device = &ds3_desc_device;
// usbd_desc_cfg = ps3_desc_cfg;
// usbd_desc_hid_report = ps3_desc_hid_report;
// usbd_app_driver = &hid_app_driver;
// usbd_send_report = send_hid_ps3_report;
// usbd_receive_report = NULL;
// break;
// case USB_MODE_DUALSHOCK4:
// usbd_desc_device = &ds4_desc_device;
// usbd_desc_cfg = ps4_desc_cfg;
// usbd_desc_hid_report = ps4_desc_hid_report;
// usbd_app_driver = &hid_app_driver;
// usbd_send_report = send_hid_ps4_report;
// usbd_receive_report = NULL;
// break;
// case USB_MODE_XBOX360:
// usbd_desc_device = &xinput_desc_device;
// usbd_desc_cfg = xinput_desc_cfg;
// usbd_app_driver = &xinput_app_driver;
// usbd_send_report = send_xinput_report;
// usbd_receive_report = receive_xinput_report;
// break;
case USB_MODE_DEBUG:
usbd_desc_device = &debug_desc_device;
usbd_desc_cfg = debug_desc_cfg;
usbd_app_driver = &debug_app_driver;
usbd_send_report = send_debug_report;
usbd_receive_report = NULL;
break;
}
tusb_init();
}
void usb_driver_task() { tud_task(); }
usb_mode_t usb_driver_get_mode() { return usbd_mode; }
void usb_driver_send_and_receive_report(usb_report_t report) {
static const uint32_t interval_ms = 1;
static uint32_t start_ms = 0;
if (board_millis() - start_ms < interval_ms) {
return;
}
start_ms += interval_ms;
if (tud_suspended()) {
tud_remote_wakeup();
}
if (usbd_send_report) {
usbd_send_report(report);
}
if (usbd_receive_report) {
usbd_receive_report();
}
}
const uint8_t *tud_descriptor_device_cb(void) { return (const uint8_t *)usbd_desc_device; }
const uint8_t *tud_descriptor_configuration_cb(uint8_t __unused index) { return usbd_desc_cfg; }
const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
#define DESC_STR_MAX (20)
(void)langid;
static uint16_t desc_str[DESC_STR_MAX];
// Assign the SN using the unique flash id
if (!usbd_serial_str[0]) {
pico_get_unique_board_id_string(usbd_serial_str, sizeof(usbd_serial_str));
usbd_serial_str[USBD_SERIAL_STR_SIZE - 4] = '-';
usbd_serial_str[USBD_SERIAL_STR_SIZE - 3] = '0' + ((usbd_mode / 10) % 10);
usbd_serial_str[USBD_SERIAL_STR_SIZE - 2] = '0' + (usbd_mode % 10);
usbd_serial_str[USBD_SERIAL_STR_SIZE - 1] = '\0';
}
uint8_t len;
if (index == USBD_STR_LANGUAGE) {
desc_str[1] = 0x0409; // Supported language is English
len = 1;
} else {
if (index >= sizeof(usbd_desc_str) / sizeof(usbd_desc_str[0])) {
return NULL;
}
const char *str = usbd_desc_str[index];
for (len = 0; len < DESC_STR_MAX - 1 && str[len]; ++len) {
desc_str[1 + len] = str[len];
}
}
// first byte is length (including header), second byte is string type
desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * len + 2));
return desc_str;
}
uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf) {
(void)itf;
return usbd_desc_hid_report;
}
// Implement callback to add our custom driver
const usbd_class_driver_t *usbd_app_driver_get_cb(uint8_t *driver_count) {
*driver_count = 1;
return usbd_app_driver;
}

View File

@ -1,7 +1,41 @@
#include "utils/InputState.h"
#include <sstream>
namespace Doncon::Utils {
InputState::InputState() : drum({false, false, false, false}) {}
usb_report_t InputState::getReport(usb_mode_t mode) {
switch (mode) {
// case USB_MODE_SWITCH_TATACON:
// case USB_MODE_SWITCH_HORIPAD:
// return getSwitchReport();
// case USB_MODE_DUALSHOCK3:
// return getPS3InputReport();
// case USB_MODE_PS4_DIVACON:
// case USB_MODE_DUALSHOCK4:
// return getPS4InputReport();
// case USB_MODE_XBOX360:
// return getXinputReport();
case USB_MODE_DEBUG:
default:
return getDebugReport();
}
}
usb_report_t InputState::getDebugReport() {
std::stringstream out;
out << "Ka Left: " << drum.ka_left << " " //
<< "Don Left: " << drum.don_left << " " //
<< "Don Right: " << drum.don_right << " " //
<< "Ka Right: " << drum.ka_right << " " //
<< "\r";
m_debug_report = out.str();
return {(uint8_t *)m_debug_report.c_str(), static_cast<uint16_t>(m_debug_report.size() + 1)};
}
} // namespace Doncon::Utils