1. 程式人生 > >STM32串列埠USART

STM32串列埠USART

一、通用同步非同步收發器(USART) 

        通用同步非同步收發器(USART)提供了一種靈活的方法與使用工業標準NRZ非同步序列資料格式的 外部裝置之間進行全雙工資料交換。USART利用分數波特率發生器提供寬範圍的波特率選擇。 它支援同步單向通訊和半雙工單線通訊,也支援LIN(區域性互連網),智慧卡協議和IrDA(紅外資料 組織)SIR ENDEC規範,以及調變解調器(CTS/RTS)操作。它還允許多處理器通訊。 使用多緩衝器配置的DMA方式,可以實現高速資料通訊。 

        介面通過三個引腳與其他裝置連線在一起。任何USART雙向通訊至少需要兩個腳:接收資料輸入(RX)和傳送資料輸出(TX)。 RX:接收資料序列輸。通過過取樣技術來區別資料和噪音,從而恢復資料。 TX:傳送資料輸出。當傳送器被禁止時,輸出引腳恢復到它的I/O埠配置。當傳送器被啟用, 並且不傳送資料時,TX引腳處於高電平。在單線和智慧卡模式裡,此I/O口被同時用於資料的傳送和接收。 

       串列埠設定的一般步驟可以總結為如下幾個步驟:

      1) 串列埠時鐘使能,GPIO 時鐘使能

      2) 串列埠復位

      3) GPIO 埠模式設定

      4) 串列埠引數初始化

      5) 開啟中斷並且初始化 NVIC(如果需要開啟中斷才需要這個步驟)

      6) 使能串列埠

      7) 編寫中斷處理函式  

void uart_init(u32 bound){
  //GPIO埠設定,bound:波特率
  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 

}

 串列埠中斷服務函式

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)//接收未完成,第15位
		{
			if(USART_RX_STA&0x4000)//接收到了0x0d,第14位
				{
				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;//接收資料錯誤,重新開始接收	  
					}		 
				}
			}   		 
     } 
#if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS為真,則需要支援OS.
	OSIntExit();  											 
#endif
} 
#endif	

主函式

int main(void)
 {		
 	u16 t;  
	u16 len;	
	u16 times=0;
	delay_init();	    	 //延時函式初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設定NVIC中斷分組2:2位搶佔優先順序,2位響應優先順序
	uart_init(115200);	 //串列埠初始化為115200
 	LED_Init();			     //LED埠初始化
	KEY_Init();          //初始化與按鍵連線的硬體介面
 	while(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("請輸入資料,以回車鍵結束\n");  
			if(times%30==0)LED1=!LED1;//閃爍LED,提示系統正在執行.
			delay_ms(10);   
		}
	}	 
 }

二、  STM32F1 的串列埠實現 485 通訊(半雙工)

        485(一般稱作 RS485/EIA-485)是隸屬於 OSI 模型物理層的電氣特性規定為 2 線,半雙工, 多點通訊的標準。它的電氣特性和 RS-232 大不一樣。用纜線兩端的電壓差值來表示傳遞訊號。 RS485 僅僅規定了接受端和傳送端的電氣特性。它沒有規定或推薦任何資料協議。 RS485 的特點包括:

        1) 介面電平低,不易損壞晶片。RS485 的電氣特性:邏輯“1”以兩線間的電壓差為+(2~6)V 表示;邏輯“0”以兩線間的電壓差為-(2~6)V 表示。介面訊號電平比 RS232 降低了, 不易損壞介面電路的晶片,且該電平與 TTL 電平相容,可方便與 TTL 電路連線。    

        2) 傳輸速率高。10 米時,RS485 的資料最高傳輸速率可達 35Mbps,在 1200m 時,傳輸  速度可達 100Kbps。

        3) 抗干擾能力強。RS485 介面是採用平衡驅動器和差分接收器的組合,抗共模干擾能力 增強,即抗噪聲干擾性好。

        4) 傳輸距離遠,支援節點多。RS485 匯流排最長可以傳輸 1200m 以上(速率≤100Kbps) 一般最大支援 32 個節點,如果使用特製的 485 晶片,可以達到 128 個或者 256 個節點, 最大的可以支援到 400 個節點。 RS485 推薦使用在點對點網路中,線型,匯流排型,不能是星型,環型網路。理想情況下 RS485 需要 2 個匹配電阻,其阻值要求等於傳輸電纜的特性阻抗(一般為 120Ω)。沒有特性阻抗的話, 當所有的裝置都靜止或者沒有能量的時候就會產生噪聲,而且線移需要雙端的電壓差。沒有終 接電阻的話,會使得較快速的傳送端產生多個數據信號的邊緣,導致資料傳輸出錯。

       我們只需要配置好串列埠 2,就可以實現正常的 485 通訊了,串列埠 2 的配置和串列埠 1 基本類似,只是串列埠的時鐘來自 APB1,最大頻率為 36Mhz。 

void RS485_Init(u32 bound)
{  
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
 	NVIC_InitTypeDef NVIC_InitStructure;
 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE);//使能GPIOA,D時鐘
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2時鐘
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;				 //PD7埠配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推輓輸出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOD, &GPIO_InitStructure);
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;	//PA2
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//複用推輓
  GPIO_Init(GPIOA, &GPIO_InitStructure);
   
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
  GPIO_Init(GPIOA, &GPIO_InitStructure);  

	RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,ENABLE);//復位串列埠2
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,DISABLE);//停止復位
 
	
 #ifdef EN_USART2_RX		  	//如果使能了接收
	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(USART2, &USART_InitStructure); ; //初始化串列埠
  
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //使能串列埠2中斷
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先佔優先順序2級
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //從優先順序2級
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道
	NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的引數初始化外設NVIC暫存器
 
  USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//開啟中斷
   
  USART_Cmd(USART2, ENABLE);                    //使能串列埠 

 #endif

  RS485_TX_EN=0;			//預設為接收模式
 
}

傳送資料

void RS485_Send_Data(u8 *buf,u8 len)
{
	u8 t;
	RS485_TX_EN=1;			//設定為傳送模式
  	for(t=0;t<len;t++)		//迴圈傳送資料
	{		   
		while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);	  
		USART_SendData(USART2,buf[t]);
	}	 
 
	while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);		
	RS485_RX_CNT=0;	  
	RS485_TX_EN=0;				//設定為接收模式	
}

接收資料

void RS485_Receive_Data(u8 *buf,u8 *len)
{
	u8 rxlen=RS485_RX_CNT;
	u8 i=0;
	*len=0;				//預設為0
	delay_ms(10);		//等待10ms,連續超過10ms沒有接收到一個數據,則認為接收結束
	if(rxlen==RS485_RX_CNT&&rxlen)//接收到了資料,且接收完成了
	{
		for(i=0;i<rxlen;i++)
		{
			buf[i]=RS485_RX_BUF[i];	
		}		
		*len=RS485_RX_CNT;	//記錄本次資料長度
		RS485_RX_CNT=0;		//清零
	}
}

中斷函式

 

void USART2_IRQHandler(void)
{
	u8 res;	    
 
 	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到資料
	{	 
	 			 
		res =USART_ReceiveData(USART2); 	//讀取接收到的資料
		if(RS485_RX_CNT<64)
		{
			RS485_RX_BUF[RS485_RX_CNT]=res;		//記錄接收到的值
			RS485_RX_CNT++;						//接收資料增加1 
		} 
	}  											 
} 

 主函式

int main(void)
 {	 
	u8 key;
	u8 i=0,t=0;
	u8 cnt=0;
	u8 rs485buf[5]; 
	 
	delay_init();	    	 //延時函式初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設定中斷優先順序分組為組2:2位搶佔優先順序,2位響應優先順序
	uart_init(115200);	 	//串列埠初始化為115200
	LED_Init();		  		//初始化與LED連線的硬體介面
	LCD_Init();			   	//初始化LCD 	
	KEY_Init();				//按鍵初始化		 	 
	RS485_Init(9600);	//初始化RS485
 	POINT_COLOR=RED;//設定字型為紅色 
	LCD_ShowString(30,50,200,16,16,"ELITE STM32");
	LCD_ShowString(30,70,200,16,16,"RS485 TEST");	
	LCD_ShowString(30,90,200,16,16,"[email protected]");
	LCD_ShowString(30,110,200,16,16,"2015/1/15");	
	LCD_ShowString(30,130,200,16,16,"KEY0:Send");	//顯示提示資訊		
 
 	POINT_COLOR=BLUE;//設定字型為藍色	  
	LCD_ShowString(30,150,200,16,16,"Count:");			//顯示當前計數值	
	LCD_ShowString(30,170,200,16,16,"Send Data:");		//提示傳送的資料	
	LCD_ShowString(30,210,200,16,16,"Receive Data:");	//提示接收到的資料		
 									  
	while(1)
	{
		key=KEY_Scan(0);
		if(key==KEY0_PRES)//KEY0按下,傳送一次資料
		{
			for(i=0;i<5;i++)
			{
				rs485buf[i]=cnt+i;//填充發送緩衝區
				LCD_ShowxNum(30+i*32,190,rs485buf[i],3,16,0X80);	//顯示資料
 			}
			RS485_Send_Data(rs485buf,5);//傳送5個位元組 									   
		}		 
		RS485_Receive_Data(rs485buf,&key);
		if(key)//接收到有資料
		{
			if(key>5)key=5;//最大是5個數據.
 			for(i=0;i<key;i++)LCD_ShowxNum(30+i*32,230,rs485buf[i],3,16,0X80);	//顯示資料
 		}
		t++; 
		delay_ms(10);
		if(t==20)
		{
			LED0=!LED0;//提示系統正在執行	
			t=0;
			cnt++;
			LCD_ShowxNum(30+48,150,cnt,3,16,0X80);	//顯示資料
		}		   
	} 
}