| /* Teensyduino Core Library | |||||
| * http://www.pjrc.com/teensy/ | |||||
| * Copyright (c) 2017 PJRC.COM, LLC. | |||||
| * | |||||
| * 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: | |||||
| * | |||||
| * 1. The above copyright notice and this permission notice shall be | |||||
| * included in all copies or substantial portions of the Software. | |||||
| * | |||||
| * 2. If the Software is incorporated into a build system that allows | |||||
| * selection among a list of target devices, then similar target | |||||
| * devices manufactured by PJRC.COM must be included in the list of | |||||
| * target devices and selectable in the same manner. | |||||
| * | |||||
| * 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. | |||||
| */ | |||||
| // Long ago this file contained code from Arduino.cc, which was | |||||
| // Copyright (c) 2008 David A. Mellis. No substantial portion of | |||||
| // Arduino's original code remains. In fact, several improvements | |||||
| // developed for Teensyduino have made their way back into | |||||
| // Arduino's code base. :-) | |||||
| #include <Arduino.h> | |||||
| #include "debug/printf.h" | |||||
| #undef printf | |||||
| size_t Print::write(const uint8_t *buffer, size_t size) | |||||
| { | |||||
| size_t count = 0; | |||||
| while (size--) count += write(*buffer++); | |||||
| return count; | |||||
| } | |||||
| size_t Print::print(const String &s) | |||||
| { | |||||
| uint8_t buffer[33]; | |||||
| size_t count = 0; | |||||
| unsigned int index = 0; | |||||
| unsigned int len = s.length(); | |||||
| while (len > 0) { | |||||
| s.getBytes(buffer, sizeof(buffer), index); | |||||
| unsigned int nbytes = len; | |||||
| if (nbytes > sizeof(buffer)-1) nbytes = sizeof(buffer)-1; | |||||
| index += nbytes; | |||||
| len -= nbytes; | |||||
| count += write(buffer, nbytes); | |||||
| } | |||||
| return count; | |||||
| } | |||||
| size_t Print::print(long n) | |||||
| { | |||||
| uint8_t sign=0; | |||||
| if (n < 0) { | |||||
| sign = '-'; | |||||
| n = -n; | |||||
| } | |||||
| return printNumber(n, 10, sign); | |||||
| } | |||||
| size_t Print::println(void) | |||||
| { | |||||
| uint8_t buf[2]={'\r', '\n'}; | |||||
| return write(buf, 2); | |||||
| } | |||||
| extern "C" { | |||||
| __attribute__((weak)) | |||||
| int _write(int file, char *ptr, int len) | |||||
| { | |||||
| ((class Print *)file)->write((uint8_t *)ptr, len); | |||||
| return len; | |||||
| } | |||||
| } | |||||
| int Print::printf(const char *format, ...) | |||||
| { | |||||
| va_list ap; | |||||
| va_start(ap, format); | |||||
| #ifdef __STRICT_ANSI__ | |||||
| return 0; // TODO: make this work with -std=c++0x | |||||
| #else | |||||
| return vdprintf((int)this, format, ap); | |||||
| #endif | |||||
| } | |||||
| int Print::printf(const __FlashStringHelper *format, ...) | |||||
| { | |||||
| va_list ap; | |||||
| va_start(ap, format); | |||||
| #ifdef __STRICT_ANSI__ | |||||
| return 0; | |||||
| #else | |||||
| return vdprintf((int)this, (const char *)format, ap); | |||||
| #endif | |||||
| } | |||||
| size_t Print::printNumber(unsigned long n, uint8_t base, uint8_t sign) | |||||
| { | |||||
| uint8_t buf[34]; | |||||
| uint8_t digit, i; | |||||
| // TODO: make these checks as inline, since base is | |||||
| // almost always a constant. base = 0 (BYTE) should | |||||
| // inline as a call directly to write() | |||||
| if (base == 0) { | |||||
| return write((uint8_t)n); | |||||
| } else if (base == 1) { | |||||
| base = 10; | |||||
| } | |||||
| if (n == 0) { | |||||
| buf[sizeof(buf) - 1] = '0'; | |||||
| i = sizeof(buf) - 1; | |||||
| } else { | |||||
| i = sizeof(buf) - 1; | |||||
| while (1) { | |||||
| digit = n % base; | |||||
| buf[i] = ((digit < 10) ? '0' + digit : 'A' + digit - 10); | |||||
| n /= base; | |||||
| if (n == 0) break; | |||||
| i--; | |||||
| } | |||||
| } | |||||
| if (sign) { | |||||
| i--; | |||||
| buf[i] = '-'; | |||||
| } | |||||
| return write(buf + i, sizeof(buf) - i); | |||||
| } | |||||
| size_t Print::printFloat(double number, uint8_t digits) | |||||
| { | |||||
| uint8_t sign=0; | |||||
| size_t count=0; | |||||
| if (isnan(number)) return print("nan"); | |||||
| if (isinf(number)) return print("inf"); | |||||
| if (number > 4294967040.0f) return print("ovf"); // constant determined empirically | |||||
| if (number <-4294967040.0f) return print("ovf"); // constant determined empirically | |||||
| // Handle negative numbers | |||||
| if (number < 0.0) { | |||||
| sign = 1; | |||||
| number = -number; | |||||
| } | |||||
| // Round correctly so that print(1.999, 2) prints as "2.00" | |||||
| double rounding = 0.5; | |||||
| for (uint8_t i=0; i<digits; ++i) { | |||||
| rounding *= 0.1; | |||||
| } | |||||
| number += rounding; | |||||
| // Extract the integer part of the number and print it | |||||
| unsigned long int_part = (unsigned long)number; | |||||
| double remainder = number - (double)int_part; | |||||
| count += printNumber(int_part, 10, sign); | |||||
| // Print the decimal point, but only if there are digits beyond | |||||
| if (digits > 0) { | |||||
| uint8_t n, buf[16], count=1; | |||||
| buf[0] = '.'; | |||||
| // Extract digits from the remainder one at a time | |||||
| if (digits > sizeof(buf) - 1) digits = sizeof(buf) - 1; | |||||
| while (digits-- > 0) { | |||||
| remainder *= 10.0; | |||||
| n = (uint8_t)(remainder); | |||||
| buf[count++] = '0' + n; | |||||
| remainder -= n; | |||||
| } | |||||
| count += write(buf, count); | |||||
| } | |||||
| return count; | |||||
| } | |||||
| private: | private: | ||||
| char write_error; | char write_error; | ||||
| size_t printFloat(double n, uint8_t digits); | size_t printFloat(double n, uint8_t digits); | ||||
| #ifdef __MKL26Z64__ | |||||
| size_t printNumberDec(unsigned long n, uint8_t sign); | |||||
| size_t printNumberHex(unsigned long n); | |||||
| size_t printNumberBin(unsigned long n); | |||||
| size_t printNumberAny(unsigned long n, uint8_t base); | |||||
| inline size_t printNumber(unsigned long n, uint8_t base, uint8_t sign) __attribute__((always_inline)) { | |||||
| // when "base" is a constant (pretty much always), the | |||||
| // compiler optimizes this to a single function call. | |||||
| if (base == 0) return write((uint8_t)n); | |||||
| if (base == 10 || base < 2) return printNumberDec(n, sign); | |||||
| if (base == 16) return printNumberHex(n); | |||||
| if (base == 2) return printNumberBin(n); | |||||
| return printNumberAny(n, base); | |||||
| } | |||||
| #else | |||||
| size_t printNumber(unsigned long n, uint8_t base, uint8_t sign); | size_t printNumber(unsigned long n, uint8_t base, uint8_t sign); | ||||
| #endif | |||||
| }; | }; | ||||
| #include <stdint.h> | |||||
| volatile uint32_t F_CPU_ACTUAL = 396000000; | |||||
| volatile uint32_t F_BUS_ACTUAL = 132000000; | |||||
| uint32_t analogWriteRes(uint32_t bits); | uint32_t analogWriteRes(uint32_t bits); | ||||
| static inline uint32_t analogWriteResolution(uint32_t bits) { return analogWriteRes(bits); } | static inline uint32_t analogWriteResolution(uint32_t bits) { return analogWriteRes(bits); } | ||||
| void analogWriteFrequency(uint8_t pin, float frequency); | void analogWriteFrequency(uint8_t pin, float frequency); | ||||
| #ifdef __cplusplus | |||||
| void attachInterruptVector(IRQ_NUMBER_t irq, void (*function)(void)); | |||||
| #else | |||||
| void attachInterruptVector(enum IRQ_NUMBER_t irq, void (*function)(void)); | |||||
| #endif | |||||
| void attachInterrupt(uint8_t pin, void (*function)(void), int mode); | void attachInterrupt(uint8_t pin, void (*function)(void), int mode); | ||||
| void detachInterrupt(uint8_t pin); | void detachInterrupt(uint8_t pin); | ||||
| void _init_Teensyduino_internal_(void); | void _init_Teensyduino_internal_(void); |
| #ifdef PRINT_DEBUG_STUFF | #ifdef PRINT_DEBUG_STUFF | ||||
| #define printf_init() printf_debug_init() | #define printf_init() printf_debug_init() | ||||
| // defining printf this way breaks things like Serial.printf() in C++ :( | |||||
| #define printf(...) printf_debug(__VA_ARGS__) | #define printf(...) printf_debug(__VA_ARGS__) | ||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| void print_debug_init(void); | void print_debug_init(void); | ||||
| void printf_debug(const char *format, ...); | void printf_debug(const char *format, ...); | ||||
| #ifdef __cplusplus | |||||
| } | |||||
| #endif | |||||
| #else | #else | ||||
| #define printf_init() | #define printf_init() |
| #include <stdarg.h> | #include <stdarg.h> | ||||
| #include "imxrt.h" | #include "imxrt.h" | ||||
| void putchar_debug(char c); | |||||
| static void puint_debug(unsigned int num); | static void puint_debug(unsigned int num); | ||||
| n = -n; | n = -n; | ||||
| putchar_debug('-'); | putchar_debug('-'); | ||||
| } | } | ||||
| val = n; | |||||
| puint_debug(n); | puint_debug(n); | ||||
| } else if (*format == 'u') { | } else if (*format == 'u') { | ||||
| puint_debug(va_arg(args, unsigned int)); | puint_debug(va_arg(args, unsigned int)); | ||||
| __attribute__((section(".progmem"))) | __attribute__((section(".progmem"))) | ||||
| void printf_debug_init(void) | void printf_debug_init(void) | ||||
| { | { | ||||
| // turn on Serial4, run at 115200 baud using 24 MHz clock (works if PLL3 off) | |||||
| // turn on Serial4, run using 24 MHz clock (works if PLL3 off or bypassed) | |||||
| CCM_CCGR0 |= CCM_CCGR0_LPUART3(CCM_CCGR_ON); | CCM_CCGR0 |= CCM_CCGR0_LPUART3(CCM_CCGR_ON); | ||||
| IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_06 = 2; // Arduino pin 17 | IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_06 = 2; // Arduino pin 17 | ||||
| CCM_CSCDR1 = (CCM_CSCDR1 & ~CCM_CSCDR1_UART_CLK_PODF(0x3F)) | CCM_CSCDR1_UART_CLK_SEL; | CCM_CSCDR1 = (CCM_CSCDR1 & ~CCM_CSCDR1_UART_CLK_PODF(0x3F)) | CCM_CSCDR1_UART_CLK_SEL; | ||||
| LPUART3_BAUD = LPUART_BAUD_OSR(25) | LPUART_BAUD_SBR(8); | |||||
| LPUART3_BAUD = LPUART_BAUD_OSR(25) | LPUART_BAUD_SBR(8); // ~115200 baud | |||||
| LPUART3_CTRL = LPUART_CTRL_TE; | LPUART3_CTRL = LPUART_CTRL_TE; | ||||
| } | } | ||||
| #define NVIC_NUM_INTERRUPTS 160 | #define NVIC_NUM_INTERRUPTS 160 | ||||
| #define DMA_NUM_CHANNELS 32 | #define DMA_NUM_CHANNELS 32 | ||||
| #ifdef __cplusplus | |||||
| extern "C" void (* _VectorsRam[NVIC_NUM_INTERRUPTS+16])(void); | |||||
| static inline void attachInterruptVector(IRQ_NUMBER_t irq, void (*function)(void)) __attribute__((always_inline, unused)); | |||||
| static inline void attachInterruptVector(IRQ_NUMBER_t irq, void (*function)(void)) { _VectorsRam[irq + 16] = function; } | |||||
| #else | |||||
| extern void (* _VectorsRam[NVIC_NUM_INTERRUPTS+16])(void); | |||||
| static inline void attachInterruptVector(enum IRQ_NUMBER_t irq, void (*function)(void)) __attribute__((always_inline, unused)); | |||||
| static inline void attachInterruptVector(enum IRQ_NUMBER_t irq, void (*function)(void)) { _VectorsRam[irq + 16] = function; } | |||||
| #endif | |||||
| #define DMAMUX_SOURCE_FLEXIO1_REQUEST0 0 | #define DMAMUX_SOURCE_FLEXIO1_REQUEST0 0 | ||||
| #define DMAMUX_SOURCE_FLEXIO1_REQUEST1 0 | #define DMAMUX_SOURCE_FLEXIO1_REQUEST1 0 | ||||
| #define DMAMUX_SOURCE_FLEXIO2_REQUEST0 1 | #define DMAMUX_SOURCE_FLEXIO2_REQUEST0 1 |
| _edata = ADDR(.data) + SIZEOF(.data); | _edata = ADDR(.data) + SIZEOF(.data); | ||||
| _sdataload = LOADADDR(.data); | _sdataload = LOADADDR(.data); | ||||
| _estack = ORIGIN(DTCM) + LENGTH(DTCM) - 16; | |||||
| _estack = ORIGIN(DTCM) + LENGTH(DTCM); | |||||
| _sbss = ADDR(.bss); | _sbss = ADDR(.bss); | ||||
| _ebss = ADDR(.bss) + SIZEOF(.bss); | _ebss = ADDR(.bss) + SIZEOF(.bss); | ||||
| _flashimagelen = SIZEOF(.text.progmem) + SIZEOF(.text) + SIZEOF(.data); | _flashimagelen = SIZEOF(.text.progmem) + SIZEOF(.text) + SIZEOF(.data); | ||||
| _teensy_model_identifier = 0x23; | |||||
| .debug_info 0 : { *(.debug_info) } | .debug_info 0 : { *(.debug_info) } | ||||
| .debug_abbrev 0 : { *(.debug_abbrev) } | .debug_abbrev 0 : { *(.debug_abbrev) } |
| #include <errno.h> | |||||
| extern unsigned long _ebss; | |||||
| char *__brkval = (char *)&_ebss; // TODO: put heap into OCRAM, not DTCM | |||||
| #define STACK_MARGIN 8192 | |||||
| void * _sbrk(int incr) | |||||
| { | |||||
| char *prev, *stack; | |||||
| prev = __brkval; | |||||
| if (incr != 0) { | |||||
| __asm__ volatile("mov %0, sp" : "=r" (stack) ::); | |||||
| if (prev + incr >= stack - STACK_MARGIN) { | |||||
| errno = ENOMEM; | |||||
| return (void *)-1; | |||||
| } | |||||
| __brkval = prev + incr; | |||||
| } | |||||
| return prev; | |||||
| } | |||||
| #include "imxrt.h" | #include "imxrt.h" | ||||
| #include "wiring.h" | #include "wiring.h" | ||||
| #include "usb_dev.h" | |||||
| #include "debug/printf.h" | #include "debug/printf.h" | ||||
| // from the linker | // from the linker | ||||
| extern void systick_isr(void); | extern void systick_isr(void); | ||||
| void configure_cache(void); | void configure_cache(void); | ||||
| void unused_interrupt_vector(void); | void unused_interrupt_vector(void); | ||||
| void usb_pll_start(); | |||||
| __attribute__((section(".startup"))) | __attribute__((section(".startup"))) | ||||
| configure_cache(); | configure_cache(); | ||||
| configure_systick(); | configure_systick(); | ||||
| usb_pll_start(); | |||||
| #if 1 | #if 1 | ||||
| //uint32_t pll1; | //uint32_t pll1; | ||||
| //set_arm_clock(300000000); | //set_arm_clock(300000000); | ||||
| #endif | #endif | ||||
| // TODO: wait at least 20ms before starting USB | |||||
| usb_init(); | |||||
| // TODO: wait tat least 300ms before calling setup | |||||
| printf("before setup\n"); | printf("before setup\n"); | ||||
| setup(); | setup(); | ||||
| printf("after setup\n"); | printf("after setup\n"); | ||||
| { | { | ||||
| while (1) { | while (1) { | ||||
| uint32_t n = CCM_ANALOG_PLL_USB1; // pg 759 | uint32_t n = CCM_ANALOG_PLL_USB1; // pg 759 | ||||
| //printf("CCM_ANALOG_PLL_USB1=%08lX\r\n", n); | |||||
| printf("CCM_ANALOG_PLL_USB1=%08lX\n", n); | |||||
| if (n & CCM_ANALOG_PLL_USB1_DIV_SELECT) { | if (n & CCM_ANALOG_PLL_USB1_DIV_SELECT) { | ||||
| //print(" ERROR, 528 MHz mode!\r\n"); // never supposed to use this mode! | |||||
| printf(" ERROR, 528 MHz mode!\n"); // never supposed to use this mode! | |||||
| CCM_ANALOG_PLL_USB1_CLR = 0xC000; // bypass 24 MHz | CCM_ANALOG_PLL_USB1_CLR = 0xC000; // bypass 24 MHz | ||||
| CCM_ANALOG_PLL_USB1_SET = CCM_ANALOG_PLL_USB1_BYPASS; // bypass | CCM_ANALOG_PLL_USB1_SET = CCM_ANALOG_PLL_USB1_BYPASS; // bypass | ||||
| CCM_ANALOG_PLL_USB1_CLR = CCM_ANALOG_PLL_USB1_POWER | // power down | CCM_ANALOG_PLL_USB1_CLR = CCM_ANALOG_PLL_USB1_POWER | // power down | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (!(n & CCM_ANALOG_PLL_USB1_ENABLE)) { | if (!(n & CCM_ANALOG_PLL_USB1_ENABLE)) { | ||||
| //print(" enable PLL\r\n"); | |||||
| printf(" enable PLL\n"); | |||||
| // TODO: should this be done so early, or later?? | // TODO: should this be done so early, or later?? | ||||
| CCM_ANALOG_PLL_USB1_SET = CCM_ANALOG_PLL_USB1_ENABLE; | CCM_ANALOG_PLL_USB1_SET = CCM_ANALOG_PLL_USB1_ENABLE; | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (!(n & CCM_ANALOG_PLL_USB1_POWER)) { | if (!(n & CCM_ANALOG_PLL_USB1_POWER)) { | ||||
| //print(" power up PLL\r\n"); | |||||
| printf(" power up PLL\n"); | |||||
| CCM_ANALOG_PLL_USB1_SET = CCM_ANALOG_PLL_USB1_POWER; | CCM_ANALOG_PLL_USB1_SET = CCM_ANALOG_PLL_USB1_POWER; | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (!(n & CCM_ANALOG_PLL_USB1_LOCK)) { | if (!(n & CCM_ANALOG_PLL_USB1_LOCK)) { | ||||
| //print(" wait for lock\r\n"); | |||||
| printf(" wait for lock\n"); | |||||
| continue; | continue; | ||||
| } | } | ||||
| if (n & CCM_ANALOG_PLL_USB1_BYPASS) { | if (n & CCM_ANALOG_PLL_USB1_BYPASS) { | ||||
| //print(" turn off bypass\r\n"); | |||||
| printf(" turn off bypass\n"); | |||||
| CCM_ANALOG_PLL_USB1_CLR = CCM_ANALOG_PLL_USB1_BYPASS; | CCM_ANALOG_PLL_USB1_CLR = CCM_ANALOG_PLL_USB1_BYPASS; | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (!(n & CCM_ANALOG_PLL_USB1_EN_USB_CLKS)) { | if (!(n & CCM_ANALOG_PLL_USB1_EN_USB_CLKS)) { | ||||
| //print(" enable USB clocks\r\n"); | |||||
| printf(" enable USB clocks\n"); | |||||
| CCM_ANALOG_PLL_USB1_SET = CCM_ANALOG_PLL_USB1_EN_USB_CLKS; | CCM_ANALOG_PLL_USB1_SET = CCM_ANALOG_PLL_USB1_EN_USB_CLKS; | ||||
| continue; | continue; | ||||
| } | } |
| #include "usb_dev.h" | #include "usb_dev.h" | ||||
| #define USB_DESC_LIST_DEFINE | #define USB_DESC_LIST_DEFINE | ||||
| #include "usb_desc.h" | #include "usb_desc.h" | ||||
| #include "usb_serial.h" | |||||
| #include <string.h> | #include <string.h> | ||||
| // Use this for access to endpoint 1 for debug | |||||
| // https://github.com/BrandonLWhite/Stellaris-LaunchPad-UsbDevBulk-AutoWinusbInstall | |||||
| #include "debug/printf.h" | |||||
| // device mode, page 3155 | // device mode, page 3155 | ||||
| typedef struct endpoint_struct endpoint_t; | typedef struct endpoint_struct endpoint_t; | ||||
| //typedef struct transfer_struct transfer_t; | |||||
| struct endpoint_struct { | struct endpoint_struct { | ||||
| uint32_t config; | uint32_t config; | ||||
| //static int reset_count=0; | //static int reset_count=0; | ||||
| volatile uint8_t usb_configuration = 0; | volatile uint8_t usb_configuration = 0; | ||||
| static uint8_t endpoint0_buffer[8]; | static uint8_t endpoint0_buffer[8]; | ||||
| static uint8_t usb_reboot_timer = 0; | |||||
| static void isr(void); | static void isr(void); | ||||
| void usb_init(void) | void usb_init(void) | ||||
| { | { | ||||
| // TODO: make sure USB voltage regulator is running at full strength | |||||
| // TODO: only enable when VBUS detected | |||||
| // TODO: return to low power mode when VBUS removed | |||||
| // TODO: protect PMU access with MPU | |||||
| PMU_REG_3P0 = PMU_REG_3P0_OUTPUT_TRG(0x0F) | PMU_REG_3P0_BO_OFFSET(6) | |||||
| | PMU_REG_3P0_ENABLE_LINREG; | |||||
| // assume PLL3 is already running - already done by usb_pll_start() in main.c | // assume PLL3 is already running - already done by usb_pll_start() in main.c | ||||
| // Before programming this register, the PHY clocks must be enabled in registers | // Before programming this register, the PHY clocks must be enabled in registers | ||||
| // USBPHYx_CTRLn and CCM_ANALOG_USBPHYx_PLL_480_CTRLn. | // USBPHYx_CTRLn and CCM_ANALOG_USBPHYx_PLL_480_CTRLn. | ||||
| //printf("USBPHY1_PWD=%08lX\r\n", USBPHY1_PWD); | |||||
| //printf("USBPHY1_TX=%08lX\r\n", USBPHY1_TX); | |||||
| //printf("USBPHY1_RX=%08lX\r\n", USBPHY1_RX); | |||||
| //printf("USBPHY1_CTRL=%08lX\r\n", USBPHY1_CTRL); | |||||
| //printf("USB1_USBMODE=%08lX\r\n", USB1_USBMODE); | |||||
| //printf("USBPHY1_PWD=%08lX\n", USBPHY1_PWD); | |||||
| //printf("USBPHY1_TX=%08lX\n", USBPHY1_TX); | |||||
| //printf("USBPHY1_RX=%08lX\n", USBPHY1_RX); | |||||
| //printf("USBPHY1_CTRL=%08lX\n", USBPHY1_CTRL); | |||||
| //printf("USB1_USBMODE=%08lX\n", USB1_USBMODE); | |||||
| // turn on PLL3, wait for 480 MHz lock? | // turn on PLL3, wait for 480 MHz lock? | ||||
| // turn on CCM clock gates? CCGR6[CG0] | // turn on CCM clock gates? CCGR6[CG0] | ||||
| #if 1 | |||||
| if ((USBPHY1_PWD & (USBPHY_PWD_RXPWDRX | USBPHY_PWD_RXPWDDIFF | USBPHY_PWD_RXPWD1PT1 | if ((USBPHY1_PWD & (USBPHY_PWD_RXPWDRX | USBPHY_PWD_RXPWDDIFF | USBPHY_PWD_RXPWD1PT1 | ||||
| | USBPHY_PWD_RXPWDENV | USBPHY_PWD_TXPWDV2I | USBPHY_PWD_TXPWDIBIAS | | USBPHY_PWD_RXPWDENV | USBPHY_PWD_TXPWDV2I | USBPHY_PWD_TXPWDIBIAS | ||||
| | USBPHY_PWD_TXPWDFS)) || (USB1_USBMODE & USB_USBMODE_CM_MASK)) { | | USBPHY_PWD_TXPWDFS)) || (USB1_USBMODE & USB_USBMODE_CM_MASK)) { | ||||
| NVIC_CLEAR_PENDING(IRQ_USB1); | NVIC_CLEAR_PENDING(IRQ_USB1); | ||||
| USBPHY1_CTRL_CLR = USBPHY_CTRL_SFTRST; // reset PHY | USBPHY1_CTRL_CLR = USBPHY_CTRL_SFTRST; // reset PHY | ||||
| //USB1_USBSTS = USB1_USBSTS; // TODO: is this needed? | //USB1_USBSTS = USB1_USBSTS; // TODO: is this needed? | ||||
| //printf("USB reset took %d loops\r\n", count); | |||||
| printf("USB reset took %d loops\n", count); | |||||
| //delay(10); | //delay(10); | ||||
| //print("\r\n"); | |||||
| //printf("USBPHY1_PWD=%08lX\r\n", USBPHY1_PWD); | |||||
| //printf("USBPHY1_TX=%08lX\r\n", USBPHY1_TX); | |||||
| //printf("USBPHY1_RX=%08lX\r\n", USBPHY1_RX); | |||||
| //printf("USBPHY1_CTRL=%08lX\r\n", USBPHY1_CTRL); | |||||
| //printf("USB1_USBMODE=%08lX\r\n", USB1_USBMODE); | |||||
| //printf("\n"); | |||||
| //printf("USBPHY1_PWD=%08lX\n", USBPHY1_PWD); | |||||
| //printf("USBPHY1_TX=%08lX\n", USBPHY1_TX); | |||||
| //printf("USBPHY1_RX=%08lX\n", USBPHY1_RX); | |||||
| //printf("USBPHY1_CTRL=%08lX\n", USBPHY1_CTRL); | |||||
| //printf("USB1_USBMODE=%08lX\n", USB1_USBMODE); | |||||
| //delay(500); | //delay(500); | ||||
| } | } | ||||
| #endif | |||||
| // Device Controller Initialization, page 3161 | // Device Controller Initialization, page 3161 | ||||
| // USBCMD pg 3216 | // USBCMD pg 3216 | ||||
| // USBSTS pg 3220 | // USBSTS pg 3220 | ||||
| USBPHY1_CTRL_CLR = USBPHY_CTRL_CLKGATE; | USBPHY1_CTRL_CLR = USBPHY_CTRL_CLKGATE; | ||||
| USBPHY1_PWD = 0; | USBPHY1_PWD = 0; | ||||
| //printf("USBPHY1_PWD=%08lX\r\n", USBPHY1_PWD); | |||||
| //printf("USBPHY1_CTRL=%08lX\r\n", USBPHY1_CTRL); | |||||
| //printf("USBPHY1_PWD=%08lX\n", USBPHY1_PWD); | |||||
| //printf("USBPHY1_CTRL=%08lX\n", USBPHY1_CTRL); | |||||
| USB1_USBMODE = USB_USBMODE_CM(2) | USB_USBMODE_SLOM; | USB1_USBMODE = USB_USBMODE_CM(2) | USB_USBMODE_SLOM; | ||||
| memset(endpoint_queue_head, 0, sizeof(endpoint_queue_head)); | memset(endpoint_queue_head, 0, sizeof(endpoint_queue_head)); | ||||
| //_VectorsRam[IRQ_USB1+16] = &isr; | //_VectorsRam[IRQ_USB1+16] = &isr; | ||||
| attachInterruptVector(IRQ_USB1, &isr); | attachInterruptVector(IRQ_USB1, &isr); | ||||
| NVIC_ENABLE_IRQ(IRQ_USB1); | NVIC_ENABLE_IRQ(IRQ_USB1); | ||||
| //printf("USB1_ENDPTCTRL0=%08lX\r\n", USB1_ENDPTCTRL0); | |||||
| //printf("USB1_ENDPTCTRL1=%08lX\r\n", USB1_ENDPTCTRL1); | |||||
| //printf("USB1_ENDPTCTRL2=%08lX\r\n", USB1_ENDPTCTRL2); | |||||
| //printf("USB1_ENDPTCTRL3=%08lX\r\n", USB1_ENDPTCTRL3); | |||||
| //printf("USB1_ENDPTCTRL0=%08lX\n", USB1_ENDPTCTRL0); | |||||
| //printf("USB1_ENDPTCTRL1=%08lX\n", USB1_ENDPTCTRL1); | |||||
| //printf("USB1_ENDPTCTRL2=%08lX\n", USB1_ENDPTCTRL2); | |||||
| //printf("USB1_ENDPTCTRL3=%08lX\n", USB1_ENDPTCTRL3); | |||||
| USB1_USBCMD |= USB_USBCMD_RS; | USB1_USBCMD |= USB_USBCMD_RS; | ||||
| } | } | ||||
| static void isr(void) | static void isr(void) | ||||
| { | { | ||||
| //print("*"); | |||||
| //printf("*"); | |||||
| // Port control in device mode is only used for | // Port control in device mode is only used for | ||||
| // status port reset, suspend, and current connect status. | // status port reset, suspend, and current connect status. | ||||
| // USB_USBSTS_SRI - set when USB reset detected | // USB_USBSTS_SRI - set when USB reset detected | ||||
| if (status & USB_USBSTS_UI) { | if (status & USB_USBSTS_UI) { | ||||
| //print("data\r\n"); | |||||
| //printf("data\n"); | |||||
| uint32_t setupstatus = USB1_ENDPTSETUPSTAT; | uint32_t setupstatus = USB1_ENDPTSETUPSTAT; | ||||
| //printf("USB1_ENDPTSETUPSTAT=%X\r\n", setupstatus); | |||||
| //printf("USB1_ENDPTSETUPSTAT=%X\n", setupstatus); | |||||
| while (setupstatus) { | while (setupstatus) { | ||||
| USB1_ENDPTSETUPSTAT = setupstatus; | USB1_ENDPTSETUPSTAT = setupstatus; | ||||
| setup_t s; | setup_t s; | ||||
| s.word2 = endpoint_queue_head[0].setup1; | s.word2 = endpoint_queue_head[0].setup1; | ||||
| } while (!(USB1_USBCMD & USB_USBCMD_SUTW)); | } while (!(USB1_USBCMD & USB_USBCMD_SUTW)); | ||||
| USB1_USBCMD &= ~USB_USBCMD_SUTW; | USB1_USBCMD &= ~USB_USBCMD_SUTW; | ||||
| //printf("setup %08lX %08lX\r\n", s.word1, s.word2); | |||||
| //printf("setup %08lX %08lX\n", s.word1, s.word2); | |||||
| USB1_ENDPTFLUSH = (1<<16) | (1<<0); // page 3174 | USB1_ENDPTFLUSH = (1<<16) | (1<<0); // page 3174 | ||||
| while (USB1_ENDPTFLUSH & ((1<<16) | (1<<0))) ; | while (USB1_ENDPTFLUSH & ((1<<16) | (1<<0))) ; | ||||
| endpoint0_notify_mask = 0; | endpoint0_notify_mask = 0; | ||||
| uint32_t completestatus = USB1_ENDPTCOMPLETE; | uint32_t completestatus = USB1_ENDPTCOMPLETE; | ||||
| if (completestatus) { | if (completestatus) { | ||||
| USB1_ENDPTCOMPLETE = completestatus; | USB1_ENDPTCOMPLETE = completestatus; | ||||
| //printf("USB1_ENDPTCOMPLETE=%lX\r\n", completestatus); | |||||
| //printf("USB1_ENDPTCOMPLETE=%lX\n", completestatus); | |||||
| if (completestatus & endpoint0_notify_mask) { | if (completestatus & endpoint0_notify_mask) { | ||||
| endpoint0_notify_mask = 0; | endpoint0_notify_mask = 0; | ||||
| endpoint0_complete(); | endpoint0_complete(); | ||||
| while (USB1_ENDPTPRIME != 0) ; // Wait for any endpoint priming | while (USB1_ENDPTPRIME != 0) ; // Wait for any endpoint priming | ||||
| USB1_ENDPTFLUSH = 0xFFFFFFFF; // Cancel all endpoint primed status | USB1_ENDPTFLUSH = 0xFFFFFFFF; // Cancel all endpoint primed status | ||||
| if ((USB1_PORTSC1 & USB_PORTSC1_PR)) { | if ((USB1_PORTSC1 & USB_PORTSC1_PR)) { | ||||
| //print("reset\r\n"); | |||||
| //printf("reset\n"); | |||||
| } else { | } else { | ||||
| // we took too long to respond :( | // we took too long to respond :( | ||||
| // TODO; is this ever really a problem? | // TODO; is this ever really a problem? | ||||
| //print("reset too slow\r\n"); | |||||
| //printf("reset too slow\n"); | |||||
| } | } | ||||
| // TODO: Free all allocated dTDs | // TODO: Free all allocated dTDs | ||||
| //if (++reset_count >= 3) { | //if (++reset_count >= 3) { | ||||
| // shut off USB - easier to see results in protocol analyzer | // shut off USB - easier to see results in protocol analyzer | ||||
| //USB1_USBCMD &= ~USB_USBCMD_RS; | //USB1_USBCMD &= ~USB_USBCMD_RS; | ||||
| //print("shut off USB\r\n"); | |||||
| //printf("shut off USB\n"); | |||||
| //} | //} | ||||
| } | } | ||||
| if (status & USB_USBSTS_PCI) { | if (status & USB_USBSTS_PCI) { | ||||
| if (USB1_PORTSC1 & USB_PORTSC1_HSP) { | if (USB1_PORTSC1 & USB_PORTSC1_HSP) { | ||||
| //print("port at 480 Mbit\r\n"); | |||||
| //printf("port at 480 Mbit\n"); | |||||
| } else { | } else { | ||||
| //print("port at 12 Mbit\r\n"); | |||||
| //printf("port at 12 Mbit\n"); | |||||
| } | } | ||||
| } | } | ||||
| if (status & USB_USBSTS_SLI) { // page 3165 | if (status & USB_USBSTS_SLI) { // page 3165 | ||||
| //print("suspend\r\n"); | |||||
| //printf("suspend\n"); | |||||
| } | } | ||||
| if (status & USB_USBSTS_UEI) { | if (status & USB_USBSTS_UEI) { | ||||
| //print("error\r\n"); | |||||
| //printf("error\n"); | |||||
| } | |||||
| if ((USB1_USBINTR & USB_USBINTR_SRE) && (status & USB_USBSTS_SRI)) { | |||||
| printf("sof %d\n", usb_reboot_timer); | |||||
| if (usb_reboot_timer) { | |||||
| if (--usb_reboot_timer == 0) { | |||||
| asm("bkpt #251"); // run bootloader | |||||
| } | |||||
| } else { | |||||
| // turn off the SOF interrupt if nothing using it | |||||
| USB1_USBINTR &= ~USB_USBINTR_SRE; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| uint32_t m = n & ~(USB_ENDPTCTRL_TXR | USB_ENDPTCTRL_RXR); | uint32_t m = n & ~(USB_ENDPTCTRL_TXR | USB_ENDPTCTRL_RXR); | ||||
| *reg = m; | *reg = m; | ||||
| //uint32_t p = *reg; | //uint32_t p = *reg; | ||||
| //printf(" ep=%d: cfg=%08lX - %08lX - %08lX\r\n", i + 1, n, m, p); | |||||
| //printf(" ep=%d: cfg=%08lX - %08lX - %08lX\n", i + 1, n, m, p); | |||||
| reg++; | reg++; | ||||
| } | } | ||||
| // TODO: configure all queue heads with max packet length, zlt & mult | // TODO: configure all queue heads with max packet length, zlt & mult | ||||
| case 0x0680: // GET_DESCRIPTOR | case 0x0680: // GET_DESCRIPTOR | ||||
| case 0x0681: | case 0x0681: | ||||
| //printf("desc:\r\n"); // yay - sending device descriptor now works!!!! | |||||
| //printf("desc:\n"); // yay - sending device descriptor now works!!!! | |||||
| for (list = usb_descriptor_list; list->addr != NULL; list++) { | for (list = usb_descriptor_list; list->addr != NULL; list++) { | ||||
| if (setup.wValue == list->wValue && setup.wIndex == list->wIndex) { | if (setup.wValue == list->wValue && setup.wIndex == list->wIndex) { | ||||
| if ((setup.wValue >> 8) == 3) { | if ((setup.wValue >> 8) == 3) { | ||||
| static void endpoint0_transmit(const void *data, uint32_t len, int notify) | static void endpoint0_transmit(const void *data, uint32_t len, int notify) | ||||
| { | { | ||||
| //printf("tx %lu\r\n", len); | |||||
| //printf("tx %lu\n", len); | |||||
| if (len > 0) { | if (len > 0) { | ||||
| // Executing A Transfer Descriptor, page 3182 | // Executing A Transfer Descriptor, page 3182 | ||||
| endpoint0_transfer_data.next = 1; | endpoint0_transfer_data.next = 1; | ||||
| static void endpoint0_receive(void *data, uint32_t len, int notify) | static void endpoint0_receive(void *data, uint32_t len, int notify) | ||||
| { | { | ||||
| //printf("rx %lu\r\n", len); | |||||
| //printf("rx %lu\n", len); | |||||
| if (len > 0) { | if (len > 0) { | ||||
| // Executing A Transfer Descriptor, page 3182 | // Executing A Transfer Descriptor, page 3182 | ||||
| endpoint0_transfer_data.next = 1; | endpoint0_transfer_data.next = 1; | ||||
| setup_t setup; | setup_t setup; | ||||
| setup.bothwords = endpoint0_setupdata.bothwords; | setup.bothwords = endpoint0_setupdata.bothwords; | ||||
| //print("complete\r\n"); | |||||
| //printf("complete\n"); | |||||
| #ifdef CDC_STATUS_INTERFACE | #ifdef CDC_STATUS_INTERFACE | ||||
| if (setup.wRequestAndType == 0x2021 /*CDC_SET_LINE_CODING*/) { | if (setup.wRequestAndType == 0x2021 /*CDC_SET_LINE_CODING*/) { | ||||
| //memcpy(usb_cdc_line_coding, endpoint0_buffer, 7); | |||||
| //if (usb_cdc_line_coding[0] == 134) usb_reboot_timer = 15; | |||||
| memcpy(usb_cdc_line_coding, endpoint0_buffer, 7); | |||||
| printf("usb_cdc_line_coding, baud=%u\n", usb_cdc_line_coding[0]); | |||||
| if (usb_cdc_line_coding[0] == 134) { | |||||
| USB1_USBINTR |= USB_USBINTR_SRE; | |||||
| usb_reboot_timer = 80; // TODO: 10 if only 12 Mbit/sec | |||||
| } | |||||
| } | } | ||||
| #endif | #endif | ||||
| } | } | ||||
| { | { | ||||
| // endpoint 0 reserved for control | // endpoint 0 reserved for control | ||||
| // endpoint 1 reserved for debug | // endpoint 1 reserved for debug | ||||
| //printf("usb_transmit %d\r\n", endpoint_number); | |||||
| //printf("usb_transmit %d\n", endpoint_number); | |||||
| if (endpoint_number < 2 || endpoint_number > NUM_ENDPOINTS) return; | if (endpoint_number < 2 || endpoint_number > NUM_ENDPOINTS) return; | ||||
| endpoint_t *endpoint = &endpoint_queue_head[endpoint_number * 2 + 1]; | endpoint_t *endpoint = &endpoint_queue_head[endpoint_number * 2 + 1]; | ||||
| if (endpoint->callback_function) { | if (endpoint->callback_function) { | ||||
| transfer->status |= (1<<15); | transfer->status |= (1<<15); | ||||
| } else { | } else { | ||||
| transfer->status |= (1<<15); | |||||
| //transfer->status |= (1<<15); | |||||
| // remove all inactive transfers | // remove all inactive transfers | ||||
| } | } | ||||
| uint32_t mask = 1 << (endpoint_number + 16); | uint32_t mask = 1 << (endpoint_number + 16); | ||||
| if (USB1_ENDPTPRIME & mask) { | if (USB1_ENDPTPRIME & mask) { | ||||
| endpoint->last_transfer = transfer; | endpoint->last_transfer = transfer; | ||||
| __enable_irq(); | __enable_irq(); | ||||
| printf(" case 2a\r\n"); | |||||
| printf(" case 2a\n"); | |||||
| return; | return; | ||||
| } | } | ||||
| uint32_t stat; | uint32_t stat; | ||||
| if (stat & mask) { | if (stat & mask) { | ||||
| endpoint->last_transfer = transfer; | endpoint->last_transfer = transfer; | ||||
| __enable_irq(); | __enable_irq(); | ||||
| printf(" case 2b\r\n"); | |||||
| printf(" case 2b\n"); | |||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| USB1_ENDPTPRIME |= mask; | USB1_ENDPTPRIME |= mask; | ||||
| while (USB1_ENDPTPRIME & mask) ; | while (USB1_ENDPTPRIME & mask) ; | ||||
| __enable_irq(); | __enable_irq(); | ||||
| //printf(" case 1\r\n"); | |||||
| //printf(" case 1\n"); | |||||
| // ENDPTPRIME - momentarily set by hardware during hardware re-priming | // ENDPTPRIME - momentarily set by hardware during hardware re-priming |
| typedef struct transfer_struct transfer_t; | typedef struct transfer_struct transfer_t; | ||||
| struct transfer_struct { | struct transfer_struct { | ||||
| uint32_t next; | uint32_t next; | ||||
| uint32_t status; | |||||
| volatile uint32_t status; | |||||
| uint32_t pointer0; | uint32_t pointer0; | ||||
| uint32_t pointer1; | uint32_t pointer1; | ||||
| uint32_t pointer2; | uint32_t pointer2; |
| /* Teensyduino Core Library | |||||
| * http://www.pjrc.com/teensy/ | |||||
| * Copyright (c) 2017 PJRC.COM, LLC. | |||||
| * | |||||
| * 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: | |||||
| * | |||||
| * 1. The above copyright notice and this permission notice shall be | |||||
| * included in all copies or substantial portions of the Software. | |||||
| * | |||||
| * 2. If the Software is incorporated into a build system that allows | |||||
| * selection among a list of target devices, then similar target | |||||
| * devices manufactured by PJRC.COM must be included in the list of | |||||
| * target devices and selectable in the same manner. | |||||
| * | |||||
| * 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 <Arduino.h> | |||||
| #include "usb_desc.h" | |||||
| #if F_CPU >= 20000000 | |||||
| #ifdef CDC_DATA_INTERFACE | |||||
| #ifdef CDC_STATUS_INTERFACE | |||||
| usb_serial_class Serial; | |||||
| #endif | |||||
| #endif | |||||
| #ifdef MIDI_INTERFACE | |||||
| usb_midi_class usbMIDI; | |||||
| #endif | |||||
| #ifdef KEYBOARD_INTERFACE | |||||
| usb_keyboard_class Keyboard; | |||||
| #endif | |||||
| #ifdef MOUSE_INTERFACE | |||||
| usb_mouse_class Mouse; | |||||
| #endif | |||||
| #ifdef RAWHID_INTERFACE | |||||
| usb_rawhid_class RawHID; | |||||
| #endif | |||||
| #ifdef FLIGHTSIM_INTERFACE | |||||
| FlightSimClass FlightSim; | |||||
| #endif | |||||
| #ifdef SEREMU_INTERFACE | |||||
| usb_seremu_class Serial; | |||||
| #endif | |||||
| #ifdef JOYSTICK_INTERFACE | |||||
| usb_joystick_class Joystick; | |||||
| uint8_t usb_joystick_class::manual_mode = 0; | |||||
| #endif | |||||
| #ifdef USB_DISABLED | |||||
| usb_serial_class Serial; | |||||
| #endif | |||||
| #else // F_CPU < 20 MHz | |||||
| #if defined(USB_SERIAL) || defined(USB_SERIAL_HID) | |||||
| usb_serial_class Serial; | |||||
| #elif (USB_DISABLED) | |||||
| usb_serial_class Serial; | |||||
| #else | |||||
| usb_seremu_class Serial; | |||||
| #endif | |||||
| #endif // F_CPU | |||||
| void serialEvent() __attribute__((weak)); | |||||
| void serialEvent() {} |
| //#include "HardwareSerial.h" | //#include "HardwareSerial.h" | ||||
| #include <string.h> // for memcpy() | #include <string.h> // for memcpy() | ||||
| #include "debug/printf.h" | |||||
| #include "core_pins.h" | |||||
| // defined by usb_dev.h -> usb_desc.h | // defined by usb_dev.h -> usb_desc.h | ||||
| #if defined(CDC_STATUS_INTERFACE) && defined(CDC_DATA_INTERFACE) | #if defined(CDC_STATUS_INTERFACE) && defined(CDC_DATA_INTERFACE) | ||||
| //#if F_CPU >= 20000000 | //#if F_CPU >= 20000000 | ||||
| } | } | ||||
| static transfer_t transfer __attribute__ ((used, aligned(32))); | |||||
| static uint8_t txbuffer[256]; | |||||
| static transfer_t volatile transfer __attribute__ ((used, aligned(32))); | |||||
| static uint8_t txbuffer[1024]; | |||||
| //static uint8_t txbuffer1[1024]; | |||||
| //static uint8_t txbuffer2[1024]; | |||||
| //static uint8_t txstate=0; | |||||
| int usb_serial_write(const void *buffer, uint32_t size) | int usb_serial_write(const void *buffer, uint32_t size) | ||||
| { | { | ||||
| // TODO: do something so much better that this quick hack.... | |||||
| if (size > sizeof(txbuffer)) size = sizeof(txbuffer); | if (size > sizeof(txbuffer)) size = sizeof(txbuffer); | ||||
| int count=0; | |||||
| digitalWriteFast(13, HIGH); | |||||
| while (1) { | |||||
| uint32_t status = (volatile)(transfer.status); | |||||
| if (count > 10) printf("status = %x\n", status); | |||||
| if (!(status & 0x80)) break; | |||||
| count++; | |||||
| //if (count > 50) break; // TODO: proper timout? | |||||
| // TODO: check for USB offline | |||||
| delayMicroseconds(5); // polling too quickly seem to block DMA - maybe DTCM issue? | |||||
| } | |||||
| digitalWriteFast(13, LOW); | |||||
| delayMicroseconds(1); // TODO: this must not be the answer! | |||||
| memcpy(txbuffer, buffer, size); | memcpy(txbuffer, buffer, size); | ||||
| usb_prepare_transfer(&transfer, txbuffer, size, 0); | usb_prepare_transfer(&transfer, txbuffer, size, 0); | ||||
| usb_transmit(CDC_TX_ENDPOINT, &transfer); | usb_transmit(CDC_TX_ENDPOINT, &transfer); |