1、什么是通訊協(xié)議
如果按照百度百科里面的定義,那么通訊協(xié)議的解釋如下:
通信協(xié)議又稱通信規(guī)程,是指通信雙方對數(shù)據(jù)傳送控制的一種約定。約定中包括對數(shù)據(jù)格式, 同步方式,傳送速度,傳送步驟,檢糾錯方式以及控制字符定義等問題做出統(tǒng)一規(guī)定,通信雙方 必須共同遵守,它也叫做鏈路控制規(guī)程。 好吧,如果抄襲到這里,我肯定不會收到讀者的鮮花,而是板磚和臭雞蛋。文抄公誰不會做?那么,對于業(yè)余電子愛好者而言,如何來快速而又簡單地理解通訊協(xié)議?其實,我們可以簡單地這么來理解,對于人類世界來說,在中國范圍內(nèi),那么我們可以將普通話看成是一個通訊協(xié)議。也就是說,當有一個人懂得漢語的人要對另外一個懂得漢語的人表達自己意見的時候,他可 以使用“普通話”這個通訊協(xié)議和另外一個懂得普通話的人進行溝通。同樣的,我們也可以將英語看成人類世界的另外一個版本的通訊協(xié)議。而在電子世界中,我們通常所謂的通訊協(xié)議都是數(shù)字通訊協(xié)議,在數(shù)字通訊協(xié)議中,小到 各種電子零件,大到電腦,它們之間互相溝通其實都是通過 0 或者 1 兩個電位水平來進行通訊的(當 然,還有別的表達方式,如差分電位,這里不細表,作為愛好者,我們將且這么認為罷)。
大家知道,當我們采用 0,1 來進行信息表達的時候,如果只表達一次,即每次只說 1 或者 0 。那么這次表達只能夠蘊含兩個意思,或者你說了 0,這就表示“沒有”,或者你說了 1,這就表示“有”。但是,我們總該知道,這個世界哪里這么簡單?比如你女友問你現(xiàn)在溫度是多少度,你卻 回答說“沒有”或者“有”,我相信,你的女友少不得給你來一記九陰白骨抓。幸好,我們并沒有走到絕路,其實,如果你有一個比較耐得住性子且愿意陪你搞怪的女友。那么我們可以這樣來解決“問溫度”的問題:我們這樣設想,如果你事先和女朋友約定如下:
如果她問你溫度,如果并且她會有足夠的耐心會反復這樣問:現(xiàn)在的溫度是 0 ?如果對了,你回答“是”,如果不對,你回答“否”如果回答了否,她就將上次問的溫度增加一度重新問你。(如第二次就該問是否是 1 )直到你回答了“是”。也就是說,當你回答了“是”的時候,你的女友也就知道了現(xiàn)在溫度。
好吧,恭喜你,盡管這個例子非常地無聊。但是,你和你的女友已經(jīng)共同創(chuàng)造了一種全新的“通 訊協(xié)議”。當然,我們不能想得這么簡單,這個世界非常復雜,所以,我們需要進行復雜的表達。比如說,你女友正在問你溫度問題的時候,你忽然發(fā)現(xiàn)她老娘在背后看著你們倆發(fā)傻,我 相信,明智的你肯定不會繼續(xù)這樣傻干下去了。這個時候,你肯定得想辦法打個哈哈蒙混過關, 諸如“阿姨,您怎么親自來了?”什么的。人類世界的溝通,可以用語言來進行復雜的表達,而語言由于其音節(jié)音調(diào)的多樣性。所以 可以進行復雜的表達。普通話中的“你”“我”“他”三個字,有三種發(fā)音,聽者肯定可以區(qū)分三者的含義。但是,在電子的數(shù)字通訊世界里,只有 0 和1這兩個基本元素。就如前面提到的,它只能在一個時刻里面只表達一次。怎么辦?解決的方法是,我們用一組 0 和 1 的組合來進行復雜意義的表達。如,我們可以用 00001 表示現(xiàn)在溫度是 1 ,00010 表示現(xiàn)在是 2 ,00011 表示現(xiàn)在是 3 。 當然,這種表達的方法是二進制的(關于二進制,八進制,十進制等數(shù)學進制的概念和互相轉(zhuǎn)換, 請參考網(wǎng)上的文檔)。所以,簡而言之,在電子的世界里面,所謂的通訊協(xié)議,其實就是一個事先規(guī)定的規(guī)則, 我發(fā)送什么樣的 0 和 1 的組合代表什么意思。你如果事先了解了這個規(guī)則,這就是所謂的你“兼容”這個通訊協(xié)議。如果不了解,那 就是所謂的“不兼容”。
2、一個通訊協(xié)議涉及到的關鍵要素
一個通訊協(xié)議包含哪些要素呢?在此羅列如下:
A、電壓規(guī)范。
處于一個通訊網(wǎng)絡下的各個電子零件在進行通訊的時候,首先必須要采用共同的電壓 水平。因為,數(shù)字通訊的基本規(guī)則就是 LOW(通常是 0V,當然還有別的電壓水平,如果是 0V,這個電壓并非指確定的 0V,而是大約在 0V 左右)表示 0,表示“沒有”。HIGH(事先 約定的高電平,如 3.3V,5V,12V 等。如果是 3.3V,那么這個值并非是確定的 3.3V 等,而 是電壓高到大約 3.3V 左右)表示 1,表示“有”。
試想一下,如果兩個電子零件相互之間連基本的工作電壓都不一致,你還妄圖讓它們進 行通訊,那么除了冒煙或者是通訊失敗,你幾乎得到什么別的結果。
舉個例子,如你手頭的一個零件 A,輸出 0V 表示開關閉合,輸出 5V 表示開關打開?,F(xiàn) 在另外一個零件 B,它認為 -12V 表示開關閉合,0V 代表開關打開。姑且不去討論會不會燒掉 電路的問題。那么不管 A 發(fā)送什么電壓給 B,B 會永遠都認為 A 是處于開關打開的狀態(tài),因為 不管是 0V 還是 5V 都已經(jīng)高于了 0V 這個限度。
繼續(xù)舉一個例子,如,你和你的女友約定,如果你拿手指頭點點她的額頭表示你現(xiàn)在很無奈(你的手指很溫暖,女友很幸福)。如果你拿著一把燒紅的烙鐵打算點點她的額頭,這還 會讓她認為你打算讓她認為你很無奈嗎?不過,需要注意的一點是:通常情況下,我們在網(wǎng)上可以下載到的各種 IEEE 的通訊協(xié)議 標準規(guī)范里面都不會對電壓進行直接的規(guī)定。其實,這也很好理解,我們只需要保證處于通訊中的雙方采用同樣的電壓就行了。這就好像,如果你和你的女友是超人和女超人,那么你拿個烙鐵點她的額頭,她還是會理 解為你是在表達你的無奈。實際上的例子則是:在實際應用中,如 canbus 總線,有的總線的通訊電壓是 3.3V,有的 在是 5V,甚至還有使用 12V。而之所以在此提出這一點。是因為有很多的新人愛好者在使用數(shù)字傳感器的時候,往往不 會去考察它的通訊電壓。而這往往會導致通訊失敗,甚至燒毀電路元器件。
B、幀長度
所謂的幀長度就是:到底一個信息用了多少個 0 和 1 來組成。這個好理解,因為是采用一串的 0 和 1 的組合來代表意義,那么我們設想,信息的發(fā)送方 如果沒有預定多少個用 0-1 來表示一個完整的組合。那么假設我們按照如下的方式發(fā)送信息呢:一次性發(fā)送:0101001011100101001010100如果拆分成 4 個一組呢?: 0101-0010-1100-1010-0101-0100顯然,前者你根本就不知道是什么意思。你哪里知道是多少個數(shù)字代表一個信息?而后者,你雖然不知道意思,但是好歹,知道發(fā)送了 6 個信息過來。在幀長度的實際應用中,有些通訊協(xié)議采用了停止位的方法,而所謂的停止位,類似于上 面的 0101-0010 之間的“-”,通常的做法是使用一個較長的低電平或者高電平。而有些的通 訊協(xié)議則是,事先約定了多少個 0-1 組合就是表示一個信息。也就是發(fā)送方一旦開始發(fā)送,接 收方直接照著固定的個數(shù),自行將一整個的 0-1 序列拆分。 無奈(你的手指很溫暖,女友很幸福)。如果你拿著一把燒紅的烙鐵打算點點她的額頭,這還 會讓她認為你打算讓她認為你很無奈嗎?
我們常見的序列長度有 8 個 0-1,16 個,32 個,64 個。看一下,正好是 2 的倍數(shù)。當然,前言 8 個的最常見,8 個 0-1 序列就可以排出 256 個可能。而我們必須要接觸到的 ASCII 編碼,其 基本的 0-1 序列長度就是 8 位。
C、通訊速率
關于這一點,我們可以使用一個簡單的實驗來得到體現(xiàn)。我們在 Arduino 中輸入如下的 代碼:
void setup(){Serial.begin(9600);}void loop(){Serial.println(“Hello!”);delay(1000);}
如果在電腦中打開串口監(jiān)視器,除非我們選擇的波特率是 9600,否則,我們在串口監(jiān)視 器中將只能看到一堆的亂碼。
我們打一個比方:好吧,你和你的女友郎情妾意,她在給你喂飯。如果你當時感到幸福,于是閉上了眼感覺 自己徜徉在幸福的海洋中,而女友也感到幸福,于是也閉眼享受和你一樣的幸福。如果她這個 時候還給你繼續(xù)喂飯呢?你知道她什么時候會喂給你,而你恰好張開嘴?于是飯勺子很可能就 直接戳你嘴皮上了。
當然,如果你們兩個都具有大哲學家的冷靜,事先約定了:
“親,你每 5 秒給我喂一次哦”
那么上面煞風景的事情就不會發(fā)生。 這是因為,你們約定了喂飯的頻率。
其實,處于通訊兩端的兩個電子零件也相當于這么一對閉眼享受幸福還要秀恩愛喂飯的男 女,一個在發(fā)送信息(喂飯)一個在接收信息(張嘴吃飯)。如果通訊雙方不事先約定好頻率, 那么就會出現(xiàn)信息丟失的現(xiàn)象。
D、校驗
如果說,兩個人互相之間是在扯談,比如像我現(xiàn)在正在干的事情。那么說過了也就罷了, 沒啥大問題。但是,如果是在非常重要和嚴肅的場合呢?萬一聽的人聽錯了,那肯定會出大問 題。在軍事指揮中,有這么一種方法來防止出問題:命令復述。也就是指揮官下重要命令的時候, 聽從命令者必須復述指揮官的命令以做確認。同樣的,在電子世界的通訊中,因為通信線路的干擾,信息發(fā)送方和接收方可能出偶爾的 問題,那么也會面臨同樣的問題—信息發(fā)送出現(xiàn)了失誤(術語叫誤碼)。這個時候,我們就必 須想辦法來解決。當然,如果接收方在接收到信息之后原封不動的反饋給發(fā)送方,發(fā)送方比對,如果對了就 回復確認;如果錯了就回復錯誤,然后重新發(fā)送,這種方法可以確保絕對的正確。但是,這種 方法顯然是相當無效率的,除非是非常重要的,一點錯都不能出的通訊場合,否則這種檢驗方 法很少會被采用。
幸好專家們想到了更有效率的方法,即所謂的校驗。通常的校驗方法有所謂的奇偶校驗, 和值校驗等等。如奇偶校驗,則是在發(fā)送信息的同時在末尾再帶上這次發(fā)送信息的 0-1 中的 0 的個數(shù)或者 1 的個數(shù)的模值。
如前面的數(shù)據(jù)列
>0101-0010-1100-1010-0101-0100
如果為了確保信息發(fā)送的完全。我們可以采用在每個4位數(shù)的后面再加上一個奇偶校驗位, 即一次性發(fā)送 5 個。
如果我們采用耦校驗位,那么我們的上面的序列就變成了如下的串列:
>0101(0)-0010(1)-1100(0)-1010(0)-0101(0)-0100(1)
括號里面的 0-1 就是所謂的校驗字(注:事實上,括號是不存在的)
采用數(shù)學的方法。如第一串 0101(0) 我們可以根據(jù)校驗字來判斷,前面的 0101 是否正確。
如果發(fā)生了錯誤,我們原本打算發(fā)送 0101,但是因為通訊出了問題,結果只接收成了0001,那么我們可以發(fā)現(xiàn),這個時候的校驗位卻是 0 的話,顯然,0001 絕對是錯誤的值。那 么這個串列就可以簡單拋棄,然后要求重發(fā)了。 當然,在實際過程中。如 TTL 串口通訊。如果你要求不是那么高,那大可以不搞什么校驗。 而如果你對奇偶校驗有跟多的興趣,可以參考維基百科。
E、握手
這個概念非常好理解。如果你在圖書館聚精會神地讀書,忽然坐你邊上的人開始說話,我 相信,你的第一反應肯定不會認為那個家伙是在對你說話,并且你幾乎記不住那個家伙剛剛在 說什么。但是,如果那個家伙先用手肘碰碰你,然后說:“喂”。等你抬頭看著他,他再和你嘰里 呱啦的時候,你肯定可以聽到他對你說了什么。
在電子通訊世界里面也是如此,兩個或者多個需要進行互相通訊的電子零件可能正在執(zhí)行 各自的工作。結果,接收方正在進行某個工作的處理,結果發(fā)送方忽然發(fā)送了一大段的信息過去。 很有可能的結果就是接收方?jīng)]法接受到這個信息。解決這個問題的方法有幾種。一種是采用所謂的握手信號,有的是一個專門的電路,如 Arduino 中的 SPI 通訊,發(fā)送方 會使用 CS 引腳發(fā)送一個高電平,告訴接受方,我要開始和你通訊了。
還有一種類似于老師在課堂上點名回答問題,參與通訊的各個電子元件事先都規(guī)定好了各 自的 ID,當發(fā)送方發(fā)送信息的時候在開頭的時候發(fā)送這個 ID。那么具有這個 ID 的接受者就會 根據(jù) ID 判斷這個信息是否是發(fā)送給自己的。這類似于在信封上面寫地址一樣。再有就是沒有握手信號。純粹雙方都具有專門的發(fā)送和接收的模塊。如 Arduino 上的 RS232 中的 RX 端口,它是相對獨立于 Arduino 的 CPU 的,一旦 Arduino 上電,它就會隨時 監(jiān)聽來自于電線上的信號。一旦收到就立即存儲起來供 CPU 調(diào)用。這就好像一個老板給自己配 了一個電話秘書,隨時替老板接收電話,然后把電話內(nèi)容記錄下來供老板隨時瀏覽。
F、并行通訊和串行通訊
關于這個,其實很好理解,如果只有一根導線,那么我們一次只能發(fā)送 0 或者 1。如果我 們要發(fā)送 0101,那么我們就需要按照先后順序連續(xù)發(fā)送四次。這就是所謂的串行通訊。但是, 如果我們在通訊的雙方連接 4 條導線呢?那么一次性我們就可以把 0101 中的第一位的 0 通過 第一根導線,第一位的 1 通過第二根,第三位的 0 通過第三根,第四位的 1 通過第四根一次性 地發(fā)送出去。 串行通訊的速度相對較慢,但是這節(jié)省連接電路,也就是說省錢。并行通訊速度相對較快, 但是這相對來說非常不省錢。并且,隨著連接電路的數(shù)量增多,它的可靠性也成級數(shù)往下降。所以, 選擇哪種通訊方式,這在于速度 VS 可靠和經(jīng)濟的權衡。 通常情況下,遠距離、低速的通訊通常都是串行的。而近距離、高速的通訊通常都是并行的。
G、單工,半雙工,全雙工。
這三個概念也比較簡單。單工就是發(fā)送方只能發(fā)送,接收方只能接受。這種方式在現(xiàn)實的世界中比如說廣播、電視。 這些就是單工。半雙工則是,雙方都能夠發(fā)送和接收,但是如果是發(fā)送信息,那么它在同一個時刻下只能 發(fā)送或者接收。無法在同一個時刻下同時干兩件事情。如 Arduino 上的 IIC 總線(TWI)就是 半雙工通訊。比較形象的就是步話機。
全雙工則是,雙方不僅能夠發(fā)送和接收,而且發(fā)送和接收可以同時進行。在 Arduino 上 的 SPI 總線和 TTL RS232 都屬于全雙工。而現(xiàn)實中的例子如電話,互聯(lián)網(wǎng)連接等。
3、了解這些對于應用 Arduino 的意義。
1、了解了關于電平的概念,那么我們在未來連接電路的時候就不會出現(xiàn)隨便拿一個數(shù)字 傳感器就往 Arduino 上接的低級錯誤。好歹我們得注意看一下,它的通訊電壓是多少的。當然, 還有一些特例,如 CMOS 電平和 TTL 電平兼容,這些是后來的升級概念。我們這兒不做延伸說明。2、了解了關于通訊速率的概念,那么我們就明白了,為什么需要設定 Serial.begin()。3、了解了奇偶校驗,那么我們在未來讀取數(shù)字傳感器的發(fā)來的數(shù)字的時候就會應用這 個位來判定讀數(shù)值正確與否。4、了解串行和并行通訊,其實,是為了讓我們理解 shiftout() 函數(shù)。5、了解握手的概念,這樣我們才會理解為何 IIC 需要設定一個 ID 去發(fā)送或者接收,而 TTL RS232 和 SPI 則不需要。6、了解了單工,半雙工,全雙工。好吧,這個概念貌似對于 Arduino 的初級應用的確沒啥用。 只是純粹為了補齊概念和湊字數(shù)。
很多的時候,很多的愛好者往往不清楚這些概念,所以總是會犯一些讓業(yè)內(nèi)人士嘲笑的 低級錯誤。但是,這些錯誤正是因為缺乏基礎知識才會發(fā)生的錯誤。如何避免?難道要像那些 業(yè)內(nèi)人士一樣老老實實地抱著專業(yè)書開啃?但是,我們真的只是愛好者,我們只需要搞清楚大概的理論框架就行了。所以,才 有了這個水煮通訊協(xié)議的扯談篇。希望能夠給有需要的讀者以幫助