STM32--USART詳解

阿槐發表於2020-10-11

STM32–USART詳解
1 串列埠的基本概念
在STM32的參考手冊中,串列埠被描述成通用同步非同步收發器(USART),它提供了一種靈活的方法與使用工業標準NRZ非同步序列資料格式的外部裝置之間進行全雙工資料交換。USART利用分數波特率發生器提供寬範圍的波特率選擇。它支援同步單向通訊和半雙工單線通訊,也支援LIN(區域性網際網路),智慧卡協議和IrDA(紅外資料組織)SIR ENDEC規範,以及調變解調器(CTS/RTS)操作。它還允許多處理器通訊。還可以使用DMA方式,實現高速資料通訊。
USART通過3個引腳與其他裝置連線在一起,任何USART雙向通訊至少需要2個引腳:接受資料輸入(RX)和傳送資料輸出(TX)。

RX: 接受資料序列輸入。通過過取樣技術來區別資料和噪音,從而恢復資料。

TX: 傳送資料輸出。當傳送器被禁止時,輸出引腳恢復到它的I/O埠配置。當傳送器被啟用,並且不傳送資料時,TX引腳處處於高電平。在單線和智慧卡模式裡,此I/O口被同時用於資料的傳送和接收。
2 串列埠工作方式
一般有兩種方式:查詢與中斷
(1)查詢:串列埠程式不斷地迴圈查詢,看看當前有沒有資料要它傳送。如果有,就幫助傳送(可以從PC到STM32板子,也可以從STM32板子到PC)。

(2)中斷:平時串列埠只要開啟中斷即可。如果發現有一箇中斷來,則意味著要它幫助傳輸資料——它就馬上進行資料的傳送。同樣,可以從 PC到STM3板子,也可以從STM32板子到PC。
3. UART的連線方式

在這裡插入圖片描述
晶片與晶片的連線方式

在這裡插入圖片描述
晶片與PC機的連線方式
4 串列埠配置一般步驟

在這裡插入圖片描述
(1)RCC配置:

由於UART的TX和RX和AFIO都掛在APB2橋上,因此採用韌體庫函式RCC_APB2PeriphClockCmd()進行初始化。UARTx需要分情況討論,如果是UART1,則掛在APB2橋上,因此採用RCC_APB2PeriphClockCmd()進行初始化,其餘的UART2~5均掛在APB1上。

(2)GPIO配置:

GPIO的屬性包含在結構體GPIO_InitTypeDef,其中對於TX引腳,GPIO_Mode欄位設定為GPIO_Mode_AF_PP(複用推輓輸出),GPIO_Speed切換速率設定為GPIO_Speed_50MHz;對於RX引腳,GPIO_Mode欄位設定為GPIO_Mode_IN_FLOATING(浮空輸入),不需要設定切換速率。最後通過GPIO_Init()使能IO口。

(3)USART配置:

STM32在只有一箇中斷的情況下,仍然需要配置優先順序,其作用是使能某條中斷的觸發通道。STM32的中斷有至多兩個層次,分別是搶佔優先順序(主優先順序)和子優先順序(從優先順序),而整個優先順序設定引數的長度為4位,因此需要首先劃分搶佔優先順序位數和子優先順序位數,通過NVIC_PriorityGroupConfig()實現;

特定裝置的中斷優先順序NVIC的屬性包含在結構體NVIC_InitTypeDef中,其中欄位NVIC_IRQChannel包含了裝置的中斷向量,儲存在啟動程式碼中;欄位NVIC_IRQChannelPreemptionPriority為主優先順序NVIC_IRQChannelSubPriority為從優先順序,取值的範圍應根據位數劃分的情況而定;最後NVIC_IRQChannelCmd欄位是是否使能,一般置為ENABLE。最後通過NVIC_Init()來使能這一中斷向量。

(4)NVIC配置:

通過結構體USART_InitTypeDef來確定。UART模式下的欄位如下:

USART_BaudRate:波特率(每秒能傳輸的資料位),預設值為9600。

USART_WordLength:字長

USART_StopBits:停止位

USART_Parity:校驗方式(奇偶校驗)

USART_HardwareFlowControl:硬體流控制

USART_Mode:單/雙工,即收發狀態。

最後通過USART_Init()來設定。

(5) 傳送/接收資料。

在RCC配置中,我們除了常規的時鐘設定以外,要記得開啟USART相對應的IO口時鐘,USART時鐘,還有管腳功能複用時鐘。

在GPIO配置中,將傳送端的管腳配置為複用推輓輸出,將接收端的管腳配置為浮空輸入。

在USART的配置中,通過USART_InitTypeDef結構體對USART進行初始化操作,按照自己所需的功能配置好就可以了。注意,在超級終端的設定中,需要和這個裡面的配置相對應。由於我是採用中斷接收資料的方式,所以記得在USART的配置中要開啟串列埠的中斷,同時最後還要開啟串列埠。

在NVIC的配置中,主要是USART1_IRQChannel的配置。

全部配置好之後就可以開始傳送/接收資料了。傳送資料用USART_SendData()函式,接收資料用USART_ReceiveData()函式。具體的函式功能可以參考韌體庫的參考檔案。根據USART的配置,在傳送和接收時,都是採用的8bits一幀來進行的,因此,在傳送的時候,先開闢一個快取區,將需要傳送的資料送入快取區,然後再將快取區中的資料傳送出去,在接收的時候,同樣也是先接收到快取區中,然後再進行相應的操作。

注意在對資料進行傳送和接收的時候,要檢查USART的狀態,只有等到資料傳送或接收完畢之後才能進行下一幀資料的傳送或接收。採用USART_GetFlagStatus()函式。

同時還要注意的是,在傳送資料的最開始,需要清除一下USART的標誌位,否則,第1位資料會丟失。因為在硬體復位之後,USART的狀態位TC是置位的。當包含有資料的一幀傳送完成之後,由硬體將該位置位。只要當USART的狀態位TC是置位的時候,就可以進行資料的傳送。然後TC位的置零則是通過軟體序列來清除的,具體的步驟是“先讀USART_SR,然後寫入USART_DR”,只有這樣才能夠清除標誌位TC,但是在傳送第一幀資料的時候,並沒有進行讀USART_SR的操作,而是直接進行寫操作,因此TC標誌位並沒有清空,那麼,當傳送第一幀資料,然後用USART_GetFlagStatus()檢測狀態時返回的是已經傳送完畢(因為TC位是置1的),所以程式會馬上傳送下一幀資料,那麼這樣,第一幀資料就被第二幀資料給覆蓋了,所以看不到第一幀資料的傳送。

按照上面的方法程式設計後,我們便可以在超級終端上檢視串列埠通訊的具體狀態了。我的這個例程,在硬體復位以後,可以馬上在超級終端上看見“Welcome to my STM32! Please press any key!”字樣,然後如果在超級終端中通過PC機鍵盤按下相應的鍵,則這個鍵會傳送到STM32中,並且馬上返回到PC機的超級終端上,因此可以馬上從超級終端的頁面中看到按下的相應的鍵。
uart初始化程式碼如下:

在這裡插入代void uart_init(u32 bound)
	{
  //GPIO埠設定
  GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA時鐘
  
	//USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//複用推輓輸出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
  //USART1_RX	  GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  //Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶佔優先順序3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子優先順序3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根據指定的引數初始化VIC暫存器
  
   //USART 初始化設定

	USART_InitStructure.USART_BaudRate = bound;//串列埠波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位資料格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬體資料流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收發模式

  USART_Init(USART1, &USART_InitStructure); //初始化串列埠1
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟串列埠接受中斷
  USART_Cmd(USART1, ENABLE);                    //使能串列埠1 

}

uart1中斷服務函式

在這裡插入代void USART1_IRQHandler(void)                	//串列埠1中斷服務程式
	{
	u8 Res;
#if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS為真,則需要支援OS.
	OSIntEnter();    
#endif
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中斷(接收到的資料必須是0x0d 0x0a結尾)
		{
		Res =USART_ReceiveData(USART1);	//讀取接收到的資料
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			{
			if(USART_RX_STA&0x4000)//接收到了0x0d
				{
				if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //還沒收到0X0D
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收資料錯誤,重新開始接收	  
					}		 
				}
			}   		 
     } 

主函式的程式碼主要是通過電腦上的丁丁給微控制器傳送指令,然後通過串列埠1再傳送到丁丁的一個過程。

if(USART_RX_STA&0x8000)
		{					   
			len=USART_RX_STA&0x3fff;//得到此次接收到的資料長度
			printf("\r\n您傳送的訊息為:\r\n\r\n");
			for(t=0;t<len;t++)
		{
				USART_SendData(USART1, USART_RX_BUF[t]);//向串列埠1傳送資料
				while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待傳送結束
		}
			printf("\r\n\r\n");//插入換行
			USART_RX_STA=0;
		}else
		{
			times++;
			if(times%5000==0)
		{
				printf("\r\n戰艦STM32開發板 串列埠實驗\r\n");
			printf("正點原子@ALIENTEK\r\n\r\n");
			}
			if(times%200==0)
				printf("請輸入資料,以Enter鍵結束\n");  
			if(times%30==0)
				LED0=!LED0;//閃爍LED,提示系統正在執行.
			delay_ms(10);   
		}

通過以上的程式碼就可以實驗微控制器與PC機的通訊,從而達到資料傳輸的目的。

串列埠列印程式
Printf支援

            
//標準庫需要的支援函式                 
struct __FILE 
{ 
	int handle; 

}; 

FILE __stdout;       
//定義_sys_exit()以避免使用半主機模式    
_sys_exit(int x) 
{ 
	x = x; 
} 
//重定義fputc函式 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//迴圈傳送,直到傳送完畢   
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 

printf("\r\n您傳送的訊息為:\r\n\r\n");

通過以上的程式段就可以實現串列埠列印,該功能應用比較廣泛,一般用於讀取程式碼中的資料,然後進行除錯。

5 串列埠通訊相關知識講解
並行通訊和序列通訊
在這裡插入圖片描述
序列通訊
在這裡插入圖片描述
URAT,SPI,IIC區別
在這裡插入圖片描述
STM32非同步通訊定義的引數
在這裡插入圖片描述
STM32串列埠通訊過程
在這裡插入圖片描述
總結:這樣我們就可以利用串列埠助手,來調控程式,知道程式的流程。從而除錯程式。STM32串列埠的學習就到此為止。當然當PC向STM32傳送資料時,直接用USART_ReceiveData函式即可,若要接受大量的資料,做成一個陣列buff即可。當然這個過程在中斷中實現比較好。