STM32_HAL库编程_串口+DMA接收数据异常问题记录

1.开发环境概述

	Keil - 5.27
	STM32CUBEMX
	STM32F103RCT6

2.问题现象

在利用RCT6做主机通信,C8T6做从机通信时。拔插从机电路板,有概率出现主机通信故障无法接收到从机数据,或者从机通信故障无法接收到主机数据的问题。两者开发均使用HAL库编程,串口数据接收方式为UART+DMA接收。

3.排查过程

利用LED灯检查每次数据过来时的串口中断触发状态,发现在拔插从机导致通信出错后,从机后续发出的数据均不能触发主机的串口接收中断。出现这种情况有两种可能,第一:主机死机了程序流程无法执行,第二:中断被屏蔽了,所以无法触发。先排除第一种情况,主板程序运转时有控制另一个LED灯定时翻转,在通信异常后,工作LED灯依旧正常翻转,由此可见主机并未死机。那么只剩第二种情况,可能在哪一步流程中,MCU把串口的接收中断给关闭了。

追入HAL_UART_IRQHandler()函数中,发现其有一段串口错误处理流程,当串口出错时。该函数会关闭串口接收状态UART_EndRxTransfer(huart);,并调用HAL_UART_ErrorCallback()回调函数给用户进行对应处理。这样一来问题就很清楚了,串口出错后,程序关闭了串口接收使能,所以后续从机所有的数据均不能触发主机的接收中断进行处理。并且程序将HAL_UART_ErrorCallback()函数而非HAL_UARTEx_RxEventCallback()函数,若没有在HAL_UARTEx_RxEventCallback()函数中写入对应的处理程序,那么就相当于少执行了一次HAL_UARTEx_RxEventCallback()函数。

解决办法

单独实现HAL_UART_ErrorCallback()函数,并在其中重新使能串口DMA接收即可。
在这里插入图片描述
中断处理流程

  1. 串口中断函数
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	  LED2_TOGGLE();
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
  /* USER CODE END USART1_IRQn 1 */
}
  1. 串口中断接收数据回调函数
/**
*串口1空闲中断回调函数
**/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	if (huart->Instance == USART1) //串口1空闲中断
	{
		if (Size >= 7)
		{
			if(RxBuffer1[0] == Slave_Data[0].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[1].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[2].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[3].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[4].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[5].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[6].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[7].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[8].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[9].Slave_Address	||	RxBuffer1[0] == Broadcast_Modbus_Addr||RxBuffer1[0] == BAK_Modbus_Addr)	//判断地址是否符合
				{
					modbus1_ok = TRUE;		//接收标志置位
					modbus1_RxNum = Size;	//保存数据长度
				}
		}
		
//		Start_Reback_Dma = 0;					//这两个是用来打印调试信息的
//		ReStart_Dma_Switch = FALSE;
		
		uint8_t Ret = 0;
		Ret = HAL_UARTEx_ReceiveToIdle_DMA(&huart1,RxBuffer1,RBufferSize); //使用串口IDLE中断接收数据
		if (Ret != HAL_OK)
		{
			char Buff[20] = {0};
			sprintf(Buff,"ERROR:%d\r\n",Ret);
			HAL_UART_Transmit(&huart2,(uint8_t *)Buff,sizeof(Buff),0xFFFF); //输出信息提示
		}
//		LED2_TOGGLE();	//收到一包数据,翻转一次LED灯
	}
	
	else if (huart->Instance == USART3)	//串口3空闲中断
	{
		if (!memcmp(RxBuffer3,"AT+Shiled_Slave:{",17))/*屏蔽从机指令*/
		{
			UI_Command = Command_1;																			//命令标号定义
			UI_Cmd_IsArrive = TRUE;																			//命令接收标志置位
			memcpy(UI_Cmd_Recevice,RxBuffer3,Size);											//内存中缓存命令
			UICmd_Recevice_Counter = Size;															//保存接收的字节数
			
			printf("\r\nRecv AT+Shiled_Slave OK\r\n");											//回应主机
			HAL_UART_Transmit(&huart2,(uint8_t *)"Recv AT+Shiled_Slave OK\r\n",25,0xffff);
		}
		
		else if (!memcmp(RxBuffer3,"AT+SelfInspection:{",19))/*通信自检指令*/
		{
			UI_Command = Command_2;																			//命令标号定义
			UI_Cmd_IsArrive = TRUE;																			//命令接收标志置位
			memcpy(UI_Cmd_Recevice,RxBuffer3,Size);											//内存中缓存命令
			UICmd_Recevice_Counter = Size;															//保存接收的字节数
			
			printf("Recv AT+SelfInspection OK\r\n");										//回应主机
			HAL_UART_Transmit(&huart2,(uint8_t *)"\r\nRecv AT+SelfInspection OK\r\n",29,0xffff);
		}
		
		else if(!memcmp(RxBuffer3,"AT+Reset_Slave:{",16))/*复位从机指令*/
		{
			UI_Command = Command_3;																			//命令标号定义
			UI_Cmd_IsArrive = TRUE;																			//命令接收标志置位
			memcpy(UI_Cmd_Recevice,RxBuffer3,Size);											//内存中缓存命令
			UICmd_Recevice_Counter = Size;															//保存接收的字节数
			
			printf("Recv AT+Reset_Slave OK\r\n");
			HAL_UART_Transmit(&huart2,(uint8_t *)"\r\nRecv AT+Reset_Slave OK\r\n",26,0xffff);
		}
		
		else if (!memcmp(RxBuffer3,"AT+Slave_Config:{",17))/*配置主板通信指令*/
		{
			UI_Command = Command_4;																			//命令标号定义
			UI_Cmd_IsArrive = TRUE;																			//命令接收标志置位
			memcpy(UI_Cmd_Recevice,RxBuffer3,Size);											//内存中缓存命令
			UICmd_Recevice_Counter = Size;															//保存接收的字节数
			
			printf("Recv AT+Slave_Config OK\r\n");
			HAL_UART_Transmit(&huart2,(uint8_t *)"\r\nRecv AT+Slave_Config OK\r\n",27,0xffff);
		}
		
		HAL_UARTEx_ReceiveToIdle_DMA(&huart3,RxBuffer3,RBufferSize); //使用串口IDLE中断接收数据
	}
}

  1. 串口错误回调函数
/**
*串口错误回调函数
**/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
		if (huart->Instance == USART1)
		{
			char Buff[20] = {0};
			snprintf(Buff,20,"USART ERROR:\r\n");
			HAL_UART_Transmit(&huart2,(uint8_t *)Buff,strlen(Buff),0xFFFF); //输出信息提示
			
			uint8_t Ret,St;
			St = (&huart1)->RxState;
			Ret = HAL_UARTEx_ReceiveToIdle_DMA(&huart1,RxBuffer1,RBufferSize); //重设,使用串口IDLE中断接收数据
			
			if (Ret != HAL_OK)
			{
				memset(Buff,0,20);
				snprintf(Buff,20,"ERROR:%d,RXs = %d\r\n",Ret,St);
				HAL_UART_Transmit(&huart2,(uint8_t *)Buff,sizeof(Buff),0xFFFF); //输出信息提示
			}
			else
				HAL_UART_Transmit(&huart2,(uint8_t *)"DMA START OK\r\n",sizeof("DMA START OK\r\n"),0xFFFF); //输出信息提示
		}
		
		else if (huart->Instance == USART3)
		{
			char Buff[20] = {0};
			snprintf(Buff,20,"USART ERROR:\r\n");
			HAL_UART_Transmit(&huart2,(uint8_t *)Buff,strlen(Buff),0xFFFF); //输出信息提示
			
			uint8_t Ret,St;
			St = (&huart3)->RxState;
			Ret = HAL_UARTEx_ReceiveToIdle_DMA(&huart3,RxBuffer3,RBufferSize); //重设,使用串口IDLE中断接收数据
			
			if (Ret != HAL_OK)
			{
				memset(Buff,0,20);
				snprintf(Buff,20,"ERROR:%d,RXs = %d\r\n",Ret,St);
				HAL_UART_Transmit(&huart2,(uint8_t *)Buff,sizeof(Buff),0xFFFF); //输出信息提示
			}
			else
				HAL_UART_Transmit(&huart2,(uint8_t *)"DMA START OK\r\n",sizeof("DMA START OK\r\n"),0xFFFF); //输出信息提示
		}
}

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
利用STM32 HAL库实现串口DMA发送和不定长度数据接收的方法如下: 1. 串口DMA发送: 首先,需要初始化串口DMA相关的参数。通过HAL_UART_Init()函数初始化串口,设置波特率、数据位、停止位等参数。然后使用HAL_UART_Transmit_DMA()函数启动DMA发送,将发送数据缓冲区的指针和数据长度传入该函数。 2. 不定长度数据接收: 在接收数据时,我们可以使用DMA模式配合中断来实现不定长度的数据接收。首先,需要初始化串口DMA相关的参数,与串口DMA发送相同。然后,使用HAL_UART_Receive_DMA()函数启动DMA接收,将接收数据存放到接收缓冲区中。 在接收数据的过程中,可以通过中断方式来判断数据是否接收完成。在中断处理函数中,可以读取接收数据缓冲区的数据,并根据接收到的数据进行处理。在处理完数据之后,可以继续启动DMA接收,以进行下一次的数据接收。 需要注意的是,在中断处理函数中,需要判断DMA接收是否完成,可以通过检查DMA接收状态寄存器的标志位来判断。如果DMA接收完成,则可以执行相应的操作,比如解析接收到的数据。 总结: 利用STM32 HAL库,可以方便地实现串口DMA发送和不定长度数据接收。通过初始化相关参数,并启动串口DMA发送和接收,可以实现高效的数据传输。在中断处理函数中,可以对接收到的数据进行处理,并根据需要继续启动DMA接收。这种方法适用于需要在接收端实时处理不定长度的数据的场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值