基於STM32F407的串口通信

串口通信

串口作爲 MCU 的重要外部接口,同時也是軟件開發重要的調試手段,其重要性不言而喻。現在基本上所有的 MCU 都會帶有串口,STM32 自然也不例外。通過串口,我們可以實現多設備間的單雙向通信。

本文主要講解對正點原子的串口通信實驗中串口中斷的通信協議的理解與應用,關於串口的初始化配置等這裏略過不講。

在正式開始之前,先把涉及到的寄存器講一下:

狀態寄存器 (USART_SR)

狀態寄存器的[9:0]爲非保留位,這裏我們主要了解其[7:5]位。
狀態寄存器 (USART_SR)
當要發送數據時,TDR寄存器將存放着的待發送的數據傳輸到移位寄存器,當TDR的數據全部移位到移位寄存器,TXEIE置1,表明數據傳輸到移位寄存器。

當傳送數據完成時,TC位置1。

當要接收數據時,移位寄存器把數據傳輸到DR寄存器中,當傳輸完成後,RXNE位置1,表明數據已完全傳輸到DR寄存器中,已準備好隨時被讀取。

[7:5]位

數據寄存器 (USART_DR)

串口通信中,STM32F4有一個專門的數據寄存器用於存放數據,該寄存器的[8:0]位(共9位)爲有效數據位,用於存放數據值。( 數據寄存器包含兩個寄存器,一個用於發送 (TDR),一個用於接收 (RDR) ) 而我們知道一個字節是一個8位長的數據單位,可用一個字節表示一個字符、數字或其他字符,或者系列二進制位。

於是我們得到這樣一個結論,串口通信中寄存器每一次通過USART_SendData或USART_ReceiveData函數可以發送或接收一個字節的數據。
數據寄存器(USART_DR)

接收大量數據

由於上面提到的,串口通信的數據寄存器每次只能存放一個字節的數據,這對我們來說顯然是不夠用的。爲了擴大接收數據的容量,首先自然而然想到的是新建一個數組變量,用於緩存數據。

正點原子定義了一個u8型的數組變量 USART_RX_BUF ,數組的大小爲USART_REC_LEN,可根據用戶需要自行定義,我們在usart.h裏宏定義其值爲200,即我們的數據緩存數組最多隻能存放200個字節的數據。

根據前文提到的狀態寄存器,我們知道每次接收完數據後,RXNE位會置1,表明隨時可讀取接收到的數據。(同時,如果 USART_CR1 寄存器中 RXNEIE = 1,則會生成中斷。)

於是,爲了一次獲取足夠多的數據,我們有這樣一個思路:在配置USART時我們開啓USART_IT_RXNE中斷,當每次中斷(表明一次數據接收完成,得到一個字節的數據)發生後,將DR寄存器中的數據讀取出來,存放到USART_RX_BUF數組中。這樣,當我們通過串口發送大量數據時,通過多次中斷,我們就能把數據依次存放到數組中,最後,我們再把數組中的數據遍歷輸出,即爲我們要接收的數據。

那麼緊接而來的有兩個問題:一、我們需要一個指針不斷進行移位,使得每次我們從DR寄存器get到的數據都能存放在USART_RX_BUF數組中的相應位置而不相互覆蓋。二、我們需要一些‘標誌’用來判斷我們的數據是否接收失敗、何時接收完畢。

正點原子是這樣解決的:建立一個USART_RX_STA的u16類型的變量,用來作爲接收狀態的標記量。該變量的低14位(即[13:0]位)用來表示接收到有效數據的個數。每從DR寄存器接收到一個數據並存放進USART_RX_BUF數組後,USART_RX_STA的值加一。(注意,表示的是數據的個數,並不是數據本身)。同時,用USART_RX_STA變量的次高位和最高位用來表示接受狀態,分別表示“即將接收完成”和“已經接收完成”。即,當USART_RX_STA的次高位置1時,表明即將接收完成。當USART_RX_STA的最高位置1時,表明數據已經傳輸完成,已經把所有數據都存放到USART_RX_BUF中。

那麼,如何判斷我們的數據“即將接收完成”和“已經接收完成”呢?我們可以通過自行定義這麼一個簡單的協議:發送的數據均以‘OK’爲末尾作爲結束。

這樣,每次判斷從DR獲取的數據,如果接收到‘O’,則表明“即將接收完成”,次高位置1,在此基礎上,如果再接收到‘K’,也就是說在次高位爲1的情況下,接收到數據‘K’,則表明接收完成。如果接收到的既不是‘O’也不是‘K’,則表明介紹到的是除末尾‘OK’以外的正常數據,則把它存放進USART_RX_BUF數組中,並使USART_RX_STA的值加一。

通過這些操作,我們就能實現接收大量數據的功能了。

下面我們來具體看一下代碼上如何實現:

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  
	{
		//讀取DR接收到的數據
		Res = USART_ReceiveData(USART1);	

		/*0x8000----1000000000000000----*/
		if((USART_RX_STA&0x8000)==0)			//最高位不爲1,則接收未完成
		{
			//次高位爲1,表明上次中斷已經接收到倒數第二個數據'O',
			//則接下來判斷這次接收到的數據是不是'K',即是否符合最後一個數據的要求
			if(USART_RX_STA&0x4000)			/*0x4000----0100000000000000----*/			
			{
				//如果接收完'O'後接收到的不是'K',
				//則說明接收錯誤,重新開始
				if(Res!='K')USART_RX_STA=0;
				
				//如果接收完'O'後接收到的是'K',
				//則說明接收完成了,把相關標誌位(最高位,即0x8000)置1
				else USART_RX_STA|=0x8000;		/*0x8000----1000000000000000----*/
			}
			else	//次高位不爲1,表明上次中斷還沒接收到倒數第二個數據'O'
			{
				//那就要判斷這次中斷是不是收到倒數第二個數據'O',
				//如果是的話,則次高位置1
				if(Res=='O')USART_RX_STA|=0x4000;										
				else		//如果這次中斷接收的不是'O',
				//則說明收到的數據是普通的數據內容,那就把它存進buffer裏面
				{
					//存儲本次接收到的數據內容,
					//USART_RX_STA相當於數組的一個索引值,
					//對於不同索引值,每次把Res的數據存放在不同位置
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res;
					/*0x3fff----0011111111111111----*/
					USART_RX_STA++;							//索引值加一
					//索引值越界,說明接收數據過長,接收數據錯誤,重新開始接收
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;		  
				}
			}
		}
	}

通過這個串口中斷函數,我們可以得到大於一個字節的數據。要獲取數據,只需要讀取USART_RX_BUF裏面的內容即可。於是我們可以在main函數裏面通過while循環判斷USART_RX_STA的最高位來判斷是否可讀取BUF裏的內容,可以的話則對其迭代打出數據。

emmmmm大概就這樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章