diff --git a/make/mcu/STM32F1.mk b/make/mcu/STM32F1.mk index a6c6a69067..9bdd33cc63 100644 --- a/make/mcu/STM32F1.mk +++ b/make/mcu/STM32F1.mk @@ -67,7 +67,7 @@ MCU_COMMON_SRC = \ drivers/dma.c \ drivers/inverter.c \ drivers/light_ws2811strip_stdperiph.c \ - drivers/serial_uart_init.c \ + drivers/serial_uart_stdperiph.c \ drivers/serial_uart_stm32f10x.c \ drivers/system_stm32f10x.c \ drivers/timer_stm32f10x.c diff --git a/make/mcu/STM32F3.mk b/make/mcu/STM32F3.mk index b1641a6ef0..e498012a53 100644 --- a/make/mcu/STM32F3.mk +++ b/make/mcu/STM32F3.mk @@ -86,7 +86,7 @@ MCU_COMMON_SRC = \ drivers/transponder_ir_io_stdperiph.c \ drivers/pwm_output_dshot.c \ drivers/pwm_output_dshot_shared.c \ - drivers/serial_uart_init.c \ + drivers/serial_uart_stdperiph.c \ drivers/serial_uart_stm32f30x.c \ drivers/system_stm32f30x.c \ drivers/timer_stm32f30x.c diff --git a/make/mcu/STM32F4.mk b/make/mcu/STM32F4.mk index 5dcf4b765e..ea7b0645cc 100644 --- a/make/mcu/STM32F4.mk +++ b/make/mcu/STM32F4.mk @@ -180,7 +180,7 @@ MCU_COMMON_SRC = \ drivers/transponder_ir_io_stdperiph.c \ drivers/pwm_output_dshot.c \ drivers/pwm_output_dshot_shared.c \ - drivers/serial_uart_init.c \ + drivers/serial_uart_stdperiph.c \ drivers/serial_uart_stm32f4xx.c \ drivers/system_stm32f4xx.c \ drivers/timer_stm32f4xx.c \ diff --git a/make/mcu/STM32F7.mk b/make/mcu/STM32F7.mk index 925d6ce975..c8f00135b6 100644 --- a/make/mcu/STM32F7.mk +++ b/make/mcu/STM32F7.mk @@ -179,13 +179,12 @@ MCU_COMMON_SRC = \ drivers/timer_hal.c \ drivers/timer_stm32f7xx.c \ drivers/system_stm32f7xx.c \ - drivers/serial_uart_stm32f7xx.c \ - drivers/serial_uart_hal.c + drivers/serial_uart_hal.c \ + drivers/serial_uart_stm32f7xx.c MCU_EXCLUDES = \ drivers/bus_i2c.c \ - drivers/timer.c \ - drivers/serial_uart.c + drivers/timer.c MSC_SRC = \ drivers/usb_msc_f7xx.c \ diff --git a/make/mcu/STM32H7.mk b/make/mcu/STM32H7.mk index a3ccf14aa0..50e267dd8f 100644 --- a/make/mcu/STM32H7.mk +++ b/make/mcu/STM32H7.mk @@ -235,8 +235,8 @@ MCU_COMMON_SRC = \ drivers/system_stm32h7xx.c \ drivers/timer_hal.c \ drivers/timer_stm32h7xx.c \ - drivers/serial_uart_stm32h7xx.c \ drivers/serial_uart_hal.c \ + drivers/serial_uart_stm32h7xx.c \ drivers/bus_quadspi_hal.c \ drivers/bus_spi_hal.c \ drivers/dma_stm32h7xx.c \ @@ -253,8 +253,7 @@ MCU_COMMON_SRC = \ MCU_EXCLUDES = \ drivers/bus_i2c.c \ - drivers/timer.c \ - drivers/serial_uart.c + drivers/timer.c #MSC_SRC = \ # drivers/usb_msc_h7xx.c \ diff --git a/src/main/drivers/serial_uart.c b/src/main/drivers/serial_uart.c index e82e1ca77a..742a2f16b5 100644 --- a/src/main/drivers/serial_uart.c +++ b/src/main/drivers/serial_uart.c @@ -33,19 +33,42 @@ #ifdef USE_UART #include "build/build_config.h" -#include "build/atomic.h" #include "common/utils.h" #include "drivers/dma.h" -#include "drivers/inverter.h" -#include "drivers/nvic.h" #include "drivers/rcc.h" #include "drivers/serial.h" #include "drivers/serial_uart.h" #include "drivers/serial_uart_impl.h" +serialPort_t *uartOpen(UARTDevice_e device, serialReceiveCallbackPtr rxCallback, void *rxCallbackData, uint32_t baudRate, portMode_e mode, portOptions_e options) +{ + uartPort_t *s = serialUART(device, baudRate, mode, options); + + if (!s) + return (serialPort_t *)s; + +#ifdef USE_DMA + s->txDMAEmpty = true; +#endif + + // common serial initialisation code should move to serialPort::init() + s->port.rxBufferHead = s->port.rxBufferTail = 0; + s->port.txBufferHead = s->port.txBufferTail = 0; + // callback works for IRQ-based RX ONLY + s->port.rxCallback = rxCallback; + s->port.rxCallbackData = rxCallbackData; + s->port.mode = mode; + s->port.baudRate = baudRate; + s->port.options = options; + + uartReconfigure(s); + + return (serialPort_t *)s; +} + static void uartSetBaudRate(serialPort_t *instance, uint32_t baudRate) { uartPort_t *uartPort = (uartPort_t *)instance; @@ -60,66 +83,26 @@ static void uartSetMode(serialPort_t *instance, portMode_e mode) uartReconfigure(uartPort); } -void uartTryStartTxDMA(uartPort_t *s) -{ - // uartTryStartTxDMA must be protected, since it is called from - // uartWrite and handleUsartTxDma (an ISR). - - ATOMIC_BLOCK(NVIC_PRIO_SERIALUART_TXDMA) { - if (IS_DMA_ENABLED(s->txDMAResource)) { - // DMA is already in progress - return; - } - - // For F4 (and F1), there are cases that NDTR (CNDTR for F1) is non-zero upon TC interrupt. - // We couldn't find out the root cause, so mask the case here. - // F3 is not confirmed to be vulnerable, but not excluded as a safety. - - if (xDMA_GetCurrDataCounter(s->txDMAResource)) { - // Possible premature TC case. - goto reenable; - } - - if (s->port.txBufferHead == s->port.txBufferTail) { - // No more data to transmit. - s->txDMAEmpty = true; - return; - } - - // Start a new transaction. - -#ifdef STM32F4 - xDMA_MemoryTargetConfig(s->txDMAResource, (uint32_t)&s->port.txBuffer[s->port.txBufferTail], DMA_Memory_0); -#else - DMAx_SetMemoryAddress(s->txDMAResource, (uint32_t)&s->port.txBuffer[s->port.txBufferTail]); -#endif - - if (s->port.txBufferHead > s->port.txBufferTail) { - xDMA_SetCurrDataCounter(s->txDMAResource, s->port.txBufferHead - s->port.txBufferTail); - s->port.txBufferTail = s->port.txBufferHead; - } else { - xDMA_SetCurrDataCounter(s->txDMAResource, s->port.txBufferSize - s->port.txBufferTail); - s->port.txBufferTail = 0; - } - s->txDMAEmpty = false; - - reenable: - xDMA_Cmd(s->txDMAResource, ENABLE); - } -} - static uint32_t uartTotalRxBytesWaiting(const serialPort_t *instance) { const uartPort_t *s = (const uartPort_t*)instance; +#ifdef USE_DMA if (s->rxDMAResource) { + // XXX Could be consolidated +#ifdef USE_HAL_DRIVER + uint32_t rxDMAHead = __HAL_DMA_GET_COUNTER(s->Handle.hdmarx); +#else uint32_t rxDMAHead = xDMA_GetCurrDataCounter(s->rxDMAResource); +#endif + if (rxDMAHead >= s->rxDMAPos) { return rxDMAHead - s->rxDMAPos; } else { return s->port.rxBufferSize + rxDMAHead - s->rxDMAPos; } } +#endif if (s->port.rxBufferHead >= s->port.rxBufferTail) { return s->port.rxBufferHead - s->port.rxBufferTail; @@ -140,12 +123,17 @@ static uint32_t uartTotalTxBytesFree(const serialPort_t *instance) bytesUsed = s->port.txBufferSize + s->port.txBufferHead - s->port.txBufferTail; } +#ifdef USE_DMA if (s->txDMAResource) { /* * When we queue up a DMA request, we advance the Tx buffer tail before the transfer finishes, so we must add * the remaining size of that in-progress transfer here instead: */ +#ifdef USE_HAL_DRIVER + bytesUsed += __HAL_DMA_GET_COUNTER(s->Handle.hdmatx); +#else bytesUsed += xDMA_GetCurrDataCounter(s->txDMAResource); +#endif /* * If the Tx buffer is being written to very quickly, we might have advanced the head into the buffer @@ -159,6 +147,7 @@ static uint32_t uartTotalTxBytesFree(const serialPort_t *instance) return 0; } } +#endif return (s->port.txBufferSize - 1) - bytesUsed; } @@ -166,9 +155,12 @@ static uint32_t uartTotalTxBytesFree(const serialPort_t *instance) static bool isUartTransmitBufferEmpty(const serialPort_t *instance) { const uartPort_t *s = (const uartPort_t *)instance; +#ifdef USE_DMA if (s->txDMAResource) { return s->txDMAEmpty; - } else { + } else +#endif + { return s->port.txBufferTail == s->port.txBufferHead; } } @@ -178,11 +170,14 @@ static uint8_t uartRead(serialPort_t *instance) uint8_t ch; uartPort_t *s = (uartPort_t *)instance; +#ifdef USE_DMA if (s->rxDMAResource) { ch = s->port.rxBuffer[s->port.rxBufferSize - s->rxDMAPos]; if (--s->rxDMAPos == 0) s->rxDMAPos = s->port.rxBufferSize; - } else { + } else +#endif + { ch = s->port.rxBuffer[s->port.rxBufferTail]; if (s->port.rxBufferTail + 1 >= s->port.rxBufferSize) { s->port.rxBufferTail = 0; @@ -197,17 +192,26 @@ static uint8_t uartRead(serialPort_t *instance) static void uartWrite(serialPort_t *instance, uint8_t ch) { uartPort_t *s = (uartPort_t *)instance; + s->port.txBuffer[s->port.txBufferHead] = ch; + if (s->port.txBufferHead + 1 >= s->port.txBufferSize) { s->port.txBufferHead = 0; } else { s->port.txBufferHead++; } +#ifdef USE_DMA if (s->txDMAResource) { uartTryStartTxDMA(s); - } else { + } else +#endif + { +#ifdef USE_HAL_DRIVER + __HAL_UART_ENABLE_IT(&s->Handle, UART_IT_TXE); +#else USART_ITConfig(s->USARTx, USART_IT_TXE, ENABLE); +#endif } } @@ -228,75 +232,43 @@ const struct serialPortVTable uartVTable[] = { } }; +#define UART_IRQHandler(type, dev) \ + void type ## dev ## _IRQHandler(void) \ + { \ + uartPort_t *s = &(uartDevmap[UARTDEV_ ## dev]->port); \ + uartIrqHandler(s); \ + } + #ifdef USE_UART1 -// USART1 Rx/Tx IRQ Handler -void USART1_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_1]->port); - uartIrqHandler(s); -} +UART_IRQHandler(USART, 1) // USART1 Rx/Tx IRQ Handler #endif #ifdef USE_UART2 -// USART2 Rx/Tx IRQ Handler -void USART2_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_2]->port); - uartIrqHandler(s); -} +UART_IRQHandler(USART, 2) // USART2 Rx/Tx IRQ Handler #endif #ifdef USE_UART3 -// USART3 Rx/Tx IRQ Handler -void USART3_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_3]->port); - uartIrqHandler(s); -} +UART_IRQHandler(USART, 3) // USART3 Rx/Tx IRQ Handler #endif #ifdef USE_UART4 -// UART4 Rx/Tx IRQ Handler -void UART4_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_4]->port); - uartIrqHandler(s); -} +UART_IRQHandler(UART, 4) // UART4 Rx/Tx IRQ Handler #endif #ifdef USE_UART5 -// UART5 Rx/Tx IRQ Handler -void UART5_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_5]->port); - uartIrqHandler(s); -} +UART_IRQHandler(UART, 5) // UART5 Rx/Tx IRQ Handler #endif #ifdef USE_UART6 -// USART6 Rx/Tx IRQ Handler -void USART6_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_6]->port); - uartIrqHandler(s); -} +UART_IRQHandler(USART, 6) // USART6 Rx/Tx IRQ Handler #endif #ifdef USE_UART7 -// UART7 Rx/Tx IRQ Handler -void UART7_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_7]->port); - uartIrqHandler(s); -} +UART_IRQHandler(UART, 7) // UART7 Rx/Tx IRQ Handler #endif #ifdef USE_UART8 -// UART8 Rx/Tx IRQ Handler -void UART8_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_8]->port); - uartIrqHandler(s); -} -#endif +UART_IRQHandler(UART, 8) // UART8 Rx/Tx IRQ Handler #endif + +#endif // USE_UART diff --git a/src/main/drivers/serial_uart_hal.c b/src/main/drivers/serial_uart_hal.c index a4e7860fc1..f8fb2c274f 100644 --- a/src/main/drivers/serial_uart_hal.c +++ b/src/main/drivers/serial_uart_hal.c @@ -68,19 +68,6 @@ static void usartConfigurePinInversion(uartPort_t *uartPort) { void uartReconfigure(uartPort_t *uartPort) { - /*RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit; - RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2|RCC_PERIPHCLK_USART3| - RCC_PERIPHCLK_UART4|RCC_PERIPHCLK_UART5|RCC_PERIPHCLK_USART6|RCC_PERIPHCLK_UART7|RCC_PERIPHCLK_UART8; - RCC_PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_SYSCLK; - RCC_PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_SYSCLK; - RCC_PeriphClkInit.Usart3ClockSelection = RCC_USART3CLKSOURCE_SYSCLK; - RCC_PeriphClkInit.Uart4ClockSelection = RCC_UART4CLKSOURCE_SYSCLK; - RCC_PeriphClkInit.Uart5ClockSelection = RCC_UART5CLKSOURCE_SYSCLK; - RCC_PeriphClkInit.Usart6ClockSelection = RCC_USART6CLKSOURCE_SYSCLK; - RCC_PeriphClkInit.Uart7ClockSelection = RCC_UART7CLKSOURCE_SYSCLK; - RCC_PeriphClkInit.Uart8ClockSelection = RCC_UART8CLKSOURCE_SYSCLK; - HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit);*/ - HAL_UART_DeInit(&uartPort->Handle); uartPort->Handle.Init.BaudRate = uartPort->port.baudRate; // according to the stm32 documentation wordlen has to be 9 for parity bits @@ -208,47 +195,6 @@ void uartReconfigure(uartPort_t *uartPort) return; } -serialPort_t *uartOpen(UARTDevice_e device, serialReceiveCallbackPtr callback, void *callbackData, uint32_t baudRate, portMode_e mode, portOptions_e options) -{ - uartPort_t *s = serialUART(device, baudRate, mode, options); - - if (!s) { - return (serialPort_t *)s; - } - -#ifdef USE_DMA - s->txDMAEmpty = true; -#endif - - // common serial initialisation code should move to serialPort::init() - s->port.rxBufferHead = s->port.rxBufferTail = 0; - s->port.txBufferHead = s->port.txBufferTail = 0; - // callback works for IRQ-based RX ONLY - s->port.rxCallback = callback; - s->port.rxCallbackData = callbackData; - s->port.mode = mode; - s->port.baudRate = baudRate; - s->port.options = options; - - uartReconfigure(s); - - return (serialPort_t *)s; -} - -void uartSetBaudRate(serialPort_t *instance, uint32_t baudRate) -{ - uartPort_t *uartPort = (uartPort_t *)instance; - uartPort->port.baudRate = baudRate; - uartReconfigure(uartPort); -} - -void uartSetMode(serialPort_t *instance, portMode_e mode) -{ - uartPort_t *uartPort = (uartPort_t *)instance; - uartPort->port.mode = mode; - uartReconfigure(uartPort); -} - #ifdef USE_DMA void uartTryStartTxDMA(uartPort_t *s) { @@ -286,209 +232,4 @@ void uartTryStartTxDMA(uartPort_t *s) } #endif -uint32_t uartTotalRxBytesWaiting(const serialPort_t *instance) -{ - uartPort_t *s = (uartPort_t*)instance; - -#ifdef USE_DMA - if (s->rxDMAResource) { - uint32_t rxDMAHead = __HAL_DMA_GET_COUNTER(s->Handle.hdmarx); - - if (rxDMAHead >= s->rxDMAPos) { - return rxDMAHead - s->rxDMAPos; - } else { - return s->port.rxBufferSize + rxDMAHead - s->rxDMAPos; - } - } -#endif - - if (s->port.rxBufferHead >= s->port.rxBufferTail) { - return s->port.rxBufferHead - s->port.rxBufferTail; - } else { - return s->port.rxBufferSize + s->port.rxBufferHead - s->port.rxBufferTail; - } -} - -uint32_t uartTotalTxBytesFree(const serialPort_t *instance) -{ - uartPort_t *s = (uartPort_t*)instance; - - uint32_t bytesUsed; - - if (s->port.txBufferHead >= s->port.txBufferTail) { - bytesUsed = s->port.txBufferHead - s->port.txBufferTail; - } else { - bytesUsed = s->port.txBufferSize + s->port.txBufferHead - s->port.txBufferTail; - } - -#ifdef USE_DMA - if (s->txDMAResource) { - /* - * When we queue up a DMA request, we advance the Tx buffer tail before the transfer finishes, so we must add - * the remaining size of that in-progress transfer here instead: - */ - bytesUsed += __HAL_DMA_GET_COUNTER(s->Handle.hdmatx); - - /* - * If the Tx buffer is being written to very quickly, we might have advanced the head into the buffer - * space occupied by the current DMA transfer. In that case the "bytesUsed" total will actually end up larger - * than the total Tx buffer size, because we'll end up transmitting the same buffer region twice. (So we'll be - * transmitting a garbage mixture of old and new bytes). - * - * Be kind to callers and pretend like our buffer can only ever be 100% full. - */ - if (bytesUsed >= s->port.txBufferSize - 1) { - return 0; - } - } -#endif - - return (s->port.txBufferSize - 1) - bytesUsed; -} - -bool isUartTransmitBufferEmpty(const serialPort_t *instance) -{ - const uartPort_t *s = (uartPort_t *)instance; -#ifdef USE_DMA - if (s->txDMAResource) - return s->txDMAEmpty; - else -#endif - return s->port.txBufferTail == s->port.txBufferHead; -} - -uint8_t uartRead(serialPort_t *instance) -{ - uint8_t ch; - uartPort_t *s = (uartPort_t *)instance; - -#ifdef USE_DMA - if (s->rxDMAResource) { - ch = s->port.rxBuffer[s->port.rxBufferSize - s->rxDMAPos]; - if (--s->rxDMAPos == 0) - s->rxDMAPos = s->port.rxBufferSize; - } else -#endif - { - ch = s->port.rxBuffer[s->port.rxBufferTail]; - if (s->port.rxBufferTail + 1 >= s->port.rxBufferSize) { - s->port.rxBufferTail = 0; - } else { - s->port.rxBufferTail++; - } - } - - return ch; -} - -void uartWrite(serialPort_t *instance, uint8_t ch) -{ - uartPort_t *s = (uartPort_t *)instance; - - s->port.txBuffer[s->port.txBufferHead] = ch; - - if (s->port.txBufferHead + 1 >= s->port.txBufferSize) { - s->port.txBufferHead = 0; - } else { - s->port.txBufferHead++; - } - -#ifdef USE_DMA - if (s->txDMAResource) { - uartTryStartTxDMA(s); - } else -#endif - { - __HAL_UART_ENABLE_IT(&s->Handle, UART_IT_TXE); - } -} - -const struct serialPortVTable uartVTable[] = { - { - .serialWrite = uartWrite, - .serialTotalRxWaiting = uartTotalRxBytesWaiting, - .serialTotalTxFree = uartTotalTxBytesFree, - .serialRead = uartRead, - .serialSetBaudRate = uartSetBaudRate, - .isSerialTransmitBufferEmpty = isUartTransmitBufferEmpty, - .setMode = uartSetMode, - .setCtrlLineStateCb = NULL, - .setBaudRateCb = NULL, - .writeBuf = NULL, - .beginWrite = NULL, - .endWrite = NULL, - } -}; - -#ifdef USE_UART1 -// USART1 Rx/Tx IRQ Handler -void USART1_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_1]->port); - uartIrqHandler(s); -} -#endif - -#ifdef USE_UART2 -// USART2 Rx/Tx IRQ Handler -void USART2_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_2]->port); - uartIrqHandler(s); -} -#endif - -#ifdef USE_UART3 -// USART3 Rx/Tx IRQ Handler -void USART3_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_3]->port); - uartIrqHandler(s); -} -#endif - -#ifdef USE_UART4 -// UART4 Rx/Tx IRQ Handler -void UART4_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_4]->port); - uartIrqHandler(s); -} -#endif - -#ifdef USE_UART5 -// UART5 Rx/Tx IRQ Handler -void UART5_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_5]->port); - uartIrqHandler(s); -} -#endif - -#ifdef USE_UART6 -// USART6 Rx/Tx IRQ Handler -void USART6_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_6]->port); - uartIrqHandler(s); -} -#endif - -#ifdef USE_UART7 -// UART7 Rx/Tx IRQ Handler -void UART7_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_7]->port); - uartIrqHandler(s); -} -#endif - -#ifdef USE_UART8 -// UART8 Rx/Tx IRQ Handler -void UART8_IRQHandler(void) -{ - uartPort_t *s = &(uartDevmap[UARTDEV_8]->port); - uartIrqHandler(s); -} -#endif -#endif +#endif // USE_UART diff --git a/src/main/drivers/serial_uart_init.c b/src/main/drivers/serial_uart_stdperiph.c similarity index 59% rename from src/main/drivers/serial_uart_init.c rename to src/main/drivers/serial_uart_stdperiph.c index 816d464923..b74ddcc1e0 100644 --- a/src/main/drivers/serial_uart_init.c +++ b/src/main/drivers/serial_uart_stdperiph.c @@ -37,9 +37,11 @@ #ifdef USE_UART #include "build/build_config.h" +#include "build/atomic.h" #include "common/utils.h" #include "drivers/inverter.h" +#include "drivers/nvic.h" #include "drivers/rcc.h" #include "drivers/serial.h" @@ -114,137 +116,140 @@ void uartReconfigure(uartPort_t *uartPort) USART_HalfDuplexCmd(uartPort->USARTx, DISABLE); USART_Cmd(uartPort->USARTx, ENABLE); -} - -serialPort_t *uartOpen(UARTDevice_e device, serialReceiveCallbackPtr rxCallback, void *rxCallbackData, uint32_t baudRate, portMode_e mode, portOptions_e options) -{ - uartPort_t *s = serialUART(device, baudRate, mode, options); - - if (!s) - return (serialPort_t *)s; - - s->txDMAEmpty = true; - - // common serial initialisation code should move to serialPort::init() - s->port.rxBufferHead = s->port.rxBufferTail = 0; - s->port.txBufferHead = s->port.txBufferTail = 0; - // callback works for IRQ-based RX ONLY - s->port.rxCallback = rxCallback; - s->port.rxCallbackData = rxCallbackData; - s->port.mode = mode; - s->port.baudRate = baudRate; - s->port.options = options; - - uartReconfigure(s); // Receive DMA or IRQ DMA_InitTypeDef DMA_InitStructure; - if (mode & MODE_RX) { -#ifdef STM32F4 - if (s->rxDMAResource) { + if (uartPort->port.mode & MODE_RX) { + if (uartPort->rxDMAResource) { DMA_StructInit(&DMA_InitStructure); - DMA_InitStructure.DMA_PeripheralBaseAddr = s->rxDMAPeripheralBaseAddr; + DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; + DMA_InitStructure.DMA_PeripheralBaseAddr = uartPort->rxDMAPeripheralBaseAddr; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStructure.DMA_BufferSize = uartPort->port.rxBufferSize; +#ifdef STM32F4 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable ; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull ; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single ; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; -#else - if (s->rxDMAResource) { - DMA_StructInit(&DMA_InitStructure); - DMA_InitStructure.DMA_PeripheralBaseAddr = s->rxDMAPeripheralBaseAddr; - DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; - DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; -#endif - DMA_InitStructure.DMA_BufferSize = s->port.rxBufferSize; -#ifdef STM32F4 - DMA_InitStructure.DMA_Channel = s->rxDMAChannel; + DMA_InitStructure.DMA_Channel = uartPort->rxDMAChannel; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; - DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; - DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)s->port.rxBuffer; - xDMA_DeInit(s->rxDMAResource); - xDMA_Init(s->rxDMAResource, &DMA_InitStructure); - xDMA_Cmd(s->rxDMAResource, ENABLE); - USART_DMACmd(s->USARTx, USART_DMAReq_Rx, ENABLE); - s->rxDMAPos = xDMA_GetCurrDataCounter(s->rxDMAResource); + DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)uartPort->port.rxBuffer; #else + DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; - DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; - DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)s->port.rxBuffer; - xDMA_DeInit(s->rxDMAResource); - xDMA_Init(s->rxDMAResource, &DMA_InitStructure); - xDMA_Cmd(s->rxDMAResource, ENABLE); - USART_DMACmd(s->USARTx, USART_DMAReq_Rx, ENABLE); - s->rxDMAPos = xDMA_GetCurrDataCounter(s->rxDMAResource); + DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)uartPort->port.rxBuffer; #endif + + xDMA_DeInit(uartPort->rxDMAResource); + xDMA_Init(uartPort->rxDMAResource, &DMA_InitStructure); + xDMA_Cmd(uartPort->rxDMAResource, ENABLE); + USART_DMACmd(uartPort->USARTx, USART_DMAReq_Rx, ENABLE); + uartPort->rxDMAPos = xDMA_GetCurrDataCounter(uartPort->rxDMAResource); } else { - USART_ClearITPendingBit(s->USARTx, USART_IT_RXNE); - USART_ITConfig(s->USARTx, USART_IT_RXNE, ENABLE); - USART_ITConfig(s->USARTx, USART_IT_IDLE, ENABLE); + USART_ClearITPendingBit(uartPort->USARTx, USART_IT_RXNE); + USART_ITConfig(uartPort->USARTx, USART_IT_RXNE, ENABLE); + USART_ITConfig(uartPort->USARTx, USART_IT_IDLE, ENABLE); } } // Transmit DMA or IRQ - if (mode & MODE_TX) { -#ifdef STM32F4 - if (s->txDMAResource) { + if (uartPort->port.mode & MODE_TX) { + if (uartPort->txDMAResource) { DMA_StructInit(&DMA_InitStructure); - DMA_InitStructure.DMA_PeripheralBaseAddr = s->txDMAPeripheralBaseAddr; + DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; + DMA_InitStructure.DMA_PeripheralBaseAddr = uartPort->txDMAPeripheralBaseAddr; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStructure.DMA_BufferSize = uartPort->port.txBufferSize; + +#ifdef STM32F4 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable ; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull ; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single ; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + + DMA_InitStructure.DMA_Channel = uartPort->txDMAChannel; + DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; #else - if (s->txDMAResource) { - DMA_StructInit(&DMA_InitStructure); - DMA_InitStructure.DMA_PeripheralBaseAddr = s->txDMAPeripheralBaseAddr; - DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; #endif - DMA_InitStructure.DMA_BufferSize = s->port.txBufferSize; + + xDMA_DeInit(uartPort->txDMAResource); + xDMA_Init(uartPort->txDMAResource, &DMA_InitStructure); #ifdef STM32F4 - DMA_InitStructure.DMA_Channel = s->txDMAChannel; - DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; - xDMA_DeInit(s->txDMAResource); - xDMA_Init(s->txDMAResource, &DMA_InitStructure); - xDMA_ITConfig(s->txDMAResource, DMA_IT_TC | DMA_IT_FE | DMA_IT_TE | DMA_IT_DME, ENABLE); - xDMA_SetCurrDataCounter(s->txDMAResource, 0); + xDMA_ITConfig(uartPort->txDMAResource, DMA_IT_TC | DMA_IT_FE | DMA_IT_TE | DMA_IT_DME, ENABLE); #else - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; - xDMA_DeInit(s->txDMAResource); - xDMA_Init(s->txDMAResource, &DMA_InitStructure); - xDMA_ITConfig(s->txDMAResource, DMA_IT_TC, ENABLE); - xDMA_SetCurrDataCounter(s->txDMAResource, 0); + xDMA_ITConfig(uartPort->txDMAResource, DMA_IT_TC, ENABLE); #endif - USART_DMACmd(s->USARTx, USART_DMAReq_Tx, ENABLE); + + xDMA_SetCurrDataCounter(uartPort->txDMAResource, 0); + USART_DMACmd(uartPort->USARTx, USART_DMAReq_Tx, ENABLE); } else { - USART_ITConfig(s->USARTx, USART_IT_TXE, ENABLE); + USART_ITConfig(uartPort->USARTx, USART_IT_TXE, ENABLE); } } - USART_Cmd(s->USARTx, ENABLE); + USART_Cmd(uartPort->USARTx, ENABLE); +} - return (serialPort_t *)s; +#ifdef USE_DMA +void uartTryStartTxDMA(uartPort_t *s) +{ + // uartTryStartTxDMA must be protected, since it is called from + // uartWrite and handleUsartTxDma (an ISR). + + ATOMIC_BLOCK(NVIC_PRIO_SERIALUART_TXDMA) { + if (IS_DMA_ENABLED(s->txDMAResource)) { + // DMA is already in progress + return; + } + + // For F4 (and F1), there are cases that NDTR (CNDTR for F1) is non-zero upon TC interrupt. + // We couldn't find out the root cause, so mask the case here. + // F3 is not confirmed to be vulnerable, but not excluded as a safety. + + if (xDMA_GetCurrDataCounter(s->txDMAResource)) { + // Possible premature TC case. + goto reenable; + } + + if (s->port.txBufferHead == s->port.txBufferTail) { + // No more data to transmit. + s->txDMAEmpty = true; + return; + } + + // Start a new transaction. + +#ifdef STM32F4 + xDMA_MemoryTargetConfig(s->txDMAResource, (uint32_t)&s->port.txBuffer[s->port.txBufferTail], DMA_Memory_0); +#else + DMAx_SetMemoryAddress(s->txDMAResource, (uint32_t)&s->port.txBuffer[s->port.txBufferTail]); +#endif + + if (s->port.txBufferHead > s->port.txBufferTail) { + xDMA_SetCurrDataCounter(s->txDMAResource, s->port.txBufferHead - s->port.txBufferTail); + s->port.txBufferTail = s->port.txBufferHead; + } else { + xDMA_SetCurrDataCounter(s->txDMAResource, s->port.txBufferSize - s->port.txBufferTail); + s->port.txBufferTail = 0; + } + s->txDMAEmpty = false; + + reenable: + xDMA_Cmd(s->txDMAResource, ENABLE); + } } #endif + +#endif // USE_UART