- 前言
- 一、 HAL 庫串口收發(fā)
- 1.1 串口發(fā)送
- 1.2 串口接收
- 二、 收發(fā)同時(shí)串口卡死?
- 2.1 問(wèn)題說(shuō)明
- 2.2 嘗試的處理方式
- 結語(yǔ)
前言
對于 STM32 串口的使用,確實(shí)很簡(jiǎn)單使用 STM32CubeMX 做好初始化,就可以直接使用了。
但是最近在某些產(chǎn)品上使用串口同時(shí)收發(fā)的時(shí)候,發(fā)現有時(shí)候串口會(huì )收不到數據了,但是發(fā)送正常,而且這個(gè)問(wèn)題再數據量大的時(shí)候很容易出現,于是乎進(jìn)行了好幾天的問(wèn)題測試……
一、 HAL 庫串口收發(fā)
先簡(jiǎn)單回顧一下 STM32 HAL庫串口收發(fā)是如何使用的。
1.1 串口發(fā)送
對于 STM32 來(lái)說(shuō),串口發(fā)送有3中方式:
- 輪詢(xún)發(fā)送;
- 中斷發(fā)送;
- DMA發(fā)送;
在實(shí)際產(chǎn)品上,大部分項目中都用的是 輪詢(xún) 方式發(fā)送,本次出現接收卡死的問(wèn)題的產(chǎn)品也是采用的輪詢(xún)發(fā)送,所以我簡(jiǎn)單的說(shuō)明一下輪詢(xún)發(fā)送,其他兩種方式為 STM32 學(xué)習的基礎問(wèn)題,這里就不過(guò)多討論。
發(fā)送相對簡(jiǎn)單,在 HAL 庫實(shí)際都是使用HAL_UART_Transmit
函數:
在這里插入圖片描述
不管是哪個(gè)串口發(fā)送,都做了個(gè)簡(jiǎn)單的發(fā)送函數:
在這里插入圖片描述
發(fā)送沒(méi)什么好說(shuō)的,簡(jiǎn)單易用。
1.2 串口接收
和串口發(fā)送一樣,串口接收有3中方式:
- 輪詢(xún)接收;
- 中斷接收;
- DMA接收;
在我們正常的項目使用中,一般都是 中斷接收 或者 DMA 接收,基本上不會(huì )使用 輪詢(xún)接收的方式。
那么對于本次出問(wèn)題的產(chǎn)品,我采用的是 中斷接收的方式。
但是相比較發(fā)送,在 HAL 庫中 使用中斷接收的方式就有點(diǎn) “五花八門(mén)” 的感覺(jué)。
1.2.1 標準庫接收
在標準庫的時(shí)候,我們經(jīng)常這么用,在串口初始化的時(shí)候使用下面的語(yǔ)句使能中斷:
//在串口初始化代碼之后加上中斷使能
USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);
如果需要自動(dòng)判斷一幀數據,我們再開(kāi)啟一下 IDLE 中斷:
USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);
然后在對應的中斷處理函數中,直接讀取 DR 寄存器(STM32F103 而言為 DR,STM32L051 為RDR),然后當 IDLE 中斷產(chǎn)生,也可以處理一下標志位:
//自己用的,用到了 USART_IT_IDLE 標志位,有時(shí)候不合適
void USART3_IRQHandler(void) //串口3中斷服務(wù)程序
{
u8 clear3=clear3; //消除編譯器沒(méi)有用到的提醒
// u8 Res;
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
USART_Enocean_BUF[Enocean_Data++] = USART3- >DR;
USART3_RX_BUF[USART3_Data++] = USART_ReceiveData(USART3);
// Res= USART_ReceiveData(USART3);
// USART_SendData(USART1,Res);while(!(USART1- >SR&USART_FLAG_TXE));
}
else if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET)
{
clear3=USART3- >SR; //讀SR寄存器 可以清空寄存器
clear3=USART3- >DR; //讀DR寄存器(先讀SR寄存器,再讀DR,為了清除IDLE中斷)
USART3_RX_STA=1; //標記接收到了一幀數據
//USART3_Data=0;
} //enocean是不是讀不到一幀數據,不用一幀數據測試一下
}
1.2.1 HAL庫接收
HAL庫接收方式一
在 HAL 庫函數接收的時(shí)候,其實(shí)也可以使用標準庫上一樣的中斷標志使能:
MX_LPUART1_UART_Init();
__HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_RXNE);
IDLE 中斷使能:
__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);
這種方式的處理方式,可以和標準庫差不多。
在 HAL 庫中,外設的中斷的入口函數都放在stm32l0xx_it.c
這個(gè)文件夾中( 以STM32L051 為例),在這個(gè)文件中可以找到和標準庫一樣的 中斷入口函數,我們可以進(jìn)行如下處理:
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_RXNE) == SET){
// USART_Enocean_BUF[Enocean_Data++] = huart2.Instance- >RDR
// RXNE 數據處理,直接讀取數據
}
if((__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) != RESET))
{
__HAL_UART_CLEAR_IT(&huart2,UART_CLEAR_IDLEF); //??3y?D??
// ReceiveState = 1;
// IDLE 數據處理,一幀數據接收標志位置位
}
}
HAL庫接收方式二
但是在于 HAL 中,還有一種比較常用的開(kāi)啟中斷方法,不是直接使能中斷,而是通過(guò)調用 HAL 庫函數 HAL_UART_Receive_IT
:
在這里插入圖片描述
一般的使用方式步驟:
在這里插入圖片描述
對于本次需要說(shuō)明的問(wèn)題,就是使用了 HAL_UART_Receive_IT
函數導致的,下文我們會(huì )說(shuō)明,這里列出了基本的使用步驟。
二、 收發(fā)同時(shí)串口卡死?
2.1 問(wèn)題說(shuō)明
最近測試部反饋,產(chǎn)品有些時(shí)候的下行沒(méi)反應,這里所說(shuō)的下行,其實(shí)就是串口接收。
霹靂扒拉一大堆多余的省略 ... ... 只說(shuō)幾個(gè)重點(diǎn):
出問(wèn)題的最后現象就是串口發(fā)送正常,但是永遠接收不到數據了,其他程序正常運行。
出問(wèn)題只存在于串口又有接收,又有發(fā)送的產(chǎn)品上。
產(chǎn)品發(fā)送一般是周期性的,但是接收是隨機的,無(wú)線(xiàn)信號串口接收,所以產(chǎn)品的出問(wèn)題的情況也是隨機的,但是數據量大起來(lái)肯定就會(huì )出現永遠接收不到的問(wèn)題。
2.2 嘗試的處理方式
因為所有的一些都是按照正常流程設計的,按理來(lái)說(shuō)實(shí)在是不知道為什么會(huì )這樣,所以網(wǎng)上查詢(xún)測試了好久,現在我把嘗試的處理方式以及步驟記錄說(shuō)明一下:
2.2.1 清除錯誤標志位
在使用 HAL 庫的時(shí)候,有4個(gè)錯誤 flag,如下圖:
在這里插入圖片描述
期初還以為是某些異常錯誤導致的,經(jīng)過(guò)網(wǎng)上的的一些查詢(xún),剛開(kāi)始是添加了清除錯誤標志位:
__HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_PE);//清標志
__HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_FE);
__HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_NE);
__HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_ORE);
在需要的地方加上錯誤標志位清除,我是在清除串口緩存中處理的:
在這里插入圖片描述
2.2.2 串口溢出錯誤
其實(shí)串口溢出錯誤在上面的已經(jīng)清除過(guò)標志位,因為這個(gè)問(wèn)題著(zhù)實(shí)搞得我頭有點(diǎn)大,所以看到網(wǎng)上前人的處理方式和上面直接清除不一樣,還是試了一把。
這里簡(jiǎn)單說(shuō)明一下,我還特意去看了下自己的 CubeMX 設置,在設置的時(shí)候 有一個(gè) Overrun 錯誤標志位,平時(shí)我們設置都不一定往下拉著(zhù)看 = =?。?/p>
在這里插入圖片描述
然后確定了開(kāi)啟了串口溢出錯誤檢測以后,我根據網(wǎng)上的方式,加了一個(gè)HAL_UART_ErrorCallback
函數,其實(shí)就類(lèi)似于HAL_UART_RxCpltCallback
函數:
在這里插入圖片描述
自己加了一個(gè) 出錯處理函數,其實(shí)現在看來(lái),當然也是沒(méi)有用的。
2.2.3 HAL庫的半雙工處理?
折騰了好長(cháng)一段時(shí)間,其實(shí)一開(kāi)始就知道問(wèn)題在于 同時(shí)收發(fā)會(huì )出問(wèn)題的情況,那么繼續上網(wǎng)找問(wèn)題。
最終確定了一個(gè)問(wèn)題就是:
我們都知道 STM32 串口是全雙工的, STM32 HAL庫在處理接收的時(shí)候會(huì )鎖一下串口一會(huì ),導致變成某個(gè)短時(shí)間的“半雙工”。
這個(gè)時(shí)候如果同時(shí)收發(fā)就會(huì )出現問(wèn)題,最后解決的辦法在這篇文章中看到了:STM32 F103串口同時(shí)收發(fā)出現死鎖問(wèn)題解決辦法
問(wèn)題在于我們使用的HAL_UART_Receive_IT
函數中,有對串口加鎖的操作:
雖然在后面有解鎖:
在這里插入圖片描述
但是根據后期的解決方式來(lái)說(shuō),確實(shí)就是這個(gè)HAL_UART_Receive_IT
函數的問(wèn)題,最后使用的方式為,在產(chǎn)生一次中斷以后開(kāi)啟的時(shí)候手動(dòng)解鎖:
在這里插入圖片描述
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart- >Instance == LPUART1){
Enocean_Data++;
if(Enocean_Data > 98)Enocean_Data = 0;
while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){
hlpuart1.RxState = HAL_UART_STATE_READY;
__HAL_UNLOCK(&hlpuart1);
}
}
else if(huart- >Instance==USART1)
{
}
}
終于,串口不再卡死 , 成功!
結語(yǔ)
沒(méi)想到和 EEPROM 一樣,一個(gè)簡(jiǎn)單的芯片,一個(gè)熟悉的串口 ,出了這種問(wèn)題 = =!還好最后解決了問(wèn)題。
踩過(guò)的坑都是積累與經(jīng)驗,加油。
-
STM32
+關(guān)注
關(guān)注
2247文章
10693瀏覽量
349920 -
串口
+關(guān)注
關(guān)注
14文章
1505瀏覽量
74760 -
HAL
+關(guān)注
關(guān)注
2文章
69瀏覽量
12492
發(fā)布評論請先 登錄
相關(guān)推薦
評論