====================================我是分割線首先介紹下網上看到的========================================================================================
一、RS232標准中的RTS與CTS
RTS,CTS------請求發送/清除發送,用於半雙工時的收發切換,屬於輔助流控信號。半雙工的意思是說,發的時候不收,收的時候不發。那么怎么區分收發呢?缺省時是DCE向DTE發送數據,當DTE決定向DCE發數據時,先有效RTS,表示DTE希望向DCE發送。一般DCE不能馬上轉換收發狀態,DTE就通過監測CTS是否有效來判斷可否發送,這樣避免了DTE在DCE未准備好時發送所導致的數據丟失。
二、MODEM硬件流控中的RTS與CTS
按照SIMCOM公司的解釋,RTS和CTS是獨立,
1.RTS是模塊的輸入端,用於MCU通知模塊,MCU是否准備好,模塊是否可向MCU發送信息,RTS的有效電平為低。
2.CTS是模塊的輸出端,用於模塊通知MCU,模塊是否准備好,MCU是否可向模塊發送信息,CTS的有效電平為低
從文字看,RTS和CTS是獨立的,不存在每次單向數據傳輸的發起者問題。如果主機輸出RTS有效,那么模塊有數據就會發往主機;如果模塊輸出CTS有效,那么主機就可以將數據送達模塊接收。
三、通信協議中的RTS與CTS
RTS/CTS協議即請求發送/允許發送協議,相當於一種握手協議,主要用來解決"隱藏終端"問題。"隱藏終端"(Hidden Stations)是指,基站A向基站B發送信息,基站C未偵測到A也向B發送,故A和C同時將信號發送至B,引起信號沖突,最終導致發送至B的信號都丟失了。"隱藏終端"多發生在大型單元中(一般在室外環境),這將帶來效率損失,並且需要錯誤恢復機制。當需要傳送大容量文件時,尤其需要杜絕"隱藏終端"現象的發生。IEEE802.11提供了如下解決方案。在參數配置中,若使用RTS/CTS協議,同時設置傳送上限字節數----一旦待傳送的數據大於此上限值時,即啟動RTS/CTS握手協議:首先,A向B發送RTS信號,表明A要向B發送若干數據,B收到RTS后,向所有基站發出CTS信號,表明已准備就緒,A可以發送,其余基站暫時"按兵不動",然后,A向B發送數據,最后,B接收完數據后,即向所有基站廣播ACK確認幀,這樣,所有基站又重新可以平等偵聽、競爭信道了。
很久很久以前,計算機還沒有出現,那時就已經存在了(計算機)史前的串口設備(電傳打字機,工控測量設備,通信調制解調器),為了連接這些串口,EIA制定了RS232標准,采用DB25接插件,支持同步和異步串口,D型的接口可以有效防止插反。標准化給使用帶來了便利。
時光荏苒,個人計算機出現了,這些已有的串口設備毫無疑問地成為了最初的外設,自然而然地RS232標准被個人計算機采納。但是設備制造商傾向於體積更小,成本更低的接口,因此,將DB25中未使用的和支持同步模式的引腳去掉,形成DB9。最初的情況相當混亂,因為DB9只定義了信號,卻沒有指定信號和引腳的對應關系,各個制造商只能自行定義。幸運的是,IBM的PC成了工業標准,DB9逐漸統一到IBM的定義上來。
DB9只有9根線,遵循RS232標准。定義如下:
DTR,DSR------DTE設備准備好/DCE設備准備好。主流控信號。
RTS,CTS------請求發送/清除發送。用於半雙工時,收發切換。屬於輔助流控信號。半雙工的意思是說,發的時候不收,收的時候不發。那么怎么區分收發呢?缺省時是DCE向DTE發送數據,當DTE決定向DCE發數據時,先有效RTS,表示DTE希望向DCE發送,一般DCE不能馬上轉換收發狀態,DTE就通過監測CTS是否有效來判斷可否發送,這樣避免了DTE在DCE未准備好時發送所導致的數據丟失。全雙工時,這兩個信號一直有效即可。
隨着計算機的日益普及,很多非RS232的串口也要接入PC機,如果為每一種新出現的串口都增加一個新的I/O口顯然不現實,因為PC后面板位置有限,因此,將RS232串口和非RS232串口都通過RS232口接入是最佳方案。UART的U(通用)指的就是這個意思。早期ROM BIOS和DOS里的通信軟件都是為RS232設計的,在沒有檢測到DCD有效前不會發送數據,因此,就連發送一個字符這樣朴素的應用也要給出DCD、DTR、DSR等控制信號。因此,串口接頭上要將一些控制線短接,或者干脆繞過系統軟件自己寫通信程序。
到此,UART的涵義就總結為:通用的 異步 (串行) I/O口。
就在UART冠以通用二字,准備一統江湖的時候,制造商們不滿於它的速度、體積和靈活性(軟件可配置),推出了USB和1394串口。目前,筆記本上的UART串口有被取消的趨勢,因而有網友發出了“沒有串口,吾誰與歸”的慨嘆,古今多少事,都付笑談中,USB取代UART是后話,暫且不表。
話說自從賀氏(Hayes)公司推出了聰明貓(SmartModem),他們制定的MODEM接口就成了業界標准,自此以后,所有公司制造的兼容貓都符合賀氏標准(連AT指令也兼容)。
細觀賀氏制定的MODEM串口,與RS232標准大不相同。DTR在整個通信過程中一直保持有效,DSR在MODEM上電后/可以撥號前有效(取決於軟件對DSR的理解),在通信過程的任意時刻,只要DTR/DSR無效,通信過程立即終止。在某種意義上,這也可以算是流控,但肯定不是RS232所指的那種主流控。如果拘泥於RS232,你是不會理解DTR和DSR的用途的。
賀氏不但改了DTR和DSR,竟然連RTS和CTS的涵義也重新定義了。因此,RTS和CTS已經不具有最開始的意義了。從字面理解RTS和CTS,是用於半雙工通信的,當DTE想從收模式改為發模式時,就有效RTS請求發送,DCE收到RTS請求后不能立即完成轉換,需要一段時間,然后有效CTS通知DTE:DCE已經轉到發模式,DTE可以開始發送了。在全雙工時,RTS和CTS都缺省置為有效即可。然而,在賀氏的MODEM串口定義中,RTS和CTS用於硬件流控,和什么全雙工/半雙工一點關系也沒有。 注意,硬件流控是靠軟件實現的,之所以強調“硬件”二字,僅僅是因為硬件流控提供了用於流量情況指示的硬件連線,並不是說,你只要把線連上,硬件就能自己流控。如果軟件不支持,光連上RTS和CTS是沒有用的。
RTS和CTS硬件流控的軟件算法如下:(RTS有效表示PC機可以收,CTS有效表示MODEM可以收,這兩個信號互相獨立,分別指示一個方向的流量情況。)
PC端處理:
發. 當發現(不一定及時發現) CTS (-3v to -15v)無效時,停止發送,
當發現(不一定及時發現) CTS (3v to 15v)有效時,恢復發送;
收. 當接收buffers中的bytes當接收buffers中的bytes>N 時,給 RTS 無效信號(-3v to -15v);
MODEM端處理:
同上,但RTS與CTS交換。
在RS232中本來CTS 與RTS 有明確的意義,但自從賀氏(HAYES ) 推出了聰明貓(SmartModem)后就有點混淆了。在RS232中RTS 與CTS 是用來半雙工模式下的方向切換;HAYES Modem中的RTS ,CTS 是用來進 行硬件流控的。通常UART的RTC、CTS 的含義指后者,即用來做硬流控的。
硬流控的RTS 、CTS :RTS (Require To Send,發送請求)為輸出信號,用於指示本設備准備好可接收;CTS(Clear To Send,發送清除)為輸入信號,有效時停止發送。假定A、B兩設備通信,A設備的RTS 連接B設備的CTS ;A設備的CTS 連接B設備 的RTS 。 前一路信號控制B設備的發送,后一路信號控制A設備的發送。對B設備的發送(A設備接收)來說,如果A設備接收緩沖快滿的時發出RTS 信號(意思 通知B設備停止發送),B設備通過CTS 檢測到該信號,停止發送;一段時間后A設備接收緩沖有了空余,發出RTS 信號,指示B設備開始發送數據。A設備發(B設備接收) 類似。上述功能也能在數據流中插入Xoff(特殊字符)和Xon(另一個特殊字符)信號來實現。A設備一旦接收到B設備發送過來的Xoff,立刻停止發 送;反之,如接收到B設備發送過來的Xon,則恢復發送數據給B設備。同理,B設備也類似,從而實現收發雙方的速度匹配。
半雙工的方向切換:RS232中使用DTR(Date Terminal Ready,數據終端准備)與DSR(Data Set Ready ,數據設備准備好)進行主流控,類似上述的RTS 與CTS 。對半雙工的通信的DTE(Date Terminal Equipment,數據終端設備)與DCE(Data circuit Equipment )來說,默認的方向是DTE接收,DCE發送。如果DTE要發送數據,必須發出RTS 信號,請求發送數據。DCE收到后如果 空閑則發出CTS 回 應RTS 信 號,表示響應請求,這樣通信方向就變為DTE->TCE,同時RTS 與CTS 信號必須一直保持。從這里可以看出,CTS ,TRS雖 然也有點流控的意思(如CTS 沒有發出,DTE也不能發送數據),但主要是用來進行方向切換的。
如果UART只有RX、TX兩個信號,要流控的話只能是軟流控;如果有RX,TX,CTS ,RTS 四個信號,則多半是支持硬流控的UART;如果有 RX,TX,CTS ,RTS ,DTR,DSR 六個信號的話,RS232標准的可能性比較大。
順便提一下:
DCD( Data Carrier Detect, 數據載波檢測):DCE向DTE指示,線路上檢測到載波。
RI(Ring Indicator,振鈴指示):DCE向DTE指示,有呼叫接入。
====================================我是分割線=====================================================================================================
這兩天基於STM32的串口做了測試。之前一直用的時候根本沒有往串口協議上靠,只是能用起來解決了問題就匆匆完事。直到最近看《深入理解計算機網絡》這本網絡基礎書,里面講232協議以及485協議時,忽然想拿板子測試下。上面提到的CTS/RTS流控方面的應用是我之前使用串口時沒有注意到的。之前在用USART做串口編程時,一般都是設備作為從機來使用,包括一些教程也都是從這樣的應用來講解。大部分的教程都是在將單片機Usart同上位機超級終端之間通過232協議轉換模塊進行通信。最最常見的用法就是使用串口中斷進行流控(當然這種做法是推薦的,因為232的CTS/RTS不是干這個用的,本文只是那上位機的232這么測試一下。。。)
前幾天看書看到232的時候,我忽然想到是不是可以用RTS/CTS來代替中斷實現上位機的交互。上位機的超級終端或者串口小助手,在接收的數據的時候,可以游刃有余,因為stm32函數庫里面,usart的發送數據是通過串口打印沖定義實現的,將fputs()函數進行了修改,最終使用printf函數進行輸出。這種方法其實是通過函數fputs()本身進行了緩存操作,使得USART_SendData函數能一位位的將數據發出。也就是說,即是不用發送中斷,我們依然能夠井然有序的通過stm32的Usart的TX端口將數據發出。如下代碼是stm32函數庫中串口打印沖定義函數,注意是USART1。
1 #ifdef __GNUC__ 2 /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf 3 set to 'Yes') calls __io_putchar() */ 4 #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) 5 #else 6 #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) 7 #endif /* __GNUC__ */ 8 9 PUTCHAR_PROTOTYPE 10 { 11 /* Place your implementation of fputc here */ 12 /* e.g. write a character to the USART */ 13 USART_SendData(USART1,(u8)ch); 14 15 /* Loop until the end of transmission */ 16 while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); 17 18 return ch; 19 }
但是,如果我們不使用中斷,而從上位機超級終端向stm32發送數據的話,你會發現,單片機只能收到你發的字符串的首個字符。其它的字符全部丟失。這就是沒有做流控的結果。比如下面:
1 int main(void) 2 { 3 u32 i=0xffffff; 4 SystemInit(); 5 usart_Configuration(); 6 //NVIC_Configuration(); 7 while(1) 8 { 9 printf("Waveshare!\r\n"); 10 while(--i); 11 i=0xffffff; 12 printf("%c",USART_ReceiveData(USART1));
} 13 }
但是如果我們使用中斷,或者是使用RTS/RTS做流控則不會發生這種現象。從機理上講,上面是發生了接收溢出錯誤。串口狀態寄存器的ORE位由於在RENE=1的情況(也就是第一個字符已經被寫滿數據寄存器DR)下接收到了數據,造成了數據溢出,此時SR.ORE位會置1.這點參考硬件手冊,而且讀取DR數據寄存器的話,僅會清除RXNE位,而不會清空數據寄存器DR。所以一直會輸出S。但是如果我們發送再次發送一個字符,比如ASDF。則會發現輸出字符變成了AWaveshare!道理跟之前一樣,因為我們已經通過調用USART_ReceiveData()清空了RXNE,所以第一個字符A還是能讀進去的,只不過當第二字字符S時又發生了前面的事情。所以,在做通信的時候必須做流控。
使用中斷做流控我們就不說了,很多。這里說一下CTS/RTS。其實這個比中斷簡單,因為中斷我們還得配置,而且中斷可以寫中斷服務函數,所以應用廣。畢竟CTS/RTS其實是用來做流控或者半雙工通信的,具體的含義不一樣,這里只講232的。以RTS為例,其含義如下:
也就是說,Tx管腳接收到1個字符(默認8bit通信),硬件上RTS會產生一個置位,使得接收數據標志位RXNE=1.所以只要在軟件里我們判斷RXNE的狀態,就可以實現流控。CTS道理一個樣。故而代碼可以如下:
1 int main(void) 2 { 3 SystemInit(); 4 USART_CTRT_Configuartion(); 5 while(NbrOfDataToTransfer--) 6 { 7 USART_SendData(USART1,TxBuffer[TxCounter++]); 8 while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //等待發送結束 9 } 10 11 /*Receive a string (Max RxBufferSize bytes) from the Hyperterminal ended by '\r' (Enter key) */ 12 do 13 { 14 if((USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET)&&(RxCounter < RxBufferSize)) //0xFF:256字符 15 { 16 RxBuffer[RxCounter] = USART_ReceiveData(USART1); 17 USART_SendData(USART1, RxBuffer[RxCounter++]); 18 } 19 20 }while((RxBuffer[RxCounter - 1] != '\r')&&(RxCounter != RxBufferSize)); 21 22 //串口配置函數 23 24 void USART_CTRT_Configuartion(void) 25 { 26 USART_InitTypeDef USART_InitStruct; 27 28 Rcc_Configuration(); 29 30 USART_InitStruct.USART_BaudRate = 115200; 31 USART_InitStruct.USART_StopBits = USART_StopBits_1; 32 USART_InitStruct.USART_WordLength = USART_WordLength_8b; 33 USART_InitStruct.USART_Parity = USART_Parity_No; 34 USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_RTS_CTS; 35 USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; 36 37 USART_Init(USART1, &USART_InitStruct); 38 39 USART_Cmd(USART1, ENABLE); 40 41 UsartGPIO_CTRT_Configuration(); 42 }
完整的代碼可以自己參考庫函數。這里不再貼了。
引用參考:http://www.cnblogs.com/sunyubo/archive/2010/04/21/2282176.html