在使用STM32 CubeMX生成的工程中用到了串口2,发送开启了DMA模式,接收使用中断,每次接收一个字节,在回调函数中调用HAL_UART_Receive_IT(&huart2,(u8 *)&rx_data_2,1)接收开启函数。刚开始工作还算正常,但运行一段时间后(时间长度随机),调试窗口上串口数据不再更新,使用示波器检查引脚仍有数据接收到,STM32整体运行正常。初步判断,串口2发生异常错误。
MX自动生成的初始化代码就不贴了。
main函数中开启接收:
void main(void)
{
...
if(HAL_UART_Receive_IT(&huart2,(u8 *)&i,1) != HAL_OK) Error_Handler();
...
}
串口接收回调函数:
if(huart->Instance == USART2)
{
...
HAL_UART_Receive_IT(&huart2,(u8 *)&rx_data_2,1);
...
}
DMA发送函数:
void dma_send(unsigned char *buffer,unsigned int length)
{
//等待上一次的数据发送完毕
while(HAL_DMA_GetState(&hdma_usart2_tx) == HAL_DMA_STATE_BUSY);
/* 关闭DMA */
__HAL_DMA_DISABLE(&hdma_usart2_tx);
//开始发送数据
HAL_UART_Transmit_DMA(&huart2,buffer,length);
}
因为串口发送和接收是分离的,感觉代码没什么问题,实践证明之前确实可以运行,但串口死机的情况仍会出现。
先是根据外部状态观察,每次死机都紧接着发送之后出现。
上网查找对应问题,发现确实有类似串口发送接收互锁的问题,貌似可以在重新初始化串口解决。
根据论坛的说法,修改回调函数为以下代码:
if(huart->Instance == USART2)
{
u16 i = 0;
...
while(HAL_UART_Receive_IT(&huart2,(u8 *)&rx_data_2,1) != HAL_OK )
{
i++;
if( i > 10000 )
{
huart2.RxState = HAL_UART_STATE_READY;
MX_USART2_UART_Init(&huart2);
i = 0;
}
}
...
}
但是问题仍然存在,并且变成整体运行卡死,初步判断卡死在该循环内。
使用ST-LINK进行硬件调试,保持debug状态全速运行,等到串口死机后,暂停运行查看。
首先确认 HAL_UART_Receive_IT(&huart2,(u8 *)&rx_data_2,1) 的返回值一直为 HAL_BUSY
该函数源码如下:
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
/* Check that a Rx process is not already ongoing */
if(huart->RxState == HAL_UART_STATE_READY)
{
if((pData == NULL ) || (Size == 0))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Enable the UART Parity Error and Data Register not empty Interrupts */
SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
一开始我以为返回 HAL_BUSY 是因为 hart->RxState != HAL_UART_STATE_READY,但多次观察后发现在__HAL_LOCK(huart);这个宏定义之后直接函数就返回了。
__HAL_LOCK(huart)的源码如下:
#define __HAL_LOCK(__HANDLE__) \
do{ \
if((__HANDLE__)->Lock == HAL_LOCKED) \
{ \
return HAL_BUSY; \
} \
else \
{ \
(__HANDLE__)->Lock = HAL_LOCKED; \
} \
}while (0U)
原来是因为串口一直加锁失败导致串口死机。
根据这个原因再次修改回调函数:
if(huart->Instance == USART2)
{
u16 i = 0;
...
while(HAL_UART_Receive_IT(&huart2,(u8 *)&rx_data_2,1) != HAL_OK )
{
i++;
if( i > 10000 )
{
huart2.RxState = HAL_UART_STATE_READY;
__HAL_UNLOCK(&huart2);
i = 0;
}
}
...
}
重新进行测试,到目前为止还没有观察到死机的现象发生。
小结:这种方法虽然可以暂时解决这个锁死的问题,但假设ST官方库文件没有问题,那么可能导致收发数据丢包。但按道理既然是全双工串口,就不应该存在发送和接收互锁的情况,况且大多数情况下,发送和接收总有一个是几乎实时进行的,怎么可能不冲突呢?是否还有更好的解决办法,还望诸位大神和ST工程师能在此指导,谢谢!