以下這篇文章是自己折騰了三天存儲芯片後,並且能夠成功進行字節操作、頁操作的一篇應用型文章,不太想研究原理的可以直接採用本文的代碼和注意事項。
在對24C02或者24C16進行字節、頁讀取、頁存取前,我們需要弄清楚I²C協議、24C02/24C16存儲空間、字節操作和頁操作的時序
- I²C協議:I²C是什麼協議網上進行百度方可知道,以下只寫使用代碼
void i2cStart(void) //開始時序
{
SDA=1;
SCL=1;
_delay(2); //延時2μs
SDA=0;
_delay(2);
SCL=0;
}
void i2cStop(void) //結束時序
{
SDA=0;
_delay(2);
SCL=1;
_delay(2);
SDA=1;
}
void i2cAck_MCU(void) //應答時序
{
SDA=0;
_delay(2);
SCL=1;
_delay(2);
SCL=0;
_delay(2);
SDA=1;
_delay(4);
}
void i2cNoAck_MCU(void) //非應答時序
{
SDA=1;
_delay(2);
SCL=1;
_delay(2);
SCL=0;
_delay(2);
}
- 24C02/24C16存儲空間大小
24C02就是有2Kbit = 2000/8 = 250個字節,24C16就是有16kbit = 2000個字節,24C02一頁最多可以寫入8個字節,因此可以分成32頁,24C16一頁最多可以寫入16個字節,因此可以分成128頁。
- 字節寫操作
void WriteByte_24LC16B(unsigned char input) { unsigned char bit_count; for(bit_count=8;bit_count!=0;bit_count--) { _delay(2); SDA=(_Bool)(input&0x80); _delay(2); SCL=1; _delay(2); SCL=0; input=input<<1; _delay(2); } SDA=1; SDA_CTRL=1; //這裏需要看自己使用的芯片設置SDA的IO口爲輸入狀態,我用的是HT45F67芯片,SDA端口設置爲輸入狀態,用於判斷SDA是否接到主機的應答信號 _delay(2); SCL=1; _delay(2); if(SDA == 1) ack = 0; else ack = 1; SCL=0; SDA_CTRL=0; //此處根據自己所使用的芯片將SDA所在的Io口設置爲輸出狀態 } void Write_24LC16B(unsigned char Wdata,unsigned int RomAddress) { unsigned char block; WriteDeviceAddress=0B10100000; block=RomAddress/256; RomAddress=RomAddress%256; WriteDeviceAddress=WriteDeviceAddress|(block<<1); i2cStart(); WriteByte_24LC16B(WriteDeviceAddress); WriteByte_24LC16B((unsigned char)RomAddress); WriteByte_24LC16B(Wdata); i2cStop(); _delay(5500); }
字節讀操作
-
unsigned char ReadByte_24LC16B() { unsigned char bit_count,rbyte=0; SDA=1; SDA_CTRL=1; _delay(10); for(bit_count=8;bit_count!=0;bit_count--) { rbyte=rbyte<<1; _delay(2); SCL=1; rbyte=rbyte|((unsigned char)(SDA)); _delay(2); SCL=0; _delay(2); } SDA_CTRL=0; return(rbyte); } unsigned char Read_24LC16B(unsigned int RomAddress) { unsigned char output,block; ReadDeviceAddress=0B10100001; WriteDeviceAddress=0B10100000; block=RomAddress/256; RomAddress=RomAddress%256; WriteDeviceAddress=WriteDeviceAddress|(block<<1); ReadDeviceAddress=ReadDeviceAddress|(block<<1); i2cStart(); WriteByte_24LC16B(WriteDeviceAddress); WriteByte_24LC16B((unsigned char)RomAddress); i2cStart(); WriteByte_24LC16B(ReadDeviceAddress); output=ReadByte_24LC16B(); i2cNoAck_MCU(); i2cStop(); _delay(2000); return(output); }
頁寫操作
Wdata爲輸入數組的首地址,RomAddress爲需要進行存儲的地址,範圍在0~2047之間,cnt爲一次需要寫入的字節個數,建議採用8的倍數的cnt,因爲本函數不採用自動分頁,不是8的倍數會在超出頁寫入最多的字節數之後覆蓋掉原來的數。 void WritePage_24LC16B(unsigned char *Wdata,unsigned int RomAddress,unsigned char cnt) { unsigned char block; WriteDeviceAddress=0B10100000; block=RomAddress/256; RomAddress=RomAddress%256; WriteDeviceAddress=WriteDeviceAddress|(block<<1); i2cStart(); WriteByte_24LC16B(WriteDeviceAddress); WriteByte_24LC16B((unsigned char)RomAddress); while(cnt--) { WriteByte_24LC16B(*Wdata++); } i2cStop(); } 注意:連續進行多頁寫操作,需要在WritePage_24LC16B函數後添加150μs以上的延遲,這段時間,24C02內部需要將數據存儲到芯片內部。 例子: D_buffer[8] = {1,2,3,4,5,6,7,8}; WritePage_24LC16B(D_buffer,0,8); _delay(150); //延遲150μs以及以上 WritePage_24LC16B(D_buffer,8,8);
-
頁讀操作
-
void ReadPage_24LC16B(unsigned char *Rdata,unsigned int RomAddress,unsigned char cnt) { unsigned char block; ReadDeviceAddress=0B10100001; WriteDeviceAddress=0B10100000; block=RomAddress/256; RomAddress=RomAddress%256; WriteDeviceAddress=WriteDeviceAddress|(block<<1); ReadDeviceAddress=ReadDeviceAddress|(block<<1); i2cStart(); WriteByte_24LC16B(WriteDeviceAddress); WriteByte_24LC16B((unsigned char)RomAddress); i2cStart(); WriteByte_24LC16B(ReadDeviceAddress); while(cnt>1) { *Rdata++ = ReadByte_24LC16B(); cnt--; i2cAck_MCU(); //發送完讀地址後,需要應答一下 } *Rdata = ReadByte_24LC16B(); i2cNoAck_MCU(); //讀取最後一個字節需要非應答 i2cStop(); _delay(2000); }