最近在测试串口收发的时候,发现串口会出现无法接收数据的情况,后来在网上查找资料,发现是库的问题
发送用的 HAL_UART_Transmit,接收数据使用的是中断方式
HAL_UART_Receive_IT
HAL_UART_Transmit在发送的过程中,如果这时候来了接收中断,就有可能会出现挂掉的情况了,为什么呢?来看一下
HAL_UART_Transmit函数内部实现
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) { uint8_t *pdata8bits; uint16_t *pdata16bits; uint32_t tickstart = 0U; /* Check that a Tx process is not already ongoing */ if (huart->gState == HAL_UART_STATE_READY) { if ((pData == NULL) || (Size == 0U)) { return HAL_ERROR; } /* Process Locked */ __HAL_LOCK(huart); huart->ErrorCode = HAL_UART_ERROR_NONE; huart->gState = HAL_UART_STATE_BUSY_TX; /* Init tickstart for timeout management */ tickstart = HAL_GetTick(); huart->TxXferSize = Size; huart->TxXferCount = Size; /* In case of 9bits/No Parity transfer, pData needs to be handled as a uint16_t pointer */ if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE)) { pdata8bits = NULL; pdata16bits = (uint16_t *) pData; } else { pdata8bits = pData; pdata16bits = NULL; } /* Process Unlocked */ __HAL_UNLOCK(huart); while (huart->TxXferCount > 0U) { if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } if (pdata8bits == NULL) { huart->Instance->DR = (uint16_t)(*pdata16bits & 0x01FFU); pdata16bits++; } else { huart->Instance->DR = (uint8_t)(*pdata8bits & 0xFFU); pdata8bits++; } huart->TxXferCount--; } if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } /* At end of Tx process, restore huart->gState to Ready */ huart->gState = HAL_UART_STATE_READY; return HAL_OK; } else { return HAL_BUSY; } }
我们注意到 __HAL_LOCK(huart); 函数,这是对串口资源的上锁,然后调用__HAL_UNLOCK(huart);进行解锁
再跟踪一下 __HAL_LOCK函数 ,这是一个宏定义
#if (USE_RTOS == 1U) /* Reserved for future use */ #error "USE_RTOS should be 0 in the current HAL release" #else #define __HAL_LOCK(__HANDLE__) \ do{ \ if((__HANDLE__)->Lock == HAL_LOCKED) \ { \ return HAL_BUSY; \ } \ else \ { \ (__HANDLE__)->Lock = HAL_LOCKED; \ } \ }while (0U) #define __HAL_UNLOCK(__HANDLE__) \ do{ \ (__HANDLE__)->Lock = HAL_UNLOCKED; \ }while (0U) #endif /* USE_RTOS */
这里, 如果资源已上锁,调用 __HAL_LOCK 会直接返回 HAL_BUSY,这很关键。
我们再来看一下 HAL_UART_Receive_IT函数
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); /* Set Reception type to Standard reception */ huart->ReceptionType = HAL_UART_RECEPTION_STANDARD; return(UART_Start_Receive_IT(huart, pData, Size)); } else { return HAL_BUSY; } }
这里 我们看到 打开中断的函数里面,也调用了__HAL_LOCK(huart); 如果这时候串口已经上锁了,就直接返回 HAL_BUSY,打开中断的 UART_Start_Receive_IT就没有调用,因此就无法打开串口接收中断了,也就出现了接收不到数据的情况了
解决办法:
屏蔽 __HAL_LOCK ,这种方法暴力直接
还有其他办法, 比如 USE_RTOS 赋值 1, 把 __HAL_LOCK 宏定义 为空