From 806787529f12611f2501536eae61b627a2571a2b Mon Sep 17 00:00:00 2001 From: Vestrel <16190165+Vestrel@users.noreply.github.com> Date: Sun, 18 Apr 2021 00:35:55 +0900 Subject: [PATCH] Use DMA for USBUSART TX/RX --- src/platforms/f4discovery/platform.h | 57 ++-- src/platforms/hydrabus/platform.h | 58 ++-- src/platforms/native/platform.h | 32 +- src/platforms/stlink/platform.h | 41 +-- src/platforms/stm32/usbuart.c | 435 +++++++++++++++++++-------- src/platforms/swlink/platform.h | 35 ++- 6 files changed, 438 insertions(+), 220 deletions(-) diff --git a/src/platforms/f4discovery/platform.h b/src/platforms/f4discovery/platform.h index 8f05909..a1a06f8 100644 --- a/src/platforms/f4discovery/platform.h +++ b/src/platforms/f4discovery/platform.h @@ -91,47 +91,54 @@ #define SWDIO_MODE_DRIVE() \ gpio_mode_setup(SWDIO_PORT, GPIO_MODE_OUTPUT, \ GPIO_PUPD_NONE, SWDIO_PIN); - +#define UART_PIN_SETUP() do { \ + gpio_mode_setup(USBUSART_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, \ + USBUSART_TX_PIN); \ + gpio_set_output_options(USBUSART_PORT, GPIO_OTYPE_PP, \ + GPIO_OSPEED_100MHZ, USBUSART_TX_PIN); \ + gpio_set_af(USBUSART_PORT, GPIO_AF7, USBUSART_TX_PIN); \ + gpio_mode_setup(USBUSART_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, \ + USBUSART_RX_PIN); \ + gpio_set_output_options(USBUSART_PORT, GPIO_OTYPE_OD, \ + GPIO_OSPEED_100MHZ, USBUSART_RX_PIN); \ + gpio_set_af(USBUSART_PORT, GPIO_AF7, USBUSART_RX_PIN); \ +} while(0) #define USB_DRIVER stm32f107_usb_driver #define USB_IRQ NVIC_OTG_FS_IRQ -#define USB_ISR otg_fs_isr +#define USB_ISR(x) otg_fs_isr(x) /* Interrupt priorities. Low numbers are high priority. - * For now USART1 preempts USB which may spin while buffer is drained. * TIM3 is used for traceswo capture and must be highest priority. */ -#define IRQ_PRI_USB (2 << 4) -#define IRQ_PRI_USBUSART (1 << 4) -#define IRQ_PRI_USBUSART_TIM (3 << 4) +#define IRQ_PRI_USB (1 << 4) +#define IRQ_PRI_USBUSART (2 << 4) +#define IRQ_PRI_USBUSART_DMA (2 << 4) #define IRQ_PRI_TRACE (0 << 4) #define USBUSART USART3 #define USBUSART_CR1 USART3_CR1 +#define USBUSART_DR USART3_DR #define USBUSART_IRQ NVIC_USART3_IRQ #define USBUSART_CLK RCC_USART3 -#define USBUSART_TX_PORT GPIOD -#define USBUSART_TX_PIN GPIO8 -#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_periph_clock_enable(RCC_TIM4) -#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, \ - USBUSART_TX_PIN); \ - gpio_mode_setup(USBUSART_RX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, \ - USBUSART_RX_PIN); \ - gpio_set_af(USBUSART_TX_PORT, GPIO_AF7, USBUSART_TX_PIN); \ - gpio_set_af(USBUSART_RX_PORT, GPIO_AF7, USBUSART_RX_PIN); \ - } while(0) +#define USBUSART_PORT GPIOD +#define USBUSART_TX_PIN GPIO8 +#define USBUSART_RX_PIN GPIO9 +#define USBUSART_ISR(x) usart3_isr(x) +#define USBUSART_DMA_BUS DMA1 +#define USBUSART_DMA_CLK RCC_DMA1 +#define USBUSART_DMA_TX_CHAN DMA_STREAM3 +#define USBUSART_DMA_TX_IRQ NVIC_DMA1_STREAM3_IRQ +#define USBUSART_DMA_TX_ISR(x) dma1_stream3_isr(x) +#define USBUSART_DMA_RX_CHAN DMA_STREAM1 +#define USBUSART_DMA_RX_IRQ NVIC_DMA1_STREAM1_IRQ +#define USBUSART_DMA_RX_ISR(x) dma1_stream1_isr(x) +/* For STM32F4 DMA trigger source must be specified */ +#define USBUSART_DMA_TRG DMA_SxCR_CHSEL_4 #define TRACE_TIM TIM3 #define TRACE_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM3) #define TRACE_IRQ NVIC_TIM3_IRQ -#define TRACE_ISR tim3_isr +#define TRACE_ISR(x) tim3_isr(x) #define gpio_set_val(port, pin, val) do { \ if(val) \ diff --git a/src/platforms/hydrabus/platform.h b/src/platforms/hydrabus/platform.h index c2c8533..93ccc75 100644 --- a/src/platforms/hydrabus/platform.h +++ b/src/platforms/hydrabus/platform.h @@ -90,47 +90,54 @@ #define SWDIO_MODE_DRIVE() \ gpio_mode_setup(SWDIO_PORT, GPIO_MODE_OUTPUT, \ GPIO_PUPD_NONE, SWDIO_PIN); - +#define UART_PIN_SETUP() do { \ + gpio_mode_setup(USBUSART_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, \ + USBUSART_TX_PIN); \ + gpio_set_output_options(USBUSART_PORT, GPIO_OTYPE_PP, \ + GPIO_OSPEED_100MHZ, USBUSART_TX_PIN); \ + gpio_set_af(USBUSART_PORT, GPIO_AF7, USBUSART_TX_PIN); \ + gpio_mode_setup(USBUSART_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, \ + USBUSART_RX_PIN); \ + gpio_set_output_options(USBUSART_PORT, GPIO_OTYPE_OD, \ + GPIO_OSPEED_100MHZ, USBUSART_RX_PIN); \ + gpio_set_af(USBUSART_PORT, GPIO_AF7, USBUSART_RX_PIN); \ +} while(0) #define USB_DRIVER stm32f107_usb_driver #define USB_IRQ NVIC_OTG_FS_IRQ -#define USB_ISR otg_fs_isr +#define USB_ISR(x) otg_fs_isr(x) /* Interrupt priorities. Low numbers are high priority. - * For now USART1 preempts USB which may spin while buffer is drained. * TIM3 is used for traceswo capture and must be highest priority. */ -#define IRQ_PRI_USB (2 << 4) -#define IRQ_PRI_USBUSART (1 << 4) -#define IRQ_PRI_USBUSART_TIM (3 << 4) +#define IRQ_PRI_USB (1 << 4) +#define IRQ_PRI_USBUSART (2 << 4) +#define IRQ_PRI_USBUSART_DMA (2 << 4) #define IRQ_PRI_TRACE (0 << 4) #define USBUSART USART1 #define USBUSART_CR1 USART1_CR1 +#define USBUSART_DR USART1_DR #define USBUSART_IRQ NVIC_USART1_IRQ #define USBUSART_CLK RCC_USART1 -#define USBUSART_TX_PORT GPIOA -#define USBUSART_TX_PIN GPIO9 -#define USBUSART_RX_PORT GPIOA -#define USBUSART_RX_PIN GPIO10 -#define USBUSART_ISR usart1_isr -#define USBUSART_TIM TIM4 -#define USBUSART_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM4) -#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, \ - USBUSART_TX_PIN); \ - gpio_mode_setup(USBUSART_RX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, \ - USBUSART_RX_PIN); \ - gpio_set_af(USBUSART_TX_PORT, GPIO_AF7, USBUSART_TX_PIN); \ - gpio_set_af(USBUSART_RX_PORT, GPIO_AF7, USBUSART_RX_PIN); \ - } while(0) +#define USBUSART_PORT GPIOA +#define USBUSART_TX_PIN GPIO9 +#define USBUSART_RX_PIN GPIO10 +#define USBUSART_ISR(x) usart1_isr(x) +#define USBUSART_DMA_BUS DMA2 +#define USBUSART_DMA_CLK RCC_DMA2 +#define USBUSART_DMA_TX_CHAN DMA_STREAM7 +#define USBUSART_DMA_TX_IRQ NVIC_DMA2_STREAM7_IRQ +#define USBUSART_DMA_TX_ISR(x) dma2_stream7_isr(x) +#define USBUSART_DMA_RX_CHAN DMA_STREAM5 +#define USBUSART_DMA_RX_IRQ NVIC_DMA2_STREAM5_IRQ +#define USBUSART_DMA_RX_ISR(x) dma2_stream5_isr(x) +/* For STM32F4 DMA trigger source must be specified */ +#define USBUSART_DMA_TRG DMA_SxCR_CHSEL_4 #define TRACE_TIM TIM3 #define TRACE_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM3) #define TRACE_IRQ NVIC_TIM3_IRQ -#define TRACE_ISR tim3_isr +#define TRACE_ISR(x) tim3_isr(x) #define gpio_set_val(port, pin, val) do { \ if(val) \ @@ -182,4 +189,3 @@ static inline int platform_hwversion(void) #endif #endif - diff --git a/src/platforms/native/platform.h b/src/platforms/native/platform.h index 0ef4178..87ce5ea 100644 --- a/src/platforms/native/platform.h +++ b/src/platforms/native/platform.h @@ -132,39 +132,47 @@ int usbuart_debug_write(const char *buf, size_t len); SWD_CR = cr; \ } while(0) #define UART_PIN_SETUP() do { \ - gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_2_MHZ, \ + gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_50_MHZ, \ GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USBUSART_TX_PIN); \ + gpio_set_mode(USBUSART_PORT, GPIO_MODE_INPUT, \ + GPIO_CNF_INPUT_PULL_UPDOWN, USBUSART_RX_PIN); \ + gpio_set(USBUSART_PORT, USBUSART_RX_PIN); \ } while(0) #define USB_DRIVER st_usbfs_v1_usb_driver #define USB_IRQ NVIC_USB_LP_CAN_RX0_IRQ -#define USB_ISR usb_lp_can_rx0_isr +#define USB_ISR(x) usb_lp_can_rx0_isr(x) /* Interrupt priorities. Low numbers are high priority. - * For now USART1 preempts USB which may spin while buffer is drained. * TIM3 is used for traceswo capture and must be highest priority. */ -#define IRQ_PRI_USB (2 << 4) -#define IRQ_PRI_USBUSART (1 << 4) -#define IRQ_PRI_USBUSART_TIM (3 << 4) +#define IRQ_PRI_USB (1 << 4) +#define IRQ_PRI_USBUSART (2 << 4) +#define IRQ_PRI_USBUSART_DMA (2 << 4) #define IRQ_PRI_USB_VBUS (14 << 4) #define IRQ_PRI_TRACE (0 << 4) #define USBUSART USART1 #define USBUSART_CR1 USART1_CR1 +#define USBUSART_DR USART1_DR #define USBUSART_IRQ NVIC_USART1_IRQ #define USBUSART_CLK RCC_USART1 #define USBUSART_PORT GPIOA #define USBUSART_TX_PIN GPIO9 -#define USBUSART_ISR usart1_isr -#define USBUSART_TIM TIM4 -#define USBUSART_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM4) -#define USBUSART_TIM_IRQ NVIC_TIM4_IRQ -#define USBUSART_TIM_ISR tim4_isr +#define USBUSART_RX_PIN GPIO10 +#define USBUSART_ISR(x) usart1_isr(x) +#define USBUSART_DMA_BUS DMA1 +#define USBUSART_DMA_CLK RCC_DMA1 +#define USBUSART_DMA_TX_CHAN DMA_CHANNEL4 +#define USBUSART_DMA_TX_IRQ NVIC_DMA1_CHANNEL4_IRQ +#define USBUSART_DMA_TX_ISR(x) dma1_channel4_isr(x) +#define USBUSART_DMA_RX_CHAN DMA_CHANNEL5 +#define USBUSART_DMA_RX_IRQ NVIC_DMA1_CHANNEL5_IRQ +#define USBUSART_DMA_RX_ISR(x) dma1_channel5_isr(x) #define TRACE_TIM TIM3 #define TRACE_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM3) #define TRACE_IRQ NVIC_TIM3_IRQ -#define TRACE_ISR tim3_isr +#define TRACE_ISR(x) tim3_isr(x) #define SET_RUN_STATE(state) {running_status = (state);} #define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, LED_IDLE_RUN, state);} diff --git a/src/platforms/stlink/platform.h b/src/platforms/stlink/platform.h index 12c498d..f848ae7 100644 --- a/src/platforms/stlink/platform.h +++ b/src/platforms/stlink/platform.h @@ -87,33 +87,41 @@ int usbuart_debug_write(const char *buf, size_t len); cr |= (0x1 * SWD_CR_MULT); \ SWD_CR = cr; \ } while(0) -#define UART_PIN_SETUP() \ - gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_2_MHZ, \ - GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USBUSART_TX_PIN); +#define UART_PIN_SETUP() do { \ + gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_50_MHZ, \ + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USBUSART_TX_PIN); \ + gpio_set_mode(USBUSART_PORT, GPIO_MODE_INPUT, \ + GPIO_CNF_INPUT_PULL_UPDOWN, USBUSART_RX_PIN); \ + gpio_set(USBUSART_PORT, USBUSART_RX_PIN); \ +} while(0) #define USB_DRIVER st_usbfs_v1_usb_driver #define USB_IRQ NVIC_USB_LP_CAN_RX0_IRQ -#define USB_ISR usb_lp_can_rx0_isr -/* Interrupt priorities. Low numbers are high priority. - * For now USART2 preempts USB which may spin while buffer is drained. - */ -#define IRQ_PRI_USB (2 << 4) -#define IRQ_PRI_USBUSART (1 << 4) -#define IRQ_PRI_USBUSART_TIM (3 << 4) +#define USB_ISR(x) usb_lp_can_rx0_isr(x) +/* Interrupt priorities. Low numbers are high priority. */ +#define IRQ_PRI_USB (1 << 4) +#define IRQ_PRI_USBUSART (2 << 4) +#define IRQ_PRI_USBUSART_DMA (2 << 4) #define IRQ_PRI_USB_VBUS (14 << 4) -#define IRQ_PRI_SWO_DMA (1 << 4) +#define IRQ_PRI_SWO_DMA (0 << 4) #define USBUSART USART2 #define USBUSART_CR1 USART2_CR1 +#define USBUSART_DR USART2_DR #define USBUSART_IRQ NVIC_USART2_IRQ #define USBUSART_CLK RCC_USART2 #define USBUSART_PORT GPIOA #define USBUSART_TX_PIN GPIO2 -#define USBUSART_ISR usart2_isr -#define USBUSART_TIM TIM4 -#define USBUSART_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM4) -#define USBUSART_TIM_IRQ NVIC_TIM4_IRQ -#define USBUSART_TIM_ISR tim4_isr +#define USBUSART_RX_PIN GPIO3 +#define USBUSART_ISR(x) usart2_isr(x) +#define USBUSART_DMA_BUS DMA1 +#define USBUSART_DMA_CLK RCC_DMA1 +#define USBUSART_DMA_TX_CHAN DMA_CHANNEL7 +#define USBUSART_DMA_TX_IRQ NVIC_DMA1_CHANNEL7_IRQ +#define USBUSART_DMA_TX_ISR(x) dma1_channel7_isr(x) +#define USBUSART_DMA_RX_CHAN DMA_CHANNEL6 +#define USBUSART_DMA_RX_IRQ NVIC_DMA1_CHANNEL6_IRQ +#define USBUSART_DMA_RX_ISR(x) dma1_channel6_isr(x) /* On F103, only USART1 is on AHB2 and can reach 4.5 MBaud at 72 MHz.*/ #define SWO_UART USART1 @@ -172,4 +180,3 @@ extern uint32_t detect_rev(void); #endif - diff --git a/src/platforms/stm32/usbuart.c b/src/platforms/stm32/usbuart.c index d2dc215..05b57df 100644 --- a/src/platforms/stm32/usbuart.c +++ b/src/platforms/stm32/usbuart.c @@ -21,7 +21,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -30,102 +31,143 @@ #include "general.h" #include "cdcacm.h" -#define USBUART_TIMER_FREQ_HZ 1000000U /* 1us per tick */ -#define USBUART_RUN_FREQ_HZ 5000U /* 200us (or 100 characters at 2Mbps) */ +#ifdef STM32F4 +#define dma_channel_reset(dma, channel) dma_stream_reset(dma, channel) +#define dma_enable_channel(dma, channel) dma_enable_stream(dma, channel) +#define dma_disable_channel(dma, channel) dma_disable_stream(dma, channel) -#define FIFO_SIZE 128 +#define DMA_PSIZE_8BIT DMA_SxCR_PSIZE_8BIT +#define DMA_MSIZE_8BIT DMA_SxCR_MSIZE_8BIT +#define DMA_PL_HIGH DMA_SxCR_PL_HIGH +#define DMA_CGIF DMA_ISR_FLAGS +#else +#define DMA_PSIZE_8BIT DMA_CCR_PSIZE_8BIT +#define DMA_MSIZE_8BIT DMA_CCR_MSIZE_8BIT +#define DMA_PL_HIGH DMA_CCR_PL_HIGH +#define DMA_CGIF DMA_IFCR_CGIF_BIT +#endif -/* 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 */ +#define TX_LED_ACT (1 << 0) +#define RX_LED_ACT (1 << 1) + +#define RX_FIFO_SIZE (128) +#define TX_BUF_SIZE (128) + +/* TX double buffer */ +static uint8_t buf_tx[TX_BUF_SIZE * 2]; +/* Active buffer part idx */ +static uint8_t buf_tx_act_idx; +/* Active buffer part used capacity */ +static uint8_t buf_tx_act_sz; +/* TX transfer complete */ +static bool tx_trfr_cplt = true; +/* RX Fifo buffer with space for copy fn overrun */ +static uint8_t buf_rx[RX_FIFO_SIZE + sizeof(uint64_t)]; +/* RX Fifo out pointer, writes assumed to be atomic */ static uint8_t buf_rx_out; +/* RX usb transfer complete */ +static bool rx_usb_trfr_cplt = true; + +#ifdef USBUART_DEBUG +/* Debug Fifo buffer with space for copy fn overrun */ +static uint8_t usb_dbg_buf[RX_FIFO_SIZE + sizeof(uint64_t)]; +/* Debug Fifo in pointer */ +static uint8_t usb_dbg_in; +/* Debug Fifo out pointer */ +static uint8_t usb_dbg_out; +#endif static void usbuart_run(void); +/* + * Update led state atomically respecting RX anb TX states. + */ +static void usbuart_set_led_state(uint8_t ledn, bool state) +{ + CM_ATOMIC_CONTEXT(); + + static uint8_t led_state = 0; + + if (state) + { + led_state |= ledn; + gpio_set(LED_PORT_UART, LED_UART); + } + else + { + led_state &= ~ledn; + if (!led_state) + gpio_clear(LED_PORT_UART, LED_UART); + } +} + void usbuart_init(void) { + /* Enable clocks */ rcc_periph_clock_enable(USBUSART_CLK); + rcc_periph_clock_enable(USBUSART_DMA_CLK); + /* Setup UART parameters */ UART_PIN_SETUP(); - - /* Setup UART parameters. */ usart_set_baudrate(USBUSART, 38400); usart_set_databits(USBUSART, 8); usart_set_stopbits(USBUSART, USART_STOPBITS_1); usart_set_mode(USBUSART, USART_MODE_TX_RX); usart_set_parity(USBUSART, USART_PARITY_NONE); usart_set_flow_control(USBUSART, USART_FLOWCONTROL_NONE); + USBUSART_CR1 |= USART_CR1_IDLEIE; - /* Finally enable the USART. */ - usart_enable(USBUSART); + /* Setup USART TX DMA */ + dma_channel_reset(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); + dma_set_peripheral_address(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, (uint32_t)&USBUSART_DR); + dma_enable_memory_increment_mode(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); + dma_set_peripheral_size(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_PSIZE_8BIT); + dma_set_memory_size(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_MSIZE_8BIT); + dma_set_priority(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_PL_HIGH); + dma_enable_transfer_complete_interrupt(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); +#ifdef STM32F4 + dma_set_transfer_mode(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_SxCR_DIR_MEM_TO_PERIPHERAL); + dma_channel_select(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, USBUSART_DMA_TRG); + dma_set_dma_flow_control(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); + dma_enable_direct_mode(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); +#else + dma_set_read_from_memory(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); +#endif + + /* Setup USART RX DMA */ + dma_channel_reset(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); + dma_set_peripheral_address(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, (uint32_t)&USBUSART_DR); + dma_set_memory_address(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, (uint32_t)buf_rx); + dma_set_number_of_data(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, RX_FIFO_SIZE); + dma_enable_memory_increment_mode(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); + dma_enable_circular_mode(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); + dma_set_peripheral_size(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, DMA_PSIZE_8BIT); + dma_set_memory_size(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, DMA_MSIZE_8BIT); + dma_set_priority(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, DMA_PL_HIGH); + dma_enable_half_transfer_interrupt(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); + dma_enable_transfer_complete_interrupt(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); +#ifdef STM32F4 + dma_set_transfer_mode(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, DMA_SxCR_DIR_PERIPHERAL_TO_MEM); + dma_channel_select(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, USBUSART_DMA_TRG); + dma_set_dma_flow_control(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); + dma_enable_direct_mode(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); +#else + dma_set_read_from_peripheral(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); +#endif + dma_enable_channel(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); /* Enable interrupts */ - USBUSART_CR1 |= USART_CR1_RXNEIE; nvic_set_priority(USBUSART_IRQ, IRQ_PRI_USBUSART); + nvic_set_priority(USBUSART_DMA_TX_IRQ, IRQ_PRI_USBUSART_DMA); + nvic_set_priority(USBUSART_DMA_RX_IRQ, IRQ_PRI_USBUSART_DMA); nvic_enable_irq(USBUSART_IRQ); + nvic_enable_irq(USBUSART_DMA_TX_IRQ); + nvic_enable_irq(USBUSART_DMA_RX_IRQ); - /* Setup timer for running deferred FIFO processing */ - USBUSART_TIM_CLK_EN(); - timer_set_mode(USBUSART_TIM, TIM_CR1_CKD_CK_INT, - TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); - timer_set_prescaler(USBUSART_TIM, - rcc_apb2_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; - } + /* Finally enable the USART */ + usart_enable(USBUSART); + usart_enable_tx_dma(USBUSART); + usart_enable_rx_dma(USBUSART); } void usbuart_set_line_coding(struct usb_cdc_line_coding *coding) @@ -133,9 +175,9 @@ void usbuart_set_line_coding(struct usb_cdc_line_coding *coding) usart_set_baudrate(USBUSART, coding->dwDTERate); if (coding->bParityType) - usart_set_databits(USBUSART, coding->bDataBits + 1); + usart_set_databits(USBUSART, (coding->bDataBits + 1 <= 8 ? 8 : 9)); else - usart_set_databits(USBUSART, coding->bDataBits); + usart_set_databits(USBUSART, (coding->bDataBits <= 8 ? 8 : 9)); switch(coding->bCharFormat) { case 0: @@ -145,6 +187,7 @@ void usbuart_set_line_coding(struct usb_cdc_line_coding *coding) usart_set_stopbits(USBUSART, USART_STOPBITS_1_5); break; case 2: + default: usart_set_stopbits(USBUSART, USART_STOPBITS_2); break; } @@ -157,100 +200,240 @@ void usbuart_set_line_coding(struct usb_cdc_line_coding *coding) usart_set_parity(USBUSART, USART_PARITY_ODD); break; case 2: + default: usart_set_parity(USBUSART, USART_PARITY_EVEN); break; } } +/* + * Copy data from fifo into continuous buffer. Return copied length. + */ +static uint32_t copy_from_fifo(uint8_t *dst, const uint8_t *src, uint32_t start, uint32_t end, uint32_t len, uint32_t fifo_sz) +{ + uint32_t out_len = 0; + for (uint32_t buf_out = start; buf_out != end && out_len < len; buf_out %= fifo_sz) + dst[out_len++] = src[buf_out++]; + + return out_len; +} + +/* + * Changes USBUSART TX buffer in which data is accumulated from USB. + * Filled buffer is submitted to DMA for transfer. + */ +static void usbuart_change_dma_tx_buf(void) +{ + /* Select buffer for transmission */ + uint8_t *const tx_buf_ptr = &buf_tx[buf_tx_act_idx * TX_BUF_SIZE]; + + /* Configure DMA */ + dma_set_memory_address(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, (uint32_t)tx_buf_ptr); + dma_set_number_of_data(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, buf_tx_act_sz); + dma_enable_channel(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); + + /* Change active buffer */ + buf_tx_act_sz = 0; + buf_tx_act_idx ^= 1; +} + void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep) { (void)ep; - char buf[CDCACM_PACKET_SIZE]; - int len = usbd_ep_read_packet(dev, CDCACM_UART_ENDPOINT, - buf, CDCACM_PACKET_SIZE); + usbd_ep_nak_set(dev, CDCACM_UART_ENDPOINT, 1); + + /* Read new packet directly into TX buffer */ + uint8_t *const tx_buf_ptr = &buf_tx[buf_tx_act_idx * TX_BUF_SIZE]; + const uint16_t len = usbd_ep_read_packet(dev, CDCACM_UART_ENDPOINT, + tx_buf_ptr + buf_tx_act_sz, CDCACM_PACKET_SIZE); #if defined(BLACKMAGIC) /* Don't bother if uart is disabled. * This will be the case on mini while we're being debugged. */ if(!(RCC_APB2ENR & RCC_APB2ENR_USART1EN)) + { + usbd_ep_nak_set(dev, CDCACM_UART_ENDPOINT, 0); return; + } #endif - gpio_set(LED_PORT_UART, LED_UART); - for(int i = 0; i < len; i++) - usart_send_blocking(USBUSART, buf[i]); - gpio_clear(LED_PORT_UART, LED_UART); + if (len) + { + buf_tx_act_sz += len; + + /* If DMA is idle, schedule new transfer */ + if (tx_trfr_cplt) + { + tx_trfr_cplt = false; + usbuart_change_dma_tx_buf(); + + /* Enable LED */ + usbuart_set_led_state(TX_LED_ACT, true); + } + } + + /* Enable USBUART TX packet reception if buffer has enough space */ + if (TX_BUF_SIZE - buf_tx_act_sz >= CDCACM_PACKET_SIZE) + usbd_ep_nak_set(dev, CDCACM_UART_ENDPOINT, 0); } #ifdef USBUART_DEBUG int usbuart_debug_write(const char *buf, size_t len) { - for (size_t i = 0; i < len; i++) { - if (buf[i] == '\n') { - buf_rx[buf_rx_in++] = '\r'; - buf_rx_in %= FIFO_SIZE; + if (nvic_get_active_irq(USB_IRQ) || nvic_get_active_irq(USBUSART_IRQ) || nvic_get_active_irq(USBUSART_DMA_RX_IRQ)) + return 0; + + CM_ATOMIC_CONTEXT(); + + for (size_t i = 0; i < len && (usb_dbg_in + 1) % RX_FIFO_SIZE != usb_dbg_out; i++) + { + if (buf[i] == '\n') + { + usb_dbg_buf[usb_dbg_in++] = '\r'; + usb_dbg_in %= RX_FIFO_SIZE; + + if ((usb_dbg_in + 1) % RX_FIFO_SIZE == usb_dbg_out) + break; } - buf_rx[buf_rx_in++] = buf[i]; - buf_rx_in %= FIFO_SIZE; + usb_dbg_buf[usb_dbg_in++] = buf[i]; + usb_dbg_in %= RX_FIFO_SIZE; } - /* enable deferred processing if we put data in the FIFO */ - timer_enable_irq(USBUSART_TIM, TIM_DIER_UIE); + + usbuart_run(); + return len; } #endif -void usbuart_usb_in_cb(usbd_device *dev, uint8_t ep) -{ - (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. + * Runs deferred processing for USBUSART RX, draining RX FIFO by sending + * characters to host PC via CDCACM. Allowed to write to FIFO OUT pointer. */ -void USBUSART_ISR(void) +static void usbuart_send_rx_packet(void) { - uint32_t err = USART_SR(USBUSART); - char c = usart_recv(USBUSART); -#if !defined(USART_SR_NE) && defined(USART_ISR_NF) -# define USART_SR_NE USART_ISR_NF + rx_usb_trfr_cplt = false; + /* Calculate writing position in the FIFO */ + const uint32_t buf_rx_in = (RX_FIFO_SIZE - dma_get_number_of_data(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN)) % RX_FIFO_SIZE; + + /* Forcibly empty fifo if no USB endpoint. + * If fifo empty, nothing further to do. */ + if (cdcacm_get_config() != 1 || (buf_rx_in == buf_rx_out +#ifdef USBUART_DEBUG + && usb_dbg_in == usb_dbg_out #endif - if (err & (USART_FLAG_ORE | USART_FLAG_FE | USART_SR_NE)) - return; - - /* Turn on LED */ - gpio_set(LED_PORT_UART, LED_UART); - - /* 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; +#ifdef USBUART_DEBUG + usb_dbg_out = usb_dbg_in; +#endif + buf_rx_out = buf_rx_in; + /* Turn off LED */ + usbuart_set_led_state(RX_LED_ACT, false); + rx_usb_trfr_cplt = true; + } + else + { + /* To avoid the need of sending ZLP don't transmit full packet. + * Also reserve space for copy function overrun. + */ + uint8_t packet_buf[CDCACM_PACKET_SIZE - 1 + sizeof(uint64_t)]; + uint32_t packet_size; - /* wrap out pointer */ - if (buf_rx_in >= FIFO_SIZE) +#ifdef USBUART_DEBUG + /* Copy data from DEBUG FIFO into local usb packet buffer */ + packet_size = copy_from_fifo(packet_buf, usb_dbg_buf, usb_dbg_out, usb_dbg_in, CDCACM_PACKET_SIZE - 1, RX_FIFO_SIZE); + /* Send if buffer not empty */ + if (packet_size) { - buf_rx_in = 0; + const uint16_t written = usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, packet_buf, packet_size); + usb_dbg_out = (usb_dbg_out + written) % RX_FIFO_SIZE; + return; } +#endif - /* enable deferred processing if we put data in the FIFO */ - timer_enable_irq(USBUSART_TIM, TIM_DIER_UIE); + /* Copy data from uart RX FIFO into local usb packet buffer */ + packet_size = copy_from_fifo(packet_buf, buf_rx, buf_rx_out, buf_rx_in, CDCACM_PACKET_SIZE - 1, RX_FIFO_SIZE); + + /* Advance fifo out pointer by amount written */ + const uint16_t written = usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, packet_buf, packet_size); + buf_rx_out = (buf_rx_out + written) % RX_FIFO_SIZE; } } -void USBUSART_TIM_ISR(void) +void usbuart_usb_in_cb(usbd_device *dev, uint8_t ep) { - /* need to clear timer update event */ - timer_clear_flag(USBUSART_TIM, TIM_SR_UIF); + (void) ep; + (void) dev; - /* process FIFO */ + usbuart_send_rx_packet(); +} + +static void usbuart_run(void) +{ + nvic_disable_irq(USB_IRQ); + + /* Enable LED */ + usbuart_set_led_state(RX_LED_ACT, true); + + /* Try to send a packet if usb is idle */ + if (rx_usb_trfr_cplt) + usbuart_send_rx_packet(); + + nvic_enable_irq(USB_IRQ); +} + +void USBUSART_ISR(void) +{ + nvic_disable_irq(USBUSART_DMA_RX_IRQ); + + /* Get IDLE flag and reset interrupt flags */ + const bool isIdle = usart_get_flag(USBUSART, USART_FLAG_IDLE); + usart_recv(USBUSART); + + /* If line is now idle, then transmit a packet */ + if (isIdle) + usbuart_run(); + + nvic_enable_irq(USBUSART_DMA_RX_IRQ); +} + +void USBUSART_DMA_TX_ISR(void) +{ + nvic_disable_irq(USB_IRQ); + + /* Stop DMA */ + dma_disable_channel(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); + dma_clear_interrupt_flags(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_CGIF); + + /* If new buffer is ready, continue transmission. + * Otherwise report transfer completion. + */ + if (buf_tx_act_sz) + { + usbuart_change_dma_tx_buf(); + usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0); + } + else + { + usbuart_set_led_state(TX_LED_ACT, false); + tx_trfr_cplt = true; + } + + nvic_enable_irq(USB_IRQ); +} + +void USBUSART_DMA_RX_ISR(void) +{ + nvic_disable_irq(USBUSART_IRQ); + + /* Clear flags */ + dma_clear_interrupt_flags(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, DMA_CGIF); + /* Transmit a packet */ usbuart_run(); + + nvic_enable_irq(USBUSART_IRQ); } #ifdef ENABLE_DEBUG diff --git a/src/platforms/swlink/platform.h b/src/platforms/swlink/platform.h index 0eb7f4d..36cab71 100644 --- a/src/platforms/swlink/platform.h +++ b/src/platforms/swlink/platform.h @@ -83,39 +83,47 @@ int usbuart_debug_write(const char *buf, size_t len); } while(0) #define UART_PIN_SETUP() do { \ AFIO_MAPR |= AFIO_MAPR_USART1_REMAP; \ - gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_2_MHZ, \ + gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_50_MHZ, \ GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USBUSART_TX_PIN); \ -} while (0) + gpio_set_mode(USBUSART_PORT, GPIO_MODE_INPUT, \ + GPIO_CNF_INPUT_PULL_UPDOWN, USBUSART_RX_PIN); \ + gpio_set(USBUSART_PORT, USBUSART_RX_PIN); \ +} while(0) #define USB_DRIVER st_usbfs_v1_usb_driver #define USB_IRQ NVIC_USB_LP_CAN_RX0_IRQ -#define USB_ISR usb_lp_can_rx0_isr +#define USB_ISR(x) usb_lp_can_rx0_isr(x) /* Interrupt priorities. Low numbers are high priority. - * For now USART1 preempts USB which may spin while buffer is drained. * TIM2 is used for traceswo capture and must be highest priority. */ -#define IRQ_PRI_USB (2 << 4) -#define IRQ_PRI_USBUSART (1 << 4) -#define IRQ_PRI_USBUSART_TIM (3 << 4) +#define IRQ_PRI_USB (1 << 4) +#define IRQ_PRI_USBUSART (2 << 4) +#define IRQ_PRI_USBUSART_DMA (2 << 4) #define IRQ_PRI_USB_VBUS (14 << 4) #define IRQ_PRI_SWO_DMA (0 << 4) #define USBUSART USART1 #define USBUSART_CR1 USART1_CR1 +#define USBUSART_DR USART1_DR #define USBUSART_IRQ NVIC_USART1_IRQ #define USBUSART_CLK RCC_USART1 #define USBUSART_PORT GPIOB #define USBUSART_TX_PIN GPIO6 -#define USBUSART_ISR usart1_isr -#define USBUSART_TIM TIM4 -#define USBUSART_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM4) -#define USBUSART_TIM_IRQ NVIC_TIM4_IRQ -#define USBUSART_TIM_ISR tim4_isr +#define USBUSART_RX_PIN GPIO7 +#define USBUSART_ISR(x) usart1_isr(x) +#define USBUSART_DMA_BUS DMA1 +#define USBUSART_DMA_CLK RCC_DMA1 +#define USBUSART_DMA_TX_CHAN DMA_CHANNEL4 +#define USBUSART_DMA_TX_IRQ NVIC_DMA1_CHANNEL4_IRQ +#define USBUSART_DMA_TX_ISR(x) dma1_channel4_isr(x) +#define USBUSART_DMA_RX_CHAN DMA_CHANNEL5 +#define USBUSART_DMA_RX_IRQ NVIC_DMA1_CHANNEL5_IRQ +#define USBUSART_DMA_RX_ISR(x) dma1_channel5_isr(x) #define TRACE_TIM TIM2 #define TRACE_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM2) #define TRACE_IRQ NVIC_TIM2_IRQ -#define TRACE_ISR tim2_isr +#define TRACE_ISR(x) tim2_isr(x) #define TRACE_IC_IN TIM_IC_IN_TI2 #define TRACE_TRIG_IN TIM_SMCR_TS_IT1FP2 @@ -177,4 +185,3 @@ extern uint8_t detect_rev(void); #endif #endif -