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.
This commit is contained in:
Allen Ibara 2013-03-27 12:09:49 -07:00 committed by Gareth McMullin
parent f1ea5ed8f9
commit 43f7e7a3cf
6 changed files with 123 additions and 26 deletions

View File

@ -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, \

View File

@ -108,8 +108,6 @@ int platform_init(void)
systick_interrupt_enable();
systick_counter_enable();
usbuart_init();
if (platform_hwversion() > 0) {
adc_init();
} else {
@ -121,6 +119,7 @@ int platform_init(void)
SCB_VTOR = 0x2000; // Relocate interrupt vector table here
cdcacm_init();
usbuart_init();
jtag_scan(NULL);

View File

@ -122,6 +122,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)
@ -133,6 +134,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)

View File

@ -107,6 +107,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)
@ -118,6 +119,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(...)

View File

@ -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;
/* 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;
}
uart_usb_buf[uart_usb_buf_size++] = c;
/* 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);
/* process FIFO */
usbuart_run();
}

View File

@ -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)