1.前言-單片機的通訊
在單片機通訊方式多種多樣的今天,基本可以划分為兩類,即同步和異步通信。單片機要正常交流(即交換數據和讀寫命令)離不開通訊,單片機之間或者單片機與及外設之間的通訊都離不開這兩類通訊。
通訊方式的分類
同步和異步通信怎么區別?
帶時鍾同步信號傳輸的是同步傳輸,不帶時鍾同步信號的是異步傳輸(此時要求通訊雙方同波特率)。
下面我將通過基於stm32f103芯片以及MDK5軟件進行開發485通訊實驗(其實485通訊就是利用uart串口實現的),需要准備:
一台裝着MDK5軟件的電腦
ST-LInk燒錄器,STM32正點原子精英開發板2套(包含TFTLCD顯示屏)
兩根杜邦線
2.485通訊簡介
要開展485通訊實驗之前,485得對自己進行一次自我介紹。485通訊本質上是通過串口經過485芯片改變電壓與及阻抗,內在的信息沒有改變,之后通過電壓電流等信號傳給另一個單片機的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Ω)。沒有特性阻抗的話,當所有的設備都靜止或者沒有能量的時候就會產生噪聲,而且線移需要雙端的電壓差。沒有終接電阻的話,會使得較快速的發送端產生多個數據信號的邊緣,導致數據傳輸出錯。485推薦的連接方式如圖29.1.1所示:
在上面的連接中,如果需要添加匹配電阻,我們一般在總線的起止端加入,也就是主機和設備4上面各加一個120Ω的匹配電阻。
由於RS485具有傳輸距離遠、傳輸速度快、支持節點多和抗干擾能力更強等特點,所以RS485有很廣泛的應用。
精英STM32開發板采用SP3485作為收發器,該芯片支持3.3V供電,最大傳輸速度可達10Mbps,支持多達32個節點,並且有輸出短路保護。該芯片的框圖如圖29.1.2所示:
圖中A、B總線接口,用於連接485總線。RO是接收輸出端,DI是發送數據收入端,RE是接收使能信號(低電平有效),DE是發送使能信號(高電平有效)。
本章,我們通過該芯片連接STM32的串口2,實現兩個開發板之間的485通信。本章將實
現這樣的功能:通過連接兩個精英STM32開發板的RS485接口,然后由KEY0控制發送,當按下一個開發板的KEY0的時候,就發送5個數據給另外一個開發板,並在兩個開發板上分別顯示發送的值和接收到的值。
本章,我們只需要配置好串口2,就可以實現正常的485通信了,串口2的配置和串口1基本類似,只是串口的時鍾來自APB1,最大頻率為36Mhz。
3.軟件代碼的實現
在此,我將對幾個重要的函數展開詳細說明,我覺得外設模塊化進行開發跟函數模塊化開發一樣重要,一個函數最多只放幾十行代碼,同時前面要有函數說明。這樣,你寫的代碼會方便以后的修改,同時也可以讓別人快速知道你這個軟件實現的是什么功能。
void RS485_Init(u32 bound) ; void RS485_Receive_Data(u8 *ReceiveBuf,u8 *len); void RS485_Send_Data(u8 *SendBuf ,u8 len); void RS485_Init(u32 bound) { GPIO_InitTypeDef GPIO_InitType; USART_InitTypeDef USART_InitType; NVIC_InitTypeDef NVIC_InitType; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD,ENABLE); //使能GPIOA和GPIOD的時鍾 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //使能USART2的時鍾 GPIO_InitType.GPIO_Pin=GPIO_Pin_7; //接收或者發送模式控制引腳PD7(初始化之后才能用) GPIO_InitType.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出 GPIO_InitType.GPIO_Speed=GPIO_Speed_50MHz; //這個引腳是485芯片中的發送接收控制引腳 GPIO_Init(GPIOD,&GPIO_InitType); //當PDout(7)輸出的是高電平‘1’時是發送,為‘0’時是接收 GPIO_InitType.GPIO_Pin=GPIO_Pin_2; //USART2的TX引腳是PA2 GPIO_InitType.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_InitType.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitType); GPIO_InitType.GPIO_Pin=GPIO_Pin_3; //USART2的RX引腳是PA3 GPIO_InitType.GPIO_Mode=GPIO_Mode_IN_FLOATING; GPIO_InitType.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitType); #ifdef EN_RS485_RX USART_InitType.USART_BaudRate=bound; //串口2的初始化 USART_InitType.USART_HardwareFlowControl=USART_HardwareFlowControl_None; USART_InitType.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; USART_InitType.USART_Parity=USART_Parity_No; //其中重要的是USART_Mode要既能接收又能發送 USART_InitType.USART_StopBits=USART_StopBits_1; //USART2的波特率都設置為9600 USART_InitType.USART_WordLength=USART_WordLength_8b; USART_Init(USART2,&USART_InitType); NVIC_InitType.NVIC_IRQChannel=USART2_IRQn; //USART2中斷初始化 NVIC_InitType.NVIC_IRQChannelCmd=ENABLE; //其實這個中斷主要是對發送的數據及時處理,使接收時便捷高效 NVIC_InitType.NVIC_IRQChannelPreemptionPriority=3; NVIC_InitType.NVIC_IRQChannelSubPriority=3; NVIC_Init(&NVIC_InitType); USART_ITConfig(USART2,USART_IT_RXNE,ENABLE); //要用到USART2中斷除了初始化之外當然要對中斷進行配置以及使能 USART_Cmd(USART2,ENABLE); //明確發生什么時會進入串口中斷 #endif RX485_TX_EN=0; //默認接收模式 } 這里的串口中斷函數,發送的數據被接收數據寄存器接收到,就會觸發中斷,進行處理 #ifdef EN_RS485_RX u8 RS485_RX_BUF[64]; u8 RS485_RX_CNT=0; void USART2_IRQHandler(void) //發送完數據就使能接收中斷,使串口可以接收數據到緩沖區(數組RS485_RX_BUF)中 { u8 Res; if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET) { Res=USART_ReceiveData(USART2); if(RS485_RX_CNT<64) //一次接收的數據最多為64,超過之后將不會對發送過來的數據進行緩存 { RS485_RX_BUF[RS485_RX_CNT]=Res; RS485_RX_CNT++; } } } #endif void RS485_Send_Data(u8 *SendBuf ,u8 len) //發送處理函數 { u8 i; RX485_TX_EN=1; for(i=0;i<len;i++) //把長度為len的數組SendBuf發送出去 { while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //等待發送移位數據寄存器的數據非空時(即發送結束)跳出來 USART_SendData(USART2,SendBuf[i]); //發送數據 } while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); RX485_TX_EN=0; //默認接收模式 RS485_RX_CNT=0; } void RS485_Receive_Data(u8 *ReceiveBuf,u8 *len) //接收處理函數 { u8 rxlen,i=0; rxlen=RS485_RX_CNT; delay_ms(10); if((rxlen==RS485_RX_CNT)&&rxlen) //把中斷函數處理之后的緩存數據取出來 { for(i=0;i<rxlen;i++) { ReceiveBuf[i]=RS485_RX_BUF[i]; //傳遞接收到的數據以及長度回去 } *len=RS485_RX_CNT; RS485_RX_CNT=0; //對接收的個數進行清零,方便下次接收 } }