本帖最后由 radio2radio 于 2019-3-11 14:32 编辑
周末有时间,测试了一下STM32F103的DMA串口收发程序,基于CubeMX的,结果却是令人大失所望。
我在去年,测试了一下【中断模式】的,结果是速度超快。
速度115200bps和1Mbps,双向同时收发100万字符无差错。 2Mbps,单方向100万字符无差错。
详情请见: STM32基于CubeMX的高速串口收发程序(中断模式)
那时就有网友,问我为什么不用DMA模式,我也认为DMA的好处多多,只是没有时间验证一下。
现在,我得到的结果是,DMA模式用在UART这种低速外设上面,可能性能并不好,不如中断模式的。
请网友们给看一看,希望我的代码有问题。
先说我的测试结果吧:
STM32F103C8T6 Bluepill板,MCU时钟72MHz,用CubeMX配置出DMA模式的两个串口收发。
添加少量代码,就做成了两个串口互相收发。 与上面说的中断模式的用法一样。
结果是,115200波特率,以10ms间隔发送接收40个字符,单方向正常,双方向同时收发就丢失数据。
如果时间间隔放到200ms,双方向同时收发,也能正常了。
下面,看看我用的代码:
CubeMX的配置过程,就不累叙了,附件里面有配置文件。
- //main.c 添加的代码:
- //变量:
- #define DMA_RX_BUFFER_SIZE 128
- uint8_t UART1_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};
- uint8_t UART2_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};
- uint8_t recv_end_flag1 = 0;
- uint8_t rx_len1 = 0;
- uint8_t recv_end_flag2 = 0;
- uint8_t rx_len2 = 0;
- 。。。
- //初始化
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
- __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
-
- HAL_UART_Receive_DMA(&huart1, UART1_Rx_Buffer, DMA_RX_BUFFER_SIZE);
- HAL_UART_Receive_DMA(&huart2, UART2_Rx_Buffer, DMA_RX_BUFFER_SIZE);
- 。。。
-
- //main()
- if(recv_end_flag1 ==1){ //UART1 Rx, UART2 Tx
- HAL_UART_Transmit_DMA(&huart2,UART1_Rx_Buffer,rx_len1);
- rx_len1=0;
- recv_end_flag1=0;
- HAL_UART_Receive_DMA(&huart1,UART1_Rx_Buffer,DMA_RX_BUFFER_SIZE);
- }
-
- if(recv_end_flag2 ==1){ //UART2 Rx, UART1 Tx
- HAL_UART_Transmit_DMA(&huart1,UART2_Rx_Buffer,rx_len2);
- rx_len2=0;
- recv_end_flag2=0;
- HAL_UART_Receive_DMA(&huart2,UART2_Rx_Buffer,DMA_RX_BUFFER_SIZE);
- }
-
-
-
-
-
- //stm32f1xx_it.c 中断服务程序里面修改的代码:
- extern uint8_t recv_end_flag1;
- extern uint8_t rx_len1;
- extern uint8_t recv_end_flag2;
- extern uint8_t rx_len2;
- #define BUFFER_SIZE 128
- void USART1_IRQHandler(void)
- {
- /* USER CODE BEGIN USART1_IRQn 0 */
- uint32_t tmp_flag = 0;
- uint32_t temp;
- /* USER CODE END USART1_IRQn 0 */
- HAL_UART_IRQHandler(&huart1);
- /* USER CODE BEGIN USART1_IRQn 1 */
- tmp_flag = __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
- if((tmp_flag != RESET)){
- __HAL_UART_CLEAR_IDLEFLAG(&huart1);
- HAL_UART_DMAStop(&huart1);
- temp = hdma_usart1_rx.Instance->CNDTR;
- rx_len1 = BUFFER_SIZE - temp;
- recv_end_flag1 = 1;
- }
- /* USER CODE END USART1_IRQn 1 */
- }
- /**
- * @brief This function handles USART2 global interrupt.
- */
- void USART2_IRQHandler(void)
- {
- /* USER CODE BEGIN USART1_IRQn 0 */
- uint32_t tmp_flag = 0;
- uint32_t temp;
- /* USER CODE END USART1_IRQn 0 */
- HAL_UART_IRQHandler(&huart2);
- /* USER CODE BEGIN USART1_IRQn 1 */
- tmp_flag = __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);
- if((tmp_flag != RESET)){
- __HAL_UART_CLEAR_IDLEFLAG(&huart2);
- HAL_UART_DMAStop(&huart2);
- temp = hdma_usart2_rx.Instance->CNDTR;
- rx_len2 = BUFFER_SIZE - temp;
- recv_end_flag2 = 1;
- }
- /* USER CODE END USART1_IRQn 1 */
- }
复制代码
上面的代码,也是参考了网上网友的帖子。 希望网友指出问题,和给出更好的代码方案。
也还听说串口DMA有三种方法,我这里用的只是其中之一的“空闲中断”法。
附完整代码:
|
1. 数据包长度不超过DMA缓存的长度。2. 发送的间隔不少于200ms。
就可以115200双向同时收发无差错。
至于单方向收发,1Mbps,2Mbps,都没有问题的,放心使用。
所以个人认为LZ的测试方式其实问题在于,用中断CPU可以收一个马上发一个,虽然两边都CPU占满了但响应肯定快;但DMA是收完全部时再全部返回,那CPU虽然很闲,但响应就肯定慢。这样测试时DMA对CPU占用少的优势就测不出来。
同意,谢谢。
总之,串口通讯,本身是没有纠错重发功能,速度又很慢的外设。
影响丢数据的因素,也就是那么几种,照顾到了就没有问题。