stm32f103 串口 DMA收发

1、stm32串口发送占用的时间

       做嵌入式以来,一直自认为在两个MCU之间的串口通信很占用时间,让我感觉很是不爽。为了远程数据传输,波特率较低,假设bps = 9600,传输20个字节大概需要20ms。

如果使用HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)等待硬件数据发送完成

MCU将一直等待发送 完成标志 while(__HAL_UART_GET_FLAG(huart, Flag) == RESET),所以效率较低

串口发送数据:

发送数据在软件层面来看是按照字节来发送的。USARTx->DR = (Data & (uint16_t)0x01FF);CPU只需要把一个字节的数据填充到DR寄存器中就可以了,然后具体的发送过程是由硬件来完成,单字节的发送过程中不消耗CPU。但是为什么我们使用串口来发送连续的数据时为什么还是感觉到这么慢,而且连续发送的过程中CPU不能去做其他任务呢?究其原因是因为我们为保证数据发送的完整性在发送过程中加入了while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){} 这条语句来等待发送结束。让CPU白白浪费在while的死循环中,等待的时间为   1(字节)x10(位) / 使用的波特率bps = 死循环时间。如果使用115200bps波特率的话,等待时间为86us。。由此来看其实发送数据的时间几乎全部都花费在了等待上(两次发送的这段时间间隔是必须要有的,但是这段时间的CPU可以去做其他任务)。时间间隔必须要有是因为在硬件发送的过程中是需要这段时间来按位发送电平信息的。如下图所示:发送完一个字节(10位,开始位+8bit数据+结束位)产生一个TXE标志位,然后接着发送下一字节数据

串口接收数据:

串口接收数据在软件层面来看也是按照字节接收,return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);返回的就是DR寄存器的数据,接收中断是在接收完1字节后来产生中断,接收在软件层面来看只是从DR寄存器取出数值这个动作而已,几乎也不占用CPU时间,具体的接收过程是由硬件来完成,在硬件层面是按照位来接收的,硬件接收过程也是需要时间的,中间传输过程占用时间与发送占用时间是一样的。


2. 解决方法

  1. 串口中断发送 中断接收

    HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)

    HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)

        优点:硬件实现收发,主程序无需等待

        缺点:频繁调用中断,bps较高时,或者使用三路串口或以上时,中断消耗时间过大(未曾测试,bps较高时,死机,数据不稳定)。

2.  DMA方式发送

     使用DMA方式发送数据,HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

        

3. DMA 加 空闲方式接收

void HAL_UART_IDLEHandler(UART_HandleTypeDef * huartx)
{
uint32_t tmp_flag = 0;
uint32_t temp;
tmp_flag =  __HAL_UART_GET_FLAG(huartx,UART_FLAG_IDLE); 
if((tmp_flag != RESET))

__HAL_UART_CLEAR_IDLEFLAG(huartx);
temp = huartx->Instance->SR;  // 清除IDLE标志位
temp = huartx->Instance->DR; 
HAL_UART_DMAStop(huartx);

if(huartx == &huart3)
{
temp  = hdma_usart3_rx.Instance->CNDTR;             
Rec_flg3.rx_len =  BUFFER_SIZE - temp;                            
Rec_flg3.recv_end_flag = 1;

inQueueBuff(&u3_rx,(int8_t *)(huartx->pRxBuffPtr),Rec_flg3.rx_len);  //将接收到的数据压如栈中
HAL_UART_Receive_DMA(huartx,aRxBuffer3,BUFFER_SIZE);
}
else if(huartx == &huart2)
{
temp  = hdma_usart2_rx.Instance->CNDTR;     // 剩余的接收数据          
Rec_flg2.rx_len =  BUFFER_SIZE - temp;                            
Rec_flg2.recv_end_flag = 1;

inQueueBuff(&u2_rx,(int8_t *)(huartx->pRxBuffPtr),Rec_flg2.rx_len);  //将接收到的数据压如栈中
HAL_UART_Receive_DMA(huartx,aRxBuffer2,BUFFER_SIZE);
}
else if(huartx == &huart1)
{
temp  = hdma_usart1_rx.Instance->CNDTR;     // 剩余的接收数据          
Rec_flg1.rx_len =  BUFFER_SIZE - temp;                            
Rec_flg1.recv_end_flag = 1;

inQueueBuff(&u1_rx,(int8_t *)(huartx->pRxBuffPtr),Rec_flg1.rx_len);  //将接收到的数据压如栈中
HAL_UART_Receive_DMA(huartx,aRxBuffer1,BUFFER_SIZE);
}
}

}


void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{


  GPIO_InitTypeDef GPIO_InitStruct;
  if(huart->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */


  /* USER CODE END USART1_MspInit 0 */
    /* Peripheral clock enable */
    __USART1_CLK_ENABLE();
  
    /**USART1 GPIO Configuration    
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);


    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);


    /* Peripheral DMA init*/
  
    hdma_usart1_rx.Instance = DMA1_Channel5;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
    HAL_DMA_Init(&hdma_usart1_rx);


    __HAL_LINKDMA(huart,hdmarx,hdma_usart1_rx);


    hdma_usart1_tx.Instance = DMA1_Channel4;
    hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_tx.Init.Mode = DMA_NORMAL;
    hdma_usart1_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
    HAL_DMA_Init(&hdma_usart1_tx);


    __HAL_LINKDMA(huart,hdmatx,hdma_usart1_tx);


    /* Peripheral interrupt init*/
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
initQueue(&u1_rx);
  /* USER CODE BEGIN USART1_MspInit 1 */


  /* USER CODE END USART1_MspInit 1 */

  }

}

猜你喜欢

转载自blog.csdn.net/Terrys0518/article/details/80493025