diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index aa6c189aaa..46dd8c8d71 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -66,6 +66,7 @@ INC += -I../../lib/nrfx/drivers INC += -I../../lib/nrfx/drivers/include INC += -I../../lib/nrfx/mdk INC += -I../../lib/nrfx/hal +INC += -I../../lib/nrfx/drivers/src/ MCU_VARIANT_UPPER = $(shell echo $(MCU_VARIANT) | tr '[:lower:]' '[:upper:]') MCU_SUB_VARIANT_UPPER = $(shell echo $(MCU_SUB_VARIANT) | tr '[:lower:]' '[:upper:]') @@ -183,6 +184,8 @@ SRC_NRFX += $(addprefix lib/nrfx/drivers/src/,\ nrfx_pwm.c \ nrfx_gpiote.c \ nrfx_nvmc.c \ + nrfx_power.c \ + nrfx_clock.c \ ) SRC_C += \ @@ -198,6 +201,34 @@ SRC_C += \ drivers/bluetooth/ble_drv.c \ drivers/bluetooth/ble_uart.c \ +ifeq ($(MCU_SUB_VARIANT), nrf52840) + +INC += -I./drivers/usb +INC += -I../../lib/tinyusb/src + + +# If SoftDevice is selected. +ifneq ($(SD), ) +# For external tinyusb drivers to enable SoftDevice mode. +CFLAGS += -DSOFTDEVICE_PRESENT +endif + +SRC_C += $(addprefix drivers/usb/,\ + usb_cdc.c \ + usb_descriptors.c \ + ) + +SRC_C += $(addprefix lib/tinyusb/src/,\ + common/tusb_fifo.c \ + device/usbd.c \ + device/usbd_control.c \ + class/cdc/cdc_device.c \ + tusb.c \ + portable/nordic/nrf5x/dcd_nrf5x.c \ + portable/nordic/nrf5x/hal_nrf5x.c \ + ) +endif + DRIVERS_SRC_C += $(addprefix modules/,\ machine/modmachine.c \ machine/uart.c \ diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index e01d118487..4401369f8b 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -40,6 +40,10 @@ #include "mphalport.h" +#if MICROPY_HW_USB_CDC +#include "usb_cdc.h" +#endif + #define BLE_DRIVER_VERBOSE 0 #if BLE_DRIVER_VERBOSE @@ -952,6 +956,10 @@ static void sd_evt_handler(uint32_t evt_id) { // unhandled event! break; } +#if MICROPY_HW_USB_CDC + // Farward SOC events to USB CDC driver. + usb_cdc_sd_event_handler(evt_id); +#endif } static void ble_evt_handler(ble_evt_t * p_ble_evt) { diff --git a/ports/nrf/drivers/usb/tusb_config.h b/ports/nrf/drivers/usb/tusb_config.h new file mode 100644 index 0000000000..619578ad6e --- /dev/null +++ b/ports/nrf/drivers/usb/tusb_config.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * 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 MICROPY_INCLUDED_NRF_TUSB_CONFIG_H +#define MICROPY_INCLUDED_NRF_TUSB_CONFIG_H + +// Common configuration + +#define CFG_TUSB_MCU OPT_MCU_NRF5X +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE + +#define CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4) + +// Device configuration + +#define CFG_TUD_ENDOINT0_SIZE (64) +#define CFG_TUD_CDC (1) +#define CFG_TUD_CDC_RX_BUFSIZE (64) +#define CFG_TUD_CDC_TX_BUFSIZE (64) + +#endif // MICROPY_INCLUDED_NRF_TUSB_CONFIG_H diff --git a/ports/nrf/drivers/usb/usb_cdc.c b/ports/nrf/drivers/usb/usb_cdc.c new file mode 100644 index 0000000000..c90bced6bb --- /dev/null +++ b/ports/nrf/drivers/usb/usb_cdc.c @@ -0,0 +1,226 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2019 Glenn Ruben Bakke + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#include "py/mphal.h" + +#if MICROPY_HW_USB_CDC + +#include "tusb.h" +#include "nrfx.h" +#include "nrfx_power.h" +#include "nrfx_uart.h" +#include "py/ringbuf.h" + +#ifdef BLUETOOTH_SD +#include "nrf_sdm.h" +#include "nrf_soc.h" +#include "ble_drv.h" +#endif + +extern void tusb_hal_nrf_power_event(uint32_t event); + +static void cdc_task(void); + +static uint8_t rx_ringbuf_array[1024]; +static uint8_t tx_ringbuf_array[1024]; +static volatile ringbuf_t rx_ringbuf; +static volatile ringbuf_t tx_ringbuf; + +static void board_init(void) { + // Config clock source. +#ifndef BLUETOOTH_SD + NRF_CLOCK->LFCLKSRC = (uint32_t)((CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos) & CLOCK_LFCLKSRC_SRC_Msk); + NRF_CLOCK->TASKS_LFCLKSTART = 1UL; +#endif + + // Priorities 0, 1, 4 (nRF52) are reserved for SoftDevice + // 2 is highest for application + NRFX_IRQ_PRIORITY_SET(USBD_IRQn, 2); + + // USB power may already be ready at this time -> no event generated + // We need to invoke the handler based on the status initially + uint32_t usb_reg; + +#ifdef BLUETOOTH_SD + uint8_t sd_en = false; + sd_softdevice_is_enabled(&sd_en); + + if (sd_en) { + sd_power_usbdetected_enable(true); + sd_power_usbpwrrdy_enable(true); + sd_power_usbremoved_enable(true); + + sd_power_usbregstatus_get(&usb_reg); + } else +#endif + { + // Power module init + const nrfx_power_config_t pwr_cfg = { 0 }; + nrfx_power_init(&pwr_cfg); + + // Register tusb function as USB power handler + const nrfx_power_usbevt_config_t config = { .handler = (nrfx_power_usb_event_handler_t) tusb_hal_nrf_power_event }; + nrfx_power_usbevt_init(&config); + + nrfx_power_usbevt_enable(); + + usb_reg = NRF_POWER->USBREGSTATUS; + } + + if (usb_reg & POWER_USBREGSTATUS_VBUSDETECT_Msk) { + tusb_hal_nrf_power_event(NRFX_POWER_USB_EVT_DETECTED); + } + +#ifndef BLUETOOTH_SD + if (usb_reg & POWER_USBREGSTATUS_OUTPUTRDY_Msk) { + tusb_hal_nrf_power_event(NRFX_POWER_USB_EVT_READY); + } +#endif +} + +static bool cdc_rx_any(void) { + return rx_ringbuf.iput != rx_ringbuf.iget; +} + +static int cdc_rx_char(void) { + return ringbuf_get((ringbuf_t*)&rx_ringbuf); +} + +static bool cdc_tx_any(void) { + return tx_ringbuf.iput != tx_ringbuf.iget; +} + +static int cdc_tx_char(void) { + return ringbuf_get((ringbuf_t*)&tx_ringbuf); +} + +static void cdc_task(void) +{ + if ( tud_cdc_connected() ) { + // connected and there are data available + while (tud_cdc_available()) { + int c; + uint32_t count = tud_cdc_read(&c, 1); + (void)count; + ringbuf_put((ringbuf_t*)&rx_ringbuf, c); + } + + int chars = 0; + while (cdc_tx_any()) { + if (chars < 64) { + tud_cdc_write_char(cdc_tx_char()); + chars++; + } else { + chars = 0; + tud_cdc_write_flush(); + } + } + + tud_cdc_write_flush(); + } +} + +static void usb_cdc_loop(void) { + tud_task(); + cdc_task(); +} + +int usb_cdc_init(void) +{ + static bool initialized = false; + if (!initialized) { + +#if BLUETOOTH_SD + // Initialize the clock and BLE stack. + ble_drv_stack_enable(); +#endif + + board_init(); + initialized = true; + } + + rx_ringbuf.buf = rx_ringbuf_array; + rx_ringbuf.size = sizeof(rx_ringbuf_array); + rx_ringbuf.iget = 0; + rx_ringbuf.iput = 0; + + tx_ringbuf.buf = tx_ringbuf_array; + tx_ringbuf.size = sizeof(tx_ringbuf_array); + tx_ringbuf.iget = 0; + tx_ringbuf.iput = 0; + + tusb_init(); + + return 0; +} + +#ifdef BLUETOOTH_SD +// process SOC event from SD +void usb_cdc_sd_event_handler(uint32_t soc_evt) { + /*------------- usb power event handler -------------*/ + int32_t usbevt = (soc_evt == NRF_EVT_POWER_USB_DETECTED ) ? NRFX_POWER_USB_EVT_DETECTED: + (soc_evt == NRF_EVT_POWER_USB_POWER_READY) ? NRFX_POWER_USB_EVT_READY : + (soc_evt == NRF_EVT_POWER_USB_REMOVED ) ? NRFX_POWER_USB_EVT_REMOVED : -1; + + if (usbevt >= 0) { + tusb_hal_nrf_power_event(usbevt); + } +} +#endif + +int mp_hal_stdin_rx_chr(void) { + for (;;) { + usb_cdc_loop(); + if (cdc_rx_any()) { + return cdc_rx_char(); + } + } + + return 0; +} + +void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { + + for (const char *top = str + len; str < top; str++) { + ringbuf_put((ringbuf_t*)&tx_ringbuf, *str); + usb_cdc_loop(); + } +} + +void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) { + + for (const char *top = str + len; str < top; str++) { + if (*str == '\n') { + ringbuf_put((ringbuf_t*)&tx_ringbuf, '\r'); + usb_cdc_loop(); + } + ringbuf_put((ringbuf_t*)&tx_ringbuf, *str); + usb_cdc_loop(); + } +} + +#endif // MICROPY_HW_USB_CDC diff --git a/ports/nrf/drivers/usb/usb_cdc.h b/ports/nrf/drivers/usb/usb_cdc.h new file mode 100644 index 0000000000..7cd94f85ba --- /dev/null +++ b/ports/nrf/drivers/usb/usb_cdc.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Glenn Ruben Bakke + * + * 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 NRF_DRIVERS_USB_CDC_H__ +#define NRF_DRIVERS_USB_CDC_H__ + +#include "tusb.h" + +void usb_cdc_init(void); + +void usb_cdc_loop(void); +int usb_cdc_read_char(void); +void usb_cdc_write_char(char c); + +void usb_cdc_sd_event_handler(uint32_t soc_evt); + +#endif // NRF_DRIVERS_USB_CDC_H__ diff --git a/ports/nrf/drivers/usb/usb_descriptors.c b/ports/nrf/drivers/usb/usb_descriptors.c new file mode 100644 index 0000000000..a061302d6b --- /dev/null +++ b/ports/nrf/drivers/usb/usb_descriptors.c @@ -0,0 +1,114 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * 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. + */ + +#include "tusb.h" + +#define USBD_VID (0xf055) +#define USBD_PID (0x9802) + +#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_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_ENDOINT0_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(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, CFG_TUD_CDC_RX_BUFSIZE), +}; + +static const char *const usbd_desc_str[] = { + [USBD_STR_MANUF] = "MicroPython", + [USBD_STR_PRODUCT] = "Board in FS mode", + [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) { + #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 len, second byte is string type + desc_str[0] = TUD_DESC_STR_HEADER(len); + + return desc_str; +} diff --git a/ports/nrf/main.c b/ports/nrf/main.c index e82cfcf58d..ce8512aee3 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -70,6 +70,10 @@ #include "softpwm.h" #endif +#if MICROPY_HW_USB_CDC +#include "usb_cdc.h" +#endif + void do_str(const char *src, mp_parse_input_kind_t input_kind) { mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); if (lex == NULL) { @@ -121,6 +125,7 @@ soft_reset: readline_init0(); + #if MICROPY_PY_MACHINE_HW_SPI spi_init0(); #endif @@ -149,7 +154,7 @@ soft_reset: uart_init0(); #endif -#if (MICROPY_PY_BLE_NUS == 0) +#if (MICROPY_PY_BLE_NUS == 0) && (MICROPY_HW_USB_CDC == 0) { mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(0), @@ -231,6 +236,10 @@ led_state(1, 0); pyexec_file_if_exists("main.py"); #endif +#if MICROPY_HW_USB_CDC + usb_cdc_init(); +#endif + for (;;) { if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { if (pyexec_raw_repl() != 0) { diff --git a/ports/nrf/mphalport.c b/ports/nrf/mphalport.c index bf89697083..b915c23e47 100644 --- a/ports/nrf/mphalport.c +++ b/ports/nrf/mphalport.c @@ -62,7 +62,9 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { } return ret; } +#endif +#if !MICROPY_PY_BLE_NUS && !MICROPY_HW_USB_CDC int mp_hal_stdin_rx_chr(void) { for (;;) { if (MP_STATE_PORT(board_stdio_uart) != NULL && uart_rx_any(MP_STATE_PORT(board_stdio_uart))) { diff --git a/ports/nrf/nrfx_config.h b/ports/nrf/nrfx_config.h index 99085a2fe8..505ecf57bd 100644 --- a/ports/nrf/nrfx_config.h +++ b/ports/nrf/nrfx_config.h @@ -47,6 +47,14 @@ #define GPIO_COUNT 2 #endif +#if defined(NRF52840) +// for tinyusb +//#define NRFX_IRQ_IS_ENABLED 1 +#define NRFX_POWER_ENABLED 1 +#define NRFX_POWER_CONFIG_IRQ_PRIORITY 2 +#define NRFX_SYSTICK_ENABLED 1 +#endif + #define NRFX_GPIOTE_ENABLED 1 #define NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 1 #if NRF51 diff --git a/ports/nrf/nrfx_glue.h b/ports/nrf/nrfx_glue.h index 316e02df1d..ffd2cff736 100644 --- a/ports/nrf/nrfx_glue.h +++ b/ports/nrf/nrfx_glue.h @@ -138,4 +138,6 @@ #endif // !BLUETOOTH_SD +#define NRFX_IRQ_IS_ENABLED(irq_number) (0 != (NVIC->ISER[irq_number / 32] & (1UL << (irq_number % 32)))) + #endif // NRFX_GLUE_H