STM32筆記(20):USART串列通訊(上)

開發板的應用中,有一項重要的功能是通訊,常見的協定有 I2C、SPI 等,還有一個常用的協定是 UART 或 USART,也就是常見的 RSR-232、RS-422、RS-485 等這些介面,本篇文章就來探討一下這個協定。
  • UART 通用非同步收發傳輸 (Universal Asynchronous Receiver/Transmitter)
  • USART 通用同步/非同步接收/發送 (Universal Synchronous/Asynchronous Receiver/Transmitter)

USART 使用同步通訊時,比 UART 多一條時鐘線,當 USART 用於非同步通訊時,就跟 UART 一樣。所謂的同步是指發送方發送完一組數據後,需要等待接受方應答,才能再發下一組通訊。而非同步就是發送方發送一組數據後,不需等待接受方應答,就繼續發送下一組數據。傳送的時候是雙方約定一樣的傳送速度,就可以互傳資料。這個速度的設定稱做「鲍率」(Baud Rate或稱波特率),表示一秒鐘內傳輸多少位位元數,單位是 BPS(Byte Per Second)。

另一個要瞭解的知識是:「串列通訊」每次傳送一個位元,且連續傳送,「並列通訊」一次同時傳送兩個位元以上的數據,透過多條導線進行傳輸。傳輸的方式可以是以下三種模式:
  • 單工(Simplex):只能單向傳送數據。
  • 半雙工(Half duplex):某一個時刻只能發送或接收的雙向傳送數據。
  • 全雙工(Full duplex):可以同時進行接收和發送數據。

以上這些是串列/並列通訊的基本常識,以下將探討 STM32 如何使用 USART/UART 進行通訊。
USART 透過 3 個引腳與其他設備連接在一起,任何 USART 雙向通信至少需要 2 個引腳:
  • 接受數據輸入(RX): 接受數據串行輸入。
  • 發送數據輸出(TX): 發送數據輸出。

使用 USART 接收資料時,有兩種方式:
  1. 輪詢(polling):是 CPU 一直問 UART 的 RXNE 旗標是否有新資料?這個方法效率太低且占用大量 CPU 運算資源,如果傳送資料量大,如未讀取 USART Data 暫存器的資料,又進來新資料時,會產生 Overrun 的錯誤。一般是用所謂的 Blocking mode (阻斷模式),就是傳送/接收資料時不被中斷,一直到傳送/接收完成為止。
  2. 中斷(interrupt):開啟 USART 的 RXNE 中斷,當接收到新資料時跳到中斷服務程式,這個方法比較不佔用 CPU 資源。使用所謂的 non-blocking mode 模式(非阻斷模式)。

還有另一種,是將要傳送的資料直接放到記憶體,透過 DMA(Direct Memory Access) 隨時讓 USART 取得資料傳送或接收。之後再來研究如何使用 DMA 的方法存取資料。

STM32 有三個 USART 及兩個 UART 串列通訊,其引腳如下表:
APB2總線APB1總線
引腳USART1USART2USART3UART4UART5
TXPA9PA2PB10PC10PC12
RXPA10PA3PB11PC11PD2

[常用的 USART 函式]

STM32F10x 要設定使用 USART 時 ,需要將 USART 函數庫 stm32f10x_usart.c 和 stm32f10x_usart.h 兩個檔案包含進來。常用到的幾個函式說明如下:

void USART_DeInit(USART_TypeDef *USARTx)
將 USARTx 設定為預設值。
- 引數 USARTx,是 USART 的編號,x 可以是 1~5。

void USART_Init(USART_TypeDef *USARTx, USART_InitTypeDef *USART_InitStruct)
對 USART 進行初始化,定義在標準函式庫的 stm32f10x_usart.h 文件中,需先定義並填入一個 USART_InitTypeDef 類型的結構體。
- 引數 USARTx,是 USART 的編號,x 可以是 1~5。
USART_InitTypeDef USART_InitStructure; 
USART_InitStructure 這個結構體有以下 6 個引數:
引數名稱說明
USART_InitStructure.USART_BaudRate = xxx;BuadRate 鲍率,xxx 可以是 9600、38400、115200...等。
USART_InitStructure.USART_WordLength = xxx;表示發送或接收到的數據位數。xxx 可以是:
▸USART_WordLength_8b:8位元
▸USART_WordLength_9b:9位元
USART_InitStructure.USART_StopBits = xxx;定義發送的停止位數目。xxx 可以是以下之一:
▸USART_StopBits_1:在幀結尾傳輸 1 個停止位
▸USART_StopBits_0.5 :在幀結尾傳輸 0.5 個停止位
▸USART_StopBits_2:在幀結尾傳輸 2 個停止位
▸USART_StopBits_1.5:在幀結尾傳輸 1.5 個停止位。
USART_InitStructure.USART_Parity = xxx;▸校驗位元,xxx 可以是以下之一:
▸USART_Parity_NO(奇偶模式)
▸USART_Parity_Even(偶模式)
▸USART_Parity_Odd(奇模式)
USART_InitStructure.USART_Mode = xxx;使能或失能發送和接收模式。xxx 可以是以下之一:
▸USART_Mode_Rx 使用接收
▸USART_Mode_Tx:使用發送
USART_InitStructure.USART_HardwareFlowControl = xxx;硬體流控制,只有在硬體流模式下有效。xxx 可以是以下模式之一:
▸USART_HardwareFlowControl_None:無硬件流控制
▸USART_HardwareFlowControl_RTS:發送請求 RTS
▸USART_HardwareFlowControl_CTS:清除發送CTS
▸USART_HardwareFlowControl_RTS_CTS:使用 RTS 和 CTS

void USART_Cmd(USART_TypeDef *USARTx, FunctionalState NewState)
設定 USART 外設啟用或停用。
- 引數 USARTx,是 USART 的編號,x 可以是 1~5,用來選擇 USART。
- 引數 NewState:設定 USARTx 中斷的啟用 ENABLE 或停用 DISABLE。

void USART_SendData(USART_TypeDef *USARTx, uint16_t Data)
由 USARTx 傳送資料。
- 引數 USARTx,是 USART 的編號,x 可以是 1~5。
- 引數 Data 是傳送的資料。

uint16_t USART_ReceiveData(USART_TypeDef *USARTx)
傳回 USART 接收到的最新的數據。
- 引數 USARTx,是 USART 的編號,x 可以是 1~5。
- 傳回值:接收的資料

void USART_ITConfig(USART_TypeDef *USARTx, uint16_t USART_IT, FunctionalState NewState)
設定 USART 中斷型別的啟用或停用。
- 引數 USARTx,是 USART 的編號,x 可以是 1~5。
- 引數 USART_IT,可以是以下值。
  • USART_IT_CTS: CTS 變更的中斷 (不適用 UART4 及 UART5)
  • USART_IT_LBD: LIN 停止偵測的中斷
  • USART_IT_TXE: 傳送的資料暫存器已空的中斷
  • USART_IT_TC: 傳輸完成的中斷
  • USART_IT_RXNE: 接收資料暫存器不是空的中斷
  • USART_IT_IDLE: 空閒總線偵測的中斷
  • USART_IT_PE: 奇偶錯誤的中斷
  • USART_IT_ERR: Frame、noise 及 overrun 錯誤的中斷。
- 引數 NewState:設定 USARTx 中斷的啟用 ENABLE 或停用 DISABLE。

USART_IT 串列埠中斷的型別很多種,需要依據不同的情境進行選擇,以串列埠接收資料時,產生中斷為例:
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
以串列埠傳送資料為例:
USART_ITConfig(USART1, USART_IT_TC, ENABLE);

ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT); - 引數 USARTx,是 USART 的編號,x 可以是 1~5。
- 引數 USART_IT 的值請參考 USART_ITConfig() 函式中的 USART_IT。
- 傳回值:USART_IT 的最新狀態:SET 或者 RESET。

FlagStatus USART_GetFlagStatus(USART_TypeDef *USARTx, uint16_t USART_FLAG);
檢查指定的 USART 標誌位。
- 引數 USARTx,是 USART 的編號,x 可以是 1~5,用來選擇 USART。
- 引數 USART_FLAG:待檢查的 USART 標誌位,可以是以下值。
  • USART_FLAG_CTS: CTS 變更的標誌位(不適用 UART4 及 UART5)
  • USART_FLAG_LBD: LIN 停止偵測的標誌位
  • USART_FLAG_TXE: 傳送的資料暫存器已空的標誌位
  • USART_FLAG_TC: 傳輸完成的標誌位
  • USART_FLAG_RXNE: 接收資料暫存器不是空的標誌位
  • USART_FLAG_IDLE: 空閒匯流排偵測的標誌位
  • USART_FLAG_ORE: OverRun 錯誤的標誌位
  • USART_FLAG_NE: noise 錯誤的標誌位
  • USART_FLAG_FE: Framing 錯誤的標誌位
  • USART_FLAG_PE: 奇偶錯誤的標誌位
- 傳回值:USART_FLAG 的最新狀態:SET 或者 RESET。

void USART_ClearFlag(USART_TypeDef *USARTx, uint16_t USART_FLAG)
清除指定的 USARTx 標誌位。
- 引數 USARTx,是 USART 的編號,x 可以是 1~5。
- 引數 USART_FLAG,可以是以下幾種標誌位:
  • USART_FLAG_CTS: CTS 變更的標誌位(不適用 UART4 及 UART5)
  • USART_FLAG_LBD: LIN 停止偵測的標誌位
  • USART_FLAG_TC: 傳輸完成的標誌位
  • USART_FLAG_RXNE: 接收資料暫存器不是空的標誌位

void USART_ClearITPendingBit(USART_TypeDef *USARTx, uint16_t USART_IT) 清除 USARTx 的中斷位元狀態。 - 引數 USARTx,是 USART 的編號,x 可以是 1~5。
- 引數 USART_IT,可以是以下幾種標誌位:
  • USART_IT_CTS: CTS 變更的中斷 (不適用 UART4 及 UART5)
  • USART_IT_LBD: LIN 停止偵測的中斷
  • USART_IT_TC: 傳輸完成的中斷
  • USART_IT_RXNE: 接收資料暫存器不是空的中斷

以上這些是較為常用的函式,以下就來探討撰寫串列通訊的程序與方法:

[串列埠通訊設定步驟]

要編寫串列通訊程式,可按照如下步驟進行設定:
(1) 開啟 USART 和 GPIO 時鐘
STM32F103C8 晶片中有 5 個 UART(3 個 USART, 2 個 UART) 通訊資源,其中 USART1 掛載在 APB1 匯流排上,USART2 ~ USART15 掛載在 APB2 匯流排上,使用 UART 時,需要分別對應匯流排上的 UART 以及 GPIO 的時鐘即可。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

(2) 設定 GPIO
將 USART Tx 的 GPIO 設定為推輓復用模式(GPIO_Mode_AF_PP),將 Rx 的 GPIO 設定為浮空輸入模式(GPIO_Mode_IN_FLOATING),GPIO_Speed 切換速率設置為 GPIO_Speed_10MHz,使用 GPIO_InitTypeDef 定義 GPIO 結構體,最後使用 GPIO_Init() 啟用 I/O 口。
// GPIO 初始化 USART1_TX PA9 
GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// GPIO初始化USART1_RX PA10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);

(3) 使用 USART_Init() 函式進行初始化,並啟用中斷
使用 USART_Init() 進行初始化。需先使用 USART_InitTypeDef 定義一個結構體型別:
//USART 初始化設置
USART_InitStructure.USART_BaudRate = 115200;			//一般設定爲 9600 或 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;	// 資料長度 = 8 Bits
USART_InitStructure.USART_StopBits = USART_StopBits_1;		// 1 個停止位
USART_InitStructure.USART_Parity = USART_Parity_No;		//無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//無硬體流控(即禁止 RTS 和 CTS )
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;    //收發模式
USART_Init(USART1, &USART_InitStructure); 			//初始化串口
啟用 USART1 中斷:
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);	//開啓 ENABLE 中斷
USART_Cmd(USART1, ENABLE);           	 // Enable USART

(4) 設定 NVIC 中斷優先順序,並進行初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //設置NVIC中斷分組2

// USART1外設中斷配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1 ;  //主優先級1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0 ;       //次優先級0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         //啟用IRQ通道
NVIC_Init(& NVIC_InitStructure);  

(5) 編寫串列埠中斷服務函數
使用串列埠開啟了中斷功能,就需要在中斷服務函數(USARTx_IRQHandler)判斷由串列埠產生的中斷是那種型別,然後實現相應的功能。判斷中斷型別,呼叫庫函數: 在 NVIC 的配置中,主要是 USART1_IRQChannel 的配置。

發送數據用 USART_SendData() 函數,接收數據用 USART_ReceiveData()函數。
void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	{
		USART_SendData(USART1, USART_ReceiveData(USART1));
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
		
		while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
	}
}

(6) 使用終端軟體發送/接收數據
在電腦端要執行串列埠通訊程式,我用的免費軟體,名為:Serial Port Utility,可以到 這裡 下載程式。下載下來解壓縮放在一個目錄內就可使用。
這裡要注意編寫好的程式是透過 ST-Link 下載到 STM32,下載完成後要拔出 ST-Link,改插入 USB 轉 TTL 序列傳輸的 CP2102。每改一次程式,檢視結果就要差拔一次,有點麻煩。

下一篇:STM32(20):USART串列通訊(下)

Post a Comment

較新的 較舊