試驗(yàn)?zāi)康?/B>:認(rèn)識(shí)計(jì)算機(jī)并口和I2C總線(xiàn),用計(jì)算機(jī)并口模擬I2C總線(xiàn),最后,以24CL02為例,完成對(duì)I2C EEPROM的讀寫(xiě)操作。
試驗(yàn)器材:一臺(tái)裝有 Tubor C 2.0 的計(jì)算機(jī)、一條25針并口電纜(看圖1插頭可要選對(duì)了)、自制的用于插入EEPROM芯片的適配器(圖2)、一片 EEPROM 如HT24LC02或AT24C02等。
試驗(yàn)前的準(zhǔn)備知識(shí):
一、I2C總線(xiàn):i2c總線(xiàn)是 Philips 公司首先推出的一種兩線(xiàn)制串行傳輸總線(xiàn)。它由一根數(shù)據(jù)線(xiàn)(SDA)和一根時(shí)鐘線(xiàn)(SDL)組成。i2c總線(xiàn)的數(shù)據(jù)傳輸過(guò)程如圖3所示,基本過(guò)程為:
1、主機(jī)發(fā)出開(kāi)始信號(hào)。
2、主機(jī)接著送出1字節(jié)的從機(jī)地址信息,其中最低位為讀寫(xiě)控制碼(1為讀、0為寫(xiě)),高7位為從機(jī)器件地址代碼。
3、從機(jī)發(fā)出認(rèn)可信號(hào)。
4、主機(jī)開(kāi)始發(fā)送信息,每發(fā)完一字節(jié)后,從機(jī)發(fā)出認(rèn)可信號(hào)給主機(jī)。
5、主機(jī)發(fā)出停止信號(hào)。
I2C總線(xiàn)上各信號(hào)的具體說(shuō)明:
開(kāi)始信號(hào):在時(shí)鐘線(xiàn)(SCL)為高電平其間,數(shù)據(jù)線(xiàn)(SDA)由高變低,將產(chǎn)生一個(gè)開(kāi)始信號(hào)。
停止信號(hào):在時(shí)鐘線(xiàn)(SCL)為高電平其間,數(shù)據(jù)線(xiàn)(SDA)由低變高,將產(chǎn)生一個(gè)停止信號(hào)。
應(yīng)答信號(hào):既認(rèn)可信號(hào),主機(jī)寫(xiě)從機(jī)時(shí)每寫(xiě)完一字節(jié),如果正確從機(jī)將在下一個(gè)時(shí)鐘周期將數(shù)據(jù)線(xiàn)(SDA)拉低,以告訴主機(jī)操作有效。在主機(jī)讀從機(jī)時(shí)正確讀完一字節(jié)后,主機(jī)在下一個(gè)時(shí)鐘周期同樣也要將數(shù)據(jù)線(xiàn)(SDA)拉低,發(fā)出認(rèn)可信號(hào),告訴從機(jī)所發(fā)數(shù)據(jù)已經(jīng)收妥。(注:讀從機(jī)時(shí)主機(jī)在最后1字節(jié)數(shù)據(jù)接收完以后不發(fā)應(yīng)答,直接發(fā)停止信號(hào))。
注意:在I2C通信過(guò)程中,所有的數(shù)據(jù)改變都必須在時(shí)鐘線(xiàn)SCL為低電平時(shí)改變,在時(shí)鐘線(xiàn)SCL為高電平時(shí)必須保
持?jǐn)?shù)據(jù)SDA信號(hào)的穩(wěn)定,任何在時(shí)鐘線(xiàn)為高電平時(shí)數(shù)據(jù)線(xiàn)上的電平改變都被認(rèn)為是起始或停止信號(hào)。
下面以24LC02為例,對(duì)幾個(gè)主要工作時(shí)序做詳細(xì)說(shuō)明。
24LC02的控制字(節(jié))格式(圖4):發(fā)送時(shí)緊跟開(kāi)始信號(hào)后的4位是器件選擇位,通常為‘1010’,它和后面的3位器件地址碼(由24LC02的A0、A1、A2上的電平?jīng)Q定)共同構(gòu)成了7位的從機(jī)地址。從機(jī)地址后緊跟1位讀/寫(xiě)控制位,該位為1表示讀,為0表示寫(xiě)。圖中最后1位是應(yīng)答位,這里它由從機(jī)給出。
24LC02寫(xiě)時(shí)序(圖⑤):主機(jī)發(fā)送開(kāi)始信號(hào),接著發(fā)出從機(jī)地址和寫(xiě)控制碼,主機(jī)接收從機(jī)發(fā)出的應(yīng)答,主機(jī)發(fā)送1字節(jié)的地址信息,主機(jī)接收應(yīng)答,主機(jī)寫(xiě)1字節(jié)數(shù)據(jù)到從機(jī),主機(jī)接收應(yīng)答,主機(jī)發(fā)出停止信號(hào)。寫(xiě)操作完成,1字節(jié)數(shù)據(jù)被寫(xiě)入24LC02內(nèi)指定地址。24LC02提供一種頁(yè)寫(xiě)的方式,每次最多可連續(xù)寫(xiě)入8字節(jié)數(shù)據(jù)再發(fā)送停止信號(hào),當(dāng)寫(xiě)入數(shù)據(jù)多時(shí)可采用這種方式以加快速度。
24LCO2隨機(jī)讀時(shí)序(圖⑥):主機(jī)發(fā)送開(kāi)始信號(hào),接著發(fā)送從機(jī)地址和寫(xiě)控制碼,主機(jī)接收應(yīng)答,主機(jī)發(fā)送1字節(jié)的的地址信息,主機(jī)接收應(yīng)答(注意:前面的時(shí)序?yàn)閷?xiě)操作,目的把起始地址寫(xiě)入24CL02緩沖中,以告知隨后的讀操作從哪個(gè)地址開(kāi)始,這個(gè)步驟在讀時(shí)序中有時(shí)被稱(chēng)為“偽寫(xiě)”),主機(jī)發(fā)送開(kāi)始信號(hào),主機(jī)發(fā)送從機(jī)地址和讀控制碼,主機(jī)接收應(yīng)答,主機(jī)讀取1字節(jié)數(shù)據(jù),主機(jī)不發(fā)應(yīng)答,主機(jī)發(fā)送停止信號(hào)。完成上面步驟,主機(jī)已從24LCO2中讀出指定地址內(nèi)1字節(jié)數(shù)據(jù)。
24LC02讀時(shí)序(圖⑦):如圖⑦所示,與隨機(jī)讀時(shí)序相比,主機(jī)沒(méi)有給從機(jī)寫(xiě)入起始地址,所以這種方式用于讀取當(dāng)前地址內(nèi)的數(shù)據(jù)。另,24LC02也可以采用連續(xù)讀的方式(見(jiàn)圖⑧),這樣每次最多可以讀取8字節(jié)。注意:連續(xù)讀時(shí)每讀完1字節(jié)后主機(jī)要發(fā)應(yīng)答給主機(jī),但在最后1字節(jié)后(即停止信號(hào)前)主機(jī)不發(fā)應(yīng)答。
數(shù)據(jù)線(xiàn)(SDA)上的信號(hào):讀時(shí),從機(jī)在SCL的上升沿將數(shù)據(jù)放到SDA上,寫(xiě)時(shí),遇到SCL的上升沿,從機(jī)將接收SDA上的數(shù)據(jù)。
二、并行口:它包含了一批輸入/輸出端口,在PC機(jī)上它是一個(gè)25針的 D 型插口,一般用于連接打印機(jī),因此有時(shí)也稱(chēng)為打印口。
并行口信號(hào):以打印機(jī)為例,并口I/0信號(hào)中有些是專(zhuān)門(mén)用來(lái)把數(shù)據(jù)傳送給打印機(jī)的,有些則是用來(lái)對(duì)傳送過(guò)程給以控制的,還有將打印機(jī)的各種工作狀態(tài)信息發(fā)送給CPU的。詳細(xì)如表1所示。表中所有信號(hào)用低電平(0V)表示邏輯0,用高電平(5V)表示邏輯1(電壓都是相對(duì)于18-25腳上的接地電勢(shì)而言),凡是前綴用符號(hào)‘-’表示的信號(hào)均指低電平為現(xiàn)役信號(hào)。
可以看出,編號(hào)為2-9針腳上的信號(hào)是傳遞實(shí)際數(shù)的信號(hào),而其它線(xiàn)上的信號(hào)則是用在對(duì)打印機(jī)進(jìn)行初始化處理和對(duì)打印機(jī)動(dòng)作進(jìn)行同步上。下面簡(jiǎn)單介紹一下打印過(guò)程以加深對(duì)并口的理解,CPU通過(guò)并口中16和17腳上的信號(hào)來(lái)選擇打印機(jī),并給以初始化處理。且用13腳上的信號(hào)給以響應(yīng)。在打印機(jī)已準(zhǔn)備好接收數(shù)據(jù)時(shí),就將11腳置為低電平(表示可以接收),CPU把數(shù)據(jù)放到并口的數(shù)據(jù)線(xiàn)(2-9)上,并通過(guò)1腳上的選通信號(hào)對(duì)打印機(jī)的數(shù)據(jù)進(jìn)行選通。打印機(jī)在收到選通信號(hào)時(shí)將忙信號(hào)(11)置為高電平,表示正在接收數(shù)據(jù)。數(shù)據(jù)接收完畢后,打印機(jī)在短時(shí)間內(nèi)把現(xiàn)役的確認(rèn)信號(hào)(10腳低電平)發(fā)送出去,然后再把忙信號(hào)(11)置成低電平(既非現(xiàn)役)并準(zhǔn)備好接收更多數(shù)據(jù)。
并行口硬件:并口行現(xiàn)在通常被集成在系統(tǒng)板上,25針插口上的信號(hào)可通過(guò)數(shù)據(jù)鎖存器、打印狀態(tài)和打印機(jī)控制三個(gè)寄存器(也就是三個(gè)輸入/輸出端口)進(jìn)行程序設(shè)計(jì)和控制。計(jì)算機(jī)系統(tǒng)中通常有多個(gè)并行端口,表2列出了它們?cè)谳斎?輸出系統(tǒng)中的地址。需要注意的是這些地址是由系統(tǒng) BIOS 給出的,并不是硬件的物理地址,所以可以通過(guò)設(shè)置 BIOS 來(lái)改變當(dāng)前端口的配置。
端口寄存器:表3列出了并行端口寄存器各位的意義。這些信號(hào)也是在外部插頭上出現(xiàn)的主要信號(hào)。不過(guò)寄存器中有些信號(hào)的極性與插頭上相應(yīng)信號(hào)的極性正好相反。比如,選通信號(hào)在插頭上為低電平時(shí),信號(hào)是現(xiàn)役的,而在打印機(jī)控制寄存器中則為高電平是現(xiàn)役的。
通過(guò)上面的準(zhǔn)備知識(shí),應(yīng)有以下理解:1、可以把并口的25個(gè)針腳理解為三個(gè)寄存器對(duì)外的映射,除了傳送8位實(shí)際數(shù)據(jù)的引腳外,還有用于控制打印機(jī)和取得打印機(jī)當(dāng)前狀態(tài)的引腳,這些引腳有的為輸入,有的為輸出,因此可以像用單片機(jī)I/O一樣靈活的運(yùn)用它們。2、I2C總線(xiàn)在通訊過(guò)程中,數(shù)據(jù)線(xiàn)(SDA)上的信號(hào)流動(dòng)方向是不斷變化的,如:主機(jī)正在寫(xiě)24LC02時(shí),SDA的方向?yàn)橹鳈C(jī)到從機(jī),SDA為輸出,寫(xiě)完一字節(jié)后,要接收應(yīng)答時(shí),SDA的方向變?yōu)閺臋C(jī)到主機(jī),SDA為輸入(對(duì)于主機(jī))。3、并口模擬I2C總線(xiàn),其實(shí)是用軟件控制并口的 I/O 來(lái)輸入輸出 I2C 總線(xiàn)需要的高、低電平信號(hào),從而產(chǎn)生I2C總線(xiàn)的各種時(shí)序。
制作試驗(yàn)電路:
試驗(yàn)用的電路如圖⑨,分析如后:P1的4-7腳并聯(lián)(為了加大輸出電流),接IC1的VCC端,為IC1供電。P1的2腳接IC1的SCL端,用做I2C總線(xiàn)的串行時(shí)鐘輸出。因I2C總線(xiàn)中數(shù)據(jù)線(xiàn)(SDA)在不同的時(shí)間可能是輸入也可能是輸出,所以接在IC1 SDA端上的信號(hào)也有兩路,輸出時(shí),P1 3腳輸出低電平T1導(dǎo)通,SDA被置為低電平,P1 3 腳輸出高電平T1截止,因 R1的作用SDA被置為高電平。輸入時(shí),P1 通過(guò)判斷 13 腳上的電平高低,來(lái)讀取SDA上的數(shù)據(jù)。要注意的是用于輸入時(shí)T1必須是截止的,以免SDA被箝位。
這個(gè)電路具有通用性,24C01、24CO2、24LC64等24系列的I2C EEPROM 均可按這個(gè)電路與并口連接,所以不妨把它當(dāng)作實(shí)用工具來(lái)認(rèn)真制作。先找一條并口電纜,看電纜插頭的形式,找一個(gè)與之配套的25針插座,購(gòu)買(mǎi)一個(gè)撥動(dòng)式的IC插座,將IC插座按圖中IC1的連接方法與找來(lái)的并口插座相連,然后按圖將T1、R1、C1、直接焊在IC插座或并口插座上,要盡量作的緊湊些。最后將電路固定在一個(gè)合適的小塑料盒內(nèi),好了,現(xiàn)在它是我們的試驗(yàn)器材,等看過(guò)后面的內(nèi)容,你會(huì)發(fā)現(xiàn)只要為其配上軟件,它就是一個(gè)用于讀寫(xiě)I2C EEPROM 的好工具。
試驗(yàn)程序編寫(xiě):
和其它高級(jí)語(yǔ)言相比,C 更適合于對(duì)硬件編程。本試驗(yàn)所用的程序就是在 Tubor C 2.0 環(huán)境下編譯通過(guò)的。
一、C 語(yǔ)言相關(guān):對(duì)本試驗(yàn)較關(guān)鍵的幾個(gè)函數(shù)和運(yùn)算。
讀端口函數(shù) inprotb(); 可從指定的輸入端口讀入一個(gè)字節(jié),并返回這個(gè)字節(jié),用法為:inprotb(端口號(hào)或端口地址);例如:b=inprotb(379H);由于379H為‘打印機(jī)狀態(tài)’寄存器的地址,因此執(zhí)行后變量 b中將存放由函數(shù)讀取的 379H 的值。
寫(xiě)端口函數(shù) outprotb(); 可寫(xiě)一字節(jié)數(shù)據(jù)到指定的輸出端口,用法為:outprotb(端口地址,整型數(shù));例如:outprotb(378H,1);由于端口地址378H為并口的‘?dāng)?shù)據(jù)鎖存器’地址,因此執(zhí)行后將在并口的 2 腳輸出高電平,3-9腳輸出低電平。
位運(yùn)算:位運(yùn)算的對(duì)象只能是整型或字符型數(shù)據(jù),本試驗(yàn)程序中用到了兩種位運(yùn)算。左移運(yùn)算(<<):運(yùn)算符左邊是位移對(duì)象,右邊是整形表達(dá)式,代表左移的位數(shù),左移時(shí),右端補(bǔ) 0;左端移出的部分舍棄。右移運(yùn)算(>>):運(yùn)算符的使用方法與左移運(yùn)算符一樣,所不同的是移位方向相反。右移時(shí),右端(低位)移出的二進(jìn)制數(shù)舍棄,左端(高位)移入的二進(jìn)制數(shù)分兩種情況:對(duì)于無(wú)符號(hào)整數(shù)和正整數(shù),高位補(bǔ) 0,對(duì)于負(fù)整數(shù),高位補(bǔ) 1。舉例:假設(shè)b和c為字符型變量,并且 b 已賦初值,用二進(jìn)制表示時(shí) b 的值為 01110110 ;現(xiàn)在若要求的 b 的第 3 位的二進(jìn)制數(shù)是 1 ,還是 0 ,可暫將 b 的值賦給變量 c (c=b;),再對(duì) c 進(jìn)行位移運(yùn)算,先將 c 右移 2 位(c=c>>2;),再左移 7 位(c=c<<7;),然后用程序判斷 c 的值是否為 0,為0則所求位的二進(jìn)制數(shù)為 0,否則為所求位的二進(jìn)制數(shù)為 1。經(jīng)過(guò)位移 c 的值變?yōu)椤?0000000’,而不是0,因此可以判斷 b 的第 3 位中的二進(jìn)制數(shù)是 1。后面的試驗(yàn)程序就是用這種方法來(lái)接收應(yīng)答和讀取SDA上的數(shù)據(jù)的。
二、編程前的分析:現(xiàn)在從編程的角度對(duì)圖⑨ 的電路再次分析。參見(jiàn)表1、表2、表3?,F(xiàn)在計(jì)算機(jī)上的并口通常被默認(rèn)的設(shè)置成端口2,既數(shù)據(jù)鎖存器地址為378H的端口。
并行口(P1)13腳:它是一個(gè)輸入端,是‘打印機(jī)狀態(tài)’寄存器(見(jiàn)表2、表3)中的位 4?!蛴C(jī)狀態(tài)’寄存器地址為379H,可以用 C 語(yǔ)言中的 inprotb() 函數(shù)來(lái)讀取379H的值,然后通過(guò)位運(yùn)算即可獲得當(dāng)前P1 13腳(IC1的SDA端)的電平狀態(tài)。注意:在讀端口時(shí),要確認(rèn)T1是截止的。
并行口(P1)2 腳:它是‘?dāng)?shù)據(jù)鎖存’寄存器中的位 0,在這里作為一個(gè)輸出端?!?dāng)?shù)據(jù)鎖存’寄存器的地址為378H,可以用 C 語(yǔ)言中的 outprotb() 函數(shù)給378H的位 0 寫(xiě)入1或0,,從而模擬出 I2C 總線(xiàn)中SCL上的高、低電平。這里需要注意的是,從2腳輸出時(shí),用函數(shù)寫(xiě)數(shù)據(jù)鎖存器每次只能改變位 0 的狀態(tài),而不能影響到其它位的狀態(tài)。
并行口(P1)3 腳:它是‘?dāng)?shù)據(jù)鎖存’寄存器中的位 1,在這里作為輸出端與T1基極相連,可以用 C 語(yǔ)言中的 outprotb() 函數(shù)給‘?dāng)?shù)據(jù)鎖存’378H的位 1 寫(xiě)入1或0,從而控制 T1 的導(dǎo)通和截止,配合 R1 的作用,模擬出I2C時(shí)鐘線(xiàn)SDA上的高、低電平信號(hào)。 3 腳輸出低電平將使 T1 導(dǎo)通,SDA既被置為低電平,3 腳輸出高電平 T1 截止,由R1將SDA上拉為高電平。要注意操作這一位時(shí)不能影響到其它位。
并行口的 4-7 腳:它們分別是‘?dāng)?shù)據(jù)鎖存’寄存器中的位2、位3、位4和位5,這4 位全部作為輸出端接在IC1的VCC上,通過(guò)寫(xiě)端口函數(shù)將它們?nèi)繉?xiě)入1(既都輸出高電平),用于給IC1提供電源。注意,因這4位是作為電源使用的,必須保證這4位的值始終為1 ,所以每次寫(xiě)378H時(shí)要特別注意。這4個(gè)引腳是并在一起的,其中若有1位被寫(xiě)成0,就會(huì)因高低電平抵消而中斷IC1的電源使操作失敗,甚至可能會(huì)損壞并口。
三、編程:通過(guò)上面分析,要用并口來(lái)模擬I2C總線(xiàn)來(lái)讀寫(xiě) 24LC02 ,程序需有以下幾部分。
發(fā)送I2C開(kāi)始信號(hào):用 outprotb() 函數(shù)向378H寫(xiě)入16進(jìn)制數(shù)“0XFF”(即2-9腳全部輸出高電平),SCL和SDA都為高電平,延時(shí)一段時(shí)間后,向378H寫(xiě)入“0XFD”(其它腳狀態(tài)不變,只是將位 1 置為低電平),使SDA由高電平變?yōu)榈碗娖?,即產(chǎn)生了I2C的開(kāi)始信號(hào)。最后將在378H中寫(xiě)入“0XFC”(即其它腳不變,將位0和位1置為低電平)使SCL為低電平,以完成一個(gè)時(shí)鐘,也為后面的讀寫(xiě)作準(zhǔn)備。
發(fā)送I2C停止信號(hào):I2C的停止信號(hào)是在SCL為高時(shí),SDA由低變高。程序可按下面步驟來(lái)寫(xiě),用寫(xiě)端口函數(shù)向378H寫(xiě)入“0XFC”,使SCL和SDA為低電平,延時(shí)一段時(shí)間,向378H寫(xiě)入“0XFD”,使SCL變?yōu)楦唠娖?,SDA為低電平,延時(shí),向378H寫(xiě)入“0XFF”SCL保持不變,使SDA由原來(lái)的低電平變?yōu)楦唠娖?,即產(chǎn)生了一個(gè)停止信號(hào)。延時(shí)一段時(shí)間,最后向378H寫(xiě)入“0XFE”,使SCL為低電平,以完成一個(gè)時(shí)鐘。
發(fā)送數(shù)據(jù):先把要發(fā)送的數(shù)據(jù)放在一個(gè)變量里,然后按位發(fā)送。方法為,通過(guò)位運(yùn)算求得欲發(fā)送位的值(1或0),然后用寫(xiě)端口函數(shù)模擬出SCL和SDA,并按I2C的寫(xiě)時(shí)序?qū)⒁晃粩?shù)據(jù)發(fā)送出去,程序中可用while循環(huán)語(yǔ)句來(lái)控制發(fā)送的位數(shù)和字節(jié)數(shù)。
主機(jī)(并口)發(fā)送應(yīng)答:I2C總線(xiàn),主機(jī)發(fā)送應(yīng)答用在連續(xù)讀時(shí)序中,每讀取一字節(jié)(8位)后,主機(jī)使SDA保持一個(gè)時(shí)鐘周期的低電平??梢杂脤?xiě)端口函數(shù)先將SDA、SCL置為 0,然后將SCL變高,SDA保持低電平,一個(gè)應(yīng)答信號(hào)既被發(fā)送,最后將SCL置低,完成一個(gè)時(shí)鐘。
接收數(shù)據(jù):并口讀取I2C總線(xiàn)的數(shù)據(jù)時(shí),必須讓 T1截止,使用并口的13腳來(lái)接收SDA上的數(shù)據(jù)??砂聪旅娌襟E操作,先用寫(xiě)端口函數(shù)使SCL為低電平,同時(shí)在并口3腳輸出高電平使 T1 截止。然后用寫(xiě)端口函數(shù)單獨(dú)將SCL置1,其它位保持不變,模擬出時(shí)鐘上升沿,IC1 將把一位數(shù)據(jù)放到數(shù)據(jù)線(xiàn)SDA上,用讀端口函數(shù) inprotb() 讀取‘打印機(jī)狀態(tài)’寄存器379H當(dāng)前的值,將結(jié)果賦值給一個(gè)變量,然后對(duì)這個(gè)變量進(jìn)行先右移4位,再左移7位的運(yùn)算(用以獲得13 腳電平狀態(tài),即打印機(jī)狀態(tài)寄存器的位 4 的值),判斷該變量是否為0,最后將判斷結(jié)果移入另外的一個(gè)用于存放‘已讀取數(shù)據(jù)’的變量中,完成讀取一位數(shù)據(jù)的操作,用寫(xiě)端口函數(shù)使SCL為低電平,在下一個(gè)SCL的上升沿,同樣用上面的方法將一位數(shù)據(jù)加入‘已讀取數(shù)據(jù)’變量中。可用while循環(huán)控制要讀的位數(shù)和字節(jié)數(shù)。注意:以上過(guò)程都是在 T1 為截止態(tài)時(shí)進(jìn)行的。
主機(jī)(并口)接收應(yīng)答:接收應(yīng)答用于寫(xiě) I2C 時(shí),每寫(xiě)一字節(jié)數(shù)據(jù)到從機(jī)后,如果操作成功,從機(jī)在下一個(gè)時(shí)鐘內(nèi)使 SDA 為低。主機(jī)查詢(xún)應(yīng)答可以加強(qiáng)操作的可靠性。接收應(yīng)答和上面說(shuō)的接收數(shù)據(jù)大致相同,只是僅接收一位數(shù)據(jù)并且不存儲(chǔ),直接判斷其值是否為 0,不為 0 時(shí)(即沒(méi)有收到應(yīng)答)轉(zhuǎn)錯(cuò)誤處理程序,為 0則繼續(xù)后面的操作。在實(shí)際編程時(shí)將這個(gè)步驟合并到寫(xiě)I2C的操作中。
有關(guān)延時(shí):I2C器件對(duì)SDA和SCL上的高、低電平信號(hào)需保持的時(shí)間是有規(guī)定的。如:開(kāi)始信號(hào)的高、低電平要保持多長(zhǎng)時(shí)間,數(shù)據(jù)信號(hào)的高、低電平最低要保持多長(zhǎng)時(shí)間等。不同的器件對(duì)這個(gè)時(shí)間有不同的規(guī)定。查找24LO02的數(shù)據(jù)手冊(cè),可以知道,它在不同的電壓下對(duì)各信號(hào)要保持的時(shí)間分別在幾百納秒到幾微秒之間。這個(gè)時(shí)間也體現(xiàn)了I2C器件的讀寫(xiě)速度。因?yàn)橛?jì)算機(jī)的速度不同,要用計(jì)算機(jī)并口來(lái)模擬I2C很難將這個(gè)時(shí)間精確到微秒。為了能夠在不同的計(jì)算機(jī)上可靠的操作I2C總線(xiàn),試驗(yàn)程序用了C語(yǔ)言的延時(shí)函數(shù)delay();這個(gè)函數(shù)能產(chǎn)生的最小延時(shí)為1毫秒。雖然這樣做降低了I2C的讀寫(xiě)速度,但可以保證操作的可靠性。
四、用并口讀寫(xiě)I2C總線(xiàn)的源程序:程序中把I2C的一些操作時(shí)序定義成了獨(dú)立的函數(shù)供主函數(shù)調(diào)用,這樣增加了程序的靈活性,也方便對(duì)程序的修改和擴(kuò)充。
源程序如下:
#include "stdio.h"
#include "dos.h"
#include "conio.h"
/***********void i2cstart()***********/
void i2cstart(){
outportb(0x378,0xff);/*scl 1, sda 1*/
delay(1);/**/
outportb(0x378,0xfd);/*scl 1, sda 0*/
delay(1);/**/
outportb(0x378,0xfc);/*scl 0, sda 0*/
delay(1);
}
/***********void i2cstop()***********/
void i2cstop(){
outportb(0x378,0xfc);/*scl 0, sda 0*/
delay(1);/**/
outportb(0x378,0xfd);/*scl 1, sda 0*/
delay(1);/***/
outportb(0x378,0xff);/*scl 1, sda 1*/
delay(1);/**/
outportb(0x378,0xfe);/*scl 0, sad 1*/
}
/***********writebyte()***********/
writebyte(char s){
short int a=7;
char d,e;
outportb(0x378,0xfc);/*scl 0, sda 0*/
delay(1);/***/
while(a>=0){
d=s>>a; d=d<<7;
if (d=='\x80')/*****"1"***/
{
outportb(0x378,0xfe);/*scl 0, sda 1*/
delay(1);/***/
outportb(0x378,0xff);/*scl 1, sda 1*/
}
else
{
outportb(0x378,0xfc);/*scl 0, sda 0*/
delay(1);/***/
outportb(0x378,0xfd);/*scl 1, sda 0*/
}
a=(a-1);
}
/**ask**/
delay(1);/***/
outportb(0x378,0xfe);/*scl 0, sda 1*/
delay(1);/***/
outportb(0x378,0xff);/*scl 1, sda 1*/
delay(1);/***/
outportb(0x378,0xfc);/*scl 0, sda 1*/
delay(1);/***/
e=inportb(0x379); d=e>>4; d=d<<7;
if (d=='\x0') return 0;
else
printf("not acknowledge!\n");
return 1;
}
/***********readbyte()***************/
char readbyte(){
unsigned short a=8;
char d,e,f='\x0';
while(a>0){
delay(1);/***/
outportb(0x378,0xfe);/*scl 0, sda 1*/
delay(1);/***/
outportb(0x378,0xff);/*scl 1, sda 1*/
delay(1);/***/
e=inportb(0x379); d=e>>4; d=d<<7;
if(d=='\x80') d='\x1';
f=f<<1; f=(f+d); a=(a-1);
outportb(0x378,0xfe);/*scl 0, sda 1*/
delay(1);/***/
}
return f;
}
/************mainask()*****************/
mainask(){
delay(1);/**/
outportb(0x378,0xfc);/*scl 0, sda 0*/
delay(1);/**/
outportb(0x378,0xfd);/*scl 1, sda 0*/
delay(1);
outportb(0x378,0xfc);/*scl 0, sda 0*/
}
/*************************************/
main(){
unsigned short a,b,c,g;
char d,e,f;
textcolor(2);
clrscr();
printf("press 'r' or 'w' :");
scanf("%c",&f);
if(f=='w')
{
/************ W 256 BYTES ****/
e='\x0'; c=32; /* 24lc02: 32=256/8 */
while(c>0){
i2cstart();/*****start****/
writebyte('\xa0');/***send contbyte***/
writebyte(e);/***send start address***/
/************W 8 bytes****/
b=8; d='\x0'; /* num */
while(b>0){
if ((writebyte(d))==1) exit(0);/***send a byte***/
b=(b-1);d=(d+1);
}
i2cstop();
delay(40); /****writer delay****/
c=(c-1);e=(e+8);
}
printf("write ok!!\n"); exit(0);
}
if(f=='r')
{
/****** read ***********************************/
printf("please import start address:");
scanf("%x",&b);
a=(256-b); c=(a%8); a=(a/8);
while(a>0){
g=8;
i2cstart();/*****start****/
writebyte('\xa0');/***send contbyte***/
d=(char)b;/****/
writebyte(d);/***send start address***/
i2cstart();/*****start****/
writebyte('\xa1');/***send contbyte***/
while(g>0){
d=readbyte();
if(d=='\xff')
printf(" FF");
else
printf(" %.2X",d);
g=(g-1); if(g>0) mainask();
b=(b+1);
}
i2cstop();
a=(a-1);
}
while(c>0){
i2cstart();/*****start****/
writebyte('\xa0');/***send contbyte***/
d=(char)b;/****/
writebyte(d);/***send start address***/
i2cstart();/*****start****/
writebyte('\xa1');/***send contbyte***/
d=readbyte();
if(d=='\xff')
printf(" FF");
else
printf(" %.2X",d);
c=(c-1); if(c>0) mainask();
}
printf("\nREAD OK!\n");
exit(0);
}
else {printf("\nCommand Error!!!"); exit(0);}
}
以上程序是在 Tubor C 2.0 環(huán)境下編譯通過(guò)的,運(yùn)行結(jié)果如后:程序先在屏幕上提示“press 'r' or 'w' :”'r'為讀24LC02,'w'為寫(xiě)。如果輸入'r'并按回車(chē),程序?qū)?huì)提示:“please import start address:”這時(shí)請(qǐng)按16進(jìn)制的格式輸入要讀的起始地址,然后回車(chē),程序?qū)?huì)從該地址開(kāi)始把后面的所有數(shù)據(jù)讀出并按大寫(xiě) 16進(jìn)制的格式在屏幕上打印出來(lái)。完成后提示“READ OK!”并結(jié)束程序。如果在程序開(kāi)始時(shí)輸入的是'w',程序?qū)?4LC02的00H地址開(kāi)始,按“00 01 02 03 04 05 06 07”的格式,每8字節(jié)循環(huán)一次,直到寫(xiě)滿(mǎn)24LC02所有的存儲(chǔ)空間,即256個(gè)字節(jié)。寫(xiě)的過(guò)程中如果出現(xiàn)錯(cuò)誤將提示“not acknowledge!”,如果操作順利完成,程序?qū)⑻崾尽皐rite ok!!”并結(jié)束運(yùn)行。在程序開(kāi)始時(shí)如果輸入的不是'r'也不是'w',程序?qū)⑻崾尽癈ommand Error!!!”并退出運(yùn)行。
這個(gè)程序雖然只是個(gè)簡(jiǎn)單的演示,但卻是并口模擬I2C的最關(guān)鍵的核心部分,只要給它加些改動(dòng)并配上簡(jiǎn)單的界面,即可以成為一個(gè)很實(shí)用的并口 I2C 總線(xiàn)讀寫(xiě)程序。
評(píng)論