Add Serial(USB) support

Serial via USB /dev/ttyACM0 now works using standalone minicom terminal
app but not via the IDE's serial monitor.
This commit is contained in:
Earle F. Philhower, III 2021-02-25 17:07:42 -08:00
parent e95f45ad10
commit 369dea88ba
6 changed files with 262 additions and 4 deletions

View file

@ -14,9 +14,13 @@ To install:
````
mkdir -p ~/Arduino/hardware/pico
git clone https://github.com/earlephilhower/arduino-pico.git ~/Arduino/hardware/pico/rp2040
cd ~/Arduino/hardware/pico/rp2040/system
cd ~/Arduino/hardware/pico/rp2040
git submodule init
git submodule update
cd pico-sdk
git submodule init
git submodule update
cd ../system
python3 ./get.py
`````

View file

@ -44,6 +44,10 @@ void delayMicroseconds(unsigned int us);
} // extern "C"
#endif
#ifdef __cplusplus
#include "SerialUSB.h"
#endif
// ARM toolchain doesn't provide itoa etc, provide them
#include "api/itoa.h"

222
cores/rp2040/SerialUSB.cpp Normal file
View file

@ -0,0 +1,222 @@
#include <Arduino.h>
SerialUSB Serial;
extern "C" {
#include "tusb.h"
#include "pico/time.h"
#include "pico/binary_info.h"
#include "hardware/irq.h"
#include "pico/mutex.h"
}
#define PICO_STDIO_USB_TASK_INTERVAL_US 1000
#define PICO_STDIO_USB_LOW_PRIORITY_IRQ 31
#define USBD_VID (0x2E8A) // Raspberry Pi
#define USBD_PID (0x000a) // Raspberry Pi Pico SDK CDC
#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_MISC,
.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 const char *const usbd_desc_str[] = {
[USBD_STR_0] = "",
[USBD_STR_MANUF] = "Raspberry Pi",
[USBD_STR_PRODUCT] = "PicoArduino",
[USBD_STR_SERIAL] = "000000000000", // TODO
[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;
}
void SerialUSB::begin(int baud) {
(void) baud; //ignored
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);
}
void SerialUSB::end() {
// TODO
}
int SerialUSB::peek() {
uint8_t c;
uint32_t owner;
if (!mutex_try_enter(&usb_mutex, &owner)) {
if (owner == get_core_num()) return -1; // would deadlock otherwise
mutex_enter_blocking(&usb_mutex);
}
auto ret = tud_cdc_peek(0, &c) ? (int) c : -1;
mutex_exit(&usb_mutex);
return ret;
}
int SerialUSB::read() {
uint32_t owner;
if (!mutex_try_enter(&usb_mutex, &owner)) {
if (owner == get_core_num()) return -1; // would deadlock otherwise
mutex_enter_blocking(&usb_mutex);
}
if (tud_cdc_connected() && tud_cdc_available()) {
int ch = tud_cdc_read_char();
mutex_exit(&usb_mutex);
return ch;
}
mutex_exit(&usb_mutex);
return -1;
}
int SerialUSB::available() {
uint32_t owner;
if (!mutex_try_enter(&usb_mutex, &owner)) {
if (owner == get_core_num()) return 0; // would deadlock otherwise
mutex_enter_blocking(&usb_mutex);
}
auto ret = tud_cdc_available();
mutex_exit(&usb_mutex);
return ret;
}
int SerialUSB::availableForWrite() {
uint32_t owner;
if (!mutex_try_enter(&usb_mutex, &owner)) {
if (owner == get_core_num()) return 0; // would deadlock otherwise
mutex_enter_blocking(&usb_mutex);
}
auto ret = tud_cdc_write_available();
mutex_exit(&usb_mutex);
return ret;
}
void SerialUSB::flush() {
uint32_t owner;
if (!mutex_try_enter(&usb_mutex, &owner)) {
if (owner == get_core_num()) return; // would deadlock otherwise
mutex_enter_blocking(&usb_mutex);
}
tud_cdc_write_flush();
mutex_exit(&usb_mutex);
}
size_t SerialUSB::write(uint8_t c) {
return write(&c, 1);
}
size_t SerialUSB::write(const uint8_t *p, size_t len) {
uint32_t owner;
if (!mutex_try_enter(&usb_mutex, &owner)) {
if (owner == get_core_num()) return 0; // would deadlock otherwise
mutex_enter_blocking(&usb_mutex);
}
size_t remain = len;
while (remain && tud_cdc_connected()) {
size_t cnt = tud_cdc_write(p, remain);
p += cnt;
remain -= cnt;
tud_task();
tud_cdc_write_flush();
}
mutex_exit(&usb_mutex);
return len - remain;
}
SerialUSB::operator bool() {
uint32_t owner;
if (!mutex_try_enter(&usb_mutex, &owner)) {
if (owner == get_core_num()) return -1; // would deadlock otherwise
mutex_enter_blocking(&usb_mutex);
}
tud_task();
auto ret = tud_cdc_connected();
mutex_exit(&usb_mutex);
return ret;
}

28
cores/rp2040/SerialUSB.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef __SERIALUSB_H__
#define __SERIALUSB_H__
#include "api/Stream.h"
class SerialUSB : public Stream
{
public:
SerialUSB() { }
void begin(int baud = 115200);
void end();
virtual int peek() override;
virtual int read() override;
virtual int available() override;
virtual int availableForWrite() override;
virtual void flush() override;
virtual size_t write(uint8_t c) override;
virtual size_t write(const uint8_t *p, size_t len) override;
using Print::write;
operator bool();
};
extern SerialUSB Serial;
#endif

View file

@ -39,18 +39,18 @@ compiler.warning_flags.more=-Wall
compiler.warning_flags.all=-Wall -Wextra
compiler.defines={build.led}
compiler.includes=-I{runtime.platform.path}/pico_base/ -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_platform/include/ -I{runtime.platform.path}/pico-sdk/src/common/pico_base/include/ -I{runtime.platform.path}/pico-sdk/src/rp2040/hardware_regs/include/ -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_timer/include/ -I{runtime.platform.path}/pico-sdk/src/common/pico_stdlib/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_gpio/include -I{runtime.platform.path}/pico-sdk/src/common/pico_base/include -I{runtime.platform.path}/pico-examples/build/generated/pico_base -I{runtime.platform.path}/pico-sdk/src/boards/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_platform/include -I{runtime.platform.path}/pico-sdk/src/rp2040/hardware_regs/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_base/include -I{runtime.platform.path}/pico-sdk/src/rp2040/hardware_structs/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_claim/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_sync/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_uart/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_divider/include -I{runtime.platform.path}/pico-sdk/src/common/pico_time/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_timer/include -I{runtime.platform.path}/pico-sdk/src/common/pico_sync/include -I{runtime.platform.path}/pico-sdk/src/common/pico_util/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_runtime/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_clocks/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_resets/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_watchdog/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_xosc/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_pll/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_vreg/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_irq/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_printf/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_bootrom/include -I{runtime.platform.path}/pico-sdk/src/common/pico_bit_ops/include -I{runtime.platform.path}/pico-sdk/src/common/pico_divider/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_double/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_int64_ops/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_float/include -I{runtime.platform.path}/pico-sdk/src/common/pico_binary_info/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_stdio/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_stdio_uart/include -I{runtime.platform.path}/pico-sdk/src/rp2040/hardware_regs/include/
compiler.includes=-I{runtime.platform.path}/pico_base/ -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_platform/include/ -I{runtime.platform.path}/pico-sdk/src/common/pico_base/include/ -I{runtime.platform.path}/pico-sdk/src/rp2040/hardware_regs/include/ -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_timer/include/ -I{runtime.platform.path}/pico-sdk/src/common/pico_stdlib/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_gpio/include -I{runtime.platform.path}/pico-sdk/src/common/pico_base/include -I{runtime.platform.path}/pico-examples/build/generated/pico_base -I{runtime.platform.path}/pico-sdk/src/boards/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_platform/include -I{runtime.platform.path}/pico-sdk/src/rp2040/hardware_regs/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_base/include -I{runtime.platform.path}/pico-sdk/src/rp2040/hardware_structs/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_claim/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_sync/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_uart/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_divider/include -I{runtime.platform.path}/pico-sdk/src/common/pico_time/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_timer/include -I{runtime.platform.path}/pico-sdk/src/common/pico_sync/include -I{runtime.platform.path}/pico-sdk/src/common/pico_util/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_runtime/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_clocks/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_resets/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_watchdog/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_xosc/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_pll/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_vreg/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/hardware_irq/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_printf/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_bootrom/include -I{runtime.platform.path}/pico-sdk/src/common/pico_bit_ops/include -I{runtime.platform.path}/pico-sdk/src/common/pico_divider/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_double/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_int64_ops/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_float/include -I{runtime.platform.path}/pico-sdk/src/common/pico_binary_info/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_stdio/include -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_stdio_uart/include -I{runtime.platform.path}/pico-sdk/src/rp2040/hardware_regs/include/ -I{runtime.platform.path}/pico-sdk/lib/tinyusb/src/ -I{runtime.platform.path}/pico-sdk/src/rp2_common/pico_stdio_usb/include
compiler.flags=-Os -march=armv6-m -mcpu=cortex-m0plus -mthumb -Os -DNDEBUG -ffunction-sections -fdata-sections
compiler.wrap=-Wl,--wrap=acos -Wl,--wrap=acosf -Wl,--wrap=acosh -Wl,--wrap=acoshf -Wl,--wrap=__aeabi_cdcmpeq -Wl,--wrap=__aeabi_cdcmple -Wl,--wrap=__aeabi_cdrcmple -Wl,--wrap=__aeabi_cfcmpeq -Wl,--wrap=__aeabi_cfcmple -Wl,--wrap=__aeabi_cfrcmple -Wl,--wrap=__aeabi_d2f -Wl,--wrap=__aeabi_d2iz -Wl,--wrap=__aeabi_d2lz -Wl,--wrap=__aeabi_d2uiz -Wl,--wrap=__aeabi_d2ulz -Wl,--wrap=__aeabi_dadd -Wl,--wrap=__aeabi_dcmpeq -Wl,--wrap=__aeabi_dcmpge -Wl,--wrap=__aeabi_dcmpgt -Wl,--wrap=__aeabi_dcmple -Wl,--wrap=__aeabi_dcmplt -Wl,--wrap=__aeabi_dcmpun -Wl,--wrap=__aeabi_ddiv -Wl,--wrap=__aeabi_dmul -Wl,--wrap=__aeabi_drsub -Wl,--wrap=__aeabi_dsub -Wl,--wrap=__aeabi_f2d -Wl,--wrap=__aeabi_f2iz -Wl,--wrap=__aeabi_f2lz -Wl,--wrap=__aeabi_f2uiz -Wl,--wrap=__aeabi_f2ulz -Wl,--wrap=__aeabi_fadd -Wl,--wrap=__aeabi_fcmpeq -Wl,--wrap=__aeabi_fcmpge -Wl,--wrap=__aeabi_fcmpgt -Wl,--wrap=__aeabi_fcmple -Wl,--wrap=__aeabi_fcmplt -Wl,--wrap=__aeabi_fcmpun -Wl,--wrap=__aeabi_fdiv -Wl,--wrap=__aeabi_fmul -Wl,--wrap=__aeabi_frsub -Wl,--wrap=__aeabi_fsub -Wl,--wrap=__aeabi_i2d -Wl,--wrap=__aeabi_i2f -Wl,--wrap=__aeabi_idiv -Wl,--wrap=__aeabi_idivmod -Wl,--wrap=__aeabi_l2d -Wl,--wrap=__aeabi_l2f -Wl,--wrap=__aeabi_ldivmod -Wl,--wrap=__aeabi_lmul -Wl,--wrap=__aeabi_memcpy -Wl,--wrap=__aeabi_memcpy4 -Wl,--wrap=__aeabi_memcpy8 -Wl,--wrap=__aeabi_memset -Wl,--wrap=__aeabi_memset4 -Wl,--wrap=__aeabi_memset8 -Wl,--wrap=__aeabi_ui2d -Wl,--wrap=__aeabi_ui2f -Wl,--wrap=__aeabi_uidiv -Wl,--wrap=__aeabi_uidivmod -Wl,--wrap=__aeabi_ul2d -Wl,--wrap=__aeabi_ul2f -Wl,--wrap=__aeabi_uldivmod -Wl,--wrap=asin -Wl,--wrap=asinf -Wl,--wrap=asinh -Wl,--wrap=asinhf -Wl,--wrap=atan -Wl,--wrap=atan2 -Wl,--wrap=atan2f -Wl,--wrap=atanf -Wl,--wrap=atanh -Wl,--wrap=atanhf -Wl,--wrap=calloc -Wl,--wrap=cbrt -Wl,--wrap=cbrtf -Wl,--wrap=ceil -Wl,--wrap=ceilf -Wl,--wrap=__clz -Wl,--wrap=__clzdi2 -Wl,--wrap=__clzl -Wl,--wrap=__clzll -Wl,--wrap=__clzsi2 -Wl,--wrap=copysign -Wl,--wrap=copysignf -Wl,--wrap=cos -Wl,--wrap=cosf -Wl,--wrap=cosh -Wl,--wrap=coshf -Wl,--wrap=__ctzdi2 -Wl,--wrap=__ctzsi2 -Wl,--wrap=drem -Wl,--wrap=dremf -Wl,--wrap=exp -Wl,--wrap=exp10 -Wl,--wrap=exp10f -Wl,--wrap=exp2 -Wl,--wrap=exp2f -Wl,--wrap=expf -Wl,--wrap=expm1 -Wl,--wrap=expm1f -Wl,--wrap=floor -Wl,--wrap=floorf -Wl,--wrap=fma -Wl,--wrap=fmaf -Wl,--wrap=fmod -Wl,--wrap=fmodf -Wl,--wrap=free -Wl,--wrap=hypot -Wl,--wrap=hypotf -Wl,--wrap=ldexp -Wl,--wrap=ldexpf -Wl,--wrap=log -Wl,--wrap=log10 -Wl,--wrap=log10f -Wl,--wrap=log1p -Wl,--wrap=log1pf -Wl,--wrap=log2 -Wl,--wrap=log2f -Wl,--wrap=logf -Wl,--wrap=malloc -Wl,--wrap=memcpy -Wl,--wrap=memset -Wl,--wrap=__popcountdi2 -Wl,--wrap=__popcountsi2 -Wl,--wrap=pow -Wl,--wrap=powf -Wl,--wrap=powint -Wl,--wrap=powintf -Wl,--wrap=printf -Wl,--wrap=putchar -Wl,--wrap=puts -Wl,--wrap=remainder -Wl,--wrap=remainderf -Wl,--wrap=remquo -Wl,--wrap=remquof -Wl,--wrap=round -Wl,--wrap=roundf -Wl,--wrap=sin -Wl,--wrap=sincos -Wl,--wrap=sincosf -Wl,--wrap=sinf -Wl,--wrap=sinh -Wl,--wrap=sinhf -Wl,--wrap=snprintf -Wl,--wrap=sprintf -Wl,--wrap=sqrt -Wl,--wrap=sqrtf -Wl,--wrap=tan -Wl,--wrap=tanf -Wl,--wrap=tanh -Wl,--wrap=tanhf -Wl,--wrap=trunc -Wl,--wrap=truncf -Wl,--wrap=vprintf -Wl,--wrap=vsnprintf
compiler.c.cmd=arm-none-eabi-gcc
compiler.c.flags=-c {compiler.defines} {compiler.flags} {compiler.includes}
compiler.c.flags=-c {compiler.defines} {compiler.flags} {compiler.includes} -std=gnu17
compiler.c.elf.cmd=arm-none-eabi-g++
compiler.c.elf.flags={compiler.defines} {compiler.flags} -Os -Wl,--gc-sections
compiler.S.cmd=arm-none-eabi-gcc
compiler.S.flags=-c -g -x assembler-with-cpp -MMD {compiler.includes}
compiler.cpp.cmd=arm-none-eabi-g++
compiler.cpp.flags=-c {compiler.defines} {compiler.flags} {compiler.includes}
compiler.cpp.flags=-c {compiler.defines} {compiler.flags} {compiler.includes} -std=gnu++17
compiler.ar.cmd=arm-none-eabi-ar

Binary file not shown.