diff --git a/bsp/stm32/libraries/HAL_Drivers/drivers/drv_usart_v2.c b/bsp/stm32/libraries/HAL_Drivers/drivers/drv_usart_v2.c index f766edb962c..13412872c7a 100644 --- a/bsp/stm32/libraries/HAL_Drivers/drivers/drv_usart_v2.c +++ b/bsp/stm32/libraries/HAL_Drivers/drivers/drv_usart_v2.c @@ -269,7 +269,7 @@ static rt_err_t stm32_control(struct rt_serial_device *serial, int cmd, void *ar if (ctrl_arg & (RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_DMA_TX)) { #ifdef RT_SERIAL_USING_DMA - stm32_uart_dma_config(serial, ctrl_arg); + return stm32_uart_dma_config(serial, ctrl_arg); #else return -RT_ENOSYS; #endif @@ -411,12 +411,113 @@ static rt_ssize_t stm32_transmit(struct rt_serial_device *serial, } #ifdef RT_SERIAL_USING_DMA +/** + * @brief Resolve effective STM32 UART RX DMA event mode. + * @param request Requested RX DMA event mode. + * @param effective Pointer to the output effective mode. + * @return Operation status. + */ +static rt_err_t stm32_uart_resolve_rx_dma_event_mode(enum rt_serial_rx_dma_event_mode request, + enum rt_serial_rx_dma_event_mode *effective) +{ + if (effective == RT_NULL) + { + return -RT_EINVAL; + } + + if (request == RT_SERIAL_RX_DMA_EVENT_AUTO) + { + *effective = RT_SERIAL_RX_DMA_EVENT_HALF_FULL; + return RT_EOK; + } + + if ((request != RT_SERIAL_RX_DMA_EVENT_NONE) && + (request != RT_SERIAL_RX_DMA_EVENT_FULL_ONLY) && + (request != RT_SERIAL_RX_DMA_EVENT_HALF_FULL)) + { + return -RT_EINVAL; + } + + *effective = request; + return RT_EOK; +} + +/** + * @brief Configure STM32 UART RX DMA transfer event interrupts. + * @param dma_handle Pointer to the RX DMA handle linked to the UART. + * @param mode Effective serial RX DMA event mode. + */ +static void stm32_uart_configure_rx_dma_events(DMA_HandleTypeDef *dma_handle, enum rt_serial_rx_dma_event_mode mode) +{ + if (dma_handle == RT_NULL) + { + return; + } + + if (mode == RT_SERIAL_RX_DMA_EVENT_NONE) + { + __HAL_DMA_DISABLE_IT(dma_handle, DMA_IT_HT); + __HAL_DMA_DISABLE_IT(dma_handle, DMA_IT_TC); + } + else if (mode == RT_SERIAL_RX_DMA_EVENT_FULL_ONLY) + { + __HAL_DMA_DISABLE_IT(dma_handle, DMA_IT_HT); + __HAL_DMA_ENABLE_IT(dma_handle, DMA_IT_TC); + } + else + { + __HAL_DMA_ENABLE_IT(dma_handle, DMA_IT_HT); + __HAL_DMA_ENABLE_IT(dma_handle, DMA_IT_TC); + } +} + +/** + * @brief Check whether one STM32 UART RX DMA event should be handled. + * @param serial Pointer to the serial device. + * @param isr_flag UART RX DMA ISR source flag. + * @return RT_TRUE if the event should be handled. + */ +static rt_bool_t stm32_uart_rx_dma_event_enabled(struct rt_serial_device *serial, rt_uint8_t isr_flag) +{ + enum rt_serial_rx_dma_event_mode mode; + + RT_ASSERT(serial != RT_NULL); + + if (isr_flag == UART_RX_DMA_IT_IDLE_FLAG) + { + return RT_TRUE; + } + + if (stm32_uart_resolve_rx_dma_event_mode(serial->config.rx_dma_event_mode, &mode) != RT_EOK) + { + return RT_FALSE; + } + + if (isr_flag == UART_RX_DMA_IT_HT_FLAG) + { + return (mode == RT_SERIAL_RX_DMA_EVENT_HALF_FULL) ? RT_TRUE : RT_FALSE; + } + + if (isr_flag == UART_RX_DMA_IT_TC_FLAG) + { + return (mode != RT_SERIAL_RX_DMA_EVENT_NONE) ? RT_TRUE : RT_FALSE; + } + + return RT_FALSE; +} + static void dma_recv_isr(struct rt_serial_device *serial, rt_uint8_t isr_flag) { struct stm32_uart *uart; rt_size_t recv_len, counter; RT_ASSERT(serial != RT_NULL); + + if (stm32_uart_rx_dma_event_enabled(serial, isr_flag) != RT_TRUE) + { + return; + } + uart = rt_container_of(serial, struct stm32_uart, serial); counter = __HAL_DMA_GET_COUNTER(&(uart->dma_rx.handle)); @@ -431,7 +532,7 @@ static void dma_recv_isr(struct rt_serial_device *serial, rt_uint8_t isr_flag) rt_uint8_t *ptr = NULL; rt_hw_serial_control_isr(serial, RT_HW_SERIAL_CTRL_GET_DMA_PING_BUF, (void *)&ptr); SCB_InvalidateDCache_by_Addr((uint32_t *)ptr, serial->config.dma_ping_bufsz); -#endif +#endif /* defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */ uart->dma_rx.remaining_cnt = counter; rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8)); } @@ -494,13 +595,13 @@ static void uart_isr(struct rt_serial_device *serial) } #ifdef RT_SERIAL_USING_DMA - if ((uart->uart_dma_flag) && (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_IDLE)) + if ((uart->uart_dma_flag & RT_DEVICE_FLAG_DMA_RX) && (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_IDLE)) && (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_IDLE))) { dma_recv_isr(serial, UART_RX_DMA_IT_IDLE_FLAG); __HAL_UART_CLEAR_IDLEFLAG(&uart->handle); } -#endif +#endif /* RT_SERIAL_USING_DMA */ if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_ORE)) { @@ -1113,6 +1214,7 @@ static rt_err_t stm32_uart_dma_config(struct rt_serial_device *serial, rt_ubase_ DMA_HandleTypeDef *DMA_Handle; const struct stm32_dma_config *dma_config; struct stm32_uart *uart; + enum rt_serial_rx_dma_event_mode event_mode; RT_ASSERT(serial != RT_NULL); RT_ASSERT(flag == RT_DEVICE_FLAG_DMA_TX || flag == RT_DEVICE_FLAG_DMA_RX); @@ -1122,6 +1224,11 @@ static rt_err_t stm32_uart_dma_config(struct rt_serial_device *serial, rt_ubase_ { DMA_Handle = &uart->dma_rx.handle; dma_config = uart->config->dma_rx; + if (stm32_uart_resolve_rx_dma_event_mode(serial->config.rx_dma_event_mode, &event_mode) != RT_EOK) + { + LOG_E("%s invalid uart dma rx event mode", uart->config->name); + return -RT_EINVAL; + } } else /* RT_DEVICE_FLAG_DMA_TX == flag */ { @@ -1172,6 +1279,7 @@ static rt_err_t stm32_uart_dma_config(struct rt_serial_device *serial, rt_ubase_ DMA_Handle->Parent = RT_NULL; return -RT_ERROR; } + stm32_uart_configure_rx_dma_events(DMA_Handle, event_mode); CLEAR_BIT(uart->handle.Instance->CR3, USART_CR3_EIE); __HAL_UART_ENABLE_IT(&(uart->handle), UART_IT_IDLE); } diff --git a/components/drivers/include/drivers/dev_serial_v2.h b/components/drivers/include/drivers/dev_serial_v2.h index b987bc45434..2a8d9145e65 100644 --- a/components/drivers/include/drivers/dev_serial_v2.h +++ b/components/drivers/include/drivers/dev_serial_v2.h @@ -225,21 +225,37 @@ #define RT_SERIAL_FLOWCONTROL_CTSRTS 1 #define RT_SERIAL_FLOWCONTROL_NONE 0 +#ifdef RT_SERIAL_USING_DMA +/** + * @brief Serial RX DMA data event mode. + * @note This mode controls only DMA half/full transfer data events. + * UART IDLE interrupt remains enabled in RX DMA mode. + */ +enum rt_serial_rx_dma_event_mode +{ + RT_SERIAL_RX_DMA_EVENT_AUTO = 0, /**< Use the default RX DMA event mode. */ + RT_SERIAL_RX_DMA_EVENT_NONE, /**< Disable DMA half/full data events; UART IDLE still reports received data. */ + RT_SERIAL_RX_DMA_EVENT_FULL_ONLY, /**< Report only DMA transfer-complete data events plus UART IDLE. */ + RT_SERIAL_RX_DMA_EVENT_HALF_FULL /**< Report both DMA half-transfer and transfer-complete data events plus UART IDLE. */ +}; +#endif /* RT_SERIAL_USING_DMA */ + /* Default config for serial_configure structure */ #ifdef RT_SERIAL_USING_DMA -#define RT_SERIAL_CONFIG_DEFAULT \ -{ \ - BAUD_RATE_115200, /* 115200 bits/s */ \ - DATA_BITS_8, /* 8 databits */ \ - STOP_BITS_1, /* 1 stopbit */ \ - PARITY_NONE, /* No parity */ \ - BIT_ORDER_LSB, /* LSB first sent */ \ - NRZ_NORMAL, /* Normal mode */ \ - RT_SERIAL_RX_MINBUFSZ, /* rxBuf size */ \ - RT_SERIAL_TX_MINBUFSZ, /* txBuf size */ \ - RT_SERIAL_FLOWCONTROL_NONE, /* Off flowcontrol */ \ - 0, /* reserved */ \ - RT_SERIAL_RX_MINBUFSZ / 2, /* dma_ping_bufsz */ \ +#define RT_SERIAL_CONFIG_DEFAULT \ +{ \ + BAUD_RATE_115200, /* 115200 bits/s */ \ + DATA_BITS_8, /* 8 databits */ \ + STOP_BITS_1, /* 1 stopbit */ \ + PARITY_NONE, /* No parity */ \ + BIT_ORDER_LSB, /* LSB first sent */ \ + NRZ_NORMAL, /* Normal mode */ \ + RT_SERIAL_RX_MINBUFSZ, /* rxBuf size */ \ + RT_SERIAL_TX_MINBUFSZ, /* txBuf size */ \ + RT_SERIAL_FLOWCONTROL_NONE, /* Off flowcontrol */ \ + 0, /* reserved */ \ + RT_SERIAL_RX_MINBUFSZ / 2, /* dma_ping_bufsz */ \ + RT_SERIAL_RX_DMA_EVENT_AUTO, /* rx dma event mode */ \ } #else #define RT_SERIAL_CONFIG_DEFAULT \ @@ -255,7 +271,7 @@ RT_SERIAL_FLOWCONTROL_NONE, /* Off flowcontrol */ \ 0, /* reserved */ \ } -#endif +#endif /* RT_SERIAL_USING_DMA */ /** * @brief Serial receive indicate hook function type @@ -280,7 +296,8 @@ struct serial_configure #ifdef RT_SERIAL_USING_DMA rt_uint32_t dma_ping_bufsz :16; -#endif + enum rt_serial_rx_dma_event_mode rx_dma_event_mode; /**< RX DMA half/full data event mode. */ +#endif /* RT_SERIAL_USING_DMA */ }; /** diff --git a/components/drivers/serial/dev_serial_v2.c b/components/drivers/serial/dev_serial_v2.c index d8cc2e0cb5a..e7851f07ea2 100644 --- a/components/drivers/serial/dev_serial_v2.c +++ b/components/drivers/serial/dev_serial_v2.c @@ -102,7 +102,7 @@ static int serial_fops_open(struct dfs_file *fd) rt_device_close(device); ret = rt_device_open(device, flags); - + if (ret == RT_EOK) { serial = (struct rt_serial_device *)device; @@ -1388,6 +1388,21 @@ static void _tc_flush(struct rt_serial_device *serial, int queue) } #endif /* RT_USING_POSIX_TERMIOS */ +#ifdef RT_SERIAL_USING_DMA +/** + * @brief Check whether one serial RX DMA event mode value is valid. + * @param mode RX DMA event mode value. + * @return RT_TRUE if the mode value is valid. + */ +static rt_bool_t rt_serial_rx_dma_event_mode_valid(enum rt_serial_rx_dma_event_mode mode) +{ + return ((mode == RT_SERIAL_RX_DMA_EVENT_AUTO) || + (mode == RT_SERIAL_RX_DMA_EVENT_NONE) || + (mode == RT_SERIAL_RX_DMA_EVENT_FULL_ONLY) || + (mode == RT_SERIAL_RX_DMA_EVENT_HALF_FULL)) ? RT_TRUE : RT_FALSE; +} +#endif /* RT_SERIAL_USING_DMA */ + /** * @brief Control the serial device. * @param dev The pointer of device driver structure @@ -1425,20 +1440,36 @@ static rt_err_t rt_serial_control(struct rt_device *dev, else { struct serial_configure *pconfig = (struct serial_configure *)args; + struct serial_configure old_config = serial->config; + +#ifdef RT_SERIAL_USING_DMA + if (rt_serial_rx_dma_event_mode_valid(pconfig->rx_dma_event_mode) != RT_TRUE) + { + ret = -RT_EINVAL; + break; + } +#endif /* RT_SERIAL_USING_DMA */ + if (((pconfig->rx_bufsz != serial->config.rx_bufsz) || (pconfig->tx_bufsz != serial->config.tx_bufsz) #ifdef RT_SERIAL_USING_DMA || (pconfig->dma_ping_bufsz != serial->config.dma_ping_bufsz) -#endif + || (pconfig->rx_dma_event_mode != serial->config.rx_dma_event_mode) +#endif /* RT_SERIAL_USING_DMA */ ) && serial->parent.ref_count != 0) { - /*can not change buffer size*/ + /* can not change buffer or DMA event configuration while opened */ ret = -RT_EBUSY; break; } + /* set serial configure */ serial->config = *pconfig; - serial->ops->configure(serial, (struct serial_configure *)args); + ret = serial->ops->configure(serial, (struct serial_configure *)args); + if (ret != RT_EOK) + { + serial->config = old_config; + } } break; case RT_SERIAL_CTRL_GET_CONFIG: