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