單片機型號:STM32L051C8T6
開發環境MDK5.12
庫版本:STM32L0xx_HAL_Driver V1.1.0
主機環境:Windows XP
承接上文,爲採用雙緩衝機制,重新開闢一個500字節的空間aTxBuffer來存放所需發送的字節,初始化uart_snd的指針
uart_snd.front = aTxBuffer;
uart_snd.rear = aTxBuffer; //兩個指針指向相同的地址空間
編輯發送單字節的函數uart_char
/**********************************************************************
函數:uart_char()
函數作用:發送一個字節數據
參數:
uint8_t *fmt--------------------------------需要發送的數據
返回值:
上一版本:無
當前版本:1.0
作者:
最後修改時間:2015-04-08
說明:
**********************************************************************/
void uart_char(uint8_t fmt)
{
uint8_t ret = HAL_OK;
if(uart_snd.rear == uart_snd.front)
{
//隊首指針和隊尾指針相等表明當前沒有數據需要發送,這裏需要手動開啓發送請求
*uart_snd.rear = fmt;
uart_snd.rear++;
if(uart_snd.rear >= aTxBuffer + BUFFSIZE)
uart_snd.rear = aTxBuffer;
do
{
ret = HAL_UART_Transmit_IT(&UartHandle,uart_snd.front,1);//請求發送下一個數據
}while(ret != HAL_OK);
}
else
{
*uart_snd.rear = fmt;
uart_snd.rear++;
if(uart_snd.rear >= aTxBuffer + BUFFSIZE)
uart_snd.rear = aTxBuffer;
}
}
在此判斷是否是重新啓動發送請求,如果是重新啓動則需要手動發起請求,即調用HAL_UART_Transmit_IT()發送函數,如果已經在發送數據過程中則只需要更新rear指針即可。我們的uart_send函數也需要更改不再調用printf而是調用uart_char函數來實現
int8_t uart_send(uint8_t *fmt, uint8_t len)
{
while(len)
{
uart_char(*fmt);
fmt++;
len--;
}
return 0;
}
當數據發送成功後中斷函數會調用HAL_UART_TxCpltCallback()回調函數來處理串口發送流程,
/**
* @brief Tx Transfer completed callback
* @param UartHandle: UART handle.
* @note This example shows a simple way to report end of IT Tx transfer, and
* you can add your own implementation.
* @retval None
*/
void HAL_UART_TxCpltCallback(UART_HandleTypeDef * huart)
{
uint8_t ret = HAL_OK;
uart_snd.front++; //更新rear指針
if(uart_snd.front >= (aTxBuffer + BUFFSIZE))
uart_snd.front = aTxBuffer;
if(uart_snd.front != uart_snd.rear)
{
//如果隊首指針和隊尾指針不同表明緩衝區中有數據還未發送
do
{
ret = HAL_UART_Transmit_IT(&UartHandle,uart_snd.front,1);//請求發送下一個數據
}while(ret != HAL_OK);
}
}
這裏我們要判斷緩衝區中的數據是否已經發送完畢即判斷front指針和rear指針是否相等,如果相等,則表明數據發送完畢,無需再調用HAL_UART_Transmit_IT()發送函數。至此發送流程編輯完畢,在主函數中調用uart_send()函數來進行測試
uint8_t *abc = "abcdefghijklmnopqrstuvwxyz0123456789\r\n";
while(1)
{
uart_send(abc,strlen(abc));
}
經測試發送正常,結果如下
一切顯得順風順水,就在我以爲大功告成之時,從PC機的串口工具向單片機發送了一些數據,結果單片機卡住了,串口輸出也沒得了。。。跟想象中的結果不一樣那,之前採用單緩衝機制是完全沒問題的,怎麼使用雙緩衝就有問題了呢,串口接收流程沒有改動,只是改動了串口發送,而且單獨測試串口發送也沒問題,調試了半天看下是否有串口錯誤產生呢。因此增加了串口出錯回調函數
/**
* @brief UART error callbacks
* @param UartHandle: UART handle
* @note This example shows a simple way to report transfer error, and you can
* add your own implementation.
* @retval None
*/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *UartHandle)
{
printf("error code:%X\r\n",UartHandle->ErrorCode);
}
在裏面輸出錯誤碼,測試結果如下
輸出的錯誤碼爲0x08,查找錯誤碼的定義發現該錯誤碼是ORE錯誤
/**
* @brief HAL UART Error Code structure definition
*/
typedef enum
{
HAL_UART_ERROR_NONE = 0x00, /*!< No error */
HAL_UART_ERROR_PE = 0x01, /*!< Parity error */
HAL_UART_ERROR_NE = 0x02, /*!< Noise error */
HAL_UART_ERROR_FE = 0x04, /*!< frame error */
HAL_UART_ERROR_ORE = 0x08, /*!< Overrun error */
HAL_UART_ERROR_DMA = 0x10 /*!< DMA transfer error */
}HAL_UART_ErrorTypeDef;
而我們知道ORE錯誤是隻會出現在串口接收中,難道打開串口發送中斷會影響到串口接收中斷?想想總覺得不可能,這應該是全雙工串口的那,兩者應該是相互獨立的纔對那。這讓我陷入了深深的糾結中。。。而且在測試結果中一旦出現了ORE錯誤會一直輸出該錯誤,很明顯該錯誤標誌一直沒有被清零,同時我也沒有往單片機發送串口數據之前使用STM32F030C8T6型號單片機時會有串口亂進的情況是因爲打開串口接收中斷時會同時開啓ORE中斷,最後解決辦法是在中斷處理函數中增加錯誤標誌的檢測並清除相應的標誌。本以爲STM32L0xx_Hal_Driver庫封裝了這麼多,會幫助我們減少這類錯誤呢,結果還是出現了,查看HAL_UART_Receive_IT()函數代碼會發現該函數同樣使能了各個錯誤中斷
/* 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);
查看源代碼中中斷處理函數HAL_UART_IRQHandle()裏面也進行了各個錯誤的判斷以及清除標誌操作。
該問題還未想到解決辦法,等解決之後再更新吧。。。
嘗試了兩天依然沒啥起色,STM32L0xx_Hal_Driver庫雖然做了很多封裝便於靈活使用3種方式進行通訊,但卻使得上層應用無法靈活檢測各個中斷標誌,在使用中斷方式發送串口數據時,是開啓的TXE中斷檢測,同時最後還要檢測到TC標誌置位後纔會調用HAL_UART_TxCpltCallback處理函數,因此我們在使用發送緩衝區時每發送一個字節需要進入兩次串口中斷才得以發送下一個字節數據,這種情況下還是StdPeriph_Lib庫靈活,個人覺得要解決這個問題可能需要修改底層庫函數,也進行過嘗試,沒搞定,期待大牛來指導一下吧,持續關注中。。。