新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設計應用 > 51單片機實現(xiàn)對24C02進行頁寫、順序讀取并顯示驗證

51單片機實現(xiàn)對24C02進行頁寫、順序讀取并顯示驗證

作者: 時間:2016-11-19 來源:網(wǎng)絡 收藏
//*************************************************************************************
//**程序名稱:51單片機實現(xiàn)對24C02進行頁寫、順序讀取顯示驗證//**編寫人:**** //**修改人:****//**程序目的:熟悉I2C總線協(xié)議,實現(xiàn)51模擬I2C時序和24C02通信//**功能描述:51單片機將8個字節(jié)數(shù)據(jù)寫入24C02的一頁中,然后順序讀出,每隔1秒送P0口LED顯示//**其他說明:本程序是采用某51開發(fā)板,若在其他地方驗證可更改相關端口及延時程序等。//** 程序編寫前曾參考過多個教程,最終自己編程通過,并詳加注釋。//** 可供初學者參考,并不對程序的可靠性等作保證。//**開發(fā)工具:keil 7.50 (C51) //**日期://*************************************************************************************#include #include     //因為用到_nop_();typedef unsigned char uchar;sbit SCL = P3^3;        //注意P1、P2、P3口有內(nèi)部上拉電阻,可直接連SDA和SCL,若想用P0需外接上拉電阻,否則連上無法輸出高電平!sbit SDA = P3^4;uchar j;                //用于計數(shù)50ms的個數(shù)的全局變量uchar code ToSDAdataBuffer[8] = {0xfe,0xfc,0xf8,0xf0,0xe0,0xc0,0x80,0x00}; //寫入24C02的一組數(shù)據(jù),8個字節(jié)對應24C02的一頁(共32頁),這里把這些要驗證的常數(shù)放到程序存儲區(qū)uchar ReceivedData[8];    //用于存儲接收的8個字節(jié)數(shù)據(jù)(1頁)的數(shù)組//本例51為單主機,24C02為從機,不需要總線裁決//延時5us子程序void delay5us(void){_nop_(); //時序圖要求開始建立時間tSU.STA大于4.7us,開始保持時間tHD.STA大于4us。51中每個_nop_();延時1個CPU cycle,即1us。_nop_(); //如考慮不同CPU頻率不同,可用帶參數(shù)的延時,參數(shù)在前面宏定義。_nop_();_nop_();_nop_();}//50ms定時器0中斷函數(shù)void timer0() interrupt 1         //j是個全局變量,不是返回值,所以這里還是void。{TH0 = (65536-46080)/256;    //11.0592MHz時每50ms一次定時器中斷TL0 = (65536-46080)%256;j++;                         //也可以把判斷j到20,并給P0口送顯示數(shù)據(jù)的程序放在中斷里處理}//延時1秒的子程序,用于將讀取的數(shù)據(jù)每隔一秒顯示在LED上void delay1s(void){j = 0;TMOD = 0x01;                 //方式1的16位計數(shù)器TH0 = (65536-46080)/256;TL0 = (65536-46080)%256;EA = 1;ET0 = 1;TR0 = 1;                     //啟動定時器0工作while(j < 20)                //j達到20之前空操作,達到20時說明已到1s,下面關中斷和定時器0;EA = 0;ET0 = 0;TR0 = 0;}//約2ms的延時void delay(uchar t){uchar x,y;for(x=0;xd address,即寫到哪個存儲單元(24C02有2kbits,所以數(shù)據(jù)字有2048/8=256個,故地址線有8位)if(!ChkAck()){for(i = 0; i < 8; i++){WriteI2CByte(ToSDAdataBuffer[i]);if(ChkAck()){//這里可添加錯誤處理代碼。如用幾個LED的亮滅組合表示此I2C器件有問題,類似主板錯誤提示。return 1;//一般返回1表示異常,且遇到return就退出整個子程序。}}StopI2C(); //寫完發(fā)送結束信號。return 0; //一般返回0表示程序正常}else{return 1; //之前可添加錯誤處理代碼。}}else{return 1;}}//不能用Current Address Read,因為那是24C02數(shù)據(jù)字地址計數(shù)器上次操作后加1的值;而SEQUENTIAL_READ如果不給一個要讀取的開始地址,會從頭輸出,//所以需要Random Read的開始部分,但不要停止信號。bit SequentialRead(uchar WordAddress){uchar i;StartI2C();WriteI2CByte(0xa0);if (!ChkAck()){WriteI2CByte(WordAddress);if (!ChkAck()){StartI2C();             //the microcontroller must generate another start conditionWriteI2CByte(0xa1);     //Device Address后緊跟的那一位R/W^是1說明是讀,24C02內(nèi)部就是根據(jù)最后這位來判斷是從SDA上讀數(shù),還是往SDA上送數(shù)//之所以設為1是讀,是因為根據(jù)WriteI2CByte子程序,最后給SDA賦1,P3^4就維持1,這樣24C02內(nèi)部Dout為高就將SDA拉低;//如果最后一位是0,24C02沒能力拉高!if (!ChkAck()){for(i = 0;i < 8;i++){ReceivedData[i] = ReadI2CByte();AckAsMaster(0); //51此時接收數(shù)據(jù),調(diào)用應答的函數(shù)(置SDA為0)}AckAsMaster(1);     //NO ACK.The microcontroller does not respond with a zero but doesgenerate a following stop condition.StopI2C();return 0;}else{return 1;             //之前可添加錯誤處理代碼。}}else{return 1;}}else{return 1;}}int main(void){uchar i;P0 = 0xff;InitI2C();//注意在24C02中用到的頁寫和順序讀的地址是同一個,且必須是8的整數(shù)倍,即每頁的首地址才行,如0x08,0x20等。因為24C02頁寫時后三位地址自動加1,//When the word address,internally generated, reaches the page boundary, the following byte is placed at the beginning of the same page.//而順序讀時只有在達到整個存儲區(qū)邊界時才會roll over。所以,如讀寫都用0x32這個地址,由于不是8的整數(shù)倍,只有前6個數(shù)顯示是正確的,最后兩個數(shù)//雖然又從頭寫在了該頁的前面,但SequentialRead確讀到了該頁之外的兩個存儲單元,造成錯誤。if (PageWrite(0x08,ToSDAdataBuffer) == 0) { //先執(zhí)行頁寫操作,設從地址00開始,沒問題就延遲一下再從同一地址讀回來。delay(100); //等待24C02頁寫操作完畢if(SequentialRead(0x08) == 0){ //如果順序讀操作成功,則每隔1秒送P0口顯示一個字節(jié)for(i = 0; i < 8; i++){P0 = ReceivedData[i];delay1s();}}}while(1);return 0;}//往I2C總線寫一個字節(jié)的數(shù)據(jù)(即將一個字節(jié)的數(shù)據(jù)發(fā)送到SDA上)void WriteI2CByte(uchar ByteData){uchar i,temp;temp = ByteData;// (StartI2C()最后已經(jīng)先將SCL變0了):for(i=0;i<8;i++){temp <<= 1;     //左移一位,I2C要求由MSB最高位開始,移出的CY即要發(fā)送到SDA上的數(shù)據(jù)。下面考慮時序:SDA = CY;       //此時SCL已為低,每次移一位送出去(下次進循環(huán)后SDA還保持著上次發(fā)出去的數(shù)據(jù))delay5us();     //SDA IN數(shù)據(jù)變化中點SCL上升沿中點的一段時間是tSU.DAT,即數(shù)據(jù)建立時間Data In Setup Time,需大于200ns,多延無所謂SCL = 1;delay5us();     //tHIGH即Clock Pulse Width High,最小4usSCL = 0;delay5us();     //tLOW即Clock Pulse Width Low,最小4.7us}}//讀取I2C總線一個字節(jié)的數(shù)據(jù)uchar ReadI2CByte()      //串行總線,51一位位接收從機發(fā)送到SDA上的數(shù)據(jù),這里只考慮數(shù)據(jù)已在SDA上時如何存下來這幾位,組成一個字節(jié){uchar i,ByteData;SDA = 1;             //SCL在ChkAck中已經(jīng)置0了。注意SCL時序仍然由主機控制!24C02只能將SDA由高拉低,象橡皮筋松手又恢復高,而下面只是讀SDA,沒賦值            //其實程序中多處給SDA置1都可省,因為檢查應答時為0就正常,無所謂,寫字節(jié)時也無所謂,就是在讀之前要保證SDA為1!            //因之前有WriteI2CByte(0xa1); 其實這句也可省略。delay5us();      //24C02作為發(fā)送方在第9個時鐘的negative edge clocks data out of each device,所以現(xiàn)在SDA上為新數(shù)據(jù)for(i=0;i<8;i++){SCL = 1;         //置時鐘線為高使數(shù)據(jù)線上數(shù)據(jù)有效delay5us();ByteData = (ByteData<<1)SDA; //SDA上已是新數(shù)據(jù)了,讀之。data不管以前多少,左移后最右邊為0,和SDA“按位或”后MLB就是SDASCL = 0;delay5us();}return ByteData;}


評論


技術專區(qū)

關閉