日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

STM32 之九 HAL 库串口(USART/UART)驱动 BUG 及解决方法

發(fā)布時間:2024/10/14 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32 之九 HAL 库串口(USART/UART)驱动 BUG 及解决方法 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

寫在前面

??在工作中,部分產(chǎn)品使用了ST最新的 HAL驅(qū)動庫,發(fā)現(xiàn) HAL 庫 BUG 還是挺多的!本文重點針對在使用HAL庫的 UART / USART 部分時,發(fā)現(xiàn)的以下幾個個比較嚴重Bug。其嚴重影響正常產(chǎn)品中使用!特此備注說明。
??不過需要說明的是,HAL庫的串口驅(qū)動確實很好使用,絕大部分繁雜的工作都被封裝在了 HAL 庫 函數(shù)之中。但是,這種封裝也存在一定的弊端,因為作為嵌入式產(chǎn)品,我不需要大而全的東西,只求精簡高效!
??在使用 USB部分驅(qū)動時,BUG也是不少(目前產(chǎn)品已全部替換使用舊版獨立版本的USB庫了),后續(xù)文章在說明!HAL庫的USB部分是真的難用!與舊版差多了!

分析解決

  • 如果在DMA發(fā)送過程中,出現(xiàn)串口錯誤(可能是發(fā)送錯誤,也可能是接收錯誤),將進入HAL的中斷處理函數(shù),但是處理函數(shù)中只處理了DMA的接收部分(將DMA關閉,清除串口的接收標志CR3->DMAR),而對于發(fā)送的DMA標志(CR3->DMAT)則沒有處理,這樣重新初始化后,CR3->DMAT先于DMA通道使能,就導致了再次配置完串口啟用DMA發(fā)送時出現(xiàn)錯誤。因此 必須在錯誤回調(diào)函數(shù)中,顯式清除CR3->DMAT
  • 由于DMA在其設計上,在使能時NDTR會自動重裝之前的值,而串口使用DMA接收時,通常是工作在循環(huán)模式下,重裝后將導致循環(huán)模式重新計算,進一步導致串口接收循環(huán)覆蓋。(STM32F407手冊有說明,其他芯片未知)因此,不能在驅(qū)動的任何地方關閉DMA(發(fā)生錯誤,重新初始化除外)。
  • 針對這一點,網(wǎng)上很多文章說,在獲取DMA收到的數(shù)據(jù)長度時,最好先關閉DMA,這種情況不適用于循環(huán)模式

  • 關于錯誤回調(diào)函數(shù)的調(diào)用問題:在DMA模式下,出現(xiàn)錯誤時,根據(jù)HAL庫的設計,錯誤回調(diào)函數(shù)是在DMA中斷處理函數(shù)中被調(diào)用的。具體流程為:串口接收產(chǎn)生錯誤 -> 關閉使用的DMA -> DMA傳輸完成 -> 產(chǎn)生完成中斷 -> 進入DMA中斷處理函數(shù)并發(fā)現(xiàn)有錯誤,則調(diào)用串口錯誤處理函數(shù)。這就導致了在正常傳輸時,DMA接收中斷是用不到的,但是一旦產(chǎn)生錯誤時,則會產(chǎn)生DMA傳輸完成中斷。 因此,DMA接收中斷只在產(chǎn)生錯誤時使用。 寄存器的值具體如下所示:
  • 在之前的使用中,我曾認為DMA的接收中斷沒有用到就刪除了,后來發(fā)現(xiàn)無法處理錯誤情況!

  • 在HAL庫的設計上,調(diào)用對應的反初始化函數(shù)是非常有必要的!例如:對于串一般需要根據(jù)波特率等信息反復初始化。但是,在使用了DMA時,HAL庫在設計上將DMA作為MSP部分,而一旦初始化過之后,MSP便不會再次初始化。這就導致了,DMA接收不會復位(NDTR寄存器不會重裝)。進一步導致DMA接收存放數(shù)據(jù)位置有偏移。但是,又不可能先調(diào)用反初始化,再調(diào)用初始化(沒初始化之前,對應的外設的Handle還沒有賦值)。反初始化必須在初始化后調(diào)用,所以對于HAL庫的該問題需要進行額外處理。以下以串口為例(僅僅是兩處例子,其他庫函數(shù)也可能有問題),說明如下:
  • /* 問題 一 */ HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart) {/* 省略 */if(huart->gState == HAL_UART_STATE_RESET) /* 只有在 HAL_UART_STATE_RESET 時,才會初始化 MSP部分 */{/* Allocate lock resource and initialize it */huart->Lock = HAL_UNLOCKED;/* Init the low level hardware */HAL_UART_MspInit(huart);}huart->gState = HAL_UART_STATE_BUSY; /* 一旦初始化之后,狀態(tài)即為 HAL_UART_STATE_BUSY *//* 省略 */huart->ErrorCode = HAL_UART_ERROR_NONE;huart->gState= HAL_UART_STATE_READY; /* 最后,狀態(tài)為 HAL_UART_STATE_READY */huart->RxState= HAL_UART_STATE_READY; } /* 問題 二 */ HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength) {/* 省略 */if(HAL_DMA_STATE_READY == hdma->State)/* 只有在 HAL_DMA_STATE_READY 時,才會初始化重新賦值DMA */{/* 省略 */}/* 省略 */ }
  • 當發(fā)生錯誤時,HAL庫的中斷處理函數(shù)void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)沒有清除錯誤中斷標志!導致重新初始化之后,一直不停的進入中斷。其手冊中給出的中斷標志清除如下所示:
  • * @param __FLAG__ specifies the flag to check. * This parameter can be any combination of the following values: * @arg UART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5). * @arg UART_FLAG_LBD: LIN Break detection flag. * @arg UART_FLAG_TC: Transmission Complete flag. * @arg UART_FLAG_RXNE: Receive data register not empty flag. * * @note PE (Parity error), FE (Framing error), NE (Noise error), ORE (Overrun * error) and IDLE (Idle line detected) flags are cleared by software * sequence: a read operation to USART_SR register followed by a read * operation to USART_DR register. * @note RXNE flag can be also cleared by a read to the USART_DR register. * @note TC flag can be also cleared by software sequence: a read operation to * USART_SR register followed by a write operation to USART_DR register. * @note TXE flag is cleared only by a write to the USART_DR register. /** @brief Clears the specified UART pending flag. * @param __HANDLE__ specifies the UART Handle. * This parameter can be UARTx where x: 1, 2, 3, 4, 5, 6, 7 or 8 to select the USART or * UART peripheral. * @param __FLAG__ specifies the flag to check. * This parameter can be any combination of the following values: * @arg UART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5). * @arg UART_FLAG_LBD: LIN Break detection flag. * @arg UART_FLAG_TC: Transmission Complete flag. * @arg UART_FLAG_RXNE: Receive data register not empty flag. * * @note PE (Parity error), FE (Framing error), NE (Noise error), ORE (Overrun * error) and IDLE (Idle line detected) flags are cleared by software * sequence: a read operation to USART_SR register followed by a read * operation to USART_DR register. * @note RXNE flag can be also cleared by a read to the USART_DR register. * @note TC flag can be also cleared by software sequence: a read operation to * USART_SR register followed by a write operation to USART_DR register. * @note TXE flag is cleared only by a write to the USART_DR register. * * @retval None */ #define __HAL_UART_CLEAR_FLAG(__HANDLE__, __FLAG__) ((__HANDLE__)->Instance->SR = ~(__FLAG__))

    由上可知,想要清除錯誤標志(FE、PE、NE等)必須先讀取USART_SR register,緊接著讀取USART_DR register。但是中斷處理函數(shù)中沒有該過程!因此,必須在錯誤處理函數(shù)中顯示執(zhí)行以上步驟。HAL庫給出了本身清除以上標志對應的宏(其實,任意調(diào)用一個即可!) ,如下:

    /** @brief Clear the UART PE pending flag. * @param __HANDLE__ specifies the UART Handle. * This parameter can be UARTx where x: 1, 2, 3, 4, 5, 6, 7 or 8 to select the USART or * UART peripheral. * @retval None */ #define __HAL_UART_CLEAR_PEFLAG(__HANDLE__) \ do{ \__IO uint32_t tmpreg = 0x00U; \tmpreg = (__HANDLE__)->Instance->SR; \tmpreg = (__HANDLE__)->Instance->DR; \UNUSED(tmpreg); \ } while(0U)/** @brief Clear the UART FE pending flag. * @param __HANDLE__ specifies the UART Handle. * This parameter can be UARTx where x: 1, 2, 3, 4, 5, 6, 7 or 8 to select the USART or * UART peripheral. * @retval None */ #define __HAL_UART_CLEAR_FEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)/** @brief Clear the UART NE pending flag. * @param __HANDLE__ specifies the UART Handle. * This parameter can be UARTx where x: 1, 2, 3, 4, 5, 6, 7 or 8 to select the USART or * UART peripheral. * @retval None */ #define __HAL_UART_CLEAR_NEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)/** @brief Clear the UART ORE pending flag. * @param __HANDLE__ specifies the UART Handle. * This parameter can be UARTx where x: 1, 2, 3, 4, 5, 6, 7 or 8 to select the USART or * UART peripheral. * @retval None */ #define __HAL_UART_CLEAR_OREFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)/** @brief Clear the UART IDLE pending flag. * @param __HANDLE__ specifies the UART Handle. * This parameter can be UARTx where x: 1, 2, 3, 4, 5, 6, 7 or 8 to select the USART or * UART peripheral. * @retval None */ #define __HAL_UART_CLEAR_IDLEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)

    在不同芯片的HAL庫中,其處理稍有不同。例如:在STM32F407的庫中,在函數(shù)HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)中會有調(diào)用__HAL_UART_CLEAR_OREFLAG(huart);,但是,在STM32F205對應的庫中則沒有調(diào)用。具體如下圖:

  • 中斷模式下的接收函數(shù)static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)存在越界問題。具體如下:
  • /* Check that a Rx process is ongoing */ if(huart->RxState == HAL_UART_STATE_BUSY_RX) {if(huart->Init.WordLength == UART_WORDLENGTH_9B){tmp = (uint16_t*) huart->pRxBuffPtr; /* 當成16位處理,當接收最后一個數(shù)據(jù)時,導致越界 */if(huart->Init.Parity == UART_PARITY_NONE){*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);huart->pRxBuffPtr += 2U;}else{*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);huart->pRxBuffPtr += 1U;}}else{if(huart->Init.Parity == UART_PARITY_NONE){*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);}else{*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);}} }

    STM32F205最新版本(1.7.0)的HAL庫中,對以上問題(5)已經(jīng)進行了修復!建議更新到最新版本!!!

    總結(jié)

    以上是生活随笔為你收集整理的STM32 之九 HAL 库串口(USART/UART)驱动 BUG 及解决方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。