From e081c3cd94a1c52ef53181338814e8569cfc71ca Mon Sep 17 00:00:00 2001 From: Andrei Date: Tue, 23 May 2017 19:44:58 +0300 Subject: [PATCH] Add IDLE-IRQ & circular half-transfer support to USARTv1-DMA driver --- os/hal/include/uart.h | 61 ++++++++++++++++++++++- os/hal/ports/STM32/LLD/USARTv1/uart_lld.c | 29 +++++++++-- os/hal/ports/STM32/LLD/USARTv1/uart_lld.h | 18 +++++++ 3 files changed, 104 insertions(+), 4 deletions(-) diff --git a/os/hal/include/uart.h b/os/hal/include/uart.h index a6a8c42986..483365894c 100644 --- a/os/hal/include/uart.h +++ b/os/hal/include/uart.h @@ -185,6 +185,23 @@ typedef enum { #define _uart_wakeup_rx_error_isr(uartp) #endif /* !UART_USE_WAIT */ +#if (UART_USE_WAIT == TRUE) || defined(__DOXYGEN__) +/** + * @brief Wakes up the waiting thread in case of RX timeout. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @notapi + */ +#define _uart_wakeup_rx_timeout_isr(uartp) { \ + osalSysLockFromISR(); \ + osalThreadResumeI(&(uartp)->threadrx, MSG_TIMEOUT); \ + osalSysUnlockFromISR(); \ +} +#else /* !UART_USE_WAIT */ +#define _uart_wakeup_rx_timeout_isr(uartp) +#endif /* !UART_USE_WAIT */ + /** * @brief Common ISR code for early TX. * @details This code handles the portable part of the ISR code: @@ -257,6 +274,27 @@ typedef enum { _uart_wakeup_rx_complete_isr(uartp); \ } +/** + * @brief Common ISR code for RX half-transfer data. + * @details This code handles the portable part of the ISR code: + * - Callback invocation. + * - Waiting thread wakeup, if any. + * - Driver state transitions. + * . + * @note This macro is meant to be used in the low level drivers + * implementation only. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] full flag set to 1 for the second half, and 0 for the first half + * + * @notapi + */ +#define _uart_rx_half_isr_code(uartp, full) { \ + if ((uartp)->config->rxhalf_cb != NULL) \ + (uartp)->config->rxhalf_cb(uartp, full); \ +} + + /** * @brief Common ISR code for RX error. * @details This code handles the portable part of the ISR code: @@ -279,7 +317,6 @@ typedef enum { _uart_wakeup_rx_error_isr(uartp); \ } - /** * @brief Common ISR code for RX on idle. * @details This code handles the portable part of the ISR code: @@ -298,6 +335,28 @@ typedef enum { if ((uartp)->config->rxchar_cb != NULL) \ (uartp)->config->rxchar_cb(uartp, (uartp)->rxbuf); \ } + +/** + * @brief Timeout ISR code for receiver. + * @details This code handles the portable part of the ISR code: + * - Callback invocation. + * - Waiting thread wakeup, if any. + * - Driver state transitions. + * . + * @note This macro is meant to be used in the low level drivers + * implementation only. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @notapi + */ +#define _uart_timeout_isr_code(uartp) { \ + if ((uartp)->config->timeout_cb != NULL) { \ + (uartp)->config->timeout_cb(uartp); \ + } \ + _uart_wakeup_rx_timeout_isr(uartp); \ +} + /** @} */ /*===========================================================================*/ diff --git a/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c b/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c index c3a84b6b16..fe6ea114c1 100644 --- a/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c +++ b/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c @@ -226,6 +226,11 @@ static void usart_start(UARTDriver *uartp) { /* Mustn't ever set TCIE here - if done, it causes an immediate interrupt.*/ cr1 = USART_CR1_UE | USART_CR1_PEIE | USART_CR1_TE | USART_CR1_RE; + + /* Add Idle interrupt if needed */ + if (uartp->config->timeout_cb != NULL) + cr1 |= USART_CR1_IDLEIE; + u->CR1 = uartp->config->cr1 | cr1; /* Starting the receiver idle loop.*/ @@ -254,7 +259,14 @@ static void uart_lld_serve_rx_end_irq(UARTDriver *uartp, uint32_t flags) { received character and then the driver stays in the same state.*/ _uart_rx_idle_code(uartp); } - else { + /* DMA half-transter interrupt handling - for the 1st/2nd half transfers. */ + else if (uartp->config->rxhalf_cb != NULL) { + if ((flags & STM32_DMA_ISR_HTIF) != 0) { + _uart_rx_half_isr_code(uartp, 0); + } else if ((flags & STM32_DMA_ISR_TCIF) != 0) { + _uart_rx_half_isr_code(uartp, 1); + } + } else { /* Receiver in active state, a callback is generated, if enabled, after a completed transfer.*/ dmaStreamDisable(uartp->dmarx); @@ -312,6 +324,11 @@ static void serve_usart_irq(UARTDriver *uartp) { /* End of transmission, a callback is generated.*/ _uart_tx2_isr_code(uartp); } + + /* Idle interrupt sources are only checked if enabled in CR1.*/ + if ((sr & USART_SR_IDLE) && (cr1 & USART_CR1_IDLEIE)) { + _uart_timeout_isr_code(uartp); + } } /*===========================================================================*/ @@ -778,8 +795,14 @@ void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf) { /* RX DMA channel preparation.*/ dmaStreamSetMemory0(uartp->dmarx, rxbuf); dmaStreamSetTransactionSize(uartp->dmarx, n); - dmaStreamSetMode(uartp->dmarx, uartp->dmamode | STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE); + + uint32_t mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE; + + /* DMA half-transfer interrupt & circular mode, if needed */ + if (uartp->config->rxhalf_cb != NULL) + mode |= STM32_DMA_CR_HTIE | STM32_DMA_CR_CIRC; + + dmaStreamSetMode(uartp->dmarx, uartp->dmamode | mode); /* Starting transfer.*/ dmaStreamEnable(uartp->dmarx); diff --git a/os/hal/ports/STM32/LLD/USARTv1/uart_lld.h b/os/hal/ports/STM32/LLD/USARTv1/uart_lld.h index a64b651504..2cd7e52245 100644 --- a/os/hal/ports/STM32/LLD/USARTv1/uart_lld.h +++ b/os/hal/ports/STM32/LLD/USARTv1/uart_lld.h @@ -460,6 +460,14 @@ typedef void (*uartccb_t)(UARTDriver *uartp, uint16_t c); */ typedef void (*uartecb_t)(UARTDriver *uartp, uartflags_t e); +/** + * @brief Receive Half-transfer UART notification callback type. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] full flag set to 1 for the second half, and 0 for the first half + */ +typedef void (*uarthcb_t)(UARTDriver *uartp, uartflags_t full); + /** * @brief Driver configuration structure. * @note It could be empty on some architectures. @@ -502,6 +510,16 @@ typedef struct { * @brief Initialization value for the CR3 register. */ uint16_t cr3; + /* Additional (optional) handlers. Placed here for the struct compatibility.*/ + /** + * @brief Receiver timeout (idle) callback. + * @details Handles an idle interrupt for USARTv1. + */ + uartcb_t timeout_cb; + /** + * @brief Half-transfer receive buffer callback. + */ + uarthcb_t rxhalf_cb; } UARTConfig; /**