問題
代碼測試時發現裝置進入hard_fault,通過call stack檢視資訊發現最後死在HAL_UART_Transmit函數。
解決
1. 調試發現huart->TxXferCount 被設定成0xFFFE,且運作不斷減小,該值的含義為剩餘發送位元組數,看工程代碼實際僅設定為1個位元組,是以按照邏輯不應該出現這種情況。根據這個值初步判斷應該為在中斷中huart->TxXferCount 被設定為0,而傳回線程模式後HAL庫繼續執行之前的操作,将會使huart->TxXferCount--;導緻溢出為0xFFFF。接下來排查是哪個中斷将這個值清零。
2. 通過仿真發現了調用關系如下
HAL_UART_IRQHandler --》 HAL_DMA_Abort_IT --》 hdma->XferAbortCallback(hdma) -》 UART_DMAAbortOnError --》 huart->TxXferCount = 0x00U; 當出錯時,該變量會被清零,如果這時剛好在while循環内部,且huart->TxXferCount =1,那麼在執行while循環體時huart->TxXferCount已經被置0,那麼在退出循環體前進行了減1操作,導緻值溢出,發送0xFFFF個位元組(64KB),導緻記憶體溢出。
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--;
}
3. 新問題引入:為什麼會出現HAL_DMA_Abort_IT, 當接收資料出現錯誤時,如果USART_CR3_DMAR使能,會失能DMAR接收,并清除uart->TxXferCount,導緻出現第1步的原因。
static void UART_DMAAbortOnError(DMA_HandleTypeDef *hdma)
{
UART_HandleTypeDef *huart = (UART_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
huart->RxXferCount = 0x00U;
huart->TxXferCount = 0x00U;
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*Call registered error callback*/
huart->ErrorCallback(huart);
#else
/*Call legacy weak error callback*/
HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
}
4. 為什麼會出現資料錯誤呢?檢視SR寄存器錯誤為FE(幀錯誤),實際序列槽不斷在進入中斷,而裝置是未進行任何資料輸入的,這裡接收到的資料肯定是雜亂無序,不按照uart協定通信的,導緻stm32檢測到幀錯誤的問題。
排查:開發闆序列槽接收引腳懸空,配置成上拉模式後正常。為保險起見,修改HAL庫函數
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 managment */
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++;
}
if(huart->TxXferCount > 0)
{
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;
}
}
修改的地方如下,這樣這個值就不會出現溢出變成65535,導緻發送緩存區位址溢出:
if(huart->TxXferCount > 0)
{
huart->TxXferCount--;
}
版權聲明:本文為CSDN部落客「Hi,Mr.Wang」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。
原文連結:https://blog.csdn.net/qq_34672688/article/details/117478889
分享不易,點個贊再走吧☺☺☺