標籤:
源:硬體和軟體相容i2c協議的24Cxx系列EEPROM儲存空間
硬體上由於24c01的A0A1A2管腳不允許懸空,故暫時的想法是相容24c02 ---24c16
使用一個dip8封裝的晶片插座,A0 A1 A2管腳都懸空即可,換晶片方便
軟體上24c02地址只有8位,而其他型號是大於8位的,故地址參數使用16位
256個位元組作為一個大頁,即largePage,測試晶片24c04空間有512位元組
上代碼,求測試和討論
#include "MY51.H"//轉載請註明:http://xouou.iteye.com 求測試討論//stc89c52rc,11.0592MHz晶振sbit sda=P2^0; //匯流排串連口定義sbit scl=P2^1; //匯流排串連口定義void delayus() //需要4個機器周期,大概4.34us{ ; //晶振頻率11.0592M,機器周期為1.085微秒}void iic_start() //啟動訊號{ sda=1; scl=1; delayus(); //sda和scl同為高電平保持4.7us以上 _nop_(); //1.085us,共5.78us sda=0; //下降沿 delayus(); //sda低電平保持4us以上 ,這裡是4.34us滿足要求}void iic_stop() //停止訊號{ sda=0;_nop_(); //準備狀態 scl=1; delayus(); //該狀態穩定時間要求保持4us以上 sda=1; //scl高電平期間,sda來一個上升沿 delayus(); //sda保持4.7us以上,4.34加上函數返回時間大於4.7us //注:此時scl和sda都為1 }void iic_sendByte(u8 byteData) //mcu發送一個位元組{ u8 i; u8 temp=byteData; for(i=0;i<8;i++) { temp=temp<<1; //移動後最高位到了PSW寄存器的CY位中 scl=0; //準備 _nop_(); //穩定一下 sda=CY; //將待發送的資料一位位的放到sda上 _nop_(); scl=1; //每一個高電平期間,ic器件都會將資料取走 _nop_(); } scl=0; //如果寫成scl=1;sda=1就是停止訊號,不能這麼寫 _nop_(); sda=1; //釋放匯流排,資料匯流排不用時要釋放 _nop_();}u8 iic_readByte() //讀一個位元組{ u8 i,temp; scl=0; //準備讀資料 _nop_(); sda=1; //釋放匯流排 _nop_(); for(i=0;i<8;i++) { scl=1; //mcu開始取資料 delayus(); //scl為高電平後,ic器件就會將1位元據送到sda上 //總共用時不會大於4.34us,然後就可以讓mcu讀sda了 temp=(temp<<1)|sda; //讀一位儲存到temp中 scl=0; delayus(); } return temp;}bool iic_checkACK() //處理應答訊號{ u8 errCounts=255; //定義逾時量為255次 scl=1; _nop_(); while(sda) //在一段時間內檢測到sda=0的話認為是應答訊號 { if(0==errCounts) { scl=0; //鉗住匯流排 _nop_(); return FALSE; //沒有應答訊號 } errCounts--; } scl=0; //鉗住匯流排,為下1次通訊做準備 _nop_(); return TRUE; //成功處理應答訊號}void iic_init() //匯流排初始化{ scl=1; sda=1; delayus();}void iic_sendACK(bool b_ACK) //發送應答或非應答訊號{ scl=0; //準備 _nop_(); if(b_ACK) //ACK 發送應該訊號 { sda=0; } else //unACK 發送非應答訊號 { sda=1; } _nop_(); scl=1; delayus(); //大於4us的延時 scl=0; //鉗住scl,以便繼續接收資料 _nop_();}void AT24Cxx_writeByte(u16 address,u8 dataByte)//向24cxx寫一位元組資料{ u8 largePage = address/256; //24c04是512位元組(定址範圍0~511),largePage最大值是1 u8 addressOffset = address%256; //largePage=0的話位址範圍是(0~255) iic_start(); iic_sendByte(0xa0|(largePage<<1));//控制字,前4位固定1010,後三位是器件地址,末位0是寫 iic_checkACK(); //mcu處理應答訊號 iic_sendByte(addressOffset); //指定要寫入的器件內地址在 largePage塊中的位移 iic_checkACK(); iic_sendByte(dataByte); //寫資料 iic_checkACK(); iic_stop(); delayms(2); //按位元組寫入時,24cxx在接收到停止訊號後將資料擦寫到內部,這需要時間 //並且在這段時間內不會響應匯流排上的任何請求,故讓mcu有2毫秒以上的等待 }void AT24Cxx_writeData(u16 address,u8 numBytes,u8* buf)//寫入任意長度資料(最大256位元組){ while(numBytes--) { AT24Cxx_writeByte(address++,*buf++); }}void AT24Cxx_readData(u16 beginAddr,u8 dataSize,u8* buf)//讀取任意長度位元組到緩衝區buf中{ u8 largePage = beginAddr/256; //計算largePage,256位元組為一大頁 u8 addressOffset = beginAddr%256; //計算相對於largePage的位移 iic_start(); //起始訊號 iic_sendByte(0xa0|(largePage<<1)); //控制字,寫 iic_checkACK(); //處理應答訊號 iic_sendByte(addressOffset); //要讀取的目標地址位移 iic_checkACK(); //處理應答訊號 iic_start(); //發送起始訊號 iic_sendByte(0xa1|(largePage<<1)); //控制字,讀 iic_checkACK(); //處理應答訊號 while(dataSize--) //讀取dataSize個位元組,最大256個位元組 { //dataSize用u16類型會暴掉ram的 *buf++=iic_readByte(); //讀取一個個位元組並儲存到緩衝區buf中 iic_sendACK(dataSize); //發送應答,當dataSize為0時mcu發送非應答 } iic_stop(); //發送停止訊號}void main()//測試{ u8 buf[3]; //接受資料的緩衝區 u8 arr[7]={0x06,1,2,3,4,0x55,0x33}; //待寫入的資料 iic_init(); //匯流排初始化 AT24Cxx_writeData(0x00+256,sizeof(arr),arr); //向指定地址處開始寫入7位元組的資料 P1=0xff; //調試代碼,用P1口的led顯示 delayms(1000); //調試代碼 AT24Cxx_readData(0x00+256,sizeof(buf),buf); //從指定地址開始讀3個位元組 P1=buf[2]; //也就是2 //led燈顯示數值 while(1) { P1=~P1; delayms(500); } }
//my51.h中主要用到#include <reg52.h>#include "mytype.h"void delayms(u16 ms) //軟延時函數{ u16 i,j; for(i=ms;i>0;i--) { for(j=113;j>0;j--) {} }}
對代碼進行了改進 去掉了在寫資料時的 delayms(2);這句軟延時代碼低效 ,而且沒有保障 改成加一個檢測函數 bool check_icWriteComplete() //檢測eeprom是否對內部擦寫完成 { iic_start(); iic_sendByte(0xa0); return iic_checkACK(); }
硬體和軟體相容i2c協議的24Cxx系列EEPROM儲存空間(轉)