24c02简介
24C02是一个2Kbit的串行EEPROM存储芯片,可存储256个字节数据。工作电压范围为1.8V到6.0V,具有低功耗CMOS技术,自定时擦写周期,1000000次编程/擦除周期,可保存数据100年。24C02有一个16字节的页写缓冲器和一个写保护功能。通过I2C总线通讯读写芯片数据,通讯时钟频率可达400KHz。
可以通过存储IC的型号来计算芯片的存储容量是多大,比如24C02后面的02表示的是可存储2Kbit的数据,转换为字节的存储量为21024/8 = 256byte;有比如24C04后面的04表示的是可存储4Kbit的数据,转换为字节的储存量为41024/8 = 512byte;以此来类推其它型号的存储空间。
24C02的管脚图如下:
VCC和VSS是芯片的电源和地,电压的工作范围为:+1.8V~+6.0V。
A0、A1、A2是IC的地址选择脚。
WP是写保护使能脚。
SCL是I2C通讯时钟引脚。
SDA是I2C通讯数据引脚。
下图为芯片从地址:
以看出对于不同大小的24Cxx,具有不同的从器件地址。由于24C02为2k容量,也就是说只需要参考图中第一行的内容:
芯片的寻址:
AT24C设备地址为如下,前四位固定为1010,A2~A0为由管脚电平。AT24CXX EEPROM Board模块中默认为接地。A2-A0=000,最后一位表示读写操作。所以AT24Cxx的读地址为0xA1,写地址为0xA0。
也就是说如果是
写24C02的时候,从器件地址为10100000(0xA0);
读24C02的时候,从器件地址为10100001(0xA1)。
片内地址寻址:
芯片寻址可对内部256B中的任一个进行读/写操作,其寻址范围为00~FF,共256个寻址单位。
具体解释:
由于24C02只有256个字节的存储空间,所以只需要1个字节就可以寻址完24C02的存储空间,但是无法寻址完更大容量的存储IC,比如24C04的存储容量是512字节,需要9个bit的地址位才能寻址完。由上图可以看到,24C04的设备地址内是没有A0参数的,被a8代替了,这个a8就是24C04的第9个bit的地址位,也就是说24C04的A0引脚是不起作用的,这样也就造成了在I2C总线上只能同时挂载4个24C04芯片。其它存储器如24C08、24C16也可以这么类推。
24C02的WP引脚是写保护引脚,当WP引脚接高电平的时,24C02只能进行读取操作,不能进行写操作。只有当WP引脚悬空或接低电平时,24C02才能进行写操作。
IIC 主设备/从设备:
简单介绍一下主从机区分,对于新手来说老是区分不开主从机还是很有利的,
通常我们为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。
IIC主设备功能:主要产生时钟,产生起始信号和停止信号
IIC从设备功能:可编程的IIC地址检测,停止位检测
IIC的协议层
I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。
开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU 接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。
IIC 总线时序图:
起始条件:在SCL高电平期间,SDA由高到低的一个下降沿;
停止条件:在SCL高电平期间,SDA由低到高的一个上升沿;
IIC的数据有效性:
在SCL的高电平期间,SDA是不允许变化的;而只有在时钟线SCL的低电平期间,SDA才能够出现变化;
应答信号
每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,
应答信号:主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答
应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
**每发送一个字节(8个bit)**在一个字节传输的8个时钟后的第九个时钟期间,接收器接收数据后必须回一个ACK应答信号给发送器,这样才能进行数据传输。
应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答,
IIC发送数据
Start: IIC开始信号,表示开始传输。
DEVICE_ADDRESS:: 从设备地址,就是7位从机地址
R/W: W(write)为写,R(read)为读
ACK: 应答信号
WORD_ADDRESS : 从机中对应的寄存器地址 比方说访问 OLED中的 某个寄存器
DATA: 发送的数据
STOP: 停止信号。结束IIC
主机要向从机写数据时:
主机首先产生START信号
然后紧跟着发送一个从机地址,这个地址共有7位,紧接着的第8位是数据方 向位(R/W),0表示主机发送数据(写),1表示主机接收数据(读)
主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,若相同,则认为自己正在被主机寻址,根据R/T位将自己确定为发送器和接收器
这时候主机等待从机的应答信号(A)
当主机收到应答信号时,发送要访问从机的那个地址, 继续等待从机的应答信号
当主机收到应答信号时,发送N个字节的数据,继续等待从机的N次应答信号,
主机产生停止信号,结束传送过程。
IIC读数据:
主机要从从机读数据时
主机首先产生START信号
然后紧跟着发送一个从机地址,注意此时该地址的第8位为0,表明是向从机写命令,
这时候主机等待从机的应答信号(ACK)
当主机收到应答信号时,发送要访问的地址,继续等待从机的应答信号,
当主机收到应答信号后,主机要改变通信模式(主机将由发送变为接收,从机将由接收变为发送)所以主机重新发送一个开始start信号,然后紧跟着发送一个从机地址,注意此时该地址的第8位为1,表明将主机设 置成接收模式开始读取数据,
这时候主机等待从机的应答信号,当主机收到应答信号时,就可以接收1个字节的数据,当接收完成后,主机发送非应答信号,表示不在接收数据
主机进而产生停止信号,结束传送过程。
向AT24C02中写数据
操作时序:
MCU先发送一个开始信号(START)启动总线
接着跟上首字节,发送器件写操作地址(DEVICE ADDRESS)+写数据(0xA0)
等待应答信号(ACK)
发送数据的存储地址。24C02一共有256个字节的存储空间,地址从0x00~0xFF,想把数据存储>在哪个位置,此刻写的就是哪个地址。
发送要存储的数据第一字节、第二字节、…注意在写数据的过程中,E2PROM每个字节都会>回应一个“应答位0”,老告诉我们写E2PROM数据成功,如果没有回应答位,说明写入不成功。
发送结束信号(STOP)停止总线
注意:
在写数据的过程中,每成功写入一个字节,E2PROM存储空间的地址就会自动加1,当加到0xFF后,再写一个字节,地址就会溢出又变成0x00。
写数据的时候需要注意,E2PROM是先写到缓冲区,然后再“搬运到”到掉电非易失区。所以这个过程需要一定的时间,AT24C02这个过程是不超过5ms!
所以,当我们在写多个字节时,写入一个字节之后,再写入下一个字节之前,必须延时5ms才可以
从AT24C02中读数据
读当前地址的数据
2、读随机地址的数据
1、MCU先发送一个开始信号(START)启动总线
2、接着跟上首字节,发送器件写操作地址(DEVICE ADDRESS)+写数据(0xA0)
注意:这里写操作是为了要把所要读的数据的存储地址先写进去,告诉E2PROM要读取哪个地址的数据。
3、发送要读取内存的地址(WORD ADDRESS),通知E2PROM读取要哪个地址的信息。
4、重新发送开始信号(START)
5、发送设备读操作地址(DEVICE ADDRESS)对E2PROM进行读操作 (0xA1)
6、E2PROM会自动向主机发送数据,主机读取从器件发回的数据,在读一个字节后,MCU会回应一个应答信号(ACK)后,
E2PROM会继续传输下一个地址的数据,MCU不断回应应答信号可以不断读取内存的数据
7、如果不想读了,告诉E2PROM不想要数据了,就发送一个“非应答位NAK(1)”。发送结束信号(STOP)停止总线
3、连续读数据
E2PROM支持连续写操作,操作和单个字节类似,先发送设备写操作地址(DEVICE ADDRESS),然后发送内存起始地址(WORD ADDRESS),MCU会回应一个应答信号(ACK)后,E2PROM会继续传输下一个地址的数据,MCU不断回应应答信号可以不断读取内存的数据。E2PROM的地址指针会自动递增,数据会依次保存在内存中。不应答发送结束信号后终止传输。
/********************************************************** Function: IIC_SDA_HIGH(void) Description: SDA脚输出高电平 Input: none output: none Return: none **********************************************************/
void IIC_SDA_HIGH(void)
{
IIC_SDA_OUT();
GPIO_SetBits(GPIOB,I2C1_SDA);
}
/********************************************************** Function: S35390A_SDA_low(void) Description: SDA脚输出低电平 Input: none output: none Return: none **********************************************************/
void IIC_SDA_LOW(void)
{
IIC_SDA_OUT();
GPIO_ResetBits(GPIOB,I2C1_SDA);
}
/********************************************************** Function: IIC_SCL_HIGH(void) Description: SCL脚输出高电平 Input: none output: none Return: none **********************************************************/
void IIC_SCL_HIGH(void)
{
GPIO_SetBits(GPIOB,I2C1_SCL);
}
/********************************************************** Function: IIC_SCL_lOW(void) Description: SCL脚输出低电平 Input: none output: none Return: none **********************************************************/
void IIC_SCL_LOW(void)
{
GPIO_ResetBits(GPIOB,I2C1_SCL);
}
/********************************************************** Function: IIC_START(void) Description: I2C通讯启动标志 Input: none output: none Return: none **********************************************************/
void IIC_START(void)
{
IIC_SDA_OUT();
IIC_SDA_HIGH(); //高
delay_us(5);
IIC_SCL_HIGH(); //高
delay_us(5);
IIC_SDA_LOW(); //低
delay_us(5);
IIC_SCL_LOW(); //低
delay_us(5);
}
/********************************************************** Function: IIC_STOP(void) Description: I2C通讯结束标志 Input: none output: none Return: none **********************************************************/
void IIC_STOP(void)
{
IIC_SDA_OUT();
IIC_SCL_LOW();
IIC_SDA_LOW(); //低
delay_us(5);
IIC_SCL_HIGH();
delay_us(5);
IIC_SDA_HIGH(); //高
delay_us(5);
}
/********************************************************** Function: IIC_SDA_GET(void) Description: SDA脚电平监测 Input: none output: none Return: none **********************************************************/
uint8_t IIC_SDA_GET(void)
{
IIC_SDA_IN();
if(GPIO_ReadInputDataBit(GPIOB,I2C1_SDA))
return 1;
else
return 0;
}
/********************************************************** Function: IIC_GETACK(void) Description: 单片机获得ACK信号 Input: none output: none Return: temp **********************************************************/
int IIC_GETACK(void)
{
unsigned char z = 0;
IIC_SDA_IN();
IIC_SDA_HIGH();
delay_us(2);
IIC_SCL_HIGH();
delay_us(2);
while(IIC_SDA_GET())
{
z++;
if(z>=250) {
IIC_STOP();
return 1;
}
}
IIC_SCL_LOW();
return 0;
}
/********************************************************** Function: IIC_SETACK(void) Description: 向从机输出ACK信号 Input: none output: none Return: none **********************************************************/
void IIC_SETACK(void)
{
IIC_SCL_LOW();
IIC_SDA_OUT();
IIC_SDA_LOW();
delay_us(2);
IIC_SCL_HIGH();
delay_us(4);
IIC_SCL_LOW();
}
/********************************************************** Function: IIC_SETNCK(void) Description: 输出NCK信号 Input: none output: none Return: none **********************************************************/
void IIC_SETNCK(void)
{
IIC_SCL_LOW();
IIC_SDA_OUT();
IIC_SDA_HIGH();
delay_us(2);
IIC_SCL_HIGH();
delay_us(4);
IIC_SCL_LOW();
}
/********************************************************** Function: IIC_RECEIVE_BYTE(void) Description: 接收一个字节数据 Input: none output: none Return: none **********************************************************/
unsigned char IIC_RECEIVE_BYTE(void)
{
unsigned char IIC_buf = 0;
unsigned char w;
IIC_SDA_IN();
for (w = 0; w < 8; w++)
{
IIC_SCL_LOW();
delay_us(2);
IIC_SCL_HIGH();
IIC_buf<<=1;
if (IIC_SDA_GET())
{
IIC_buf++;
delay_us(2);
}
}
IIC_SCL_LOW();
return (IIC_buf);
}
/********************************************************** Function: IIC_SEND_BYTE(void) Description: 发送一个字节数据 Input: none output: none Return: none **********************************************************/
void IIC_SEND_BYTE(unsigned char senddata)
{
unsigned char w;
IIC_SDA_OUT();
IIC_SCL_LOW();
for (w = 0; w < 8; w++)
{
if (senddata & 0x80)
{
IIC_SDA_HIGH();
}
else
{
IIC_SDA_LOW();
}
senddata <<= 1;
delay_us(2);
IIC_SCL_HIGH();
delay_us(2);
IIC_SCL_LOW();
delay_us(2);
}
}
下面开始着重写24c02:
24C02有两种工作模式:(1)、字节写入模式:结合技术文档我认为该模式是这样工作的:首先是可以再任意的地址(0x00~0xFF)写入一个字节,也可以在某一地址连续的写入N字节,而且不需要翻页,从技术手册得知,答题时说字节写入模式下,页指针根写入数据的多少来自动增加实现翻页功能,不用自己在程序里边实现;
(2)、页写入模式:页写入模式下,手册上写着,一页可以存8字节,当存储的数据大于8时,则会覆盖先前保存的数据,例如,有16个数据 uchar data[16]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},从24C02的0x00地址开始存放,当存完一页(8个)时,第9个数据会保存在0x00,覆盖掉开始保存的1,后边的数据一次类推,这样的现象叫做“翻转”,如果想写完一页后将剩余的数据保存在下一页,页指针需要自己来设定。
用到的宏定义
#define MAX_SIZE 256
#define PAGES 32
#define PAGESIZE 8
#define BLOCK_SIZE 256
///***************************************************************************
// Function: I2C_EE_WaitEepromStandbyState()
// Description: 换页的时候需要一个等待时间,不然会有跳页的现象
// Input: BlockCode
// output: none
// Return: none
//***************************************************************************/
static void I2C_EE_WaitEepromStandbyState(u8 BlockCode)
{
int i = 50;
do
{
delay_us(100);
IIC_START();
IIC_SEND_BYTE(BlockCode );
} while(IIC_GETACK() == 0 && i-- > 0);
IIC_STOP();
}
///***************************************************************************
// Function: I2C_PageWrite()
// Description: 页内写入
// Input: 待写地址,块区间,写地址,几个字节
// output: none
// Return: none
//***************************************************************************/
bool I2C_PageWrite(u8* pBuffer, u8 BlockCode, u16 WriteAddr, u8 n)
{
u16 i;
IIC_START();
IIC_SEND_BYTE(BlockCode);
IIC_GETACK();
IIC_SEND_BYTE((u8)WriteAddr);
IIC_GETACK();
for(i = 0; i < n; i++)
{
IIC_SEND_BYTE(pBuffer[i]);
IIC_GETACK();
}
IIC_STOP();
I2C_EE_WaitEepromStandbyState(BlockCode);
return true;
}
///***************************************************************************
// Function: I2C_BlockWrite()
// Description: 在块区间内,计算页数,两头填补
// Input: 待写地址,块区间,写地址,几个字节
// output: none
// Return: none
//***************************************************************************/
bool I2C_BlockWrite(u8* pBlock, u8 BlockCode, u16 WriteAddr, u16 n)
{
u16 ByteOf1st = 0, ByteOfOther = 0; // 第一页和最后一页
u8 NumOfMPage = 0; //完整页数
ByteOf1st = PAGESIZE - WriteAddr %PAGESIZE;
if(n > ByteOf1st)
{
NumOfMPage = (n - ByteOf1st) / PAGESIZE;
ByteOfOther = (n - ByteOf1st) % PAGESIZE;
}
else
{
ByteOf1st = n;
NumOfMPage = 0;
ByteOfOther = 0;
}
if(I2C_PageWrite(pBlock, BlockCode, WriteAddr, ByteOf1st) == false)
{
return false;
}
WriteAddr += ByteOf1st;
pBlock += ByteOf1st;
while(NumOfMPage--)
{
delay_ms(10);
if(I2C_PageWrite(pBlock, BlockCode, WriteAddr, PAGESIZE) == false)
{
return false;
}
WriteAddr += PAGESIZE;
pBlock += PAGESIZE;
}
delay_ms(10);
if(I2C_PageWrite(pBlock, BlockCode, WriteAddr, ByteOfOther) == false)
{
return false;
}
return true;
}
///***************************************************************************
// Function: I2C_BufferWrite()
// Description: 指定地址写入指定个字节的数据,对外提供的函数
// Input: 写入数据起始地址,16位写地址,写入字节数
// output: none
// Return: none
//***************************************************************************/
bool I2C_BufferWrite(u8* pBuffer, u16 WriteAddr, u16 n)
{
u16 ByteOf1st = 0, ByteOfOther = 0; // 第一块和最后一块的字节数
u8 NumOfMBlock = 0; //需要写满数据的块
u8* pstr = pBuffer;
u8 BlockCode = 0; //起始器件地址
// u16 i = 0;
if(n + WriteAddr > MAX_SIZE || n + WriteAddr == 0) //para check
return false;
ByteOf1st = BLOCK_SIZE - WriteAddr % BLOCK_SIZE;
if(n > ByteOf1st)
{
NumOfMBlock = (n - ByteOf1st) / BLOCK_SIZE;
ByteOfOther = (n - ByteOf1st) % BLOCK_SIZE;
}
else
{
ByteOf1st = n;
NumOfMBlock = 0;
ByteOfOther = 0;
}
BlockCode = (u8)((WriteAddr/255 << 1) | 0xA0);
I2C_EE_WaitEepromStandbyState(BlockCode);
if(I2C_BlockWrite(pstr, BlockCode, WriteAddr, ByteOf1st) == false)
{
return false;
}
WriteAddr += ByteOf1st;
pstr += ByteOf1st;
while(NumOfMBlock--)
{
BlockCode += 0x02;
if(I2C_BlockWrite(pstr, BlockCode, WriteAddr, BLOCK_SIZE) == false)
{
return false;
}
WriteAddr += BLOCK_SIZE;
pstr += BLOCK_SIZE;
}
BlockCode += 0x02;
if(I2C_BlockWrite(pstr, BlockCode, WriteAddr, ByteOfOther) == false)
{
return false;
}
return true;
}
///***************************************************************************
// Function: I2C_BufferRead()
// Description: 指定地址读出指定个字节的数据,对外提供的函数
// Input: 写入数据起始地址,16位读地址,读出字节数
// output: none
// Return: none
//***************************************************************************/
bool I2C_BufferRead(u8* pBuffer, u16 ReadAddr, u16 n)
{
u16 i;
u8 BlockCode = 0;
BlockCode = (u8)((ReadAddr/BLOCK_SIZE << 1) | 0xA0);
I2C_EE_WaitEepromStandbyState(BlockCode);
if(n > MAX_SIZE)
{
return false;
}
IIC_START();
IIC_SEND_BYTE(BlockCode);
IIC_GETACK();
IIC_SEND_BYTE((u8)ReadAddr);
IIC_GETACK();
IIC_START();
IIC_SEND_BYTE(BlockCode | 0X01); //读地址
IIC_GETACK();
for(i = 0; i < n - 1; i++)
{
pBuffer[i]=IIC_RECEIVE_BYTE();
IIC_SETACK();
}
pBuffer[n - 1] = IIC_RECEIVE_BYTE();
IIC_SETNCK();
IIC_STOP();
return true;
}
u16 clean_reg[256]= {
0};
void ResetParam(void)
{
u8 j;
for(j=4; j<104; j++)
{
clean_reg[j] = 10;
}
for(j=104;j<204;j++)
{
clean_reg[j] = 1;
}
}
void clean_EEparam(void)
{
u8 i;
ResetParam();
for(i=0; i<204; i++)
{
I2C_BufferWrite((u8*)clean_reg+(i<<1),i,1);
delay_ms(1);
}
}
今天的文章存储芯片24c08_24c02a芯片参数分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/78865.html