Unverified Commit 4a8ac3d9 authored by Earle F. Philhower, III's avatar Earle F. Philhower, III Committed by GitHub

Add support for shared USB Serial, Keyboard, Mouse (#132)

Use a shared infrastructure based on TinyUSB, allow users to use sketches
with ported Arduino Keyboard and Mouse libraries.
parent 9c17986a
......@@ -16,3 +16,9 @@
[submodule "pico-extras"]
path = pico-extras
url = https://github.com/raspberrypi/pico-extras.git
[submodule "libraries/Keyboard"]
path = libraries/Keyboard
url = https://github.com/earlephilhower/Keyboard
[submodule "libraries/Mouse"]
path = libraries/Mouse
url = https://github.com/earlephilhower/Mouse
......@@ -32,6 +32,11 @@
#include "hardware/watchdog.h"
#include "pico/unique_id.h"
#ifndef DISABLE_USB_SERIAL
// Ensure we are installed in the USB chain
void __USBInstallSerial() { /* noop */ }
#endif
// SerialEvent functions are weak, so when the user doesn't define them,
// the linker just sets their address to 0 (which is checked below).
// The Serialx_available is just a wrapper around Serialx.available(),
......@@ -39,121 +44,8 @@
// HardwareSerial instance if the user doesn't also refer to it.
extern void serialEvent() __attribute__((weak));
#define PICO_STDIO_USB_TASK_INTERVAL_US 1000
#define PICO_STDIO_USB_LOW_PRIORITY_IRQ 31
#define USBD_VID (0x2E8A) // Raspberry Pi
#ifdef SERIALUSB_PID
#define USBD_PID (SERIALUSB_PID)
#else
#define USBD_PID (0x000a) // Raspberry Pi Pico SDK CDC
#endif
#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
#define USBD_MAX_POWER_MA (250)
#define USBD_ITF_CDC (0) // needs 2 interfaces
#define USBD_ITF_MAX (2)
#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)
#define USBD_STR_0 (0x00)
#define USBD_STR_MANUF (0x01)
#define USBD_STR_PRODUCT (0x02)
#define USBD_STR_SERIAL (0x03)
#define USBD_STR_CDC (0x04)
// Note: descriptors returned from callbacks must exist long enough for transfer to complete
static const tusb_desc_device_t usbd_desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_CDC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = USBD_VID,
.idProduct = USBD_PID,
.bcdDevice = 0x0100,
.iManufacturer = USBD_STR_MANUF,
.iProduct = USBD_STR_PRODUCT,
.iSerialNumber = USBD_STR_SERIAL,
.bNumConfigurations = 1,
};
static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = {
TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA),
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),
};
static char _idString[PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2 + 1];
static const char *const usbd_desc_str[] = {
[USBD_STR_0] = "",
[USBD_STR_MANUF] = "Raspberry Pi",
[USBD_STR_PRODUCT] = "PicoArduino",
[USBD_STR_SERIAL] = _idString,
[USBD_STR_CDC] = "Board CDC",
};
const uint8_t *tud_descriptor_device_cb(void) {
return (const uint8_t *)&usbd_desc_device;
}
const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
(void)index;
return usbd_desc_cfg;
}
const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
#define DESC_STR_MAX (20)
static uint16_t desc_str[DESC_STR_MAX];
uint8_t len;
if (index == 0) {
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] = (TUSB_DESC_STRING << 8) | (2 * len + 2);
return desc_str;
}
static mutex_t usb_mutex;
static void low_priority_worker_irq() {
// if the mutex is already owned, then we are in user code
// in this file which will do a tud_task itself, so we'll just do nothing
// until the next tick; we won't starve
if (mutex_try_enter(&usb_mutex, NULL)) {
tud_task();
mutex_exit(&usb_mutex);
}
}
static int64_t timer_task(__unused alarm_id_t id, __unused void *user_data) {
irq_set_pending(PICO_STDIO_USB_LOW_PRIORITY_IRQ);
return PICO_STDIO_USB_TASK_INTERVAL_US;
}
extern mutex_t __usb_mutex;
void SerialUSB::begin(unsigned long baud) {
(void) baud; //ignored
......@@ -162,24 +54,6 @@ void SerialUSB::begin(unsigned long baud) {
return;
}
// Get ID string into human readable serial number
pico_unique_board_id_t id;
pico_get_unique_board_id(&id);
_idString[0] = 0;
for (auto i = 0; i < PICO_UNIQUE_BOARD_ID_SIZE_BYTES; i++) {
char hx[3];
sprintf(hx, "%02X", id.id[i]);
strcat(_idString, hx);
}
tusb_init();
irq_set_exclusive_handler(PICO_STDIO_USB_LOW_PRIORITY_IRQ, low_priority_worker_irq);
irq_set_enabled(PICO_STDIO_USB_LOW_PRIORITY_IRQ, true);
mutex_init(&usb_mutex);
add_alarm_in_us(PICO_STDIO_USB_TASK_INTERVAL_US, timer_task, NULL, true);
_running = true;
}
......@@ -188,7 +62,7 @@ void SerialUSB::end() {
}
int SerialUSB::peek() {
CoreMutex m(&usb_mutex);
CoreMutex m(&__usb_mutex);
if (!_running || !m) {
return 0;
}
......@@ -198,7 +72,7 @@ int SerialUSB::peek() {
}
int SerialUSB::read() {
CoreMutex m(&usb_mutex);
CoreMutex m(&__usb_mutex);
if (!_running || !m) {
return -1;
}
......@@ -210,7 +84,7 @@ int SerialUSB::read() {
}
int SerialUSB::available() {
CoreMutex m(&usb_mutex);
CoreMutex m(&__usb_mutex);
if (!_running || !m) {
return 0;
}
......@@ -219,7 +93,7 @@ int SerialUSB::available() {
}
int SerialUSB::availableForWrite() {
CoreMutex m(&usb_mutex);
CoreMutex m(&__usb_mutex);
if (!_running || !m) {
return 0;
}
......@@ -228,7 +102,7 @@ int SerialUSB::availableForWrite() {
}
void SerialUSB::flush() {
CoreMutex m(&usb_mutex);
CoreMutex m(&__usb_mutex);
if (!_running || !m) {
return;
}
......@@ -241,7 +115,7 @@ size_t SerialUSB::write(uint8_t c) {
}
size_t SerialUSB::write(const uint8_t *buf, size_t length) {
CoreMutex m(&usb_mutex);
CoreMutex m(&__usb_mutex);
if (!_running || !m) {
return 0;
}
......@@ -276,7 +150,7 @@ size_t SerialUSB::write(const uint8_t *buf, size_t length) {
}
SerialUSB::operator bool() {
CoreMutex m(&usb_mutex);
CoreMutex m(&__usb_mutex);
if (!_running || !m) {
return false;
}
......
This diff is collapsed.
......@@ -52,6 +52,8 @@ static void main1() {
}
}
extern void __StartUSB();
extern "C" int main() {
#if F_CPU != 125000000
set_sys_clock_khz(F_CPU / 1000, true);
......@@ -59,6 +61,7 @@ extern "C" int main() {
mutex_init(&_pioMutex);
initVariant();
__StartUSB();
#ifndef DISABLE_USB_SERIAL
// Enable serial port for reset/upload always
......
No preview for this file type
Subproject commit b29ed09525169dcbe2964eb332780a14f2c5f466
Subproject commit 7c824ec29c75dac6393d3fb18f0dd474bc270113
......@@ -23,6 +23,8 @@ target_compile_options(pico PUBLIC
$<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>
)
include_directories(BEFORE ${PICO_SDK_PATH}/../tools/libpico)
target_link_libraries(pico
boot_stage2
hardware_adc
......@@ -63,10 +65,10 @@ target_link_libraries(pico
pico_platform
pico_runtime
pico_standard_link
pico_stdio_usb
pico_stdlib
pico_unique_id
tinyusb
tinyusb_device_unmarked
pico_audio
pico_audio_i2s
)
......
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
// defined by board.mk
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
// 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 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
// This example doesn't use an RTOS
//#define CFG_TUSB_OS OPT_OS_NONE
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
// #define CFG_TUSB_DEBUG 0
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_HID (2)
#define CFG_TUD_CDC (1)
#define CFG_TUD_MSC (0)
#define CFG_TUD_MIDI (1)
#define CFG_TUD_VENDOR (0)
#define CFG_TUD_CDC_RX_BUFSIZE (256)
#define CFG_TUD_CDC_TX_BUFSIZE (256)
#define CFG_TUD_MIDI_RX_BUFSIZE (64)
#define CFG_TUD_MIDI_TX_BUFSIZE (64)
// HID buffer size Should be sufficient to hold ID (if any) + Data
#define CFG_TUD_HID_EP_BUFSIZE (64)
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment