STM32基于DMA的高效串口通信实现

本文章实现STM32F103C8T6开发板基于HAL库使用DMA方式的串口通信。

文章目录

  • 一、认识DMA
  • (1)DMA定义
  • (2)STM32F10xxxDMA资源
  • 二、CubeMX配置工程
  • 三、代码编写
  • 1.DMA串口发送接收函数
  • 2.DMA串口空闲中断函数
  • 3.DMA串口空闲中断回调函数
  • 4.主函数编写
  • 5.中断服务函数编写
  • 四、板级验证
  • 五、总结

  • 一、认识DMA

    (1)DMA定义

       在STM32微控制器中,DMA(Direct MemoryAccess)是指直接内存访问技术。
    它是一种数据传输技术,通过将数据直接从外设(如UART、SPI、I2C等)传
    输到内存,或者从内存传输到外设,而不需要CPU的干预,从而提高了数据传
    输的效率。
    
       DMA控制器有自己的寄存器和逻辑,可以独立地读取和写入数据,而不需要
     CPU的干预。它可以通过配置寄存器来控制数据传输的源地址、目标地址、
     数据大小、传输方向和触发条件等。
    
       使用DMA的好处是可以减少CPU的负载,提高数据传输的效率。当需要大量数
    据传输时,例如音频、图像或其他大型数据块时,采用DMA可以显著减少CPU的
    开销,使CPU能够专注于其他任务。
    
      在STM32中,DMA控制器通常与外设和存储器之间进行数据传输。它可以实现
    多个外设之间的数据传输,或者在外设和存储器之间进行数据传输。通过配置DMA
    通道和传输参数,可以实现高效的数据传输,从而提高系统的性能。
    

    (2)STM32F10xxxDMA资源

    DMA1控制器:请求映象

    上图可以看到我们本次使用的串口1对应DMA通道和优先级。
    DMA2控制器:

    二、CubeMX配置工程

    配置RCC:

    串口配置:


    DMA配置:

    时钟配置:

    工程设置:


    完成以上配置后生成工程代码。

    三、代码编写

    1.DMA串口发送接收函数

    HAL库在使用DMA经行串口发送时,我们可以调用下面这个函数

    HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)
    

    上述函数参数定义为:哪个串口发送,需要发送的变量,发送变量的字节长度。
    DMA串口接收数据,可以调用下面这个函数(参数同发送):

    HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size);
    

    2.DMA串口空闲中断函数

    我们在使用DMA方式进行串口通信时,不可能每次都等到接收到定长的数据后才进行我们的数据处理,所以,我们需要用到DMA串口空闲中断,当串口的RX端没有数据进入时,发生中断。就可以实现不定长数据的接收与发送了。下面是这个函数:

    HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
    

    值得注意的是,其中前两个参数的定义依旧是哪个串口和接收到的数据,最后的Size参数不再是接收到数据的长度,而是接收的一帧数据最大限度长度。

    使用上述函数,就可以在接收到一帧数据后,进入中断。

    3.DMA串口空闲中断回调函数

    注意,HAL_UARTEx_ReceiveToIdle_DMA ,的回调函数不再是RxCpltcallback 函数,而是下面这个:

    void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size);
    

    参数定义是:发生中断的对应串口,和接收数据的长度。

    4.主函数编写

    int main(void)
    {
    
      HAL_Init();
      SystemClock_Config();
      MX_GPIO_Init();
      MX_DMA_Init();
      MX_USART1_UART_Init();
      __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//开启空闲中断
      uint8_t flag = 0;
      uint8_t message[] = "hello windows!\n";	
      while (1)
      {
    	   HAL_UARTEx_ReceiveToIdle_DMA(&huart1,receivedata,sizeof(receivedata));
    	   if(flag==1)
    	   {
    		HAL_UART_Transmit_DMA(&huart1,(uint8_t *)message,sizeof(message));
    	    HAL_Delay(500);
    	   }
      }
    }
    

    5.中断服务函数编写

     void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
     {
    	 
    	 if(huart == &huart1)
    	 {
    		 //将接受到的数据原封不动的转发
    		 HAL_UART_Transmit_DMA(&huart1,receivedata,Size);
    		 //数据处理
    		 if(strcmp(receivedata,"start")==0)
    			 flag = 1;
    		 else if(strcmp(receivedata,"stop")==0)
    			 flag = 0;
    		 else
    			 flag = 0; 
    		 //重新打开空闲中断
    		 HAL_UARTEx_ReceiveToIdle_DMA(&huart1,receivedata,sizeof(receivedata));
    	 }	 
     }
    

    通过上面代码就可以实现以下功能:STM32系统给上位机(win10)连续发送“hello windows!”;当上位机给stm32发送字符“stop”后,stm32暂停发送“hello windows!”;发送一个字符“start”后,stm32继续发送。


    四、板级验证

    五、总结

    本次实验,从认识DMA到使用DMA,虽然只是使用了DMA很简单的功能,但可以认识DMA功能之强大,当程序功能揉杂的时候,能够使用DMA功能释放CPU的压力。
    本次实验,在使用DMA空闲中断时,当接收数据的长度等于中断发生最大长度的一半时,也会触发中断,这是因为每个DMA通道都可以在DMA传输过半、传输完成和传输错误时产生中断。我们需要将对应的中断功能关闭,使用我们自己想要的功能。
    HAL库中关闭该功能可以使用:__HAL_UART_DISABLE_IT() 这个函数。

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32基于DMA的高效串口通信实现

    发表评论