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接收即可。
在这里插入图片描述
- 串口中断函数
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空闲中断回调函数
**/
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中断接收数据
}
}
- 串口错误回调函数
/**
*串口错误回调函数
**/
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); //输出信息提示
}
}