最近在測試串口收發的時候,發現串口會出現無法接收數據的情況,后來在網上查找資料,發現是庫的問題
發送用的 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 宏定義 為空
