STM32F4 HAL庫 UART相關操作API介紹
本文絕大部分翻譯自ST的官方用戶手冊 Description of STM32F4 HAL and LL drivers
USART 與 UART 的區別在於有沒有同步通信的功能。
USART: 通用同步異步收發器 ; UART: 通用異步收發器。當進行異步通信時,這兩者是沒有區別的。
這個同步通信功能可以把USART當做SPI來用,比如用USART來驅動SPI設備。同步(阻塞模式)是指:發送方發出數據后,等接收方發回響應以后才發下一個數據包的通訊方式。
異步(非阻塞模式)是指:發送方發出數據后,不等接收方發回響應,接着發送下個數據包的通訊方式。其中SPI IIC為同步通信 UART為異步通信, usart為同步&異步通信。
參考:https://blog.csdn.net/anbaixiu/article/details/78635913
硬件相關知識
STM32F427/STM32F429 共有4個USART與4個UART,如下表
序號 | U(S)ART_RX引腳 | U(S)ART_TX引腳 | U(S)ART_CK引腳 | USART_ CTS引腳 | USART_ RTS引腳 |
---|---|---|---|---|---|
USART1 | PA10 | PA9 | PA8 | PA11 | PA12 |
USART2 | PA3/PD6 | PA2/PD5 | PA4/PD7 | PA0/PD3 | PA1/PD4 |
USART3 | PB11/PC11/PD9 | PB10/PC10/PD8 | PB12/PC12/PD10 | PB13/PD11 | PB14/PD12 |
UART4 | PA1/PC11 | PA0/PC10 | |||
UART5 | PD2 | PC12 | |||
USART6 | PC7/PG9 | PC6/PG14 | PC8/PG7 | PG13/PG15 | PG8/PG12 |
UART7 | PE7 | PE8 | |||
UART8 | PE0 | PE1 |
RT: Receive Data 接收數據
TX: Transmit Data 發送數據
CK: Clock (同步)時鍾硬件流控制
RTS: Request To Send 請求發送數據
CTS: Clear To Send 允許發送數據
參見: UART通信中流控RTS和CTS的理解 https://blog.csdn.net/u013797023/article/details/77935535
相關結構體變量
USRT_InitTypeDef
- 該結構體定義了用於初始化UART的一些相關參數
typedef struct
{
uint32_t BaudRate; //波特率
uint32_t WordLength;//字長 取值參考 UART_Word_Length 宏定義
uint32_t StopBits; //停止位 取值參考 UART_Stop_Bits 宏定義
uint32_t Parity; //奇偶校驗模式 取值參考 UART_Parity 宏定義
uint32_t Mode; //收發模式 取值參考 UART_Mode 宏定義
uint32_t HwFlowCtl; //是否打開硬件流控制 取值參考 UART_Hardware_Flow_Control 宏定義
uint32_t OverSampling;//是否打開過采樣模式 取值參考 UART_Over_Sampling 宏定義 .
}UART_InitTypeDef;
UART_HandleTypeDef
-
該結構體定義的則是UART句柄(個人理解為用於操作UART)的一些參數
-
該結構體中只有
*Instance
與Init
兩個成員變量是需要我們配置的
typedef struct
{
USART_TypeDef *Instance; /*!< UART registers base address */
// UART/USART相關寄存器的地址 已經在HAL庫中定義完 參數為 U(S)ARTx x=1...8
UART_InitTypeDef Init; /*!< UART communication parameters */
// UART初始化參數結構體
uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */
// 緩存指針
uint16_t TxXferSize; /*!< UART Tx Transfer size */
// 緩存指針指向的數據的大小(字節)
uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
// 緩存指針指向的數據的的數量(字節)
uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
uint16_t RxXferSize; /*!< UART Rx Transfer size */
uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters *
// DMA句柄
DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */
HAL_LockTypeDef Lock; /*!< Locking object */
// 鎖對象
__IO HAL_UART_StateTypeDef State; /*!< UART communication state */
// UART通信狀態
__IO uint32_t ErrorCode; /*!< UART Error code */
// 錯誤碼
}UART_HandleTypeDef;
HAL Locked The HAL lock is used by all HAL APIs to prevent accessing by accident shared resources.
HAL庫中的API通過該參數來判斷某個API是否正在執行,如__HAL_LOCK(__HANDLE__)
與__HAL_UNLOCK(__HANDLE__)
所實現的typedef enum { HAL_UNLOCKED = 0x00, /*!<Resources unlocked*/ HAL_LOCKED = 0x01 /*!< Resources locked */ }HAL_LockTypeDef; #define __HAL_LOCK(__HANDLE__) \ do{ \ if((__HANDLE__)->Lock == HAL_LOCKED) \ { \ return HAL_BUSY; \ } \ else \ { \ (__HANDLE__)->Lock = HAL_LOCKED; \ } \ }while (0) #define __HAL_UNLOCK(__HANDLE__) \ do{ \ (__HANDLE__)->Lock = HAL_UNLOCKED; \ }while (0)
使用方法
- 聲明
UART_HandleTyperDef
句柄結構體 - 使用
HAL_UART_MspInit()
初始化 UART 的底層資源- 打開 USARTx 接口的時鍾
- 配置 UART 引腳
- 開啟 GPIO 時鍾
- 配置引腳模式為 復用上拉
- 如果使用了中斷需要配合 NVIC 中斷優先級
- 配置優先級
- 啟動 NVIC USART IRQ 句柄
- 如果使用了DMA處理需要配置DMA
- 為 輸入流 或 輸出流 聲明DMA句柄
- 開啟DMAx接口的時鍾
- 配置DMA句柄結構體的參數
- 配置DMA的輸入輸出流
- 將DMA句柄與UART DMA Tx/Rx句柄關聯
- 配置並啟用 NVIC
- 配置 UART 通信的上層參數,包括波特率、字長、停止位等
- 根據不同的工作模式調用不同的初始化函數
HAL_UART_Init()
異步通信HAL_HalfDuplex_Init()
半雙工通信HAL_LIN_Init()
LIN總線通信HAL_MultiProcessor_Init()
多處理器通信
相關函數API介紹
初始化類函數
HAL_StatusTypeDef HAL_UART_Init (UART_HandleTypeDef * huart)
HAL_StatusTypeDef HAL_HalfDuplex_Init (UART_HandleTypeDef * huart)
HAL_StatusTypeDef HAL_LIN_Init (UART_HandleTypeDef * huart, uint32_t BreakDetectLength)
HAL_StatusTypeDef HAL_MultiProcessor_Init (UART_HandleTypeDef * huart, uint8_t Address, uint32_t WakeUpMethod)
HAL_StatusTypeDef HAL_UART_DeInit (UART_HandleTypeDef * huart)
-
UART上層參數的初始化/注銷函數,根據工作模式選用相應的初始化函數
-
需要配置的參數包括 波特率、字長、停止位、奇偶校驗模式、是否開啟硬件流控制、收發模式、過采樣模式
-
參數說明:
*huart
UART句柄結構體的指針
-
返回API的執行情況,調用時注意檢查
void HAL_UART_MspInit (UART_HandleTypeDef * huart)
void HAL_UART_MspDeInit (UART_HandleTypeDef * huart)
- UART底層參數的初始化/注銷函數,具體配置內容見使用方法說明
- 在代碼移植時修改該配置初始化函數即可
輪詢(Polling)模式的IO操作
-
HAL_StatusTypeDef HAL_UART_Transmit (UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size, uint32_t Timeout)
-
HAL_StatusTypeDef HAL_UART_Receive (UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size, uint32_t Timeout)
-
用於接收與發送數據
-
阻塞模式,在接收、發送數據時無法進行其他操作
-
參數說明:
*huart
: UART句柄,用於操作UART*pData
: 緩沖區指針Size
: 緩沖區大小(以字節為單位)Timeout
: 超時時間,不可能讓程序一直等待數據接收或發送,超過這個時間之后將放棄發送或接收
-
返回該API的執行狀態
中斷模式的IO操作
-
HAL_StatusTypeDef HAL_UART_Transmit_IT (UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size)
-
HAL_StatusTypeDef HAL_UART_Receive_IT (UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size)
-
用於開啟UART接收/發送中斷,這兩個函數會設置發送/接收的緩沖區地址,大小,數量並且開啟相應的中斷
-
參數說明:
*huart
: UART句柄,用於操作UART*pData
: 緩沖區指針Size
: 緩沖區大小(以字節為單位)
-
返回該API的執行狀態,調用時注意檢查
以HAL_UART_Receive_IT()
為例
/**
* @brief Receives an amount of data in non blocking mode
* @param huart: pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @param pData: Pointer to data buffer
* @param Size: Amount of data to be received
* @retval HAL status
*/
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
uint32_t tmp = 0;
tmp = huart->State;
if((tmp == HAL_UART_STATE_READY) || (tmp == HAL_UART_STATE_BUSY_TX))
{
if((pData == NULL ) || (Size == 0))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
/* Check if a transmit process is ongoing or not */
if(huart->State == HAL_UART_STATE_BUSY_TX)
{
huart->State = HAL_UART_STATE_BUSY_TX_RX;
}
else
{
huart->State = HAL_UART_STATE_BUSY_RX;
}
/* 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);
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
void HAL_UART_IRQHandler (UART_HandleTypeDef * huart)
- UART中斷處理的公共函數,需要用戶在中斷處理函數
void USARTx_IRQHandler()
中調用該函數,或者可以由STM32CubeMX
程序自動生成。 - 該函數會遍歷所有與UART有關的中斷類型並判斷是否發生錯誤(設置錯誤代碼),若沒有發生錯誤則進行發送/接收操作
- 若發生錯誤會調用 回調函數
HAL_UART_ErrorCallback()
- 發送完成將調用 回掉函數
HAL_UART_TxCpltCallback()
- 接收完成(接收緩沖區滿)將調用會 回調函數
HAL_UART_RxCpltCallback()
,若不想關閉接收需要在該回調函數中重新開啟接收中斷HAL_UART_Receive_IT()
/**
* @brief This function handles UART interrupt request.
* @param huart: pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @retval None
*/
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t tmp1 = 0, tmp2 = 0;
tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_PE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_PE);
/* UART parity error interrupt occurred ------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
__HAL_UART_CLEAR_PEFLAG(huart);
huart->ErrorCode |= HAL_UART_ERROR_PE;
}
tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_FE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR);
/* UART frame error interrupt occurred -------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
__HAL_UART_CLEAR_FEFLAG(huart);
huart->ErrorCode |= HAL_UART_ERROR_FE;
}
tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_NE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR);
/* UART noise error interrupt occurred -------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
__HAL_UART_CLEAR_NEFLAG(huart);
huart->ErrorCode |= HAL_UART_ERROR_NE;
}
tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_ORE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR);
/* UART Over-Run interrupt occurred ----------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
__HAL_UART_CLEAR_OREFLAG(huart);
huart->ErrorCode |= HAL_UART_ERROR_ORE;
}
tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE);
/* UART in mode Receiver ---------------------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
UART_Receive_IT(huart);
}
tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_TXE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TXE);
/* UART in mode Transmitter ------------------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
UART_Transmit_IT(huart);
}
tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_TC);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TC);
/* UART in mode Transmitter end --------------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
UART_EndTransmit_IT(huart);
}
if(huart->ErrorCode != HAL_UART_ERROR_NONE)
{
/* Set the UART state ready to be able to start again the process */
huart->State = HAL_UART_STATE_READY;
HAL_UART_ErrorCallback(huart);
}
}
總結一下中斷模式下HAL庫實現UART通信的流程
-
調用
HAL_UART_Init()
初始化UART(該函數會調用HAL_UART_MspInit()
來初始化底層服務) -
調用
HAL_UART_Receive/Transmit_IT()
准備開始接收或者發送數據 -
每接收/發送一個字節的數據時便會觸發中斷服務函數
USARTx_IRQHandler()
-
中斷服務函數中調用中斷公共函數
HAL_UART_IRQHandler()
-
檢查沒有錯誤后按照條件調用
UART_Receive_IT()
UART_Transmit_IT()
UART_EndTransmit_IT()
三個函數中的一個,當發送/接收完成后調用相應的回調函數(根據HAL_UART_Receive/Transmit_IT()
的size參數來判斷是否完成發送/接收) -
若發生錯誤則調用處理錯誤的回調函數。
- 其中需要用戶實現的函數只有UARTx的中斷服務函數與相應的回調函數
如要提高程序執行的效率可以重寫USARTx的中斷服務函數,無需調用UART中斷的公共接口而自行更具相應的標識來處理接收或發送的流程。
DMA模式的IO操作
HAL_StatusTypeDef HAL_UART_Transmit_DMA (UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size)
HAL_StatusTypeDef HAL_UART_Receive_DMA (UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size)
6
- DMA模式下的發送/接收API
- 非阻塞模式
UART外設狀態函數
HAL_UART_StateTypeDef HAL_UART_GetState (UART_HandleTypeDef * huart)
uint32_t HAL_UART_GetError (UART_HandleTypeDef * huart)
- 分別獲取UART的狀態與錯誤代碼(宏定義)
宏定義介紹
具體參見stm32f4xx_hal.h
頭文件