[STM32F0] STM32的串口DMA收发以及双缓冲区的实现

[复制链接]
1171|10
 楼主| 过期的塔头 发表于 2021-8-4 22:18 | 显示全部楼层 |阅读模式
在使用STM32的UART的DMA功能总结如下:

首先上代码,这里采用STM32 的USART1作为Demo,RX的DMA为DMA1_Channel5,TX的DMA为DMA1_Channel4.初始化如下,红色的标记需要注意:

RX-DMA初始化

  1. 1 // DMA Rx
  2. 2 USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);  
  3. 3 DMA_Cmd(DMA1_Channel5,DISABLE);
  4. 4 DMA_InitStruct.DMA_PeripheralBaseAddr = (u32)(&USART1->DR);
  5. 5 DMA_InitStruct.DMA_MemoryBaseAddr = (u32)RxBuf0;
  6. 6 DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
  7. 7 DMA_InitStruct.DMA_BufferSize = 10;
  8. 8 DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  9. 9 DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
  10. 10 DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  11. 11 DMA_InitStruct.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
  12. 12 DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
  13. 13 DMA_InitStruct.DMA_Priority = DMA_Priority_High;
  14. 14 DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
  15. 15 DMA_Init(DMA1_Channel5,&DMA_InitStruct);
  16. 16 DMA_Cmd(DMA1_Channel5,ENABLE);



 楼主| 过期的塔头 发表于 2021-8-4 22:19 | 显示全部楼层
中断的初始化如下:
  1. DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);
  2.     NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel5_IRQn;
  3.     NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
  4.     NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
  5.     NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  6.     NVIC_Init(&NVIC_InitStruct);

  7.     //ENABLE DMA TX ISR
  8.     DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
  9.     NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel4_IRQn;
  10.     NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
  11.     NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
  12.     NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  13.     NVIC_Init(&NVIC_InitStruct);
 楼主| 过期的塔头 发表于 2021-8-4 22:19 | 显示全部楼层
TX发送函数如下:
  1. 1 void USART1_SendDMA(uint8_t* buf,int len)
  2. 2 {
  3. 3     DMA_InitTypeDef DMA_InitStruct;
  4. 5     DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
  5. 6
  6. 7     DMA_Cmd(DMA1_Channel4,DISABLE); 8
  7. 9     DMA_InitStruct.DMA_PeripheralBaseAddr = (u32)(&USART1->DR);
  8. 10     DMA_InitStruct.DMA_MemoryBaseAddr = (u32)buf;
  9. 11     DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;
  10. 12     DMA_InitStruct.DMA_BufferSize = len;
  11. 13     DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  12. 14     DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
  13. 15     DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  14. 16     DMA_InitStruct.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
  15. 17     DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
  16. 18     DMA_InitStruct.DMA_Priority = DMA_Priority_VeryHigh;
  17. 19     DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
  18. 20     DMA_Init(DMA1_Channel4,&DMA_InitStruct);
  19. 21
  20. 22     DMA_Cmd(DMA1_Channel4,ENABLE);   
  21. 23 }
 楼主| 过期的塔头 发表于 2021-8-4 22:21 | 显示全部楼层
两个服务函数如下:

RX-DMA中断函数,实现RX的双缓冲区功能。值得注意的是,你的DMA设置成Normal模式的时候,在中断函数中就得设置DMABufferSize,应为Normal模式完成一次传输之后,BufferSize直接清零,设置成Circle模式就不会存在这个问题,下面的中断就是设置成Circle模式的。
 楼主| 过期的塔头 发表于 2021-8-4 22:24 | 显示全部楼层
  1. void DMA1_Channel5_IRQHandler()
  2. {
  3.     if(DMA_GetITStatus(DMA1_IT_TC5))
  4.   {
  5.       if(using_buf0 ==0)
  6.     {
  7.       DMA1_Channel5->CMAR = (u32)RxBuf0;
  8.          using_buf0 = 1;
  9.     }
  10.     else
  11.     {
  12.          DMA1_Channel5->CMAR = (u32)RxBuf1;
  13.          using_buf0 = 0;
  14.     }
  15.     recv_flag = 1;
  16.     DMA_ClearITPendingBit(DMA1_IT_TC5);
  17.   }   
  18. }
 楼主| 过期的塔头 发表于 2021-8-4 22:24 | 显示全部楼层
TX-DMA中断函数
  1. void DMA1_Channel4_IRQHandler()
  2. {
  3.     if(DMA_GetITStatus(DMA1_IT_TC4))
  4.     {
  5.         //TODO:Add code here
  6.         DMA_ClearITPendingBit(DMA1_IT_TC4);
  7.     }   
  8. }
 楼主| 过期的塔头 发表于 2021-8-4 22:25 | 显示全部楼层
几点说明

1)这里采用双缓冲区的方式,对于大数据量的时候是很有效的,在处理一个缓冲区数据的时候,另外一个缓冲区能够正常接收数据
 楼主| 过期的塔头 发表于 2021-8-4 22:26 | 显示全部楼层
2)TX的DMA中断,可以采用类似于操作系统中互斥量的操作,当一个数组还在发送的时候,另外一个数组如果检测到一个在发送,则不能够进行发送,不然这样子数据会乱掉;
 楼主| 过期的塔头 发表于 2021-8-4 22:26 | 显示全部楼层
3)如果DMA接收想采用循环缓冲区的方式,可以直接将RX-DMA设置成Circle方式,然后数据就会硬件上自动实现环形缓冲区的功能,省了不少时间。
 楼主| 过期的塔头 发表于 2021-8-4 22:27 | 显示全部楼层
4)DMA在采用Normal模式的时候,当一次任务完成后,DMA->DMA_BufferSize自动清零,并且DMA自动停止。如果想再次设置DMA的BufferSize的话,必须要进行如下操作:

   step1:DMA_CMD(DMAx_Channely,DISABLE);

   step2: 设置DMA_BufferLen

   step3:DMA_CMD(DMAx_Channely,ENABLE)
 楼主| 过期的塔头 发表于 2021-8-4 22:28 | 显示全部楼层
5)DMA采用Circle模式的时候,在发送或者接受完成之后,仍然保存着BufferSize,并且DMA还处于使能状态,一直连续工作,直到用户停止DMA
您需要登录后才可以回帖 登录 | 注册

本版积分规则

85

主题

999

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部