前言:
stm32是嵌入式MCU開發中最多應用的芯片,很早之前我們開發ST芯一般都是標准庫開發,標准庫簡潔好讀,現在要配合CubeMX生成代碼,所以官方主推HAL庫和LL庫,但是HAL代碼冗雜很繞,因為出來也不久,有些代碼使用之后不是那么好用。
這次我就來分享兩個實際使用過程中遇到的兩個問題,一個是使用uart的發送中斷進行數據發送產生的數組訪問越界的問題。一個是stop模式下,dma相關的外設休眠喚醒需要注意重新初始化。
這篇是uart使用的介紹:
作者:良知猶存
轉載授權以及圍觀:歡迎關注微信公眾號:羽林君
或者添加作者個人微信:become_me
情節介紹:
串口是我們經常是用的一個外設,一般我們為了發送速度變快,會使用DMA或者中斷發送接收。而CubeMX配置下,HAL調用了自己的一套函數 HAL_UART_IRQHandler 層層調用。
在官方提供的 stm32f4xx_hal_uart.c 文件中你可以看到如下函數:
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
.....
/* UART in mode Transmitter ------------------------------------------------*/
if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
}
......
}
其中在UART_Transmit_IT 函數中 有一段 函數為
if (--huart->TxXferCount == 0U)
{
/* Disable the UART Transmit Complete Interrupt */
__HAL_UART_DISABLE_IT(huart, UART_IT_TXE);
/* Enable the UART Transmit Complete Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_TC);
}
這里會把發送的 TxXferCount 的計數值自減,並判斷是否為零。正常工作都沒有問題,可是我們的設備實際使用過程中,上層的部分斷電之后,會給底層通訊串口帶了一個中斷,這個時候繼續減下去就會出現出現一個很大值,這個是因為 TxXferCount 是一個無符號的16位數據。
__IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
debug看到如下數據,原因上文提到。就是我遇到我們Linux核心板掉電之后會產生一個中斷,導致這里判斷時候自動減一,TxXferCount從 0 變成 -1 因為是無符號數據,所以數據表現為65535。
在全局搜索TxXferCount調用,我們可以看到TxXferSize 是發送buf的長度數據
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
/* 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->pTxBuffPtr = pData;
huart->TxXferSize = Size;
huart->TxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->gState = HAL_UART_STATE_BUSY_TX;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Transmit data register empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_TXE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
此外我們還會發現一處 huart->TxXferCount 計數 自減 使用。
此處的函數如下, 伴隨着一個很大的 TxXferCount開始自減, pdata16bits開始自加。剛開始越界的時候由於該內存被初始化過,所以沒有問題,該循環執行一會之后,程序就會進入hardfault。
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
......
huart->TxXferSize = Size;
huart->TxXferCount = Size;
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--;
}
......
}
修改建議:
和硬件溝通過,他們的掉電機制,就是如此無法修改。所以我們進行軟件的一些修改,因為會產生一個中斷導致計數值自減,所以我們初步確認進行自減處進行限制,先增加一個零值判斷。
huart->TxXferCount == 0U
又考慮到我們單包數據單次不會超過150byte,所以又加上150字節的控制。(此處的數據流控制大家可以按照自己實際使用的情況進行酌情使用)
huart->TxXferCount > 150U
原函數:
static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)
{
......
if (--huart->TxXferCount == 0U)
{
/* Disable the UART Transmit Complete Interrupt */
__HAL_UART_DISABLE_IT(huart, UART_IT_TXE);
/* Enable the UART Transmit Complete Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_TC);
}
......
}
改為:
static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)
{
......
if (huart->TxXferCount == 0U || --huart->TxXferCount == 0U || huart->TxXferCount > 150U )
{
/* Disable the UART Transmit Complete Interrupt */
__HAL_UART_DISABLE_IT(huart, UART_IT_TXE);
/* Enable the UART Transmit Complete Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_TC);
}
......
}
最后代碼可以正常的使用。
結語
這就是我分享的項目中遇到一個st官方庫使用的問題,如果大家有更好的想法和需求,也歡迎大家加我好友交流分享哈。
作者:良知猶存,白天努力工作,晚上原創公號號主。公眾號內容除了技術還有些人生感悟,一個認真輸出內容的職場老司機,也是一個技術之外豐富生活的人,攝影、音樂 and 籃球。關注我,與我一起同行。
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
推薦閱讀
【3】CPU中的程序是怎么運行起來的 必讀
本公眾號全部原創干貨已整理成一個目錄,回復[ 資源 ]即可獲得。