diff options
-rw-r--r-- | demos/ARMCM3-STM32F103-GCC/mcuconf.h | 7 | ||||
-rw-r--r-- | os/hal/platforms/STM32/serial_lld.c | 2 | ||||
-rw-r--r-- | os/hal/platforms/STM32/stm32_dma.h | 12 | ||||
-rw-r--r-- | os/hal/platforms/STM32/uart_lld.c | 183 | ||||
-rw-r--r-- | 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 @@ -147,6 +147,18 @@ typedef struct { }
/**
+ * @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
* as parameter.
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 @@ -48,47 +48,79 @@ UARTDriver UARTD1; /*===========================================================================*/
/**
+ * @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;
/*===========================================================================*/
|