STM32-HAL库串口空闲中断DMA接收数据Demo

一、Demo说明

  • 主控stm32f103rct6
  • 串口1、2、3、4采用DMA+空闲中断方式实现接收数据
  • 串口5采用接收中断+空闲中断实现接收数据
  • 定时器1执行定时任务

二、HAL库配置

1. 系统配置

  • 开启SWD下载
  • 开启外部时钟

2. 串口1配置(串口2/3/4类似)

  • 配置波特率
  • 添加接收DMA
  • 开启中断

3. 定时器配置

4. 时钟选择

硬件电路外部接有8M晶振所以选择外部8M晶振

5. 工程生成

三、代码编写

1. 初始化

串口接收使用DMA+空闲中断实现,初始化中需要开启串口空闲中断,以及串口5的接收中断。

void UserSys_Init(void)
{
	HAL_NVIC_EnableIRQ(UART5_IRQn);
	__HAL_UART_ENABLE_IT(&huart5, UART_IT_IDLE);
	HAL_UART_Receive_IT(&huart5,(uint8_t*)&g_usart5_recv_byte,1);
	
	HAL_NVIC_EnableIRQ(UART4_IRQn);
	__HAL_UART_ENABLE_IT(&huart4, UART_IT_IDLE);
	HAL_UART_Receive_DMA(&huart4, (uint8_t*)g_USART4_Recv_Data, RECV_DATA_MAX_LEN);
	
	HAL_NVIC_EnableIRQ(USART3_IRQn);
	__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
	HAL_UART_Receive_DMA(&huart3, (uint8_t*)g_USART3_Recv_Data, RECV_DATA_MAX_LEN);

	HAL_NVIC_EnableIRQ(USART2_IRQn);
	__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
	HAL_UART_Receive_DMA(&huart2, (uint8_t*)g_USART2_Recv_Data, RECV_DATA_MAX_LEN);
	
	HAL_NVIC_EnableIRQ(USART1_IRQn);
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
	HAL_UART_Receive_DMA(&huart1, (uint8_t*)g_USART1_Recv_Data, RECV_DATA_MAX_LEN);
	
	HAL_TIM_Base_Start_IT(&htim1);
}

2. 串口空闲中断

2.1. 自定义串口空闲中断服务函数
  • 串口5无法使用DMA传输,所以使用接收中断和空闲中断的方式实现,使用接收中断将数据保存后,利用空闲中断处理数据;
  • 串口1、2、3、4采用空闲中断+DMA方式,具体流程如下:
    • 判断是否为空闲中断
    • 清除空闲中断标志
    • 关闭DMA传输,关闭空闲中断(在主函数数据处理完成后开启)
    • 备份数据
    • 标志位置1
/** 空闲中断回调函数
  * @brief  HAL_UART_IdleCpltCallback
  * @param  huart 串口
  * @retval None
  */
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{
	//USART5 IDLE/RXNE
	if(huart == &huart5)
	{
		//RXNE
		g_USART5_Recv_Data[usart5_recv_cnt++] = g_usart5_recv_byte;
		HAL_UART_Receive_IT(&huart5,(uint8_t*)&g_usart5_recv_byte,1);
		
		//IDLE
		if(RESET != __HAL_UART_GET_FLAG(&huart5,UART_FLAG_IDLE))
		{
			g_usart5_recv_len = usart5_recv_cnt;
			__HAL_UART_CLEAR_IDLEFLAG(&huart5);//Clear the UART IDLE pending flag.(Otherwise it will continue to interrupt)

			memset(g_USART5_Recv_Data_BAK,0,RECV_DATA_MAX_LEN);
			memcpy(g_USART5_Recv_Data_BAK,g_USART5_Recv_Data,g_usart5_recv_len);
			memset(g_USART5_Recv_Data,0,RECV_DATA_MAX_LEN);
			usart5_recv_cnt = 0;
			g_USART5_Recv_Flag = 1;
		}		
	}
	//USART4 IDLE
	if(huart == &huart4)
	{
		if(RESET != __HAL_UART_GET_FLAG(&huart4,UART_FLAG_IDLE))
		{
			__HAL_UART_CLEAR_IDLEFLAG(&huart4);
			HAL_UART_DMAStop(&huart4);						//关闭DMA传输
			__HAL_UART_DISABLE_IT(&huart4, UART_IT_IDLE);	//关闭空闲中断
			
			g_usart4_recv_len = RECV_DATA_MAX_LEN - (__HAL_DMA_GET_COUNTER(&hdma_uart4_rx));//计算接收数据长度
			
			memset(g_USART4_Recv_Data_BAK,0,RECV_DATA_MAX_LEN);
			memcpy(g_USART4_Recv_Data_BAK,g_USART4_Recv_Data,g_usart4_recv_len);
			memset(g_USART4_Recv_Data,0,RECV_DATA_MAX_LEN);	//处理数据
			
			g_USART4_Recv_Flag = 1;		//标志位置位
		}
	}
	//USART3 IDLE 
	if(huart == &huart3)
	{
		if(RESET != __HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE))
		{			
			__HAL_UART_CLEAR_IDLEFLAG(&huart3);//Clear the UART IDLE pending flag.(Otherwise it will continue to interrupt)
			HAL_UART_DMAStop(&huart3);
			__HAL_UART_DISABLE_IT(&huart3, UART_IT_IDLE);
			g_usart3_recv_len = RECV_DATA_MAX_LEN - (__HAL_DMA_GET_COUNTER(&hdma_usart3_rx));
			
			memset(g_USART3_Recv_Data_BAK,0,RECV_DATA_MAX_LEN);
			memcpy(g_USART3_Recv_Data_BAK,g_USART3_Recv_Data,g_usart3_recv_len);
			memset(g_USART3_Recv_Data,0,RECV_DATA_MAX_LEN);
			g_USART3_Recv_Flag = 1;
		}
	}
	//USART2 IDLE 
	if(huart == &huart2)
	{
		if(RESET != __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE))
		{			
			__HAL_UART_CLEAR_IDLEFLAG(&huart2);//Clear the UART IDLE pending flag.(Otherwise it will continue to interrupt)
			HAL_UART_DMAStop(&huart2);
			__HAL_UART_DISABLE_IT(&huart2, UART_IT_IDLE);
			g_usart2_recv_len = RECV_DATA_MAX_LEN -(__HAL_DMA_GET_COUNTER(&hdma_usart2_rx));
			
			memset(g_USART2_Recv_Data_BAK,0,RECV_DATA_MAX_LEN);
			memcpy(g_USART2_Recv_Data_BAK,g_USART2_Recv_Data,g_usart2_recv_len);
			memset(g_USART2_Recv_Data,0,RECV_DATA_MAX_LEN);
			g_USART2_Recv_Flag = 1;
		}
	}
	//USART1 IDLE 
	if(huart == &huart1)
	{
		if(RESET != __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE))
		{			
			__HAL_UART_CLEAR_IDLEFLAG(&huart1);//Clear the UART IDLE pending flag.(Otherwise it will continue to interrupt)
			HAL_UART_DMAStop(&huart1);
			__HAL_UART_DISABLE_IT(&huart1, UART_IT_IDLE);
			g_usart1_recv_len = RECV_DATA_MAX_LEN -(__HAL_DMA_GET_COUNTER(&hdma_usart1_rx));
			memset(g_USART1_Recv_Data_BAK,0,RECV_DATA_MAX_LEN);
			memcpy(g_USART1_Recv_Data_BAK,g_USART1_Recv_Data,g_usart1_recv_len);
			memset(g_USART1_Recv_Data,0,RECV_DATA_MAX_LEN);
			g_USART1_Recv_Flag = 1;
		}
	}
}
2.2. 中断复位函数中调用

在中断服务函数中调用

3. 主函数中数据处理

  • 判断接收标志位
  • 标志位清零
  • 数据解析操作
  • 开启空闲中断、开启DMA接收
void Board_Run(void)
{
	/*uart5--接收*/
	if(g_USART5_Recv_Flag)
	{
		g_USART5_Recv_Flag = 0;
		//数据处理 g_USART5_Recv_Data_BAK
		HAL_UART_Transmit(&huart1,g_USART5_Recv_Data_BAK,g_usart5_recv_len,0xff);
		
	}
	/*uart4--接收*/
	if(g_USART4_Recv_Flag)
	{
		g_USART4_Recv_Flag = 0;	
		/* USER CODE BEGIN 解析数据 内容:g_USART4_Recv_Data_BAK,长度:g_usart4_recv_len*/
		HAL_UART_Transmit(&huart1,g_USART4_Recv_Data_BAK,g_usart4_recv_len,0xff);
		
		/* USER CODE END */
		/*解析完成打开中断*/
		HAL_UART_AbortReceive(&huart4);
		__HAL_UART_ENABLE_IT(&huart4, UART_IT_IDLE);
		HAL_UART_Receive_DMA(&huart4, (uint8_t*)g_USART4_Recv_Data, RECV_DATA_MAX_LEN);
	}
	/*usart3--接收*/
	if(g_USART3_Recv_Flag)
	{
		g_USART3_Recv_Flag=0;
		/* USER CODE BEGIN 解析数据 内容:g_USART3_Recv_Data_BAK,长度:g_usart3_recv_len*/
		HAL_UART_Transmit(&huart1,g_USART3_Recv_Data_BAK,g_usart3_recv_len,0xff);
		
		/* USER CODE END */
		HAL_UART_AbortReceive(&huart3);
		__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
		HAL_UART_Receive_DMA(&huart3, (uint8_t*)g_USART3_Recv_Data, RECV_DATA_MAX_LEN);
	}
	/*usart2--接收*/
	if(g_USART2_Recv_Flag)
	{
		g_USART2_Recv_Flag=0;
		/* USER CODE BEGIN 解析数据 内容:g_USART2_Recv_Data_BAK,长度:g_usart2_recv_len*/
		HAL_UART_Transmit(&huart1,g_USART2_Recv_Data_BAK,g_usart2_recv_len,0xff);
		
		/* USER CODE END */
		HAL_UART_AbortReceive(&huart2);
		__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
		HAL_UART_Receive_DMA(&huart2, (uint8_t*)g_USART2_Recv_Data, RECV_DATA_MAX_LEN);
	}
	/*usart1--接收*/
	if(g_USART1_Recv_Flag)
	{
		g_USART1_Recv_Flag=0;
		/* USER CODE BEGIN 解析数据 内容:g_USART1_Recv_Data_BAK,长度:g_usart1_recv_len*/
		HAL_UART_Transmit(&huart1,g_USART1_Recv_Data_BAK,g_usart1_recv_len,0xff);
		
		/* USER CODE END */
		HAL_UART_AbortReceive(&huart1);
		__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
		HAL_UART_Receive_DMA(&huart1, (uint8_t*)g_USART1_Recv_Data, RECV_DATA_MAX_LEN);
		
	}
}

四、源码地址

https://download.csdn.net/download/LJ_96/89771971

针对STM32G4HAL库串口空闲中断接收不定长数据的问题,可以按照以下步骤进行解决: 1. 开启串口空闲中断,即在初始化串口时,将USART_CR1_REG中的USART_CR1_IDLEIE位设置为1。 2. 在串口空闲中断中,通过读取USART_ISR_REG寄存器中的位USART_ISR_RXNE和USART_ISR_IDLE来判断是接收到数据还是空闲中断。 3. 如果是接收到数据,则读取USART_RDR_REG寄存器中的数据,并将其存储到缓冲区中。 4. 如果是空闲中断,则通过计算接收到的数据长度来确定数据的长度,并将其存储到缓冲区中。 5. 在数据长度达到预定长度时,可以通过回调函数或者其他方式来通知数据接收已经完成。 下面是一个示例代码: ```c uint8_t rx_buffer[100]; uint8_t rx_counter = 0; uint8_t rx_length = 0; uint8_t rx_flag = 0; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USARTx) { if(rx_flag == 0) { //接收到数据 if((__HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE) != RESET) && (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) == RESET)) { rx_buffer[rx_counter++] = (uint8_t)(huart->Instance->RDR & 0x00FF); } //空闲中断 else if(__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET) { rx_length = rx_counter; rx_counter = 0; rx_flag = 1; } } } } ``` 在上面的代码中,当接收到数据时,将数据存储到缓冲区中,并将计数器rx_counter加1。当空闲中断发生时,计算接收到的数据长度,并将其存储到rx_length中。在接收完成后,将rx_flag设置为1,表示数据接收已经完成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值