最近做项目,使用HAL库,调试串口总遇到卡死问题,卡死原因五花八门。
第一个坑:
现象描述:串口开启后,一开始可以收发数据,运行一段时间后,收不到数据,经测试无法进去中断,且卡死时间很随机
**问题查找:**查了不少资料,看了一下卡死后的串口寄存器,发现ORE溢出标志位未清除,RX寄存器里面还有数据未读出。然后又查了usart中断处理的代码,果然入网友所说,usart发送和接收互锁,导致的溢出,也就是说全双工通信被ST的250工程师写成了半双工,接收发送不能同时进行。
解决方案:
1.增加串口卡死检测程序,在进行通信时,在串口中断里面写一个标志位,每次进中断标志位置1,在主循环,把标志位置零,如果发现通信期间标志位长时间等于0,则串口卡死,接收不到数据。此时重新配置串口,即可收到数据
2.更换HAL库,我是用的是16年的库,最新的库不存在这个问题,把库更新问题就可以彻底解决。
网上对此的说明很多,但都是指出了问题点,没有解决该问题。其实我理解中该问题的原因很简单,在你使能串口中断USART_ITConfig(USART1 , USART_IT_IDLE, ENABLE);
USART_ITConfig(USART1 ,USART_IT_RXNE , ENABLE);时,
同时会半开启USART_FLAG_ORE中断,这种情况就是BUG,出现溢出会进入中断函数,但是却清除不掉标志位,因为你没有调用开启函数开启该中断。
所以解决办法就是我们必须在开启中断同时,开启错误中断
USART_ITConfig(USART1, USART_IT_ERR, ENABLE);
这个配置设置后就可以使用前面的清除标志位的函数了,不然清除标志位会失效。同时由于开启了该中断会导致其他错误标志位响应中断我们也需要清除标志位。以正确的处理的代码如下:
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 = 9600;
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;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
HAL_UART_Receive_DMA(&huart1,huart1_rx_buffer,BufferSize);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_ORE);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_ERR);
RS485_TX_Set(0); //设置为接收模式
/* USER CODE END USART1_Init 2 */
}
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_FE) != RESET)
{
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_FE);
}
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_PE) != RESET)
{
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_PE);
}
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE) != RESET)
{
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_ORE);//清除溢出中断
}
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_NE) != RESET)
{
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_NE);
}
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET)//通过标志位判断接收是否结束
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
HAL_UART_DMAStop(&huart1);
uint8_t temp=__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
huart1_rx_len =BufferSize-temp; //计算出数据长度 //100
RS485_send_data(huart1_rx_buffer,huart1_rx_len);
HAL_UART_Receive_DMA(&huart1,huart1_rx_buffer,BufferSize);//开启DMA接收,方便下一次接收数据
RS485_TX_Set(0); //设置为接收模式
}
/* USER CODE END USART1_IRQn 1 */
}
第二个坑:
现象描述:库更新后,开始的时候我用串口助手不断给单片机数据,同时单片机不断往外发数据,测了一下午,没遇到卡死现象,后来进行网络通信,第一帧数据比较大,大概600字节左右,又遇到串口卡死,发完数据后接收不到数据,串口卡死,查寄存器,ORE未清零,串口中还有数据未读出,想着刚换了库,应该不是库的问题。
解决方案
1.多方查找,最终在修改了数据发送超时时间后,串口在没再卡死,原因应该是数据量大,但超时时间设置的太小导致的。
第三个坑
**现象描述:**当时我开了两路串口,调试的时候,我发现另外一个串口悬空没有数据收发,依然每次会接收到随机数据,我在中断里打了断点,发现隔一段时间就会进中断,太神奇了有木有,大白天的也不会出鬼吧。
解决方案:
1.串口初始化时,管脚上拉,不要浮空,不然会出鬼。
最后还想说几点,在之前的程序中,溢出中断也与死机如影随行,如果出现了溢出中断就意味着百分百丢失了数据。而我们能做的处理无非就是清除标志位防止中断反复触发,从而使整个系统能继续运行下去。至于丢失的数据,已经不可能找回来了。
溢出中断只是作为一种防备手段来预防极小概率发生的情况,我当时用了3套串口,其中一个几乎满负荷运行,另一个用于接收GPS数据的低优先级串口使用了溢出中断,最后本来一秒一次的GPS数据在校验之后一分钟才能更新一次,甚至需要更长时间,最后还是不得不使用了串口空闲中断,为此需要为接收GPS数据保留一个1k的ram作缓存区。希望之后能找到更好的处理办法。。。
环境:STM32F7 RS485通信,一主多从,9600bps,主机的串口开启了发送DMA与接收DMA,从机只开启了发送DMA,接收采用中断的方式,开启接收中断
运行一段时间后,从机一直反复进入串口接收中断,中断只判断接收不为空标志,但是进入中断后却没有接收不为空的标志产生,查看串口SR寄存器,发现ORE位置位了
STM32手册描述:开启串口接收中断,将会自动产生ORE上溢中断,而有的单片机则需要手动打开ORE中断
调试串口时,发现串口会出现频繁跳中断,导致无法执行主循环的问题!
调试发现是串口中断硬件BUG:
1. USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);使能了接收中断,那么ORE中断也同时被开启了。
2. ORE中断只能使用USART_GetFlagStatus(USART1, USART_FLAG_ORE) 读到(没有使能USART_IT_ERR中断时)
解决办法:
1. 初始化时,开启中断
USART_ITConfig(USART1, USART_IT_PE, ENABLE); //开启PE错误接收中断Bit 8PEIE: PE interrupt enable
//CR2 开启ERR中断
USART_ITConfig(USART1, USART_IT_ERR, ENABLE);
2. 在STM32中断函数增加USART_IT_ORE等异常中断的处理!
//开启CR3,bit0的EIE: Error interrupt enable, 处理USART_IT_ERR,USART_IT_ORE_ER,USART_IT_NE,USART_IT_FE 错误
if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET)
{//同 @arg USART_IT_ORE_ER : OverRun Error interrupt if the EIE bit is set
ushTemp = USART_ReceiveData(USART1); //取出来扔掉
USART_ClearFlag(USART1, USART_FLAG_ORE);
}
if(USART_GetFlagStatus(USART1, USART_FLAG_NE) != RESET)
{//同 @arg USART_IT_NE : Noise Error interrupt
USART_ClearFlag(USART1, USART_FLAG_NE);
}
if(USART_GetFlagStatus(USART1, USART_FLAG_FE) != RESET)
{//同 @arg USART_IT_FE : Framing Error interrupt
USART_ClearFlag(USART1, USART_FLAG_FE);
}
if(USART_GetFlagStatus(USART1, USART_FLAG_PE) != RESET)
{//同 @arg USART_IT_PE : Parity Error interrupt
USART_ClearFlag(USART1, USART_FLAG_PE);
}
网络上也有一篇文章讲的比较仔细,请看!
http://bbs.21ic.com/viewthread.php?tid=160999&highlight=USART