分享

Stm32F4 系列使用DMA发送串口数据

 怪叔叔的书城 2022-02-17

以前在使用stm32F103系列单片机时,若要将串口配置成DMA发送,只需要正确配置DMA通道,然后发送数据时候,给DMA通道的CNDTR寄存器赋以发送长度,再使能DMA即可。

如下代码所示:F103版本

  1. while (DMA_GetCurrDataCounter(DMA1_Channel7));// 检查DMA发送通道内是否还有数据
  2. memcpy(DMASendBuf, Buffer, (ucSend_num > 1024 ? 1024 : ucSend_num));
  3. //DMA发送数据-要先关 设置发送长度 开启DMA
  4. DMA_Cmd(DMA1_Channel7, DISABLE);
  5. DMA1_Channel7->CNDTR = ucSend_num;// 设置发送长度
  6. DMA_Cmd(DMA1_Channel7, ENABLE); // 启动DMA发送

这次在F407上使用,发现像上面一样设置并不能用,DMA只能发送一次,后面直接不发送了。

如下代码所示:F407 错误配置DMA

  1. while (DMA_GetCurrDataCounter(DEBUG_USART_DMA_STREAM));// 检查DMA发送通道内是否还有数据
  2. memcpy((uint8_t *)SendBuff, Buf,(Len > 1024 ? 1024 : Len));
  3. //DMA发送数据 设置发送长度 开启DMA
  4. DMA_Cmd(DEBUG_USART_DMA_STREAM, DISABLE); // 关闭DMA发送
  5. DEBUG_USART_DMA_STREAM->NDTR = Len;
  6. DMA_Cmd(DEBUG_USART_DMA_STREAM, ENABLE); // 启动DMA发送

后来查阅手册,发现串口使用DMA通信的话,官方要求使能DMA发送完成中断,并在中断中清除TC标志位。

如下代码所示:官方手册中要求的DMA发送过程

  1. // 配置DMA
  2. void USART_DMA_Init()
  3. {
  4. //...
  5. NVIC_InitTypeDef NVIC_InitStructure;
  6. NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
  7. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  8. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  9. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  10. NVIC_Init(&NVIC_InitStructure);
  11. DMA_ITConfig(DMA2_Stream7,DMA_IT_TC,ENABLE);
  12. }
  13. // DMA中断
  14. void DMA2_Stream7_IRQHandler(void)
  15. {
  16. //清除标志
  17. if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET)//等待DMA2_Steam7传输完成
  18. {
  19. DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);//清除DMA2_Steam7传输完成标志
  20. }
  21. }
  22. void Send_Data(u8 *Buf, uint16_t Len)
  23. {
  24. //...
  25. //发送函数 此时不用配置DMA TC中断
  26. while (DMA_GetCurrDataCounter(DMA2_Stream7));// 检查DMA发送通道内是否还有数据
  27. memcpy((uint8_t *)SendBuff, Buf,(Len > 1024 ? 1024 : Len));
  28. //DMA发送数据 设置发送长度 开启DMA
  29. DMA_Cmd(DMA2_Stream7, DISABLE); // 关闭DMA发送
  30. DMA2_Stream7->NDTR = Len;
  31. DMA_Cmd(DMA2_Stream7, ENABLE); // 启动DMA发送
  32. }

我认为此举没有必要,便关闭了DMA TC中断,再每次发送前清除TC标志位即可。

如下代码所示:在之前代码基础上增加一条清除DMA TC标志位的指令。

  1. //发送函数 此时不用配置DMA TC中断
  2. while (DMA_GetCurrDataCounter(DMA2_Stream7));// 检查DMA发送通道内是否还有数据
  3. while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) /*确保发送结束*/
  4. DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);
  5. if(Buf)
  6. {
  7. memcpy((uint8_t *)Rs485No1DmaSendBuf, Buf,(Len > 1024 ? 1024 : Len));
  8. }
  9. //DMA发送数据 设置发送长度 开启DMA
  10. DMA_Cmd(DMA2_Stream7, DISABLE); // 关闭DMA发送
  11. while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE){} //确保DMA可以被设置
  12. DMA_SetCurrDataCounter(DMA2_Stream7, Len);// 设置发送长度
  13. DMA_Cmd(DMA2_Stream7, ENABLE); // 启动DMA发送

但是发现一个问题,如果不使用DMA TC中断,在DMA在初始化配置时,缓存区大小不能设置为0,即:

DMA_Initstructure1.DMA_BufferSize = 1;        //此值随便赋,但是不能为0。

我猜测这可能是芯片的一个BUG,着实有点坑。

以下是Stm32F1手册中关于串口DMA发送的介绍

以下是Stm32F4手册中关于串口DMA发送的介绍

QQ扫一扫,加入答疑群聊

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多