一、发现问题
用STMG070的4个串口中两个串口实时通信时,偶发某个串口通信挂掉,进入不了接收中断函数,但是能进入接收回调函数,另一个串口通信正常,其他程序正常运行?
二、分析定位问题
起初,以为是多串口通信,导致抢占资源,频繁访问中断,导致通信冲突;
于是,在接收回调函数中,各接收中断函数加入if, else if条件,同一时间只能进入一个串口接收中断,但是还是会偶发串口通信挂掉;
然后,网上查资料,看到HAL库的接收中断里面有加锁、解锁操作,数据量大会导致串口锁死,进入串口接收中断函数,STM32Cube_FW_G0_V1.6.0版本里面没有加锁;
然后,看到有说overrun导致过载溢出错误,串口接收死掉,仿真检测当串口通信挂掉时,果然overrun被置位,这里终于定位到了问题。
三、解决问题
overrun标志,就是状态寄存器ISR寄存器的bit3:ORE,如下图
提示:可以通过OVRDIS关闭ORE检测,如下图
三种解决方案:(推荐第3种)
1.在串口故障回调函数中检测ORE标志,如果被置位,则清除,重新打开串口接收中断,代码如下:
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_ORE) != RESET)
{
__HAL_UART_CLEAR_OREFLAG(&huart2);
//__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);
HAL_UART_Receive_IT(&huart2,(uint8_t*)&rx, sizeof(rx));
su16Uart2OreCnt++;
if(su16Uart2OreCnt >= 60000)
{
su16Uart2OreCnt= 0;
}
}
if(__HAL_UART_GET_FLAG(&huart3, UART_FLAG_ORE) != RESET)
{
__HAL_UART_CLEAR_OREFLAG(&huart3);
//__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);
HAL_UART_Receive_IT(&huart3,(uint8_t*)&rx, sizeof(rx));
su16Uart3OreCnt++;
if(su16Uart3OreCnt >= 60000)
{
su16Uart3OreCnt= 0;
}
}
}
通过检测进入串口故障回调函数中,串口ORE置位清除的次数,发现进入几次之后,后边就进不来了,不知什么原因?
2.STM32CubeMX串口配置中默认overrun默认使能,关闭该使能,但是,有丢包的风险。该方法未尝试。
3.定时500ms,检测几个串口的ORE是否置位,置位则清除ORE标志,重新打开中断,这个比较稳定靠谱一些,即使串口接收回调函数异常,也不影响清除ORE标志。代码如下:
//定时检测ORE
//static uint16_t su16INVUart1OreCnt = 0;
//static uint16_t su16APPUart2OreCnt = 0;
//static uint16_t su16BMSUart3OreCnt = 0;
//static uint16_t su16MPPTUart4OreCnt = 0;
void PS_Modules_Uart_Clear_ORE(void)
{
static uint16_t su16OreCnt = 0;
// static uint16_t su16PrintfOreCnt = 0;
// su16PrintfOreCnt++;
// if(su16PrintfOreCnt > 3000)
// {
// su16PrintfOreCnt = 0;
// printf("INV ORE cnt(USART1) = %d\r\n",su16INVUart1OreCnt);
// printf("APP ORE cnt(USART2) = %d\r\n",su16APPUart2OreCnt);
// printf("BMS ORE cnt(USART3) = %d\r\n",su16BMSUart3OreCnt);
// printf("MPPT ORE cnt(USART4) = %d\r\n",su16MPPTUart4OreCnt);
// }
su16OreCnt++;
if(su16OreCnt > 500)
{
su16OreCnt = 0;
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE) != RESET)
{
__HAL_UART_CLEAR_OREFLAG(&huart1);
HAL_UART_Receive_IT(&huart1,(uint8_t*)&rx, sizeof(rx));
// su16INVUart1OreCnt++;
}
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_ORE) != RESET)
{
__HAL_UART_CLEAR_OREFLAG(&huart2);
HAL_UART_Receive_IT(&huart2,(uint8_t*)&rx, sizeof(rx));
// su16APPUart2OreCnt++;
}
if(__HAL_UART_GET_FLAG(&huart3, UART_FLAG_ORE) != RESET)
{
__HAL_UART_CLEAR_OREFLAG(&huart3);
HAL_UART_Receive_IT(&huart3,(uint8_t*)&rx, sizeof(rx));
// su16BMSUart3OreCnt++;
}
if(__HAL_UART_GET_FLAG(&huart4, UART_FLAG_ORE) != RESET)
{
__HAL_UART_CLEAR_OREFLAG(&huart4);
HAL_UART_Receive_IT(&huart4,(uint8_t*)&rx, sizeof(rx));
// su16MPPTUart4OreCnt++;
}
}
}
串口2和串口3通信压力测试2小时,通信均正常。
通过打印清除次数,发现串口3清除了3次,串口2清除了4次。
四、为什么会出现overrun?
还是要看这个ORE位
当RXFF = 1时,当移位寄存器中正在被接收到的数据转移到USART_RDR寄存器,该位由硬件设置。当RXFF = 1数据被转移到USART_RDR寄存器未完成时,又来一个数据,则发生过载溢出,ORE置位。
STM32F1中这样解释:
软件序列先读SR,再读CR,可将其清零。
STM32G070也可通过串口中断标志位清除寄存器,将ORE标志清除,如下: