用開發(fā)和仿真【2.8】)
5.7 I2C5.7.1 I2C規(guī)范介紹I2CInter-Integrated Circuit是一種只使用兩根信號(hào)線的多主機(jī)、多從機(jī)總線。兩根線分別為SDA串行數(shù)據(jù)和SCL串行時(shí)鐘雖然有名為時(shí)鐘的SCL線但本質(zhì)上并不能稱為同步串行總線因?yàn)镾CL還參與了許多協(xié)議內(nèi)容。I2C總線使用線與邏輯充分利用了SDA、SCL這兩根線實(shí)現(xiàn)了許多較為復(fù)雜的協(xié)議內(nèi)容包括·多從機(jī)7位或10位編址從機(jī)尋址和應(yīng)答?!r(shí)鐘同步。·多主機(jī)和沖突仲裁?!ぶС种鲝囊惑w?!げ煌俾蕪臋C(jī)混合使用?!?shù)據(jù)應(yīng)答。·廣播等等。下面將介紹I2C規(guī)范中常用的一些內(nèi)容。對(duì)于完整的規(guī)范讀者應(yīng)參閱規(guī)范文檔。1.I2C電氣連接圖5-32是I2C總線的典型連接。所有主從機(jī)的SDA和SCL均為漏極開路輸出。所有SDA連接在一起、SCL連接在一起并由RP電阻上拉構(gòu)成線與。任何一個(gè)主機(jī)或從機(jī)下拉將使得總線變低所有主、從機(jī)均釋放總線時(shí)才會(huì)由電阻上拉到高。VCC由主機(jī)和從機(jī)的工作電壓決定常見為3.3V、3.0V、2.5V或5VRP的取值與VCC和總線速度有關(guān)VCC越低或速度越快RP越小RP也可替換為1mA至3mA的電流源。RS可用于消除主從機(jī)之間的些許時(shí)序差異導(dǎo)致的毛刺。圖5-32 I2C總線的電氣連接I2C總線的數(shù)據(jù)率即SCL頻率在規(guī)范中定義的有標(biāo)準(zhǔn)速率100kHz、快速400kHz和高速3.4MHz三種。2.I2C位定義SCL和SDA在空閑時(shí)均為高電平所有主從機(jī)均釋放總線。數(shù)據(jù)位傳輸時(shí)要求在SCL為高電平期間SDA保持穩(wěn)定即SDA只能在SCL為低電平時(shí)跳變?nèi)鐖D5-33所示。SCL為高時(shí)的SDA跳變則被專門定義為一次傳輸?shù)钠鹗己屯V狗Q為起始位和停止位如圖5-34和圖5-35所示。圖5-33 I2C位傳輸圖5-34 I2C起始位圖5-35 I2C停止位注意圖5-33圖5-35中上跳沿為曲線段而下降沿為直線段。曲線段代表了釋放總線由外部電阻上拉到高的過程。因而一次I2C傳輸過程的波形將如圖5-36所示。其中還以快速模式400kHz為例標(biāo)注了一些重要的時(shí)序要求。圖5-36 I2C傳輸波形和時(shí)序以400kHz速率為例除正常的起始位之外還可以有重復(fù)起始位。在一次傳輸結(jié)束時(shí)不發(fā)送停止位而發(fā)送起始位緊接著進(jìn)行下一次傳輸稱為重復(fù)起始位。重復(fù)起始位用于直接發(fā)起下一次傳輸而不讓總線回到空閑狀態(tài)這樣主機(jī)可以一直掌握總線控制權(quán)避免進(jìn)入空閑狀態(tài)后被其他主機(jī)搶奪控制權(quán)。圖5-37所示是重復(fù)起始位。圖5-37 重復(fù)起始位3.I2C字節(jié)傳輸和尋址I2C的數(shù)據(jù)傳輸以字節(jié)為單位并且MSB在先每個(gè)字節(jié)后附一位應(yīng)答位因此傳輸中的位以9位為一組一次完整傳輸?shù)奈粩?shù)必然是9的整數(shù)倍。應(yīng)答位的意義在規(guī)范中并沒有嚴(yán)格定義可以是接收方對(duì)字節(jié)數(shù)據(jù)的確認(rèn)也可以是接收方表達(dá)欲結(jié)束傳輸之意圖甚至還可以是發(fā)送方發(fā)送的附加信息這都取決于從機(jī)的功能定義。應(yīng)答位的“確認(rèn)”、“應(yīng)答”之含義一般以低電平表達(dá)記為“ACK”“不確認(rèn)”、“無應(yīng)答”則以高電平表達(dá)記為“NAK”。在7位從機(jī)地址尋址的模式下每次I2C傳輸?shù)牡谝粋€(gè)字節(jié)用作從機(jī)尋址。這個(gè)字節(jié)的高7位為從機(jī)地址而最低位則指示本次傳輸中后續(xù)數(shù)據(jù)的方向是主機(jī)向從機(jī)發(fā)送數(shù)據(jù)稱為寫操作理解為主機(jī)視角還是從機(jī)向主機(jī)發(fā)送數(shù)據(jù)稱為讀操作寫操作以低電平表示讀操作以高電平表示??偩€上的從機(jī)均會(huì)至少監(jiān)聽每次傳輸?shù)牡谝蛔止?jié)如果其中的高7位與某個(gè)從機(jī)預(yù)先分配的地址一致則該從機(jī)會(huì)在第一字節(jié)后的應(yīng)答位給出ACK低電平并配合主機(jī)完成后續(xù)數(shù)據(jù)傳輸如果沒有從機(jī)地址吻合則總線上不會(huì)有應(yīng)答主機(jī)應(yīng)忽略此次操作可立即發(fā)送停止位或走完預(yù)先的傳輸流程當(dāng)然后續(xù)也得不到任何有效數(shù)據(jù)或應(yīng)答。7位一共可以表達(dá)128個(gè)地址但其中有十余個(gè)地址在規(guī)范中定義為特殊用途如廣播、高速模式主機(jī)碼、10位尋址擴(kuò)展等并不能作為從機(jī)地址。圖5-38所示是一次包含一字節(jié)數(shù)據(jù)寫從主機(jī)到從機(jī)操作的傳輸從機(jī)應(yīng)答了尋址和數(shù)據(jù)。其中從機(jī)的7位地址為0b10011010x4d傳輸?shù)臄?shù)據(jù)為0xa5。有時(shí)也常常將一個(gè)從機(jī)的讀寫分開表述為兩個(gè)8位地址即讀地址和寫地址那么這個(gè)從機(jī)的讀地址和寫地址就分別為0b100110110x9b和0b100110100x9a。注意在第一個(gè)應(yīng)答前主機(jī)釋放SDA上升而后從機(jī)拉低SDA應(yīng)答而產(chǎn)生了一個(gè)小毛刺是正常現(xiàn)象。圖5-38 向從機(jī)0x4d寫一字節(jié)數(shù)據(jù)0xa5的示例圖5-38中“S”表示起始位Start“P”表示停止位“stoP”。圖5-39所示則是向從機(jī)寫入一個(gè)字節(jié)后立即重復(fù)起始條件然后向同一個(gè)從機(jī)讀取一字節(jié)的例子。在這個(gè)例子中主機(jī)從從機(jī)讀取到的字節(jié)數(shù)據(jù)為0xf5但最后主機(jī)給出NAK即表達(dá)“即將結(jié)束傳輸不必準(zhǔn)備下一個(gè)數(shù)據(jù)”之意。圖5-39 重復(fù)起始條件的示例波形圖5-39中“sA”表示來自從機(jī)的ACK“mNA”表示來自主機(jī)的NAK“rS”表示重復(fù)起始位repeat Start。圖5-39所示的I2C總線波形也常簡化為圖5-40所示的圖形其中灰色部分為主機(jī)操縱SDA白色部分為從機(jī)操縱SDA。圖5-40 簡化的I2C總線操作4.從機(jī)內(nèi)部地址數(shù)據(jù)字節(jié)是否有進(jìn)一步的意義一般不是I2C規(guī)范的范疇這取決于從機(jī)的功能。例如有些從機(jī)是存儲(chǔ)設(shè)備或內(nèi)部有大量寄存器構(gòu)成一個(gè)地址空間則往往將寫操作中的前一個(gè)或數(shù)個(gè)數(shù)據(jù)字節(jié)用作內(nèi)部地址尋址。例如一個(gè)256字節(jié)存儲(chǔ)器會(huì)使用寫操作中的第一個(gè)數(shù)據(jù)字節(jié)作為內(nèi)部地址當(dāng)它接收到這個(gè)表達(dá)內(nèi)部地址的字節(jié)時(shí)會(huì)將內(nèi)部操作指針指向這個(gè)內(nèi)部地址以便以后的寫或讀操作在這個(gè)地址上進(jìn)行。圖5-41演示了向一個(gè)從機(jī)地址為0x52的存儲(chǔ)器的內(nèi)部地址0x39處寫入數(shù)據(jù)0x7a的操作。圖5-41 帶有內(nèi)部地址的從機(jī)的寫數(shù)據(jù)操作而圖5-42則演示了從該存儲(chǔ)器內(nèi)部地址0x76處讀取數(shù)據(jù)的操作。因設(shè)定內(nèi)部地址需要用I2C寫傳輸而讀取數(shù)據(jù)需要用I2C讀傳輸因而整個(gè)操作用一個(gè)重復(fù)起始位拆分成兩次I2C傳輸且不會(huì)丟失總線控制權(quán)。讀出的數(shù)據(jù)為0xcf。圖5-42 帶有內(nèi)部地址的從機(jī)的讀操作大多數(shù)帶有內(nèi)部地址的從機(jī)在每次寫進(jìn)數(shù)據(jù)后或在讀出數(shù)據(jù)并收到主機(jī)ACK時(shí)會(huì)將內(nèi)部操作指針自增1以便主機(jī)向連續(xù)地址上寫入或讀取多個(gè)數(shù)據(jù)。圖5-43演示了向上述存儲(chǔ)器的內(nèi)部地址0x39~0x3b分別寫入0xf2、0xf3、0xf4的操作。圖5-43 帶有內(nèi)部地址的從機(jī)的連續(xù)寫數(shù)據(jù)操作圖5-44則演示了從上述存儲(chǔ)器的內(nèi)部地址0x39~0x3b讀出數(shù)據(jù)的操作。讀出的數(shù)據(jù)為0xf2、0xf3和0xf4。圖5-44 帶有內(nèi)部地址的從機(jī)的連續(xù)讀數(shù)據(jù)操作5.時(shí)鐘同步和仲裁這里簡單介紹時(shí)鐘同步機(jī)制和多主機(jī)的仲裁機(jī)制。當(dāng)多個(gè)主機(jī)同時(shí)操作從機(jī)時(shí)如果兩者SCL步調(diào)不一致根據(jù)線與邏輯只要有一個(gè)將SCL拉低SCL線便為低因而先下拉SCL的主機(jī)將導(dǎo)致SCL下跳而后釋放SCL的主機(jī)才會(huì)使SCL上跳。如圖5-45所示在T0時(shí)刻主機(jī)1拉低SCLSCL即刻下跳同時(shí)運(yùn)作的主機(jī)2在T1時(shí)刻監(jiān)測(cè)到SCL為低與自身意圖不一致立即開始其低電平周期與主機(jī)1同步下降沿。在T2時(shí)刻主機(jī)1意圖釋放SCL但主機(jī)2仍然拉低SCL此時(shí)主機(jī)1監(jiān)測(cè)到SCL電平與自身意圖不一致便進(jìn)入等待直到主機(jī)2釋放SCL才繼續(xù)自己的高電平周期與主機(jī)2同步上升沿。最終SCL的低電平時(shí)間由低電平最長的主機(jī)決定而高電平時(shí)間由高電平最短的主機(jī)決定。從機(jī)也可在SCL為低時(shí)下拉住SCL線使得主機(jī)釋放SCL時(shí)SCL不上跳迫使主機(jī)進(jìn)入等待。如圖5-46所示T1時(shí)刻從機(jī)可能因內(nèi)部邏輯忙而跟不上主機(jī)的節(jié)奏于是拉低SCL到T2時(shí)刻主機(jī)意圖釋放SCL但從機(jī)仍然拉低SCL迫使主機(jī)進(jìn)入等待直到T3時(shí)刻從機(jī)準(zhǔn)備好繼續(xù)傳輸過程而釋放SCL。圖5-45 I2C主機(jī)間的時(shí)鐘同步圖5-46 I2C從機(jī)迫使主機(jī)等待主機(jī)只能在總線空閑的時(shí)候發(fā)起傳輸因而前述的重復(fù)起始位可以避免總線回到空閑狀態(tài)從而避免后面可能與別的主機(jī)搶奪控制權(quán)失敗。在SCL線為高電平期間主機(jī)們也會(huì)監(jiān)測(cè)SDA電平是否與自身意圖一致監(jiān)測(cè)到與自身意圖不一致的主機(jī)喪失控制權(quán)剩下的主機(jī)們繼續(xù)直到最終剩余一個(gè)主機(jī)或者剩下的主機(jī)都對(duì)同一個(gè)外設(shè)做一模一樣的操作并完成傳輸。這個(gè)過程稱為仲裁。容易想到訪問小地址值從機(jī)的主機(jī)會(huì)贏得仲裁。輸?shù)糁俨玫闹鳈C(jī)可以繼續(xù)控制SCL直到當(dāng)前字節(jié)完成。主從一體機(jī)在作為主機(jī)輸?shù)糁俨脮r(shí)有可能檢測(cè)到贏得仲裁的主機(jī)欲訪問的就是自身的從機(jī)角色此時(shí)它應(yīng)立即切換到從機(jī)角色發(fā)送應(yīng)答并配合贏得仲裁的主機(jī)完成傳輸。5.7.2 通用I2C主機(jī)設(shè)計(jì)這里我們定義一個(gè)命令FIFO驅(qū)動(dòng)的I2C主機(jī)向FIFO中寫入命令來控制I2C主機(jī)工作這些命令包括·產(chǎn)生起始位/重復(fù)起始位?!ぎa(chǎn)生停止位?!懽止?jié)并寫ACK?!懽止?jié)并讀ACK讀ACK也等同于發(fā)送NAK?!ぷx字節(jié)并寫ACK?!ぷx字節(jié)并讀ACK讀ACK也等同于發(fā)送NAK。上層邏輯只需將所需的I2C操作轉(zhuǎn)換為上述6種命令的序列寫入FIFO即可控制I2C完成工作。從I2C總線讀到的數(shù)據(jù)8位數(shù)據(jù)和1位應(yīng)答位將被I2C主機(jī)寫入到一個(gè)數(shù)據(jù)FIFO因?qū)懗鲎止?jié)時(shí)可能更需要讀入應(yīng)答位所以I2C主機(jī)并不區(qū)分讀寫的數(shù)據(jù)而是會(huì)記錄總線上的每次字節(jié)傳輸及其應(yīng)答位以供上層邏輯根據(jù)上層協(xié)議選擇分析。我們定義命令FIFO中的命令格式如表5-7所示每個(gè)命令均為10位。從表中可以看到發(fā)送數(shù)據(jù)0xff與讀入數(shù)據(jù)并沒有區(qū)別因?yàn)閷?duì)于I2C總線來說都是釋放SDA。I2C協(xié)議和時(shí)序相對(duì)復(fù)雜因此這里并不能像前兩節(jié)介紹的UART或SPI主機(jī)那樣依賴簡單的計(jì)數(shù)來產(chǎn)生時(shí)序。為降低模塊復(fù)雜度可將位時(shí)序生成和字節(jié)處理均獨(dú)立成模塊分別取名為位引擎Bit Engine模塊和字節(jié)引擎Byte Engine模塊。表5-7 I2C主機(jī)的控制命令對(duì)于位時(shí)序生成參見圖5-47可將起始位、停止位和數(shù)據(jù)位的各個(gè)時(shí)序要求總結(jié)為圖中所示的9個(gè)時(shí)間段。圖5-47 I2C主機(jī)BitEngine的時(shí)序分段這9個(gè)時(shí)間段如下?!て鹗嘉幌嚓P(guān)SSU起始位建立時(shí)間SH起始位保持時(shí)間SDH起始位后的SDA保持時(shí)間?!?shù)據(jù)位相關(guān)DSU數(shù)據(jù)建立時(shí)間SCLH數(shù)據(jù)位中SCL的高電平時(shí)間DH數(shù)據(jù)保持時(shí)間?!ねV刮幌嚓P(guān)PDSU停止位前的數(shù)據(jù)保持時(shí)間PSU停止位建立時(shí)間PH停止位保持時(shí)間。定義好這9個(gè)時(shí)間段的長度即可保證I2C總線時(shí)序。在位引擎中使用一個(gè)9狀態(tài)的狀態(tài)機(jī)來完成時(shí)序的生成狀態(tài)機(jī)的狀態(tài)轉(zhuǎn)換如圖5-48所示。狀態(tài)轉(zhuǎn)換依賴一個(gè)計(jì)數(shù)器t_cnt用于計(jì)每一個(gè)狀態(tài)所需的時(shí)間這些時(shí)間以參數(shù)形式在模塊中給出。根據(jù)前述的I2C時(shí)序SDH時(shí)間和DH時(shí)間、PSDU時(shí)間和DSU時(shí)間可合用同一個(gè)參數(shù)。圖5-48 BitEngine的狀態(tài)機(jī)位引擎模塊的參數(shù)和端口定義如表5-8所示。表5-8 BitEngine模塊的參數(shù)和端口說明根據(jù)上述說明BitEngine模塊描述為代碼5-7注意這個(gè)位引擎并未實(shí)現(xiàn)多主機(jī)的時(shí)鐘同步和仲裁。對(duì)于幾個(gè)用來設(shè)定時(shí)序的參數(shù)應(yīng)遵循以下限制·TDSU應(yīng)不小于其他6個(gè)參數(shù)。·TDSU最大為1023/CLK_FREQ因t_cnt為10位??偩€空閑的時(shí)間為TSSUTPH總線時(shí)鐘頻率為1TDSUTSCLHTDH。代碼5-7 I2C主機(jī)的BitEngine模塊對(duì)于字節(jié)引擎功能定義為從FIFO讀取命令并控制位引擎工作也使用一個(gè)狀態(tài)機(jī)來控制其工作流程。狀態(tài)轉(zhuǎn)換如圖5-49所示。圖5-49 ByteEngine的狀態(tài)機(jī)其中的狀態(tài)輸出·fifo_rd讀取命令FIFO因RDFIFO狀態(tài)只會(huì)持續(xù)一個(gè)周期fifo_rd為單周期?!it_go控制BitEngine開始產(chǎn)生一個(gè)I2C位?!ic_bit根據(jù)data記錄的命令FIFO中的命令向BitEngine給出位類型連接至BitEngine的iic_bit輸入端口。其中的事件·9b_end輸出事件表示一次9位傳輸結(jié)束用于指示將收到的數(shù)據(jù)寫到數(shù)據(jù)FIFO?!atacmd[80]內(nèi)部事件將命令FIFO讀到的命令中的內(nèi)容賦給datadata將用作移位寄存器參與構(gòu)造iic_bit?!ata1內(nèi)部事件將data左移一位。字節(jié)引擎模塊的端口定義如表5-9所示。表5-9 ByteEngine模塊的端口說明IicByteEngine的描述如代碼5-8所示。代碼5-8 I2C主機(jī)的ByteEngine模塊最后將BitEngine和ByteEngine模塊實(shí)例化到一個(gè)完整的IicMaster模塊中并使用一個(gè)簡單的移位寄存器模塊記錄總線上的數(shù)據(jù)。表5-10是其參數(shù)和端口定義。表5-10 IicMaster模塊的端口說明代碼5-9是完整的IicMaster代碼。其中將I2C總線信號(hào)定義到了一個(gè)名為IicBus的接口中因無論主從機(jī)SCL和SDA的形態(tài)均一樣所以并不需要使用modport關(guān)鍵字定義角色I(xiàn)icBus中將SCL和SDA的讀、寫和三態(tài)控制分開定義應(yīng)在整個(gè)工程的頂層模塊中使用它們實(shí)例化三態(tài)門實(shí)現(xiàn)模擬漏極開路輸入。代碼5-9 完整的IicMaster模塊5.7.3 通用I2C從機(jī)設(shè)計(jì)這里我們定義一個(gè)I2C從機(jī)IicSlave模塊的功能如下·7位從機(jī)地址參數(shù)化設(shè)定?!た膳渲脙?nèi)部地址位寬或無內(nèi)部地址?!ぬ峁┮粋€(gè)存儲(chǔ)器讀寫口如配置為有內(nèi)部地址則可直接連接存儲(chǔ)器如配置為無內(nèi)部地址則由其寫信號(hào)指示從I2C總線獲取的數(shù)據(jù)更新到了寫數(shù)據(jù)口上。與前述的UART接收器、SPI從機(jī)類似通過將SCL和SDA的跳沿轉(zhuǎn)換為使能信號(hào)來控制模塊的工作。但I(xiàn)2C協(xié)議較復(fù)雜還需要使用狀態(tài)機(jī)根據(jù)上述功能定義和I2C協(xié)議可總結(jié)出狀態(tài)機(jī)的轉(zhuǎn)換圖如圖5-50所示。當(dāng)然這個(gè)狀態(tài)機(jī)的工作還需要兩個(gè)位計(jì)數(shù)數(shù)據(jù)位計(jì)數(shù)bit_cnt和內(nèi)部地址位計(jì)數(shù)ia_bit_cnt來配合因較為復(fù)雜在圖中并未畫出它們的驅(qū)動(dòng)事件應(yīng)對(duì)照?qǐng)D5-51和后文代碼來理解。圖5-50 IicSlave模塊中的狀態(tài)機(jī)數(shù)據(jù)位計(jì)數(shù)、內(nèi)部地址計(jì)數(shù)以及狀態(tài)變化的波形如圖5-51所示。圖中以IADDR_WIDTH8為例。圖5-51 IicSlave模塊的工作波形IicSlave模塊的參數(shù)和端口定義如表5-11所示。表5-11 IicSlave模塊的參數(shù)和端口說明IicSlave模塊的描述如代碼5-10所示。注意其中的狀態(tài)機(jī)部分的狀態(tài)驅(qū)動(dòng)和狀態(tài)轉(zhuǎn)移兩段為節(jié)約篇幅將state、bitcnt和ia_bitcnt都寫在了同一個(gè)過程里與前述的帶有輔助計(jì)數(shù)器的狀態(tài)機(jī)稍有不同讀者應(yīng)著重理解。代碼5-10 IicSlave模塊