From f098e079d0fd66419adbb226d7045ca810fe9890 Mon Sep 17 00:00:00 2001 From: Giovanni Di Sirio Date: Fri, 13 Nov 2015 12:33:18 +0000 Subject: [PATCH] Improved UART driver. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@8479 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/include/spi.h | 4 +- os/hal/include/uart.h | 223 ++++++++++++++++++++++ os/hal/ports/STM32/LLD/USARTv1/uart_lld.c | 35 +--- os/hal/ports/STM32/LLD/USARTv1/uart_lld.h | 20 ++ os/hal/ports/STM32/LLD/USARTv2/uart_lld.c | 35 +--- os/hal/ports/STM32/LLD/USARTv2/uart_lld.h | 20 ++ os/hal/src/uart.c | 187 +++++++++++++++++- os/hal/templates/uart_lld.h | 20 ++ readme.txt | 1 + 9 files changed, 483 insertions(+), 62 deletions(-) diff --git a/os/hal/include/spi.h b/os/hal/include/spi.h index 40592776d..2db152ada 100644 --- a/os/hal/include/spi.h +++ b/os/hal/include/spi.h @@ -270,13 +270,13 @@ extern "C" { const void *txbuf, void *rxbuf); void spiStartSend(SPIDriver *spip, size_t n, const void *txbuf); void spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf); -#if SPI_USE_WAIT +#if SPI_USE_WAIT == TRUE void spiIgnore(SPIDriver *spip, size_t n); void spiExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf); void spiSend(SPIDriver *spip, size_t n, const void *txbuf); void spiReceive(SPIDriver *spip, size_t n, void *rxbuf); #endif -#if SPI_USE_MUTUAL_EXCLUSION +#if SPI_USE_MUTUAL_EXCLUSION == TRUE void spiAcquireBus(SPIDriver *spip); void spiReleaseBus(SPIDriver *spip); #endif diff --git a/os/hal/include/uart.h b/os/hal/include/uart.h index 75a9e2b58..f619599d9 100644 --- a/os/hal/include/uart.h +++ b/os/hal/include/uart.h @@ -47,6 +47,27 @@ /* Driver pre-compile time settings. */ /*===========================================================================*/ +/** + * @name UART configuration options + * @{ + */ +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__) +#define UART_USE_WAIT TRUE +#endif + +/** + * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define UART_USE_MUTUAL_EXCLUSION TRUE +#endif +/** @} */ + /*===========================================================================*/ /* Derived constants and error checks. */ /*===========================================================================*/ @@ -88,6 +109,196 @@ typedef enum { /* Driver macros. */ /*===========================================================================*/ +/** + * @name Low level driver helper macros + * @{ + */ +#if (UART_USE_WAIT == TRUE) || defined(__DOXYGEN__) +/** + * @brief Wakes up the waiting thread in case of early TX complete. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @notapi + */ +#define _uart_wakeup_tx1_isr(uartp) { \ + if ((uartp)->early == true) { \ + osalSysLockFromISR(); \ + osalThreadResumeI(&(uartp)->threadtx, MSG_OK); \ + osalSysUnlockFromISR(); \ + } \ +} +#else /* !UART_USE_WAIT */ +#define _uart_wakeup_tx1_isr(uartp) +#endif /* !UART_USE_WAIT */ + +#if (UART_USE_WAIT == TRUE) || defined(__DOXYGEN__) +/** + * @brief Wakes up the waiting thread in case of late TX complete. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @notapi + */ +#define _uart_wakeup_tx2_isr(uartp) { \ + if ((uartp)->early == false) { \ + osalSysLockFromISR(); \ + osalThreadResumeI(&(uartp)->threadtx, MSG_OK); \ + osalSysUnlockFromISR(); \ + } \ +} +#else /* !UART_USE_WAIT */ +#define _uart_wakeup_tx2_isr(uartp) +#endif /* !UART_USE_WAIT */ + +#if (UART_USE_WAIT == TRUE) || defined(__DOXYGEN__) +/** + * @brief Wakes up the waiting thread in case of RX complete. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @notapi + */ +#define _uart_wakeup_rx_complete_isr(uartp) { \ + osalSysLockFromISR(); \ + osalThreadResumeI(&(uartp)->threadrx, MSG_OK); \ + osalSysUnlockFromISR(); \ +} +#else /* !UART_USE_WAIT */ +#define _uart_wakeup_rx_complete_isr(uartp) +#endif /* !UART_USE_WAIT */ + +#if (UART_USE_WAIT == TRUE) || defined(__DOXYGEN__) +/** + * @brief Wakes up the waiting thread in case of RX error. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @notapi + */ +#define _uart_wakeup_rx_error_isr(uartp) { \ + osalSysLockFromISR(); \ + osalThreadResumeI(&(uartp)->threadrx, MSG_RESET); \ + osalSysUnlockFromISR(); \ +} +#else /* !UART_USE_WAIT */ +#define _uart_wakeup_rx_error_isr(uartp) +#endif /* !UART_USE_WAIT */ + +/** + * @brief Common ISR code for early TX. + * @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_tx1_isr_code(uartp) { \ + (uartp)->txstate = UART_TX_COMPLETE; \ + if ((uartp)->config->txend1_cb != NULL) { \ + (uartp)->config->txend1_cb(uartp); \ + } \ + if ((uartp)->txstate == UART_TX_COMPLETE) { \ + (uartp)->txstate = UART_TX_IDLE; \ + } \ + _uart_wakeup_tx1_isr(uartp); \ +} + +/** + * @brief Common ISR code for late TX. + * @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_tx2_isr_code(uartp) { \ + if ((uartp)->config->txend2_cb != NULL) { \ + (uartp)->config->txend2_cb(uartp); \ + } \ + _uart_wakeup_tx2_isr(uartp); \ +} + +/** + * @brief Common ISR code for RX complete. + * @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_rx_complete_isr_code(uartp) { \ + (uartp)->rxstate = UART_RX_COMPLETE; \ + if ((uartp)->config->rxend_cb != NULL) { \ + (uartp)->config->rxend_cb(uartp); \ + } \ + if ((uartp)->rxstate == UART_RX_COMPLETE) { \ + (uartp)->rxstate = UART_RX_IDLE; \ + uart_enter_rx_idle_loop(uartp); \ + } \ + _uart_wakeup_rx_complete_isr(uartp); \ +} + +/** + * @brief Common ISR code for RX error. + * @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_rx_error_isr_code(uartp, errors) { \ + if ((uartp)->config->rxerr_cb != NULL) { \ + (uartp)->config->rxerr_cb(uartp, errors); \ + } \ + _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: + * - 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_rx_idle_code(uartp) { \ + if ((uartp)->config->rxchar_cb != NULL) \ + (uartp)->config->rxchar_cb(uartp, (uartp)->rxbuf); \ +} +/** @} */ + /*===========================================================================*/ /* External declarations. */ /*===========================================================================*/ @@ -107,6 +318,18 @@ extern "C" { void uartStartReceiveI(UARTDriver *uartp, size_t n, void *rxbuf); size_t uartStopReceive(UARTDriver *uartp); size_t uartStopReceiveI(UARTDriver *uartp); +#if UART_USE_WAIT == TRUE + msg_t uartSendTimeout(UARTDriver *uartp, size_t *np, + const void *txbuf, systime_t time); + msg_t uartSendFullTimeout(UARTDriver *uartp, size_t *np, + const void *txbuf, systime_t time); + msg_t uartReceiveTimeout(UARTDriver *uartp, size_t *np, + void *rxbuf, systime_t time); +#endif +#if UART_USE_MUTUAL_EXCLUSION == TRUE + void uartAcquireBus(UARTDriver *uartp); + void uartReleaseBus(UARTDriver *uartp); +#endif #ifdef __cplusplus } #endif diff --git a/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c b/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c index b346dba82..ec422dc6c 100644 --- a/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c +++ b/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c @@ -156,7 +156,7 @@ static uartflags_t translate_errors(uint16_t sr) { * * @param[in] uartp pointer to the @p UARTDriver object */ -static void set_rx_idle_loop(UARTDriver *uartp) { +static void uart_enter_rx_idle_loop(UARTDriver *uartp) { uint32_t mode; /* RX DMA channel preparation, if the char callback is defined then the @@ -229,7 +229,7 @@ static void usart_start(UARTDriver *uartp) { u->CR1 = uartp->config->cr1 | cr1; /* Starting the receiver idle loop.*/ - set_rx_idle_loop(uartp); + uart_enter_rx_idle_loop(uartp); } /** @@ -252,23 +252,13 @@ static void uart_lld_serve_rx_end_irq(UARTDriver *uartp, uint32_t flags) { if (uartp->rxstate == UART_RX_IDLE) { /* Receiver in idle state, a callback is generated, if enabled, for each received character and then the driver stays in the same state.*/ - if (uartp->config->rxchar_cb != NULL) - uartp->config->rxchar_cb(uartp, uartp->rxbuf); + _uart_rx_idle_code(uartp); } else { /* Receiver in active state, a callback is generated, if enabled, after a completed transfer.*/ dmaStreamDisable(uartp->dmarx); - uartp->rxstate = UART_RX_COMPLETE; - if (uartp->config->rxend_cb != NULL) - uartp->config->rxend_cb(uartp); - - /* If the callback didn't explicitly change state then the receiver - automatically returns to the idle state.*/ - if (uartp->rxstate == UART_RX_COMPLETE) { - uartp->rxstate = UART_RX_IDLE; - set_rx_idle_loop(uartp); - } + _uart_rx_complete_isr_code(uartp); } } @@ -292,14 +282,7 @@ static void uart_lld_serve_tx_end_irq(UARTDriver *uartp, uint32_t flags) { dmaStreamDisable(uartp->dmatx); /* A callback is generated, if enabled, after a completed transfer.*/ - uartp->txstate = UART_TX_COMPLETE; - if (uartp->config->txend1_cb != NULL) - uartp->config->txend1_cb(uartp); - - /* If the callback didn't explicitly change state then the transmitter - automatically returns to the idle state.*/ - if (uartp->txstate == UART_TX_COMPLETE) - uartp->txstate = UART_TX_IDLE; + _uart_wakeup_tx1_isr(uartp); } /** @@ -318,8 +301,7 @@ static void serve_usart_irq(UARTDriver *uartp) { if (sr & (USART_SR_LBD | USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE)) { u->SR = ~USART_SR_LBD; - if (uartp->config->rxerr_cb != NULL) - uartp->config->rxerr_cb(uartp, translate_errors(sr)); + _uart_rx_error_isr_code(uartp, translate_errors(sr)); } if ((sr & USART_SR_TC) && (cr1 & USART_CR1_TCIE)) { @@ -328,8 +310,7 @@ static void serve_usart_irq(UARTDriver *uartp) { u->CR1 = cr1 & ~USART_CR1_TCIE; /* End of transmission, a callback is generated.*/ - if (uartp->config->txend2_cb != NULL) - uartp->config->txend2_cb(uartp); + _uart_wakeup_tx2_isr(uartp); } } @@ -820,7 +801,7 @@ size_t uart_lld_stop_receive(UARTDriver *uartp) { dmaStreamDisable(uartp->dmarx); n = dmaStreamGetTransactionSize(uartp->dmarx); - set_rx_idle_loop(uartp); + uart_enter_rx_idle_loop(uartp); return n; } diff --git a/os/hal/ports/STM32/LLD/USARTv1/uart_lld.h b/os/hal/ports/STM32/LLD/USARTv1/uart_lld.h index 3f3bef590..fee0d1413 100644 --- a/os/hal/ports/STM32/LLD/USARTv1/uart_lld.h +++ b/os/hal/ports/STM32/LLD/USARTv1/uart_lld.h @@ -524,6 +524,26 @@ struct UARTDriver { * @brief Current configuration data. */ const UARTConfig *config; +#if UART_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Synchronization flag for transmit operations. + */ + bool early; + /** + * @brief Waiting thread on RX. + */ + thread_reference_t threadrx; + /** + * @brief Waiting thread on TX. + */ + thread_reference_t threadtx; +#endif /* UART_USE_WAIT */ +#if UART_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* UART_USE_MUTUAL_EXCLUSION */ #if defined(UART_DRIVER_EXT_FIELDS) UART_DRIVER_EXT_FIELDS #endif diff --git a/os/hal/ports/STM32/LLD/USARTv2/uart_lld.c b/os/hal/ports/STM32/LLD/USARTv2/uart_lld.c index 59bb6c229..7594669f0 100644 --- a/os/hal/ports/STM32/LLD/USARTv2/uart_lld.c +++ b/os/hal/ports/STM32/LLD/USARTv2/uart_lld.c @@ -179,7 +179,7 @@ static uartflags_t translate_errors(uint32_t isr) { * * @param[in] uartp pointer to the @p UARTDriver object */ -static void set_rx_idle_loop(UARTDriver *uartp) { +static void uart_enter_rx_idle_loop(UARTDriver *uartp) { uint32_t mode; /* RX DMA channel preparation, if the char callback is defined then the @@ -243,7 +243,7 @@ static void usart_start(UARTDriver *uartp) { u->CR1 = uartp->config->cr1 | cr1; /* Starting the receiver idle loop.*/ - set_rx_idle_loop(uartp); + uart_enter_rx_idle_loop(uartp); } /** @@ -266,23 +266,13 @@ static void uart_lld_serve_rx_end_irq(UARTDriver *uartp, uint32_t flags) { if (uartp->rxstate == UART_RX_IDLE) { /* Receiver in idle state, a callback is generated, if enabled, for each received character and then the driver stays in the same state.*/ - if (uartp->config->rxchar_cb != NULL) - uartp->config->rxchar_cb(uartp, uartp->rxbuf); + _uart_rx_idle_code(uartp); } else { /* Receiver in active state, a callback is generated, if enabled, after a completed transfer.*/ dmaStreamDisable(uartp->dmarx); - uartp->rxstate = UART_RX_COMPLETE; - if (uartp->config->rxend_cb != NULL) - uartp->config->rxend_cb(uartp); - - /* If the callback didn't explicitly change state then the receiver - automatically returns to the idle state.*/ - if (uartp->rxstate == UART_RX_COMPLETE) { - uartp->rxstate = UART_RX_IDLE; - set_rx_idle_loop(uartp); - } + _uart_rx_complete_isr_code(uartp); } } @@ -306,14 +296,7 @@ static void uart_lld_serve_tx_end_irq(UARTDriver *uartp, uint32_t flags) { dmaStreamDisable(uartp->dmatx); /* A callback is generated, if enabled, after a completed transfer.*/ - uartp->txstate = UART_TX_COMPLETE; - if (uartp->config->txend1_cb != NULL) - uartp->config->txend1_cb(uartp); - - /* If the callback didn't explicitly change state then the transmitter - automatically returns to the idle state.*/ - if (uartp->txstate == UART_TX_COMPLETE) - uartp->txstate = UART_TX_IDLE; + _uart_wakeup_tx1_isr(uartp); } /** @@ -332,8 +315,7 @@ static void serve_usart_irq(UARTDriver *uartp) { if (isr & (USART_ISR_LBDF | USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE | USART_ISR_PE)) { - if (uartp->config->rxerr_cb != NULL) - uartp->config->rxerr_cb(uartp, translate_errors(isr)); + _uart_rx_error_isr_code(uartp, translate_errors(isr)); } if ((isr & USART_ISR_TC) && (cr1 & USART_CR1_TCIE)) { @@ -341,8 +323,7 @@ static void serve_usart_irq(UARTDriver *uartp) { u->CR1 = cr1 & ~USART_CR1_TCIE; /* End of transmission, a callback is generated.*/ - if (uartp->config->txend2_cb != NULL) - uartp->config->txend2_cb(uartp); + _uart_wakeup_tx2_isr(uartp); } } @@ -940,7 +921,7 @@ size_t uart_lld_stop_receive(UARTDriver *uartp) { dmaStreamDisable(uartp->dmarx); n = dmaStreamGetTransactionSize(uartp->dmarx); - set_rx_idle_loop(uartp); + uart_enter_rx_idle_loop(uartp); return n; } diff --git a/os/hal/ports/STM32/LLD/USARTv2/uart_lld.h b/os/hal/ports/STM32/LLD/USARTv2/uart_lld.h index b8c227842..395cb3054 100644 --- a/os/hal/ports/STM32/LLD/USARTv2/uart_lld.h +++ b/os/hal/ports/STM32/LLD/USARTv2/uart_lld.h @@ -623,6 +623,26 @@ struct UARTDriver { * @brief Current configuration data. */ const UARTConfig *config; +#if UART_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Synchronization flag for transmit operations. + */ + bool early; + /** + * @brief Waiting thread on RX. + */ + thread_reference_t threadrx; + /** + * @brief Waiting thread on TX. + */ + thread_reference_t threadtx; +#endif /* UART_USE_WAIT */ +#if UART_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* UART_USE_MUTUAL_EXCLUSION */ #if defined(UART_DRIVER_EXT_FIELDS) UART_DRIVER_EXT_FIELDS #endif diff --git a/os/hal/src/uart.c b/os/hal/src/uart.c index f5e28fd48..f521412ec 100644 --- a/os/hal/src/uart.c +++ b/os/hal/src/uart.c @@ -67,10 +67,19 @@ void uartInit(void) { */ void uartObjectInit(UARTDriver *uartp) { - uartp->state = UART_STOP; - uartp->txstate = UART_TX_IDLE; - uartp->rxstate = UART_RX_IDLE; - uartp->config = NULL; + uartp->state = UART_STOP; + uartp->txstate = UART_TX_IDLE; + uartp->rxstate = UART_RX_IDLE; + uartp->config = NULL; +#if UART_USE_WAIT || defined(__DOXYGEN__) + uartp->early = false; + uartp->threadrx = NULL; + uartp->threadtx = NULL; +#endif /* UART_USE_WAIT */ +#if UART_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + osalMutexObjectInit(&uartp->mutex); +#endif /* UART_USE_MUTUAL_EXCLUSION */ + /* Optional, user-defined initializer.*/ #if defined(UART_DRIVER_EXT_INIT_HOOK) UART_DRIVER_EXT_INIT_HOOK(uartp); @@ -233,7 +242,7 @@ size_t uartStopSendI(UARTDriver *uartp) { * or equal to 8 bits else it is organized as uint16_t arrays. * * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] n number of data frames to send + * @param[in] n number of data frames to receive * @param[in] rxbuf the pointer to the receive buffer * * @api @@ -258,7 +267,7 @@ void uartStartReceive(UARTDriver *uartp, size_t n, void *rxbuf) { * @note This function has to be invoked from a lock zone. * * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] n number of data frames to send + * @param[in] n number of data frames to receive * @param[out] rxbuf the pointer to the receive buffer * * @iclass @@ -333,6 +342,172 @@ size_t uartStopReceiveI(UARTDriver *uartp) { return 0; } +#if (UART_USE_WAIT == TRUE) || defined(__DOXYGEN__) +/** + * @brief Performs a transmission on the UART peripheral. + * @note The function returns when the specified number of frames have been + * sent to the UART or on timeout. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in,out] np number of data frames to transmit, on exit the number + * of frames actually transmitted + * @param[in] txbuf the pointer to the transmit buffer + * @return The operation status. + * @retval MSG_OK if the operation completed successfully. + * @retval MSG_TIMEOUT if the operation timed out. + * + * @api + */ +msg_t uartSendTimeout(UARTDriver *uartp, size_t *np, + const void *txbuf, systime_t time) { + msg_t msg; + + osalDbgCheck((uartp != NULL) && (*np > 0U) && (txbuf != NULL)); + + osalSysLock(); + osalDbgAssert(uartp->state == UART_READY, "is active"); + osalDbgAssert(uartp->txstate != UART_TX_ACTIVE, "tx active"); + + /* Transmission start.*/ + uartp->early = true; + uart_lld_start_send(uartp, *np, txbuf); + uartp->txstate = UART_TX_ACTIVE; + + /* Waiting for result.*/ + msg = osalThreadSuspendTimeoutS(&uartp->threadtx, time); + if (msg != MSG_OK) { + *np = uartStopSendI(uartp); + } + osalSysUnlock(); + + return msg; +} + +/** + * @brief Performs a transmission on the UART peripheral. + * @note The function returns when the specified number of frames have been + * physically transmitted or on timeout. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in,out] np number of data frames to transmit, on exit the number + * of frames actually transmitted + * @param[in] txbuf the pointer to the transmit buffer + * @return The operation status. + * @retval MSG_OK if the operation completed successfully. + * @retval MSG_TIMEOUT if the operation timed out. + * + * @api + */ +msg_t uartSendFullTimeout(UARTDriver *uartp, size_t *np, + const void *txbuf, systime_t time) { + msg_t msg; + + osalDbgCheck((uartp != NULL) && (*np > 0U) && (txbuf != NULL)); + + osalSysLock(); + osalDbgAssert(uartp->state == UART_READY, "is active"); + osalDbgAssert(uartp->txstate != UART_TX_ACTIVE, "tx active"); + + /* Transmission start.*/ + uartp->early = false; + uart_lld_start_send(uartp, *np, txbuf); + uartp->txstate = UART_TX_ACTIVE; + + /* Waiting for result.*/ + msg = osalThreadSuspendTimeoutS(&uartp->threadtx, time); + if (msg != MSG_OK) { + *np = uartStopSendI(uartp); + } + osalSysUnlock(); + + return msg; +} + +/** + * @brief Performs a receive operation on the UART peripheral. + * @note The function returns when the specified number of frames have been + * received or on error/timeout. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in,out] np number of data frames to receive, on exit the number + * of frames actually received + * @param[in] rxbuf the pointer to the receive buffer + * @param[in] time operation timeout + * + * @return The operation status. + * @retval MSG_OK if the operation completed successfully. + * @retval MSG_TIMEOUT if the operation timed out. + * @retval MSG_RESET in case of a receive error. + * + * @api + */ +msg_t uartReceiveTimeout(UARTDriver *uartp, size_t *np, + void *rxbuf, systime_t time) { + msg_t msg; + + osalDbgCheck((uartp != NULL) && (*np > 0U) && (rxbuf != NULL)); + + osalSysLock(); + osalDbgAssert(uartp->state == UART_READY, "is active"); + osalDbgAssert(uartp->rxstate != UART_RX_ACTIVE, "rx active"); + + /* Receive start.*/ + uart_lld_start_receive(uartp, *np, rxbuf); + uartp->rxstate = UART_RX_ACTIVE; + + /* Waiting for result.*/ + msg = osalThreadSuspendTimeoutS(&uartp->threadrx, time); + if (msg != MSG_OK) { + *np = uartStopReceiveI(uartp); + } + osalSysUnlock(); + + return msg; +} +#endif + +#if (UART_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__) +/** + * @brief Gains exclusive access to the UART bus. + * @details This function tries to gain ownership to the UART bus, if the bus + * is already being used then the invoking thread is queued. + * @pre In order to use this function the option @p UART_USE_MUTUAL_EXCLUSION + * must be enabled. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @api + */ +void uartAcquireBus(UARTDriver *uartp) { + + osalDbgCheck(uartp != NULL); + + osalMutexLock(&uartp->mutex); +} + +/** + * @brief Releases exclusive access to the UART bus. + * @pre In order to use this function the option @p UART_USE_MUTUAL_EXCLUSION + * must be enabled. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @api + */ +void uartReleaseBus(UARTDriver *uartp) { + + osalDbgCheck(uartp != NULL); + + osalMutexUnlock(&uartp->mutex); +} +#endif + #endif /* HAL_USE_UART == TRUE */ /** @} */ diff --git a/os/hal/templates/uart_lld.h b/os/hal/templates/uart_lld.h index 13d105b16..0451dcb21 100644 --- a/os/hal/templates/uart_lld.h +++ b/os/hal/templates/uart_lld.h @@ -143,6 +143,26 @@ struct UARTDriver { * @brief Current configuration data. */ const UARTConfig *config; +#if UART_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Synchronization flag for transmit operations. + */ + bool early; + /** + * @brief Waiting thread on RX. + */ + thread_reference_t threadrx; + /** + * @brief Waiting thread on TX. + */ + thread_reference_t threadtx; +#endif /* UART_USE_WAIT */ +#if UART_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* UART_USE_MUTUAL_EXCLUSION */ #if defined(UART_DRIVER_EXT_FIELDS) UART_DRIVER_EXT_FIELDS #endif diff --git a/readme.txt b/readme.txt index 995175aa4..06857c225 100644 --- a/readme.txt +++ b/readme.txt @@ -76,6 +76,7 @@ - HAL: Introduced preliminary support for STM32F7xx devices. - HAL: Introduced preliminary support for STM32L4xx devices. - HAL: Introduced preliminary support for STM32L0xx devices. +- HAL: Added synchronous API and mutual exclusion to the UART driver. - HAL: Added PAL driver for STM32L4xx GPIOv3 peripheral. - HAL: Added I2S driver for STM32 SPIv2 peripheral. - HAL: Added demos and board files for ST's Nucleo32 boards (F031, F042, F303).