芯查查logo
  • 数据服务
    1. 新产品
    2. 物料选型
    3. 查替代
    4. 丝印反查
    5. 查品牌
    6. PCN/PDN
    7. 查方案
    8. 查代理
    9. 数据合作
  • SaaS/方案
      SaaS产品
    1. 供应链波动监控
    2. 半导体产业链地图
    3. BOM管理
    4. 解决方案
    5. 汽车电子
    6. 政府机构
    7. 数据API
  • 商城
  • 行业资讯
    1. 资讯
    2. 直播
  • 论坛社区
    1. 论坛
    2. 学习
    3. 测评中心
    4. 活动中心
    5. 积分商城
  • 查一下
  • 开通会员
【星允派 NEBULA PI】04:DMA+空闲中断收发数据
原创 发布时间:前天 21:34
版块:
单片机/MCU论坛
简介:空闲中断与DMA接收不定长数据

前言:使用hal库的DMA接收与串口的空闲中断,实现接收不定长的数据长度。

 

一 :串口的空闲中断:

顾名思义,就是当串口在一定的时间内没有接收到数据时,触发控制状态,从而产生空闲中断;一般来说,STM32在数据交互时,传输字节之间的间隔很短,然后再一个字节的通讯时间内,没有收到数据时,意味着程序进入了空闲中断,所以在程序的初始化后,我们只需要开启空闲中断后,然后在串口的回调函数中,进行数据迁移就可以,这里为了方便验证,将数据做回传处理。

     

   在STM32的串口通讯模式中,我个人的理解是,阻塞方式,非阻塞方式和DMA发送,共计三种不同的的通讯方式,在HAL库中相关的函数代码如下:

HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
 HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size);
 HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size);

我在工作实际使用中,在代码量不大,要求不高的情况下,一般使用阻塞式的发送和中断方式接收,即HAL_UART_Transmit 和HAL_UART_Receive_IT的方式,实现CPU与外设的交互。

这种通讯方式比较简单,串口每接收到一个字节单片机就会进入一次串口的中断,我们在接收的过程中,只需要判断一下接收到数据的有效性,然后放入决定是否放到接收数据组。这种接收方式是最简单的,但是换来却是单片机需要频繁的进入中断,这样代码大的情况下,根据中断优先级的不同,可能会导致项目中某些程序被打断的情况出现。

使用DMA的接收方式可以实现内存与外设之间的数据直接传输,可以为CPU分担一些工作量,从而为CPU减轻负担,增加数据的传输处理速度。

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

(一)函数功能是CPU以DAM模式发送 *pData指针指向的数据中固定长度的数据,并同时设置和使能DMA中断。

我们从代码中不难发现,DMA中断函数最终使用的居然是串口的中断函数。

进入到HAL_UART_Transmit_DMA这个函数中可以看到,它将DMA传输完成、半完成、错误的回调函数分别定向到了串口DMA传输完成、半完成、错误的回调函数

我们在DMA的函数中发现 static void UART_DMATransmitCplt(DMA_HandleTypeDef *hdma),打开函数功能的内部其实就是串口的发送完成中断函数 HAL_UART_TxCpltCallback(huart);可见即使使用DMA的发送方式,最后还是需要串口的发送完成中断函数相联系。

(二)串口的DMA接收

HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

(三)串口DMA的停止函数

HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart);

(四)查询DMA中有效的数据个数

#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR)

在宏定义中我们不难发现,其实就是读取CNDTR寄存器内的数值。这个寄存器存储的是DMA传输的剩余传输数量。在接收数据时,用定义的最大传输数量减去这个剩余数量就是已经接收的数据个数。在发送数据时,用定义的最大传输数量减去这个剩余数量就是已经发送的数据个数。

(五)串口DMA的接收函数

HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart);

二:软件代码如下所示:

2.1 串口1的初始化函数:

void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_TC);

	__HAL_DMA_DISABLE(&hdma_usart1_rx);
	HAL_UARTEx_ReceiveToIdle_DMA(&huart1,(uint8_t *)&RecBuffer1, reclength1);
	__HAL_DMA_ENABLE(&hdma_usart1_rx);		
  /* USER CODE END USART1_Init 2 */

}

2.2 串口接收回调函数:

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	if(huart->Instance == USART1)
	{
			HAL_UARTEx_ReceiveToIdle_DMA(&huart1, RecBuffer1, reclength1);
			HAL_UART_Transmit(&huart1, RecBuffer1, Size, 100);      
			__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);		
			memset(RecBuffer1, 0, 32);							
	}
}

2.3 接收错误回调函数:

void HAL_UART_ErrorCallback(UART_HandleTypeDef * huart)
{
    if(huart->Instance == USART1)
    {
			HAL_UARTEx_ReceiveToIdle_DMA(&huart1, RecBuffer1, reclength1); 
			__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);		  
			memset(RecBuffer1, 0, reclength1);							     
    }

}

方法二:

使用编写空闲中断函数:

void UsartReceive_IDLE(UART_HandleTypeDef *huart) 
{

  if((__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)) 
	{
		 if(huart->Instance == USART1)
		{

			__HAL_UART_CLEAR_IDLEFLAG(huart); 
			HAL_UART_DMAStop(huart); 			
			usart1_rx_len = 256 - (__HAL_DMA_GET_COUNTER(&hdma_usart1_rx)); //
			memcpy(Usart1_DEAL_RX_Buf, RecBuffer1, usart1_rx_len);
			usart2_Flag = 1;			
	
			HAL_UART_Receive_DMA(&huart1, (uint8_t *)&RecBuffer1, 256);
		}
	}
}

然后再串口中断中调用该函数即可。

三:测试图片如下所示:

申请原创奖励
前天 21:34
  • 举报
😁😂😃😄😅😆😉😊😋😌😍😏😒😓😔😖😘😚😜😝😞😠😡😢😣😤😥😨😩😪😫😭😰😱😲😳😵😷😸😹😺😻😼😽😾😿🙀🙅🙆🙇🙈🙉🙊🙋🙌🙍🙎🙏✂✅✈✉✊✋✌✏✒✔✖✨✳✴❄❇❌❎❓❔❕❗❤➕➖➗➡➰🚀🚃🚄🚅🚇🚉🚌🚏🚑🚒🚓🚕🚗🚙🚚🚢🚤🚥🚧🚨🚩🚪🚫🚬🚭🚲🚶🚹🚺🚻🚼🚽🚾🛀Ⓜ🅰🅱🅾🅿🆎🆑🆒🆓🆔🆕
@好友

全部评论

加载中
游客登录通知
已选择 0 人
自定义圈子
移动
发布帖子
发布动态
发布问答
发布者
聪聪哥哥
最新帖子
【星允派 NEBULA PI】04:DMA+空闲中断收发数据【星允派 NEBULA PI】03:调试串口1的输出【星允派 NEBULA PI】01:了解开发板资源,环境准备芯查查技术沙龙第4期—ADI智能音频解决方案分享完美落幕LDO串联或并联二极管有什么用?电路保护与特殊应用解析
热门版块
查看更多
电子DIY
问型号
问技术
问行情
汽车电子工程师论坛
工业电子专区
新手入门指南
单片机/MCU论坛
PCB设计
开源项目

2

收藏

分享

微信扫码
分享给好友

评论