From 8249123228e7de9f1694905d090f91a063306bdf Mon Sep 17 00:00:00 2001 From: gdisirio Date: Tue, 27 Jul 2010 14:44:28 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@2095 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/platforms/STM32/uart_lld.c | 183 ++++++++++++++++++++++++++------------ 1 file changed, 128 insertions(+), 55 deletions(-) (limited to 'os/hal/platforms/STM32/uart_lld.c') diff --git a/os/hal/platforms/STM32/uart_lld.c b/os/hal/platforms/STM32/uart_lld.c index 56930a686..619fcebb6 100644 --- a/os/hal/platforms/STM32/uart_lld.c +++ b/os/hal/platforms/STM32/uart_lld.c @@ -47,48 +47,80 @@ UARTDriver UARTD1; /* Driver local functions. */ /*===========================================================================*/ +/** + * @brief Status bits translation. + * + * @param[in] sr USART SR register value + * + * @return The error flags. + */ +static uartflags_t translate_errors(uint16_t sr) { + uartflags_t sts = 0; + + if (sr & USART_SR_ORE) + sts |= UART_OVERRUN_ERROR; + if (sr & USART_SR_PE) + sts |= UART_PARITY_ERROR; + if (sr & USART_SR_FE) + sts |= UART_FRAMING_ERROR; + if (sr & USART_SR_NE) + sts |= UART_NOISE_ERROR; + if (sr & USART_SR_LBD) + sts |= UART_BREAK_DETECTED; + return sts; +} + /** * @brief Puts the receiver in the UART_RX_IDLE state. * - * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] uartp pointer to the @p UARTDriver object */ -static void set_rx_idle(UARTDriver *uartp) { +static void set_rx_idle_loop(UARTDriver *uartp) { uint32_t ccr; - dmaDisableChannel(uartp->ud_dmap, uartp->ud_dmarx); - dmaClearChannel(uartp->ud_dmap, uartp->ud_dmarx); - uartp->ud_rxstate = UART_RX_IDLE; - - /* RX DMA channel preparation, circular 1 frame transfers, an interrupt is - generated for each received character if the callback is defined.*/ - ccr = DMA_CCR1_TEIE | DMA_CCR1_CIRC | DMA_CCR1_EN; - if (uartp->ud_config->uc_rxchar != NULL) - ccr |= DMA_CCR1_TCIE; + /* RX DMA channel preparation, if the char callback is defined then the + TCIE interrupt is enabled too.*/ + if (uartp->ud_config->uc_rxchar == NULL) + ccr = DMA_CCR1_CIRC | DMA_CCR1_TEIE; + else + ccr = DMA_CCR1_CIRC | DMA_CCR1_TEIE | DMA_CCR1_TCIE; dmaSetupChannel(uartp->ud_dmap, uartp->ud_dmarx, 1, &uartp->ud_rxbuf, uartp->ud_dmaccr | ccr); + dmaEnableChannel(uartp->ud_dmap, uartp->ud_dmarx); } /** - * @brief Puts the transmitter in the UART_TX_IDLE state. + * @brief USART de-initialization. + * @details This function must be invoked with interrupts disabled. * - * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] uartp pointer to the @p UARTDriver object */ -static void set_tx_idle(UARTDriver *uartp) { +static void usart_stop(UARTDriver *uartp) { + /* Stops RX and TX DMA channels.*/ + dmaDisableChannel(uartp->ud_dmap, uartp->ud_dmarx); dmaDisableChannel(uartp->ud_dmap, uartp->ud_dmatx); + dmaClearChannel(uartp->ud_dmap, uartp->ud_dmarx); dmaClearChannel(uartp->ud_dmap, uartp->ud_dmatx); - uartp->ud_txstate = UART_TX_IDLE; + + /* Stops USART operations.*/ + uartp->ud_usart->CR1 = 0; + uartp->ud_usart->CR2 = 0; + uartp->ud_usart->CR3 = 0; } /** * @brief USART initialization. * @details This function must be invoked with interrupts disabled. * - * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] uartp pointer to the @p UARTDriver object */ static void usart_start(UARTDriver *uartp) { USART_TypeDef *u = uartp->ud_usart; + /* Defensive programming, starting from a clean state.*/ + usart_stop(uartp); + /* Baud rate setting.*/ if (uartp->ud_usart == USART1) u->BRR = STM32_PCLK2 / uartp->ud_config->uc_speed; @@ -106,28 +138,77 @@ static void usart_start(UARTDriver *uartp) { (void)u->SR; /* SR reset step 1.*/ (void)u->DR; /* SR reset step 2.*/ - set_rx_idle(uartp); - set_tx_idle(uartp); + /* Starting the receiver idle loop.*/ + set_rx_idle_loop(uartp); } /** - * @brief USART de-initialization. - * @details This function must be invoked with interrupts disabled. + * @brief RX DMA common service routine. * - * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] uartp pointer to the @p UARTDriver object */ -static void usart_stop(UARTDriver *uartp) { +static void serve_rx_end_irq(UARTDriver *uartp) { + + uartp->ud_rxstate = UART_RX_COMPLETE; + if (uartp->ud_config->uc_rxend != NULL) + uartp->ud_config->uc_rxend(); + /* If the callback didn't explicitely change state then the receiver + automatically returns to the idle state.*/ + if (uartp->ud_rxstate == UART_RX_COMPLETE) { + uartp->ud_rxstate = UART_RX_IDLE; + set_rx_idle_loop(uartp); + } +} - /* Stops RX and TX DMA channels.*/ - dmaDisableChannel(uartp->ud_dmap, uartp->ud_dmarx); - dmaDisableChannel(uartp->ud_dmap, uartp->ud_dmatx); - dmaClearChannel(uartp->ud_dmap, uartp->ud_dmarx); - dmaClearChannel(uartp->ud_dmap, uartp->ud_dmatx); +/** + * @brief TX DMA common service routine. + * + * @param[in] uartp pointer to the @p UARTDriver object + */ +static void serve_tx_end_irq(UARTDriver *uartp) { + + /* A callback is generated, if enabled, after a completed transfer.*/ + uartp->ud_txstate = UART_TX_COMPLETE; + if (uartp->ud_config->uc_txend1 != NULL) + uartp->ud_config->uc_txend1(); + /* If the callback didn't explicitely change state then the transmitter + automatically returns to the idle state.*/ + if (uartp->ud_txstate == UART_TX_COMPLETE) + uartp->ud_txstate = UART_TX_IDLE; +} +/** + * @brief USART common service routine. + * + * @param[in] uartp pointer to the @p UARTDriver object + */ +static void serve_usart_irq(UARTDriver *uartp) { + uint16_t sr; + USART_TypeDef *u = uartp->ud_usart; - /* Stops USART operations.*/ - uartp->ud_usart->CR1 = 0; - uartp->ud_usart->CR2 = 0; - uartp->ud_usart->CR3 = 0; + sr = u->SR; /* SR reset step 1.*/ + (void)u->DR; /* SR reset step 2.*/ +///////////////// u->SR = 0; /* Clears the LBD bit in the SR.*/ + if (uartp->ud_rxstate == UART_RX_IDLE) { + /* Receiver in idle state, a callback is generated, if enabled, for each + receive error and then the driver stays in the same state.*/ + if (uartp->ud_config->uc_rxerr != NULL) + uartp->ud_config->uc_rxerr(translate_errors(sr)); + } + else { + /* Receiver in active state, a callback is generated and the receive + operation aborts.*/ + dmaDisableChannel(uartp->ud_dmap, uartp->ud_dmarx); + dmaClearChannel(uartp->ud_dmap, uartp->ud_dmarx); + uartp->ud_rxstate = UART_RX_ERROR; + if (uartp->ud_config->uc_rxerr != NULL) + uartp->ud_config->uc_rxerr(translate_errors(sr)); + /* If the callback didn't explicitely change state then the receiver + automatically returns to the idle state.*/ + if (uartp->ud_rxstate == UART_RX_ERROR) { + uartp->ud_rxstate = UART_RX_IDLE; + set_rx_idle_loop(uartp); + } + } } /*===========================================================================*/ @@ -143,25 +224,20 @@ CH_IRQ_HANDLER(DMA1_Ch4_IRQHandler) { CH_IRQ_PROLOGUE(); - dmaClearChannel(&DMA1, STM32_DMA_CHANNEL_4); - + dmaClearChannel(STM32_DMA1, STM32_DMA_CHANNEL_4); uartp = &UARTD1; if (uartp->ud_rxstate == UART_RX_IDLE) { + /* Fast IRQ path, this is why it is not centralized in serve_rx_end_irq().*/ /* Receiver in idle state, a callback is generated, if enabled, for each - received character.*/ + received character and then the driver stays in the same state.*/ if (uartp->ud_config->uc_rxchar != NULL) uartp->ud_config->uc_rxchar(uartp->ud_rxbuf); } else { /* Receiver in active state, a callback is generated, if enabled, after a completed transfer.*/ - uartp->ud_rxstate = UART_RX_COMPLETE; - if (uartp->ud_config->uc_rxend != NULL) - uartp->ud_config->uc_rxend(); - /* If the callback didn't restart a receive operation then the receiver - returns to the idle state.*/ - if (uartp->ud_rxstate == UART_RX_COMPLETE) - set_rx_idle(uartp); + dmaDisableChannel(STM32_DMA1, STM32_DMA_CHANNEL_4); + serve_rx_end_irq(uartp); } CH_IRQ_EPILOGUE(); @@ -174,16 +250,9 @@ CH_IRQ_HANDLER(DMA1_Ch5_IRQHandler) { CH_IRQ_PROLOGUE(); - dmaClearChannel(&DMA1, STM32_DMA_CHANNEL_5); - - /* A callback is generated, if enabled, after a completed transfer.*/ - uartp->ud_txstate = UART_TX_COMPLETE; - if (UARTD1.ud_config->uc_txend1 != NULL) - UARTD1.ud_config->uc_txend1(); - /* If the callback didn't restart a transmit operation then the transmitter - returns to the idle state.*/ - if (uartp->ud_txstate == UART_TX_COMPLETE) - set_tx_idle(uartp); + dmaClearChannel(STM32_DMA1, STM32_DMA_CHANNEL_5); + dmaDisableChannel(STM32_DMA1, STM32_DMA_CHANNEL_5); + serve_tx_end_irq(&UARTD1); CH_IRQ_EPILOGUE(); } @@ -192,6 +261,7 @@ CH_IRQ_HANDLER(USART1_IRQHandler) { CH_IRQ_PROLOGUE(); + serve_usart_irq(&UARTD1); CH_IRQ_EPILOGUE(); } @@ -220,7 +290,7 @@ void uart_lld_init(void) { /** * @brief Configures and activates the UART peripheral. * - * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] uartp pointer to the @p UARTDriver object */ void uart_lld_start(UARTDriver *uartp) { @@ -246,13 +316,16 @@ void uart_lld_start(UARTDriver *uartp) { uartp->ud_dmap->channels[uartp->ud_dmarx].CPAR = (uint32_t)&uartp->ud_usart->DR; uartp->ud_dmap->channels[uartp->ud_dmatx].CPAR = (uint32_t)&uartp->ud_usart->DR; } + + uartp->ud_rxstate = UART_RX_IDLE; + uartp->ud_txstate = UART_TX_IDLE; usart_start(uartp); } /** * @brief Deactivates the UART peripheral. * - * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] uartp pointer to the @p UARTDriver object */ void uart_lld_stop(UARTDriver *uartp) { @@ -289,7 +362,7 @@ void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf) { * @brief Stops any ongoing transmission. * @note Stopping a transmission also suppresses the transmission callbacks. * - * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] uartp pointer to the @p UARTDriver object */ void uart_lld_stop_send(UARTDriver *uartp) { @@ -312,7 +385,7 @@ void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf) { * @brief Stops any ongoing receive operation. * @note Stopping a receive operation also suppresses the receive callbacks. * - * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] uartp pointer to the @p UARTDriver object */ void uart_lld_stop_receive(UARTDriver *uartp) { -- cgit v1.2.3