1. 24C16 E2PROM簡(jiǎn)介 24C16是一個(gè)16K位串行CMOS的E2PROM,內(nèi)部含有2048個(gè)8位字節(jié),它有一個(gè)16字節(jié)的頁(yè)寫(xiě)緩沖器,該器件通過(guò)I2C總線(xiàn)接口進(jìn)行操作,有一個(gè)專(zhuān)門(mén)的寫(xiě)保護(hù)功能引腳。在本實(shí)驗(yàn)系統(tǒng)中,24C16的接口電路如圖4-4所示。 file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.gif 圖4-4 24C16的接口電路 從圖4-4可看出,24C16的接口電路非常簡(jiǎn)單,只引出了2根控制線(xiàn)SDA和SCL到J6。各引腳功能簡(jiǎn)單描述如下: SCL:串行時(shí)鐘。24C16串行時(shí)鐘輸入管腳,用于產(chǎn)生器件數(shù)據(jù)收發(fā)的時(shí)鐘; SDA:串行數(shù)據(jù)/地址。24C16雙向串行數(shù)據(jù)/地址管腳用于器件所有的收據(jù)收發(fā)。SDA是一個(gè)開(kāi)漏輸出管腳,可與其它開(kāi)漏輸出或集電極開(kāi)路輸出進(jìn)行線(xiàn)或(wire-OR); A0、A1、A2:器件地址輸入。這些輸入腳用于多個(gè)器件掛接在同一對(duì)I2C總線(xiàn)上時(shí)設(shè)置器件地址,以便主控器件識(shí)別。當(dāng)這些引腳懸空時(shí)其默認(rèn)值為0。 WP:寫(xiě)保護(hù)。當(dāng)WP腳連接到VCC時(shí),所有內(nèi)存變成寫(xiě)保護(hù)(只能讀)。當(dāng)WP引腳連接到VSS或懸空時(shí),允許器件進(jìn)行讀/寫(xiě)操作。 2. I2C總線(xiàn)接口的特性 I2C接口的信息傳輸僅需要SDA和SCL兩條線(xiàn),均為雙向I/O口,通過(guò)上拉電阻接正電源。當(dāng)總線(xiàn)空閑時(shí),兩根線(xiàn)都是高電平。接入總線(xiàn)器件的輸出必須是集電極或漏極開(kāi)路方式的,即具有線(xiàn)“與”功能。 I2C總線(xiàn)是一個(gè)半雙工、多主器件的總線(xiàn),即總線(xiàn)上可以連接多個(gè)可控制總線(xiàn)的器件。總線(xiàn)上發(fā)送數(shù)據(jù)的發(fā)送器(主器件)與接收數(shù)據(jù)的接收器(從器件)的角色不是一成不變的,而是取決于當(dāng)時(shí)數(shù)據(jù)傳送的方向。當(dāng)一個(gè)器件開(kāi)始一個(gè)總線(xiàn)周期,尋址其它器件并發(fā)送數(shù)據(jù)時(shí),它就是發(fā)送器,其他被尋址器件均作為接收器存在。 I2C總線(xiàn)進(jìn)行數(shù)據(jù)傳送時(shí),每一位數(shù)據(jù)都與時(shí)鐘脈沖相對(duì)應(yīng),在時(shí)鐘信號(hào)高電平期間,數(shù)據(jù)線(xiàn)上必須保持穩(wěn)定的邏輯電平。只有在時(shí)鐘線(xiàn)為低電平時(shí),才允許數(shù)據(jù)線(xiàn)的電平發(fā)生變化。 3. I2C總線(xiàn)的時(shí)序 一次完整的I2C總線(xiàn)時(shí)序過(guò)程由起始信號(hào)、從器件地址信號(hào)、應(yīng)答信號(hào)ACK、字節(jié)數(shù)據(jù)信號(hào)和停止信號(hào)等幾部分組成。 (1) 起始和停止信號(hào) 在一次通信過(guò)程中,應(yīng)該有一個(gè)起始信號(hào)和一個(gè)停止信號(hào)。在I2C總線(xiàn)協(xié)議中,起始信號(hào)(S)和停止信號(hào)(P)都是由主器件產(chǎn)生的。起始信號(hào)表明一次I2C總線(xiàn)傳送的開(kāi)始,停止信號(hào)則表明I2C總線(xiàn)通信結(jié)束。當(dāng)SCL線(xiàn)為高電平時(shí),SDA線(xiàn)由高電平到低電平的負(fù)跳變被定義為起始信號(hào),而SDA由低電平到高電平的正跳變?yōu)橥V剐盘?hào)。I2C總線(xiàn)的起始和停止信號(hào)時(shí)序如圖4-5所示。 file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image004.gif 圖4-5 I2C總線(xiàn)起始信號(hào)和停止信號(hào)的時(shí)序 當(dāng)總線(xiàn)上出現(xiàn)起始信號(hào)后,就認(rèn)為總線(xiàn)處于工作狀態(tài);總線(xiàn)上出現(xiàn)停止信號(hào),總線(xiàn)就被認(rèn)為是處在空閑狀態(tài)。如果連接到總線(xiàn)上的設(shè)備具有I2C的接口硬件,那么檢測(cè)起始和停止信號(hào)的過(guò)程將由硬件自動(dòng)完成。但是,如果微處理器沒(méi)有I2C硬件接口電路,則必須由軟件檢測(cè)電平的跳變判斷起始與停止信號(hào)。 (2) 器件地址 I2C總線(xiàn)上的每一個(gè)器件均有一個(gè)唯一的地址。每次發(fā)送器發(fā)出起始信號(hào)后,必須接著發(fā)出一個(gè)字節(jié)的地址信息,以選取連接在總線(xiàn)上的某一從機(jī)。地址字節(jié)用“從器件地址+ R/W(____)”表示。從器件地址是7bit的器件地址編碼,占用字節(jié)的高7位(D7~D1);D0位是數(shù)據(jù)的傳送方向位,又稱(chēng)讀/寫(xiě)選擇位,用R/W(____)表示,當(dāng)R/W(____)=0時(shí),表示主器件向從器件寫(xiě)數(shù)據(jù)(發(fā)送數(shù)據(jù));R/W(____)=1時(shí),表示主器件從從器件讀取數(shù)據(jù)(接收數(shù)據(jù))。 從器件地址由一個(gè)固定部分和一個(gè)可編程部分組成。固定部分為器件的標(biāo)識(shí),表明器件類(lèi)型,在出廠時(shí)設(shè)置。可編程部分為器件的地址,用以區(qū)分連接在同一I2C總線(xiàn)上的同類(lèi)器件。器件的地址由硬件接線(xiàn)而定,只有主器件送來(lái)的地址信息中的可編程部分和從器件的地址引腳狀態(tài)一致,該器件才會(huì)響應(yīng)總線(xiàn)的操作。例如E2PROM器件24C16的地址格式如下: 其中:高四位1010為E2PROM器件標(biāo)識(shí)類(lèi)型,A2~A0為引腳地址,對(duì)應(yīng)于該芯片引腳A2~A0的接線(xiàn),最低位為讀寫(xiě)選擇比特。當(dāng)A2~A0引腳均接低電平時(shí),該器件的地址為A0H或A1H,主器件訪(fǎng)問(wèn)地址0xA0表示寫(xiě)數(shù)據(jù)到該器件,訪(fǎng)問(wèn)0xA1表示從該器件讀數(shù)據(jù)。 (3) 應(yīng)答信號(hào)ACK I2C總線(xiàn)上的發(fā)送器發(fā)送完地址字節(jié)和每一個(gè)字節(jié)數(shù)據(jù)后,接收器都必須產(chǎn)生一個(gè)應(yīng)答信號(hào),應(yīng)答的器件在第9個(gè)時(shí)鐘周期時(shí)將SDA線(xiàn)拉低,表示已收到一個(gè)8位數(shù)據(jù)。 與應(yīng)答信號(hào)相對(duì)應(yīng)的第9個(gè)時(shí)鐘由發(fā)送器產(chǎn)生,發(fā)送器必須在輸出該時(shí)鐘時(shí)釋放數(shù)據(jù)線(xiàn)SDA,使其處于高阻狀態(tài),以便接收器在SDA線(xiàn)上輸出低電平應(yīng)答信號(hào)(ACK),表示繼續(xù)接收。若接收器輸出高電平則為非應(yīng)答信號(hào)(NO ACK),表示結(jié)束接收。 如果是主器件在接收數(shù)據(jù),例如當(dāng)從器件為存儲(chǔ)器,主器件讀從器件中的數(shù)據(jù)時(shí),它收到最后一個(gè)數(shù)據(jù)字節(jié)后,必須向從器件發(fā)送一個(gè)非應(yīng)答信號(hào)(NO ACK),使從器件釋放SDA線(xiàn),以便主器件產(chǎn)生終止信號(hào),停止數(shù)據(jù)傳送。 I2C總線(xiàn)的數(shù)據(jù)應(yīng)答時(shí)序如圖4-6所示。 file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image006.gif 圖4-6 I2C總線(xiàn)應(yīng)答時(shí)序 (4) 數(shù)據(jù)字節(jié)信號(hào) 利用I2C總線(xiàn)進(jìn)行數(shù)據(jù)傳送時(shí),傳送的字節(jié)數(shù)是沒(méi)有限制的,但是每一個(gè)字節(jié)必須保證是8位長(zhǎng)度,并且首先發(fā)送數(shù)據(jù)的最高位,每傳送一個(gè)字節(jié)數(shù)據(jù)后都必須跟隨一位應(yīng)答脈沖,即接收器發(fā)回的應(yīng)答信號(hào)ACK。然后由發(fā)送器繼續(xù)發(fā)送數(shù)據(jù)字節(jié)或發(fā)出停止信號(hào)P后結(jié)束數(shù)據(jù)的傳送。如果接收器不能接收下一個(gè)字節(jié),例如正在處理一個(gè)外部中斷,可以把SCL線(xiàn)拉成低電平,迫使發(fā)送器處于等待狀態(tài)。當(dāng)從機(jī)準(zhǔn)備好接收下一個(gè)字節(jié)時(shí)再釋放時(shí)鐘線(xiàn)SCL,使數(shù)據(jù)傳輸繼續(xù)進(jìn)行。連續(xù)發(fā)送多字節(jié)數(shù)據(jù)的格式如圖4-7所示。 file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image008.gif 圖4-7 I2C總線(xiàn)多字節(jié)操作時(shí)序 4. 單片機(jī)通過(guò)IO端口模擬I2C總線(xiàn)時(shí)序操作24Cxx系列E2PROM的接口程序 參考I2C總線(xiàn)的時(shí)序以及24Cxx芯片的數(shù)據(jù)手冊(cè),可設(shè)計(jì)出單片機(jī)通過(guò)IO端口模擬I2C總線(xiàn)時(shí)序控制24Cxx系列E2PROM的接口程序。程序分為24Cxx.H和24Cxx.C兩部分。24Cxx.H文件用來(lái)做函數(shù)說(shuō)明和常量定義等,可供最終的調(diào)用程序包含,24Cxx.C文件為各功能函數(shù)的具體實(shí)現(xiàn)。程序列表如下: 文件:24Cxx.H #ifndef __24Cxx__ #define __24Cxx__ #define ERRORCOUNT 10 enum E2PROMType{M2401,M2402,M2404,M2408,M2416,M2432,M2464,M24128,M24256}; void Delay(unsigned char); void I2CStart(void); void I2CStop(void); bit I2CRecAck(void); void I2CNoAck(void); void I2CAck(void); unsigned char I2CReceiveByte(void); void I2CSendByte(unsigned char); bit RWE2PROM(unsigned char *, unsigned char, unsignedint, unsigned char, enum E2PROMType); #endif 文件:24Cxx.C #include <reg51.h> #include <intrins.h> #include "24Cxx.h" sbit SDA =P1^0; sbit SCL =P1^1; // 模擬總線(xiàn)接口引腳,可根據(jù)實(shí)際電路修改 enum EEPROMTypeEepromType; /************************************************************************ 函數(shù)說(shuō)明:24C01~24C256共9種E2PROM的讀寫(xiě)操作函數(shù)。 參數(shù)說(shuō)明:讀寫(xiě)數(shù)據(jù)緩沖區(qū)指針,讀寫(xiě)的字節(jié)數(shù),E2PROM首址,EEPROM控制字節(jié),E2PROM類(lèi)型 ************************************************************************/ bit RWE2PROM( unsignedchar *Buf, unsignedchar Bytes, unsignedint Address, unsignedchar ControlByte, enumE2PROMType EepromType) { unsigned chardata j,i = ERRORCOUNT; bit errorflag= 1; while(i--) { I2CStart(); // 首先選擇器件及設(shè)置器件的內(nèi)部地址 I2CSendByte(ControlByte&0xfe); if(I2CRecAck())continue; if(EepromType>M2416) { I2CSendByte((unsignedchar)(Address>>8)); if(I2CRecAck())continue; } I2CSendByte((unsignedchar)Address); if(I2CRecAck())continue; if(!(ControlByte&0x01)) // 判斷進(jìn)行的是否是寫(xiě)操作 { // 是寫(xiě)操作 j =Bytes; errorflag= 0; // 清除錯(cuò)誤標(biāo)志 while(j--) { I2CSendByte(*Buf++); if(!I2CRecAck())continue; errorflag= 1; break; } if(errorflag==1)continue; break; } else // 是讀操作 { I2CStart(); I2CSendByte(ControlByte); if(I2CRecAck())continue; while(--Bytes) { *Buf++= I2CReceiveByte(); I2CAck(); } *Buf= I2CReceiveByte(); // 讀最后一個(gè)字節(jié) I2CNoAck(); errorflag= 0; break; } } I2CStop(); // 向總線(xiàn)送停止信號(hào) if(!(ControlByte&0x01)) // 判斷剛才進(jìn)行的是否為寫(xiě)操作 { Delay(255); // 如果是寫(xiě)操作,延時(shí)以等待E2PROM Delay(255); //完成擦寫(xiě)過(guò)程。具體延時(shí)長(zhǎng)度可參考 } // 數(shù)據(jù)手冊(cè)并根據(jù)實(shí)際情況加以調(diào)整。 return(errorflag); } /********************以下是模擬I2C總線(xiàn)操作時(shí)序的子程序********************/ // 啟動(dòng)I2C總線(xiàn) void I2CStart(void) { SCL=0; SDA=1;SCL=1; // 參見(jiàn)I2C總線(xiàn)的Start狀態(tài) _nop_(); SDA=0; // 少許延時(shí),等待總線(xiàn)信號(hào)穩(wěn)定 _nop_(); SCL=0; SDA=1; } // 停止I2C總線(xiàn) void I2CStop(void) { SCL=0; SDA=0;SCL=1; _nop_(); SDA=1; _nop_(); SCL=0; } // 檢查ACK信號(hào) bit I2CRecAck(void) { SCL=0; SDA=1;SCL=1; _nop_(); CY=SDA; // 結(jié)果放入進(jìn)位位:PSW的一位 SCL=0; return(CY); } // 在I2C總線(xiàn)上產(chǎn)生ACK信號(hào) void I2CACK(void) { SDA=0; SCL=1; _nop_(); SCL=0; _nop_(); SDA=1; } // 在I2C總線(xiàn)上產(chǎn)生NOACK信號(hào) void I2CNoAck(void) { SDA=1; SCL=1; _nop_(); SCL=0; } // 向I2C總線(xiàn)寫(xiě)數(shù)據(jù) void I2CSendByte(unsigned char sendbyte) { unsigned chardata j = 8; for(;j>0;j--) { SCL=0; sendbyte<<=1; SDA=CY; SCL=1; } SCL=0; } // 從I2C總線(xiàn)讀數(shù)據(jù) unsigned char I2CReceiveByte(void) { registerreceivebyte,i=8; SCL=0; while(i--) { SCL=1; //延時(shí)4.7us穩(wěn)定數(shù)據(jù) receivebyte=(receivebyte<<1)|SDA; SCL = 0; } return(receivebyte); } // 循環(huán)延時(shí)函數(shù) void Delay(unsigned char DelayCount) { while(DelayCount--); } 在實(shí)際項(xiàng)目中使用24Cxx系列E2PROM接口函數(shù)時(shí),主控程序只要包含24Cxx.H,并將24Cxx.C加入到工程中即可。 一、 實(shí)驗(yàn)過(guò)程 1. 電路連接 將CPU板上的單片機(jī)P1.0(J2或J6的1號(hào)引腳)和模擬總線(xiàn)接口IO板上24C16的SDA(J6的1號(hào)引腳)相連; 將CPU板上的單片機(jī)P1.1(J2或J6的2號(hào)引腳)和模擬總線(xiàn)接口IO板上24C16的SCL(J6的2號(hào)引腳)相連; 將CPU板上的COM1和PC機(jī)的串行口相連。 2. 程序設(shè)計(jì) 根據(jù)實(shí)驗(yàn)要求,設(shè)計(jì)實(shí)驗(yàn)代碼如下: #include <reg51.h> #include <stdio.h> #include <string.h> #include "24Cxx.h" #define OSC 11059200 #define BAUDRATE 9600 void main(void) { char Buf[32]; int i; bit bb; TMOD = 0x20; SCON = 0x50; PCON |= 0x80; TL1 =256-(OSC/12/16/BAUDRATE); TH1 =256-(OSC/12/16/BAUDRATE); TR1 = 1; TI = 1; printf("\r\nTesting24Cxx..."); bb =RW24XX(Buf,16,0,0xA1,M2416); if(bb == 0) { printf("\r\nReaded!\r\n"); for(i=0;i<16;i++)printf("%02bX ",Buf); printf("| "); for(i=0;i<16;i++)printf("%c",Buf); } elseprintf("\r\nRead failed!"); printf("\r\nEnterto write to 24Cxx..."); scanf("%s",Buf); bb =RW24XX(Buf,strlen(Buf),0,0xA0,M2416); if(bb == 0)printf("\r\nWrite success..."); elseprintf("\r\nWrite failed..."); while(1); } 3. 驗(yàn)證結(jié)果 按實(shí)驗(yàn)要求連接好電路,在Keil中建立新工程,將上述程序代碼加入工程,編譯鏈接后,將生成的HEX文件燒寫(xiě)到單片機(jī)中; 將PC機(jī)的串行口和單片機(jī)的串行口相連,打開(kāi)超級(jí)終端并設(shè)置波特率等參數(shù); 復(fù)位單片機(jī)即可接收到單片機(jī)輸出的24C16的數(shù)據(jù)。根據(jù)提示在PC機(jī)輸入數(shù)據(jù),按回車(chē)發(fā)送后可觀察到單片機(jī)寫(xiě)24C16的結(jié)果。如果寫(xiě)入正確,再次復(fù)位單片機(jī)后即可接收到單片機(jī)讀出的上次寫(xiě)入24C16的數(shù)據(jù)。
|