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