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