新聞中心

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

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

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


評(píng)論


技術(shù)專區(qū)

關(guān)閉