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