diff --git a/src/platforms/f4discovery/platform.h b/src/platforms/f4discovery/platform.h index e50b6c7..5523b9c 100644 --- a/src/platforms/f4discovery/platform.h +++ b/src/platforms/f4discovery/platform.h @@ -112,6 +112,7 @@ extern usbd_device *usbdev; */ #define IRQ_PRI_USB (2 << 4) #define IRQ_PRI_USBUSART (1 << 4) +#define IRQ_PRI_USBUSART_TIM (3 << 4) #define IRQ_PRI_TRACE (0 << 4) #define USBUSART USART3 @@ -124,6 +125,10 @@ extern usbd_device *usbdev; #define USBUSART_RX_PORT GPIOD #define USBUSART_RX_PIN GPIO9 #define USBUSART_ISR usart3_isr +#define USBUSART_TIM TIM4 +#define USBUSART_TIM_CLK_EN() rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM4EN) +#define USBUSART_TIM_IRQ NVIC_TIM4_IRQ +#define USBUSART_TIM_ISR tim4_isr #define UART_PIN_SETUP() do { \ gpio_mode_setup(USBUSART_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, \ diff --git a/src/platforms/native/platform.c b/src/platforms/native/platform.c index 640ba58..214625e 100644 --- a/src/platforms/native/platform.c +++ b/src/platforms/native/platform.c @@ -126,8 +126,6 @@ int platform_init(void) systick_interrupt_enable(); systick_counter_enable(); - usbuart_init(); - if (platform_hwversion() > 0) { adc_init(); } else { @@ -139,6 +137,7 @@ int platform_init(void) SCB_VTOR = 0x2000; // Relocate interrupt vector table here cdcacm_init(); + usbuart_init(); jtag_scan(NULL); diff --git a/src/platforms/native/platform.h b/src/platforms/native/platform.h index 20d1510..62d565b 100644 --- a/src/platforms/native/platform.h +++ b/src/platforms/native/platform.h @@ -128,6 +128,7 @@ extern usbd_device *usbdev; */ #define IRQ_PRI_USB (2 << 4) #define IRQ_PRI_USBUSART (1 << 4) +#define IRQ_PRI_USBUSART_TIM (3 << 4) #define IRQ_PRI_USB_VBUS (14 << 4) #define IRQ_PRI_TRACE (0 << 4) @@ -139,6 +140,10 @@ extern usbd_device *usbdev; #define USBUSART_PORT GPIOA #define USBUSART_TX_PIN GPIO9 #define USBUSART_ISR usart1_isr +#define USBUSART_TIM TIM4 +#define USBUSART_TIM_CLK_EN() rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM4EN) +#define USBUSART_TIM_IRQ NVIC_TIM4_IRQ +#define USBUSART_TIM_ISR tim4_isr #define TRACE_TIM TIM3 #define TRACE_TIM_CLK_EN() rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM3EN) diff --git a/src/platforms/stlink/platform.h b/src/platforms/stlink/platform.h index c79ea13..a9e405b 100644 --- a/src/platforms/stlink/platform.h +++ b/src/platforms/stlink/platform.h @@ -114,6 +114,7 @@ extern usbd_device *usbdev; */ #define IRQ_PRI_USB (2 << 4) #define IRQ_PRI_USBUSART (1 << 4) +#define IRQ_PRI_USBUSART_TIM (3 << 4) #define IRQ_PRI_USB_VBUS (14 << 4) #define IRQ_PRI_TIM3 (0 << 4) @@ -125,6 +126,10 @@ extern usbd_device *usbdev; #define USBUSART_PORT GPIOA #define USBUSART_TX_PIN GPIO2 #define USBUSART_ISR usart2_isr +#define USBUSART_TIM TIM4 +#define USBUSART_TIM_CLK_EN() rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM4EN) +#define USBUSART_TIM_IRQ NVIC_TIM4_IRQ +#define USBUSART_TIM_ISR tim4_isr #define DEBUG(...) diff --git a/src/platforms/stm32/usbuart.c b/src/platforms/stm32/usbuart.c index 1c71105..7f5ebde 100644 --- a/src/platforms/stm32/usbuart.c +++ b/src/platforms/stm32/usbuart.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,20 @@ #include +#define USBUART_TIMER_FREQ_HZ 1000000U /* 1us per tick */ +#define USBUART_RUN_FREQ_HZ 5000U /* 200us (or 100 characters at 2Mbps) */ + +#define FIFO_SIZE 128 + +/* RX Fifo buffer */ +static uint8_t buf_rx[FIFO_SIZE]; +/* Fifo in pointer, writes assumed to be atomic, should be only incremented within RX ISR */ +static uint8_t buf_rx_in; +/* Fifo out pointer, writes assumed to be atomic, should be only incremented outside RX ISR */ +static uint8_t buf_rx_out; + +static void usbuart_run(void); + void usbuart_init(void) { #if defined(BLACKMAGIC) @@ -56,6 +71,68 @@ void usbuart_init(void) USBUSART_CR1 |= USART_CR1_RXNEIE; nvic_set_priority(USBUSART_IRQ, IRQ_PRI_USBUSART); nvic_enable_irq(USBUSART_IRQ); + + /* Setup timer for running deferred FIFO processing */ + USBUSART_TIM_CLK_EN(); + timer_reset(USBUSART_TIM); + timer_set_mode(USBUSART_TIM, TIM_CR1_CKD_CK_INT, + TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); + timer_set_prescaler(USBUSART_TIM, + rcc_ppre2_frequency / USBUART_TIMER_FREQ_HZ * 2 - 1); + timer_set_period(USBUSART_TIM, + USBUART_TIMER_FREQ_HZ / USBUART_RUN_FREQ_HZ - 1); + + /* Setup update interrupt in NVIC */ + nvic_set_priority(USBUSART_TIM_IRQ, IRQ_PRI_USBUSART_TIM); + nvic_enable_irq(USBUSART_TIM_IRQ); + + /* turn the timer on */ + timer_enable_counter(USBUSART_TIM); +} + +/* + * Runs deferred processing for usb uart rx, draining RX FIFO by sending + * characters to host PC via CDCACM. Allowed to read from FIFO in pointer, + * but not write to it. Allowed to write to FIFO out pointer. + */ +static void usbuart_run(void) +{ + /* forcibly empty fifo if no USB endpoint */ + if (cdcacm_get_config() != 1) + { + buf_rx_out = buf_rx_in; + } + + /* if fifo empty, nothing further to do */ + if (buf_rx_in == buf_rx_out) { + /* turn off LED, disable IRQ */ + timer_disable_irq(USBUSART_TIM, TIM_DIER_UIE); + gpio_clear(LED_PORT_UART, LED_UART); + } + else + { + uint8_t packet_buf[CDCACM_PACKET_SIZE]; + uint8_t packet_size = 0; + uint8_t buf_out = buf_rx_out; + + /* copy from uart FIFO into local usb packet buffer */ + while (buf_rx_in != buf_out && packet_size < CDCACM_PACKET_SIZE) + { + packet_buf[packet_size++] = buf_rx[buf_out++]; + + /* wrap out pointer */ + if (buf_out >= FIFO_SIZE) + { + buf_out = 0; + } + + } + + /* advance fifo out pointer by amount written */ + buf_rx_out += usbd_ep_write_packet(usbdev, + CDCACM_UART_ENDPOINT, packet_buf, packet_size); + buf_rx_out %= FIFO_SIZE; + } } void usbuart_set_line_coding(struct usb_cdc_line_coding *coding) @@ -108,41 +185,50 @@ void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep) gpio_clear(LED_PORT_UART, LED_UART); } -static uint8_t uart_usb_buf[CDCACM_PACKET_SIZE]; -static uint8_t uart_usb_buf_size; void usbuart_usb_in_cb(usbd_device *dev, uint8_t ep) { - if (!uart_usb_buf_size) { - gpio_clear(LED_PORT_UART, LED_UART); - return; - } - - usbd_ep_write_packet(dev, ep, uart_usb_buf, uart_usb_buf_size); - uart_usb_buf_size = 0; + (void) dev; + (void) ep; } +/* + * Read a character from the UART RX and stuff it in a software FIFO. + * Allowed to read from FIFO out pointer, but not write to it. + * Allowed to write to FIFO in pointer. + */ void USBUSART_ISR(void) { char c = usart_recv(USBUSART); - /* Don't try to write until we are configured. - * Otherwise enumeration hanged in some cases. - */ - if (cdcacm_get_config() != 1) - return; - + /* Turn on LED */ gpio_set(LED_PORT_UART, LED_UART); - /* Try to send now */ - if (usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, &c, 1) == 1) - return; + /* If the next increment of rx_in would put it at the same point + * as rx_out, the FIFO is considered full. + */ + if (((buf_rx_in + 1) % FIFO_SIZE) != buf_rx_out) + { + /* insert into FIFO */ + buf_rx[buf_rx_in++] = c; - /* We failed, so queue for later */ - if (uart_usb_buf_size == CDCACM_PACKET_SIZE) { - /* Drop if the buffer's full: we have no flow control */ - return; + /* wrap out pointer */ + if (buf_rx_in >= FIFO_SIZE) + { + buf_rx_in = 0; + } + + /* enable deferred processing if we put data in the FIFO */ + timer_enable_irq(USBUSART_TIM, TIM_DIER_UIE); } - - uart_usb_buf[uart_usb_buf_size++] = c; } + +void USBUSART_TIM_ISR(void) +{ + /* need to clear timer update event */ + timer_clear_flag(USBUSART_TIM, TIM_SR_UIF); + + /* process FIFO */ + usbuart_run(); +} + diff --git a/src/platforms/swlink/platform.h b/src/platforms/swlink/platform.h index 614a389..d0c9618 100644 --- a/src/platforms/swlink/platform.h +++ b/src/platforms/swlink/platform.h @@ -106,6 +106,7 @@ extern usbd_device *usbdev; */ #define IRQ_PRI_USB (2 << 4) #define IRQ_PRI_USBUSART (1 << 4) +#define IRQ_PRI_USBUSART_TIM (3 << 4) #define IRQ_PRI_USB_VBUS (14 << 4) #define IRQ_PRI_TRACE (0 << 4) @@ -117,6 +118,10 @@ extern usbd_device *usbdev; #define USBUSART_PORT GPIOB #define USBUSART_TX_PIN GPIO6 #define USBUSART_ISR usart1_isr +#define USBUSART_TIM TIM4 +#define USBUSART_TIM_CLK_EN() rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM4EN) +#define USBUSART_TIM_IRQ NVIC_TIM4_IRQ +#define USBUSART_TIM_ISR tim4_isr #define TRACE_TIM TIM2 #define TRACE_TIM_CLK_EN() rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM2EN)