aboutsummaryrefslogtreecommitdiff
path: root/src/platforms/stm32/usbuart.c
diff options
context:
space:
mode:
authorAllen Ibara2013-03-27 12:09:49 -0700
committerGareth McMullin2013-04-27 12:56:58 -0700
commit43f7e7a3cf384e880bfefa6257f15c8915b02e3b (patch)
tree9622a7c2b363279e61cff4f0f2996093cb3cf018 /src/platforms/stm32/usbuart.c
parentf1ea5ed8f9f6dc1f006185d051da682bed63c6ac (diff)
Changes to the USBUART to make it less likely to drop characters at higher baud rates.
USB UART seems to work fine at 115.2Kbps or 230.4Kbps, but starts to drop characters as the data rate goes higher. This commit changes the usbuart ISR to fill a software FIFO, and adds a low priority timer interrupt to run deferred processing to drain a FIFO and send USB CDCACM packets, rather than calling the usb send within the UART ISR. Tested on native platform, up to 1.5MBps.
Diffstat (limited to 'src/platforms/stm32/usbuart.c')
-rw-r--r--src/platforms/stm32/usbuart.c126
1 files changed, 102 insertions, 24 deletions
diff --git a/src/platforms/stm32/usbuart.c b/src/platforms/stm32/usbuart.c
index 1c71105..6694d90 100644
--- a/src/platforms/stm32/usbuart.c
+++ b/src/platforms/stm32/usbuart.c
@@ -21,6 +21,7 @@
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
+#include <libopencm3/stm32/timer.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/scs.h>
#include <libopencm3/usb/usbd.h>
@@ -28,6 +29,20 @@
#include <platform.h>
+#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,62 @@ 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/TIMER_FREQ_HZ*2 - 1);
+ timer_set_period(USBUSART_TIM, 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 = (buf_rx_out + usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, packet_buf, packet_size)) % FIFO_SIZE;
+ }
}
void usbuart_set_line_coding(struct usb_cdc_line_coding *coding)
@@ -108,41 +179,48 @@ 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;
-
- /* 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;
+ /* 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;
+
+ /* 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);
}
+}
+
+void USBUSART_TIM_ISR(void)
+{
+ /* need to clear timer update event */
+ timer_clear_flag(USBUSART_TIM, TIM_SR_UIF);
- uart_usb_buf[uart_usb_buf_size++] = c;
+ /* process FIFO */
+ usbuart_run();
}