|
通訊協議是干什么用的?它是各設備包括單片機以及各種外設之間用于傳輸數據的人為規則。 前面的12864屏,我們采用的是一次傳輸一個字節8位二進制數的方式來傳輸數據的,這種傳輸方式的極其明顯的缺點是占用的單片機的端口太多,本身引腳也太多而難以縮小體積,于是有聰明人想到了一次只傳輸一位數據的方法,這個區別于一次多位的方法,業界稱之為串行通訊方式,而一次多位的方式稱之為并行通訊方式。 串行的方式有很多種,從連接線的多少來分,可以分為一線、兩線、三線等,I2C屬于2線方式即它只用到了單片機的兩個引腳。 這里補充一個名詞:通訊通道用線稱之為總線,一線稱之為單總線模式,兩線稱之為雙總線模式。 不同于同是兩線方式的串口通訊協議,I2C方式的兩根線一根用于傳輸數據,另一根由主器件(控制方,另一方則稱之為從器件)來控制發出指揮信號。 器件之間的數據傳輸工作并非時時刻刻都在進行,它是只在需要時才會進行,所以,數據傳輸有幾個必然的過程:發出確定開始的信號、確定參與傳輸的器件、確定輸入與輸出方、傳輸數據、完成后發出停止信號以終止本次傳輸工作。 對于開始信號,I2C總線的規則是在總線不工作時,總線上所有的器件都交出對這兩根線的控制權即都輸出高電位,同時監測SDA線上電位。當其中一個器件想要開始數據傳輸工作時,它的第一個動作就是拉低SDA的電位,這個信號就是開始信號,總線上的其它器件一監測到這個信號,就立即作出接收數據的準備。在這里,發出信號的器件被稱之為主器件(主機),它隨后會控制住SCL和SDA這兩條線上電位的高低,直到它發出停止信號交出對這兩條線的控制權。 接下來的事,就是開始傳輸數據了,這個時候,理所當然地,數據發送方為主器件,數據接收方為從器件(從機)。 在數據位發送的順序上,I2C協議的規定是高位在前,低位順序在后,所以,它最后發送的是一字節中的第0位。 在I2C通訊協議中,對主從雙方有一個規則,就是數據發送方只在SCL為低電位時向SDA上放置數據,數據接收方只在SCL為高電位時讀取SDA上的數據,所以,既然要開始數據傳輸了,那第一步就是主器件拉低SCL的電位;第二步就是主器件向SDA上放置一個電位高低數據;第三步告訴從器件這個SDA上的數據放置好了你可以讀取這個數據了,這一步的實現就是主器件拉高SCL的電位,而從器件監測到SCL電位從低變成高了,就按協議的規定讀取SDA上的電位值,這樣第一位數據就被從器件讀取了;第四步就該由主器件再次往SDA上放置一位數據了,這時先做的仍然是由主器件拉低SCL的電位,然后放置數據。其它的6位數據都按這個方法來進行。當第八位數據被從器件讀取完成時,兩根線的狀態是SCL=1,SDA為主器件放置的最后一位數據的值,這個時候主器件將暫時不再向SDA上放置數據,而是要看看從器件是否成功收到了這8位一字節數據,從而決定下一步怎么做。 從器件在讀取數據時,它內部是設計有一個計數器的,每讀取一位就計一個數,當它讀取完成8位數時,它會產生一個完成標志值,在規則中,這個標志值會由從器件決定是否返饋給主器件以告訴主器件這個從器件已收到完整的8位一字節數據,這個反饋的方式依然依靠SDA線,也就是從器件在SDA上放置這個信號數據,在放置之前,它必須要先獲取對SDA的控制權,所以,主器件這時就要先交出對SDA的控制權,即主器件要向SDA輸出高電位。根據I2C總線數據傳輸過程中只有當SCL為低電位時,SDA上的電位才可以改變的規則,在主器件向SDA輸出高電位之前,主器件還得先把SCL的電位拉低。 綜合說來,從器件向主器件反饋收到信號的流程是:主器件先將SCL拉低,SCL拉低之后,主器件的動作是交出SDA的控制權即向SDA輸出高電位,從器件的動作則是將反饋信號放置在SDA上(這兩個動作可能同時進行),接下來是主器件拉高SCL,再接著主器件按規則在高電位時讀取SDA,主器件收到這個信號之后就可以開始判斷它是否是從器件發出的反饋信號。 有關這個反饋信號的形式,我們知道要么是高電位,要么是低電位。要想區別是不是從器件發出的信號,那這個信號的形式就只能是低電位,所以,當主器件讀取到的SDA為低電位時,就可以確定是從器件發出的信號,這樣就可以確認從器件已收到一字節完整的數據。 以上這個過程,規范的稱呼是“應答”兩個字,而這個反饋信號,稱之為應答信號。 應答如果成功了則SDA的電位為低,但這也意味著這時SDA的控制權還在從器件手中,所以它得交出控制權,這樣主器件才能做接下來的事。方法依然是從器件向SDA輸出高電位,這個輸出的時間依然遵從只有當SCL只為低電位時才能改變SDA電位的規則,所以,在主器件完成應答信號接收之后,它要把SCL拉低,這樣從器件就知道這時可以拉高SDA的電位以交出對SDA的控制權了,這樣一個完成的應答過程才算完成。 應答完成之后,主器件可以繼續數據傳輸,也可以發出停止信號以終止本次傳輸工作,發出停止信號的規則是當SCL處于高電位時,SDA改變電位而且是從低到高的改變,完成后主器件就交出了對這兩條線的控制權。實現的流程是:對于有應答的傳輸,因為應答流程結束時,SCL處于低電位,所以就應該在這個時候將SDA的電位拉低,然后必須要讓SCL成為高電位,然后再按照規則拉高SDA以發出停止信號。 以下劃重點:SCL為高電位時,如果主器件從高拉低SDA,則從器件會判斷為收到起始信號;如果主器件從低拉高SDA,則從器件會判斷為收到停止信號。所以,在數據傳輸及應答期間,當SCL為高電位時,不要改變SDA的電位高低,這個原則,從器件在應答期間,對SDA的控制同樣遵從。這也意味著,在任何時候,只要是在SCL處于高電位時發生SDA從高到低的變化,那么I2C總線就會重新啟動;只要是在SCL處于高電位時發生SCL低到高的變化,就會終止本次傳輸。 I2C協議的數據發送時序圖如下,怎么說呢,我所看到的所有教材上的時序圖,反正沒看到一個完整準確的,所以,我專門用心畫了這個圖:
時序圖.png (2.21 MB, 下載次數: 0)
下載附件
2025-12-8 20:12 上傳
這里要注意I2C的一個很重要的規則:它的數據位的傳輸順序是高位在前,低位在后,如上圖所示。 有關數據發送與應答的規則就說到這里,下面我們對這部分規則開展實驗,實驗的主要目的,是觀察主器件能否收到正確的應答信號,以及在應答完成之后,從器件是否能正常地交出對SDA的控制權。實驗的方法,我們來個騷操作,不用任何儀器,就用一個LED接到SDA上來觀察,LED的正極接SDA,負極串一個電阻接地,這樣當SDA為低電位時則LED不亮,高電位時亮。 I2C器件有很多,這里我們用教材中最常用的24C02存儲器芯片來做實驗,這個芯片沒有其它用途,它只是用來存儲數據,我們的實驗,就是先向其中寫入數據,然后將寫入的數據讀出并顯示出來,看看是否與寫入的數據一樣。 任何通訊協議的功能,它只有傳輸數據這一項功能,你別想著它能直接實現什么功能。至于接收方收到數據后去干什么,如果是外設器件,那它是有固定規則的,不同種類的I2C器件有不同的規則;如果是單片機的話,那就看程序是怎么處理的,也就是說,由你來決定。 還有一點,同一條I2C總線上是可以存在多個器件包括單片機的,理論上任何器件都可以成為主器件也可以成為從器件,主器件可以同時向所有器件發送數據,也可以只與其中一個器件展開通訊。當只與一個器件展開通訊時,就得采取定位措施了。 固定功能的I2C器件是有器件分類編碼的,24C02這類器件的分類編碼為二進制數1010;它還有一個為三位二進制數的地址值,這個地址值由器件上1~3號引腳的電位高低來決定,使用時由用戶自己來確定;器件還要確定其被操作時是寫入還是被讀取,這個方向的表示方法是二進制0表示寫入、1表示讀取。這前后八位數正好組成一個字節數,用于主器件定位從器件,這八位數的排列順序,I2C協議規定,四位分類碼在前,緊跟著的是其三位地址值,最后一位是讀寫方向位。單片機對任何I2C器件的操作,首先都是依據這八位數進行定位并確定讀寫方向。 對于單片機與24C02寫入通訊來說,單片機向其輸入的第一字節數據,是其地址加一個寫入方向位數據,也就是10100000B即0xA0,第二個字節的數據則是24C02內部存儲單元的地址,它內部最多可以存儲256個字節數據,所以它的地址值是從0到255。 如果我們給它輸入的第一個字節為10100001B,那就是要讀取這個器件的內部的數據,因為向它給出的指令是讀取,所以這個指令之后不能向其輸入改變其內部任何內容的數據,包括其內部的地址指針和存儲內容。這個系列的器件的讀取位置取決于器件內部的地址指針的當前值,如果你在讀取數據前不修改其內部的地址指針的話,則每次啟動讀取動作獲得的其內部存儲單元的地址,都是上一次讀或寫操作最后一個單元的地址+1,所以,如果你想讀取某一指定地址的數據,你得先修改其內部的地址指針,修改的方式就是直接寫入這個地址值,即啟動總線、以寫入模式定位該器件、寫入這個地址、終止本次傳輸。 在讀取數據的過程中,SDA的電位由從器件控制即從器件往SDA上放數據,這時,主器件就得交出對SDA的控制權即主器件向SDA輸出高電位。從器件在放置數據時,同樣遵從只有在SCL低電位時才能改變SDA電位的規則,也就是說,當從器件檢測到SCL為低電位時,才能往SDA上面放數據,而當SCL為高電位時,從器件并不能改變SDA的電位,這也意味著,在讀取數據時,你既可以在SCL低電位時也可以在其高電位時讀取SDA上面的數據,但是,規范的做法,是在SCL高電位時進行讀取,這個時候數據是穩定的。 讀操作時,先輸入器件內部地址指針數據——收到應答信號——直接重新啟動總線的做法是通行做法,不過這種做法并不算規范,不規范的是最后一步直接重啟,當然這種做法完全無影響并可稍微省點事,算是偷雞,前面也解釋過這樣做能通過的原因。嚴謹的做法,應該是在收到應答信號后發出停止信號,然后再重新啟動總線輸入讀指令進行讀操作。之所以要提這點,是因為現有教材中都是這樣講的,我這算是一個解釋。 實驗的電路圖如下:
實驗電路圖.png (2.16 MB, 下載次數: 0)
下載附件
2025-12-8 20:12 上傳
搭建好電路的實物圖如下:
實物圖(1).png (1.23 MB, 下載次數: 0)
下載附件
2025-12-8 20:12 上傳
前面說了,我們要用一個LED燈接在SDA上觀察數據傳輸過程,但只有這一個燈是不夠的,為了觀察時序,我們在SCL上也接上一個燈。在數據寫入實驗中,為了觀察到我們輸入的數據是否是我們設定的數據,我們在P1端口接上八個LED燈,我們每向SDA上放置一位數據,都會立即將SDA的電位送入P1口對應引腳,第一位對應LED7(P1.7),第二位對應LED6(P1.6),依此類推。這八個燈在數據讀取實驗時則用于顯示讀出的數據值。 通過前面的介紹,我們看到,這個協議對于每一步操作之間的時間間隔并沒有最長限制,所以,為了讓用LED燈觀察這個實驗的方法具備可行性,在編程時我們可以將每一個步驟之間的時間設計得長一點,比如SCL電位的每一次跳變之后都間隔1秒鐘才進行下一個步驟或下一個跳變,將應答的持續時長設計為2秒以區別于數據讀寫,等等。 應答觀察是我們的重點觀察內容,可以說,只要應答正確了,那我們的程序基本就沒問題了,所以,我們在編寫程序時,第一次將10100000只寫到最末一位數據在SDAH 放上去之后+拉高SCL+等待2秒+拉低SCL+拉高SDA,然后編譯、下載運行,觀察SCL上的燈的閃爍,觀察最后SDA燈是亮還是滅,通過前面的知識,我們知道,如果SDA上的燈不亮,那說明我們的應答信號是正確的,程序沒問題了。 如果你對這個結果還有懷疑的話,那我們把上面的這個實驗稍微修改一下,我們改成輸入10100001這個“讀”命令,為的是在應答期之前就讓SDA為高電平而讓SDA燈亮。我們再運行程序,然后我們會觀察到最后一位數放置在SDA上之后,SDA燈為亮,然后我們再觀察當SCL變化時,SDA燈會否熄滅,什么時候熄滅,如果熄滅了,那說明從器件的應答信號成功被放置在SDA上;然后我們再觀察當SCL變化時,SDA燈是否會亮,什么時候亮,如果亮了,說明從器件成功交出了對SDA的控制權,主器件就重新獲得了對SDA的控制權,然后主器件就可以繼續下面的操作了。 給新手說一個小小的建議:在編程時,最好不要采用循環語句,而是一步一步老老實實地寫程序,等你這個程序通過之后,再去使用循環語句等對程序進行優化和簡化,免得因為對某種語句的使用不熟練出錯而導致程序通不過。 加時的啟動流程可以這樣:拉高SCL、稍等、拉高SDA、等待2秒、拉低SDA、稍等、拉低SCL、等待2秒(啟動觀察拉長時間); 一位數據傳輸流程可以這樣,比如第7位:拉低SCL、稍等、SDA放置第7位數據、將SDA狀態送入LED7、等待1秒、拉高SCL、等待1秒,嚴謹一點的可以再加一個拉低SCL。至于最后一位數據的傳輸,為方便實驗觀察,則按前面說的來編寫和運行。 應答流程可以這樣:拉低SCL、稍等、拉高SDA、等2秒、拉高SCL、等2秒、拉低SCL。 停止流程可以這樣:拉低SCL、等1秒、拉低SDA、等1秒、拉高SCL、等1秒、拉高SDA。 接收數據的實驗我就不講了,自己都覺得啰嗦。 通訊協議為什么叫做協議,因為數據傳輸是主從器件雙方之間的事,它們得事先協商好規則也即每個動作的含意,這樣才能順利進行數據的發送與接收。 總結并補充一下,在I2C協議中,它們雙方是協商好了以下動作的含意:總線空閑時各器件都向自己的兩個端口輸出高電位以示自己沒有控制SDA和SCL,并必須保持對SDA電位變化的監測;總線空閑時若有器件想開始傳輸數據,則拉低SDA電位而成為主器件,此時總線上的所有其它器件一檢測到這個情況,就知道總線上有器件想開始傳輸數據了,并立即作好接收第一字節數據的準備;主器件這時并不考慮從器件們是否已經作好了準備,而只是延時一下,然后就開始一位一位數據的傳輸,雙方事前已協商好,在這個傳輸過程中,主器件是在SCL為低電位時才能往SDA上放置數據,而從器件則保持對SCL電位從低到高變化的檢測,一旦檢測到SCL電位從低到高進行了變化,則按約定讀取SDA;從器件在一個8位數據接收完成后,會以拉低SDA的方式告訴主器件我已接收到了一個8位數據;從器件在接收到第一個8位數據后會將這個數據與自己本身的地址值進行對比,如果不符,則不作響應,但它會繼續監測SDA及SCL,并只接收當SCL在高電位時,SDA電位的變化,也就是只接收總線上的啟動信號和停止信號,接收到啟動信號意味著它又要作接收數據的準備了,接收到停止信號意味著它有了成為主器件的條件。 PS:請糾錯。
|