以下这篇文章是自己折腾了三天存储芯片后,并且能够成功进行字节操作、页操作的一篇应用型文章,不太想研究原理的可以直接采用本文的代码和注意事项。
在对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); }