当前位置: 首页 > news >正文

基于HAL库的STM32的串口DMA发送数据(解决只发送一次数据)及DMA+空闲中断接受数据

目录

前提回顾

配置串口的DMA发送数据和空闲中断+DMA接受数据

关于串口的DMA发送只发送一次的问题解决

前人经验(DMA初始化在串口初始化之前,这里我只用了串口1的DMA发送) 

我遇到的坑点

DMA+空闲中断


前提回顾

通过CUBEMX配置串口以DMA方式发送。

为什么使用DMA,DMA可以为外设和内存提供一条数据通道,使得数据的复制不需要CPU去参与,减低CPU的负担,在实时性的工作时显得格外重要。

通过一般的串口发送数据函数:

   HAL_UART_Transmit(&huart5,(uint8_t*)Rx5sBuf,length,10);      

可以看到有一个等待时间,这里表示CPU参与数据搬运最多等待10ms,所以CPU是在等待串口的。

而配置成DMA发送,基于STM32F103ZET6只有usart1 ,usart2,usart3的发送和接受有DMA模式。串口4,和串口5都只能异步发送(一般我们都使用异步发送数据,只有在某些时序中需要使用到同步)。

再回到串口以DMA的方式发送数据

  HAL_UART_Transmit_DMA(&huart1, (uint8_t *)RX1sBuf, sizeof(RX1sBuf));   

此时没有等待时间,这里表示CPU不需要等待串口,在程序后台DMA通道将数据搬运到串口再发送。这里需要注意的是,虽然cpu无须干预,可以继续执行后面的代码,但DMA将数据从内存(定义数组的位置)搬运到外设(串口)是需要时间的,如果是连续使用DMA串口发送(连续调用上面代码),必然是不合理的,这时需要判断此串口发送所在的DMA通道是否是空闲再继续发送。

在CUBEMX配置时,默认是把DMA的中断打开的,无论是adc的dma还是串口,它们的完成中断都是打开的,为了避免不必要的进入中断,一般我把ADC的DMA中断关闭,把串口的以DMA接受数据的中断也关闭.

如下图中的强制DMA中断 

配置串口的DMA发送数据和空闲中断+DMA接受数据

串口一配置DMA发送和DMA接受(这里以DMA加空闲中断的方式) 

串口接受数据模式normal ,发送数据也为normal。(接受数据若设置为循环模式则不知道接受的数组缓存是否错位,除非在

 

在这里,DMA接受数据的DMA中断关闭,发送数据的DMA中断使能 。(这里的DMA中断是发送数据和接受完数据后会进入中断)

串口全局中断使能。

关于串口的DMA发送只发送一次的问题解决

前人经验(DMA初始化在串口初始化之前,这里我只用了串口1的DMA发送) 

我遇到的坑点

而我则是未打开串口以DMA发送的中断。

这里串口发送用到的是DMA1通道4

 

在stm32f1xx_it.c中看到其通道4的中断函数 

再进入其回调函数 HAL_DMA_IRQHandler

(在传输一半完成中断管理,传输完成中断管理,传输错误中断管理里中做标记处理)

通过点灯判断出了串口DMA发送进入了传输完成中断管理,清除了该dma通道的中断标记位和

设置dma通道为就绪状态以及该dma通道作为资源的释放。

/**
  * @brief  Handles DMA interrupt request.
  * @param  hdma: pointer to a DMA_HandleTypeDef structure that contains
  *               the configuration information for the specified DMA Channel.  
  * @retval None
  */
void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma)
{
  uint32_t flag_it = hdma->DmaBaseAddress->ISR;
  uint32_t source_it = hdma->Instance->CCR;
  
  /* Half Transfer Complete Interrupt management ******************************/
  if (((flag_it & (DMA_FLAG_HT1 << hdma->ChannelIndex)) != RESET) && ((source_it & DMA_IT_HT) != RESET))
  {
    /* Disable the half transfer interrupt if the DMA mode is not CIRCULAR */
    if((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U)
    {
      /* Disable the half transfer interrupt */
      __HAL_DMA_DISABLE_IT(hdma, DMA_IT_HT);
    }
    /* Clear the half transfer complete flag */
    __HAL_DMA_CLEAR_FLAG(hdma, __HAL_DMA_GET_HT_FLAG_INDEX(hdma));

    /* DMA peripheral state is not updated in Half Transfer */
    /* but in Transfer Complete case */

    if(hdma->XferHalfCpltCallback != NULL)
    {
      /* Half transfer callback */
      hdma->XferHalfCpltCallback(hdma);
    }
  }

  /* Transfer Complete Interrupt management ***********************************/
  else if (((flag_it & (DMA_FLAG_TC1 << hdma->ChannelIndex)) != RESET) && ((source_it & DMA_IT_TC) != RESET))
  {
    if((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U)
    {
      /* Disable the transfer complete and error interrupt */
      __HAL_DMA_DISABLE_IT(hdma, DMA_IT_TE | DMA_IT_TC);  

      /* Change the DMA state */
      hdma->State = HAL_DMA_STATE_READY;
    }
    /* Clear the transfer complete flag */
      __HAL_DMA_CLEAR_FLAG(hdma, __HAL_DMA_GET_TC_FLAG_INDEX(hdma));
  

    /* Process Unlocked */
    __HAL_UNLOCK(hdma);

    if(hdma->XferCpltCallback != NULL)
    {
      /* Transfer complete callback */
      hdma->XferCpltCallback(hdma);
    }
  }

  /* Transfer Error Interrupt management **************************************/
  else if (( RESET != (flag_it & (DMA_FLAG_TE1 << hdma->ChannelIndex))) && (RESET != (source_it & DMA_IT_TE)))
  {
    /* When a DMA transfer error occurs */
    /* A hardware clear of its EN bits is performed */
    /* Disable ALL DMA IT */
    __HAL_DMA_DISABLE_IT(hdma, (DMA_IT_TC | DMA_IT_HT | DMA_IT_TE));

    /* Clear all flags */
    hdma->DmaBaseAddress->IFCR = (DMA_ISR_GIF1 << hdma->ChannelIndex);

    /* Update error code */
    hdma->ErrorCode = HAL_DMA_ERROR_TE;

    /* Change the DMA state */
    hdma->State = HAL_DMA_STATE_READY;

    /* Process Unlocked */
    __HAL_UNLOCK(hdma);

    if (hdma->XferErrorCallback != NULL)
    {
      /* Transfer error callback */
      hdma->XferErrorCallback(hdma);
    }
  }
  return;
}

最后通过调用

  HAL_UART_Transmit_DMA(&huart1, (uint8_t *)RX1sBuf, sizeof(RX1sBuf)); 

完成DMA的数据发送。

一般使用DMA发送数据我还是采用定时发送。

DMA+空闲中断

这个极大地提高了效率,而且简洁,hal库不愧是不断完善的。

相比于之前我用串口接受中断和空闲中断接受数据会接受一个字节就进入接受数据中断。而DMA+空闲中断则是接受完一个数据帧再进入空闲中断。

总共就2个函数解决问题。

 HAL_UARTEx_ReceiveToIdle_DMA(&huart1,Rx1Buf,102); //串口1开启DMA接受

while之前开启数据接受。

再重写其回调函数

//  DMA加串口空闲中断
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	if(huart->Instance == USART1)
	{
	//这里的DMA为Normal模式
	//HAL_UART_DMAStop(huart)//如果DMA为Circular模式,回调函数加上stop函数
		//USER_FNC();//用户自定义函数
        
        printf("[RX1:] %s\r\n",Rx1Buf);
        
        
    
        memset(Rx3Buf,0x00,sizeof(Rx1Buf));
        
        
		HAL_UARTEx_ReceiveToIdle_DMA(&huart1,Rx1Buf,102);//继续接收数据
	}
}

效果 

参考博客

STM32使用串口空闲中断(IDLE)和 DMA接收一串数据流_ONE_Day|的博客-CSDN博客_idle标志位

STM32基于HAL库的串口接受中断和空闲中断_昊月光华的博客-CSDN博客_stm32hal库串口空闲中断

相关文章:

  • 【图灵MySQL】SQL底层执行原理详解
  • 【电磁】基于Matlab求解瞬变电磁TEM层状介质正演
  • Unity使用新输入系统InputSystem制作飞机大战Demo(敌人生成管理器UI场景跳转)
  • Python 文件存储:pickle 和 json 库的使用
  • 【推荐收藏】matplotlib 制作的动态条形图其实很好看
  • 计算机组成原理 ------ 存储系统(1)
  • Open3D (C++) 基于投影点密度的建筑物立面提取
  • SpringCloud Alibaba系列 Nacos(一)
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • JS的精髓,事件详解
  • 高等数学(第七版)同济大学 习题8-6 个人解答
  • 【Linux】进程地址空间
  • 【计算机组成原理】输入/输出系统(四)—— I/O方式
  • 让GPU跑的更快
  • 给课题组师弟师妹们的开荒手册
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • AHK 中 = 和 == 等比较运算符的用法
  • angular2开源库收集
  • canvas 高仿 Apple Watch 表盘
  • JavaScript中的对象个人分享
  • Java多态
  • Mocha测试初探
  • opencv python Meanshift 和 Camshift
  • Redis学习笔记 - pipline(流水线、管道)
  • Spring框架之我见(三)——IOC、AOP
  • windows-nginx-https-本地配置
  • 程序员最讨厌的9句话,你可有补充?
  • 动手做个聊天室,前端工程师百无聊赖的人生
  • 基于web的全景—— Pannellum小试
  • 计算机在识别图像时“看到”了什么?
  • 前言-如何学习区块链
  • 树莓派 - 使用须知
  • 小李飞刀:SQL题目刷起来!
  • hi-nginx-1.3.4编译安装
  • #{}和${}的区别?
  • #13 yum、编译安装与sed命令的使用
  • #单片机(TB6600驱动42步进电机)
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (蓝桥杯每日一题)平方末尾及补充(常用的字符串函数功能)
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (十八)SpringBoot之发送QQ邮件
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转)程序员疫苗:代码注入
  • ***测试-HTTP方法
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .net core Swagger 过滤部分Api
  • .net core 依赖注入的基本用发
  • .NET 中创建支持集合初始化器的类型
  • .skip() 和 .only() 的使用
  • @EventListener注解使用说明
  • [ C++ ] template 模板进阶 (特化,分离编译)