一直使用F4系列,没有出现此类现象,也可能出现了没有发现。最近在做和研华工控机通讯时出现串口接收问题。总结如下:
1. 使用DMA+空闲中断未出现串口同时收发死锁现象,但是由于研华某款工控机在与板子通信时,出现丢包现象
由于此项目是接手前人同事的,他之前用的是标准库,而且标准库官方已经不再维护,我一直用的是HAL库。就在想难道是库的bug吗,但是和电脑通信很正常,放在设备上就会异常,然后是各种操作,依然无用,来来回回搞了半个月因为这个通信问题。最后通过板子和工控机之间增加DB9转usb的线,发现可以正常通信,本想就这样结束这个问题。因为这样已经排除了软件原因,只能是硬件行为影响到软件的收发。
最终几天过后,另外的同事找到研华工控机技术,查阅工控机的串口芯片手册,发现了重大问题,是因为该芯片缓存的FIFO大小是16,而我们出现问题的帧长度是40,硬件将其分3次发送(16+16+8),但是板子的空闲中断又认为是3次空闲中断,所以包不完整导致校验不通过。但是项目已经临近,没有再做修改,只计划在新一版上修改。
2.
这里又到了新的一版,由于上面的串口缓存FIFO问题,这样我就考虑不能再使用高效方便的DMA+串口中断方式了。直接使用串口普通中断进行接收工控机数据。问题又来了,出现现象如下:
这下GG,尝试了多种方式依然还是无法解决此类现象(换中断与发送方式,重新制作新的测试工程,尝试了更换更新的库,参照网上使能串口错误回调函数在里面进行清除各种错误标志等等),只是有长有短,没办法根治。发现问题是在
HAL_UART_Receive_IT(&huart2, (uint8_t *)aRxBuffer, 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 == 0U))
{
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 Parity Error Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
以为是 if (huart->RxState == HAL_UART_STATE_READY)此语句判断不过导致,在串口接收完成回调函数里增加强制改变该串口状态为HAL_UART_STATE_READY,依旧无法解决问题。然后看到网上有人在串口错误回调函数里进行清除错误操作,照例使用,该函数改为
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_UART_ErrorCallback could be implemented in the user file
*/
uint32_t isrflags = READ_REG(huart->Instance->SR);//手册上有讲,清错误都要先读SR
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_PE))!=RESET)
{
READ_REG(huart->Instance->DR);//PE清标志,第二步读DR
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_PE);//清标志
}
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_FE))!=RESET)
{
READ_REG(huart->Instance->DR);//FE清标志,第二步读DR
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_FE);
}
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_NE))!=RESET)
{
READ_REG(huart->Instance->DR);//NE清标志,第二步读DR
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_NE);
}
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_ORE))!=RESET)
{
READ_REG(huart->Instance->CR1);//ORE清标志,第二步读CR
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_ORE);
}
if (HAL_UART_GetError(huart) & HAL_UART_ERROR_ORE)
__HAL_UART_FLUSH_DRREGISTER(huart);
if(huart->ErrorCode&HAL_UART_ERROR_ORE)
{
__HAL_UART_CLEAR_OREFLAG(huart);
}
if(__HAL_UART_GET_FLAG(huart, UART_FLAG_ORE) != RESET)
{
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_ORE); //清除溢出中断
HAL_UART_Receive_IT(&huart2, (uint8_t *)aRxBuffer, 1);
}
if(__HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE) != RESET)
{
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_RXNE);
//rebuf[num++] = USART_ReceiveData(USART2); 读取串口数据
}
}
看到这么多错误该清除的都清除了,心想应该没问题了,但是烧写后问题依旧存在。
最后的最后,有幸看到资料,解决此类问题。
main函数主要部分如下:
/* USER CODE BEGIN PV */
extern uint32_t cnt;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart2, (uint8_t *)aRxBuffer, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
sprintf((char*)sendbuff,(const char*)"recv data cnt %d\r\n",cnt);
HAL_UART_Transmit(&huart2,sendbuff,strlen((char*)sendbuff),100);
HAL_Delay(20);
}
/* USER CODE END 3 */
主要功能就是初始化串口和时钟,然后再while循环里打印串口中断进入的次数。
在usart.c文件中,最重要的两部,
在usart.h文件中
原因分析:问题出现在串口接收中断函数中,锁住了串口,但是后面那句已经解锁了啊,但是为什么还是锁住了呢
按照上面进行解锁操作后,没有出现互锁现象了。
参考博客:
https://blog.csdn.net/mickey35/article/details/74255041