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