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 --- demos/ARMCM3-STM32F103-GCC/mcuconf.h | 7 ++ os/hal/platforms/STM32/serial_lld.c | 2 +- os/hal/platforms/STM32/stm32_dma.h | 12 +++ os/hal/platforms/STM32/uart_lld.c | 183 ++++++++++++++++++++++++----------- os/hal/platforms/STM32/uart_lld.h | 2 +- 5 files changed, 149 insertions(+), 57 deletions(-) diff --git a/demos/ARMCM3-STM32F103-GCC/mcuconf.h b/demos/ARMCM3-STM32F103-GCC/mcuconf.h index 731ec9f85..723b5b991 100644 --- a/demos/ARMCM3-STM32F103-GCC/mcuconf.h +++ b/demos/ARMCM3-STM32F103-GCC/mcuconf.h @@ -98,3 +98,10 @@ #define STM32_SPI1_IRQ_PRIORITY 10 #define STM32_SPI2_IRQ_PRIORITY 10 #define STM32_SPI1_DMA_ERROR_HOOK() chSysHalt() + +/* + * UART driver system settings. + */ +#define STM32_UART_USE_USART1 TRUE +#define STM32_UART_USART1_IRQ_PRIORITY 12 +#define STM32_UART_USART1_DMA_PRIORITY 1 diff --git a/os/hal/platforms/STM32/serial_lld.c b/os/hal/platforms/STM32/serial_lld.c index 9ddb8dfa1..8885af9af 100644 --- a/os/hal/platforms/STM32/serial_lld.c +++ b/os/hal/platforms/STM32/serial_lld.c @@ -122,7 +122,7 @@ static void usart_deinit(USART_TypeDef *u) { } /** - * @brief Error handling routine. + * @brief Error handling routine. * * @param[in] sdp pointer to a @p SerialDriver object * @param[in] sr USART SR register value diff --git a/os/hal/platforms/STM32/stm32_dma.h b/os/hal/platforms/STM32/stm32_dma.h index 94e863d31..567b3ef0b 100644 --- a/os/hal/platforms/STM32/stm32_dma.h +++ b/os/hal/platforms/STM32/stm32_dma.h @@ -146,6 +146,18 @@ typedef struct { (dmachp)->CCR = (uint32_t)(ccr); \ } +/** + * @brief DMA channel enable. + * @note Channels are numbered from 0 to 6, use the appropriate macro + * as parameter. + * + * @param[in] dmap pointer to a stm32_dma_t structure + * @param[in] ch channel number + */ +#define dmaEnableChannel(dmap, ch) { \ + (dmap)->channels[ch].CCR |= 1; \ +} + /** * @brief DMA channel disable. * @note Channels are numbered from 0 to 6, use the appropriate macro 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) { diff --git a/os/hal/platforms/STM32/uart_lld.h b/os/hal/platforms/STM32/uart_lld.h index 2977e15c5..50eacbd9c 100644 --- a/os/hal/platforms/STM32/uart_lld.h +++ b/os/hal/platforms/STM32/uart_lld.h @@ -148,7 +148,7 @@ typedef struct { /** @brief Transmit DMA channel.*/ uint8_t ud_dmatx; /** @brief Default receive buffer while into @p UART_RX_IDLE state.*/ - uint16_t ud_rxbuf; + volatile uint16_t ud_rxbuf; } UARTDriver; /*===========================================================================*/ -- cgit v1.2.3