一直使用F4系列,沒有出現此類現象,也可能出現了沒有發現。最近在做和研華工控機通訊時出現串口接收問題。總結如下:
1. 使用DMA+空閒中斷未出現串口同時收發死鎖現象,但是由於研華某款工控機在與板子通信時,出現丟包現象
- 工控機接收到板子發送數據正常,無丟包
- 工控機發送數據到板子,發送正常,接收總是顯示校驗不通過,不通過時只收到部分包。
- 板子與電腦通過串口工具相互發送數據,板子無丟包現象,串口工具無丟包現象。
- 工控機與電腦通過串口工具相互發送數據,工控機無丟包現象,串口工具無丟包現象。
由於此項目是接手前人同事的,他之前用的是標準庫,而且標準庫官方已經不再維護,我一直用的是HAL庫。就在想難道是庫的bug嗎,但是和電腦通信很正常,放在設備上就會異常,然後是各種操作,依然無用,來來回回搞了半個月因爲這個通信問題。最後通過板子和工控機之間增加DB9轉usb的線,發現可以正常通信,本想就這樣結束這個問題。因爲這樣已經排除了軟件原因,只能是硬件行爲影響到軟件的收發。
最終幾天過後,另外的同事找到研華工控機技術,查閱工控機的串口芯片手冊,發現了重大問題,是因爲該芯片緩存的FIFO大小是16,而我們出現問題的幀長度是40,硬件將其分3次發送(16+16+8),但是板子的空閒中斷又認爲是3次空閒中斷,所以包不完整導致校驗不通過。但是項目已經臨近,沒有再做修改,只計劃在新一版上修改。
2.
這裏又到了新的一版,由於上面的串口緩存FIFO問題,這樣我就考慮不能再使用高效方便的DMA+串口中斷方式了。直接使用串口普通中斷進行接收工控機數據。問題又來了,出現現象如下:
- 板子接收串口工具下發的數據然後再返回該數據,兩者可以正常通信。
- 板子週期30ms主動發送數據,同時接收數據,串口工具發送一定次數後板子不在收到數據,且次數不定。
這下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文件中
原因分析:問題出現在串口接收中斷函數中,鎖住了串口,但是後面那句已經解鎖了啊,但是爲什麼還是鎖住了呢
按照上面進行解鎖操作後,沒有出現互鎖現象了。
參考博客: