一、AT24C02簡介
AT24C01/02/04/08/16...是一個1K/2K/4K/8K/16K位電可擦除PROM,內(nèi)部含有128/256/512/1024/2048個8位字節(jié),AT24C01有一個8字節(jié)頁寫緩沖器,AT24C02/04/08/16有一個16字節(jié)頁寫緩沖器。
電壓可允許低至1.8V,待機電流和工作電流分別為1uA和1mA。該器件通過I2C總線接口進行操作,這里就不再對IIC做詳細介紹了,具體可見外設(shè)系列OLED篇。
二、AT24C02引腳
AT24C02引腳定義
三、AT24C02尋址
使能芯片讀寫操作后,EEPROM都要求有8位的器件地址信息。
AT24C02地址信息
器件地址信息由"1"、"0"序列組成,前4位對于所有串行EEPROM都是一樣的。對于24C02/32/64,隨后3位A2、A1和A0為器件地址位,必須與硬件輸入引腳保持一致。
四、AT24C02讀/寫操作
4.1 AT24C02寫操作
寫操作要求主設(shè)備發(fā)送器件地址,收到應(yīng)答信號后,先接收8位的字地址。接收到這個地址后EEPROM應(yīng)答"0"(ACK),然后再是一個8位數(shù)據(jù)。在接收8位數(shù)據(jù)后,EEPROM應(yīng)答"0"(ACK),接著必須由主器件發(fā)送停止條件來終止寫序列。時序圖如下
寫操作時序圖
24C02器件按8字節(jié)/頁執(zhí)行頁寫,24C04/08/16器件按16字節(jié)/頁執(zhí)行頁寫,24C32/64器件按32字節(jié)/頁執(zhí)行頁寫。頁寫初始化與字節(jié)寫相同,只是主器件不會在第一個數(shù)據(jù)后發(fā)送停止條件,而是在EEPROMEEPROM收到每個數(shù)據(jù)后都應(yīng)答“0”。最后仍需由主器件發(fā)送停止條件,終止寫序列。
接收到每個數(shù)據(jù)后,字地址的低3位 (24C02) 或4位(24C04/08/16) 或5位(24C32/64)內(nèi)部自動加1,高位地址位不變,維持在當前頁內(nèi)。當內(nèi)部產(chǎn)生的字地址達到該頁邊界地址時,隨后的數(shù)據(jù)將寫入該頁的頁首。如果超過8個 (24C02) 或16個 (24C04/08/16) 或32個(24C32/64) 數(shù)據(jù)傳送給了EEPROM,字地址將回轉(zhuǎn)到該頁的首字節(jié),先前的字節(jié)將會被覆蓋。
4.2 AT24C02讀操作
AT24C02的讀操作有三種,分別是當前地址讀,隨機讀和順序讀。
- ? 當前地址讀 內(nèi)部地址計數(shù)器保存著上次訪問時最后一個地址加1的值。只要芯片有電,該地址就一直保存當讀到最后頁的最后字節(jié),地址會回轉(zhuǎn)到0。當寫到某頁尾的最后一個字節(jié),地址會回轉(zhuǎn)到該頁的首字節(jié)。接收器件地址(讀/寫選擇位為"1") 且EEPROM應(yīng)答ACK后,當前地址的數(shù)據(jù)就隨時鐘送出。主器件無需應(yīng)答"0",但需發(fā)送停止條件。當前地址讀操作時序圖如下
讀當前地址時序圖
- ? 隨機讀 隨機讀需先寫一個目標字地址,一旦EEPROM接收器件地址和字地址并應(yīng)答了ACK,主器件就產(chǎn)生一個重復(fù)的起始條件。然后,主器件發(fā)送器件地址(讀/寫選擇位為"1") ,EEPROM應(yīng)答ACK,并隨時鐘送出數(shù)據(jù)。主器件無需應(yīng)答"0",但需發(fā)送停止條件。
這里的隨機讀就是讀取任意一個字地址的數(shù)據(jù),并不是隨即返回一個數(shù)據(jù)的意思。
隨機讀時序圖如下
隨機讀時序圖
- ? 順序讀 順序讀可以通過“當前地址讀”或“隨機讀”啟動。主器件接收到一個數(shù)據(jù)后,應(yīng)答ACK。只要EEPROM接收到ACK,將自動增加字地址并繼續(xù)隨時鐘發(fā)送后面的數(shù)據(jù)。若達到存儲器地址末尾,地址自動回轉(zhuǎn)到0,仍可繼續(xù)順序讀取數(shù)據(jù)。主器件不應(yīng)答"0",而發(fā)送停止條件,即可結(jié)束順序讀操作。順序讀時序圖如下
順序讀時序圖
五、AT24C02程序
這里給出一個AT24C02的程序,僅供參考
/*******************************************************************************
* 函 數(shù) 名 : IIC_Init
* 函數(shù)功能 : IIC初始化
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(IIC_SCL_PORT_RCC|IIC_SDA_PORT_RCC,ENABLE);
GPIO_InitStructure.GPIO_Pin=IIC_SCL_PIN;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_Init(IIC_SCL_PORT,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN;
GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
IIC_SCL=1;
IIC_SDA=1;
}
/*******************************************************************************
* 函 數(shù) 名 : SDA_OUT
* 函數(shù)功能 : SDA輸出配置
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
}
/*******************************************************************************
* 函 數(shù) 名 : SDA_IN
* 函數(shù)功能 : SDA輸入配置
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
}
/*******************************************************************************
* 函 數(shù) 名 : IIC_Start
* 函數(shù)功能 : 產(chǎn)生IIC起始信號
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void IIC_Start(void)
{
SDA_OUT(); //sda線輸出
IIC_SDA=1;
IIC_SCL=1;
delay_us(5);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(6);
IIC_SCL=0;//鉗住I2C總線,準備發(fā)送或接收數(shù)據(jù)
}
/*******************************************************************************
* 函 數(shù) 名 : IIC_Stop
* 函數(shù)功能 : 產(chǎn)生IIC停止信號
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void IIC_Stop(void)
{
SDA_OUT();//sda線輸出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
IIC_SCL=1;
delay_us(6);
IIC_SDA=1;//發(fā)送I2C總線結(jié)束信號
delay_us(6);
}
/*******************************************************************************
* 函 數(shù) 名 : IIC_Wait_Ack
* 函數(shù)功能 : 等待應(yīng)答信號到來
* 輸 入 : 無
* 輸 出 : 1,接收應(yīng)答失敗
0,接收應(yīng)答成功
*******************************************************************************/
u8 IIC_Wait_Ack(void)
{
u8 tempTime=0;
SDA_IN(); //SDA設(shè)置為輸入
IIC_SDA=1;
delay_us(1);
IIC_SCL=1;
delay_us(1);
while(READ_SDA)
{
tempTime++;
if(tempTime >250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//時鐘輸出0
return 0;
}
/*******************************************************************************
* 函 數(shù) 名 : IIC_Ack
* 函數(shù)功能 : 產(chǎn)生ACK應(yīng)答
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(5);
IIC_SCL=0;
}
/*******************************************************************************
* 函 數(shù) 名 : IIC_NAck
* 函數(shù)功能 : 產(chǎn)生NACK非應(yīng)答
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(5);
IIC_SCL=0;
}
/*******************************************************************************
* 函 數(shù) 名 : IIC_Send_Byte
* 函數(shù)功能 : IIC發(fā)送一個字節(jié)
* 輸 入 : txd:發(fā)送一個字節(jié)
* 輸 出 : 無
*******************************************************************************/
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低時鐘開始數(shù)據(jù)傳輸
for(t=0;t< 8;t++)
{
if((txd&0x80) >0) //0x80 1000 0000
IIC_SDA=1;
else
IIC_SDA=0;
txd< <=1;
delay_us(2); //對TEA5767這三個延時都是必須的
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
/*******************************************************************************
* 函 數(shù) 名 : IIC_Read_Byte
* 函數(shù)功能 : IIC讀一個字節(jié)
* 輸 入 : ack=1時,發(fā)送ACK,ack=0,發(fā)送nACK
* 輸 出 : 應(yīng)答或非應(yīng)答
*******************************************************************************/
u8 IIC_Read_Byte(u8 ack)
{
u8 i,receive=0;
SDA_IN();//SDA設(shè)置為輸入
for(i=0;i< 8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive< <=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//發(fā)送nACK
else
IIC_Ack(); //發(fā)送ACK
return receive;
}
/*******************************************************************************
* 函 數(shù) 名 : AT24CXX_Init
* 函數(shù)功能 : AT24CXX初始化
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void AT24CXX_Init(void)
{
IIC_Init();//IIC初始化
}
/*******************************************************************************
* 函 數(shù) 名 : AT24CXX_ReadOneByte
* 函數(shù)功能 : 在AT24CXX指定地址讀出一個數(shù)據(jù)
* 輸 入 : ReadAddr:開始讀數(shù)的地址
* 輸 出 : 讀到的數(shù)據(jù)
*******************************************************************************/
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{
u8 temp=0;
IIC_Start();
if(EE_TYPE >AT24C16)
{
IIC_Send_Byte(0XA0); //發(fā)送寫命令
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr >>8);//發(fā)送高地址
}
else
{
IIC_Send_Byte(0XA0+((ReadAddr/256)< 1)); //發(fā)送器件地址0XA0,寫數(shù)據(jù)
}
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr%256); //發(fā)送低地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0XA1); //進入接收模式
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop();//產(chǎn)生一個停止條件
return temp;
}
/*******************************************************************************
* 函 數(shù) 名 : AT24CXX_WriteOneByte
* 函數(shù)功能 : 在AT24CXX指定地址寫入一個數(shù)據(jù)
* 輸 入 : WriteAddr :寫入數(shù)據(jù)的目的地址
DataToWrite:要寫入的數(shù)據(jù)
* 輸 出 : 無
*******************************************************************************/
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{
IIC_Start();
if(EE_TYPE >AT24C16)
{
IIC_Send_Byte(0XA0); //發(fā)送寫命令
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr >>8);//發(fā)送高地址
}
else
{
IIC_Send_Byte(0XA0+((WriteAddr/256)< 1)); //發(fā)送器件地址0XA0,寫數(shù)據(jù)
}
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr%256); //發(fā)送低地址
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite); //發(fā)送字節(jié)
IIC_Wait_Ack();
IIC_Stop();//產(chǎn)生一個停止條件
delay_ms(10);
}
/*******************************************************************************
* 函 數(shù) 名 : AT24CXX_WriteLenByte
* 函數(shù)功能 : 在AT24CXX里面的指定地址開始寫入長度為Len的數(shù)據(jù)
用于寫入16bit或者32bit的數(shù)據(jù)
* 輸 入 : WriteAddr :寫入數(shù)據(jù)的目的地址
DataToWrite:要寫入的數(shù)據(jù)
Len :要寫入數(shù)據(jù)的長度2,4
* 輸 出 : 無
*******************************************************************************/
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
{
u8 t;
for(t=0;t< Len;t++)
{
AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite > >(8*t))&0xff);
}
}
/*******************************************************************************
* 函 數(shù) 名 : AT24CXX_ReadLenByte
* 函數(shù)功能 : 在AT24CXX里面的指定地址開始讀出長度為Len的數(shù)據(jù)
用于讀出16bit或者32bit的數(shù)據(jù)
* 輸 入 : ReadAddr :開始讀出的地址
Len :要讀出數(shù)據(jù)的長度2,4
* 輸 出 : 讀取的數(shù)據(jù)
*******************************************************************************/
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)
{
u8 t;
u32 temp=0;
for(t=0;t< Len;t++)
{
temp< <=8;
temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);
}
return temp;
}
/*******************************************************************************
- 函 數(shù) 名 : AT24CXX_Check
- 函數(shù)功能 : 檢查AT24CXX是否正常
- 輸 入 : 無
- 輸 出 : 1:檢測失敗,0:檢測成功
*******************************************************************************/
u8 AT24CXX_Check(void)
{
u8 temp;
temp=AT24CXX_ReadOneByte(255);//避免每次開機都寫AT24CXX
if(temp==0x36)return 0;
else//排除第一次初始化的情況
{
AT24CXX_WriteOneByte(255,0X36);
temp=AT24CXX_ReadOneByte(255);
if(temp==0X36)return 0;
}
return 1;
}
/*******************************************************************************
- 函 數(shù) 名 : AT24CXX_Read
- 函數(shù)功能 : 在AT24CXX里面的指定地址開始讀出指定個數(shù)的數(shù)據(jù)
- 輸 入 : ReadAddr :開始讀出的地址 對24c02為0~255
pBuffer :數(shù)據(jù)數(shù)組首地址
NumToRead:要讀出數(shù)據(jù)的個數(shù) - 輸 出 : 無
*******************************************************************************/
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
while(NumToRead)
{
*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
NumToRead--;
}
}
/*******************************************************************************
- 函 數(shù) 名 : AT24CXX_Write
- 函數(shù)功能 : 在AT24CXX里面的指定地址開始寫入指定個數(shù)的數(shù)據(jù)
- 輸 入 : WriteAddr :開始寫入的地址 對24c02為0~255
pBuffer :數(shù)據(jù)數(shù)組首地址
NumToRead:要讀出數(shù)據(jù)的個數(shù) - 輸 出 : 無
*******************************************************************************/
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
while(NumToWrite--)
{
AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
WriteAddr++;
pBuffer++;
}
}
.h文件如下
// 核心板使用的是24c02,所以定義EE_TYPE為AT24C02
// 可修改成AT24CXX系列中的任意一個
#define EE_TYPE AT24C02
// IIC函數(shù)
void IIC_Init(void); // 初始化IIC的IO口
void IIC_Start(void); // 發(fā)送IIC開始信號
void IIC_Stop(void); // 發(fā)送IIC停止信號
void IIC_Send_Byte(u8 txd); // IIC發(fā)送一個字節(jié)
u8 IIC_Read_Byte(u8 ack); // IIC讀取一個字節(jié)
u8 IIC_Wait_Ack(void); // IIC等待ACK信號
void IIC_Ack(void); // IIC發(fā)送ACK信號
void IIC_NAck(void); // IIC不發(fā)送ACK信號
u8 AT24CXX_ReadOneByte(u16 ReadAddr); //指定地址讀取一個字節(jié)
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite); // 指定地址寫入一個字節(jié)
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len); // 指定地址開始寫入指定長度的數(shù)據(jù)
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len); // 指定地址開始讀取指定長度數(shù)據(jù)
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite); // 從指定地址開始寫入指定長度的數(shù)據(jù)
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead); // 從指定地址開始讀出指定長度的數(shù)據(jù)
u8 AT24CXX_Check(void); // 檢查器件
void AT24CXX_Init(void); // 初始化IIC
六、應(yīng)用實例
給AT24C02寫入一個數(shù)據(jù),讀取一次確認寫入正常。注釋掉寫入程序,拔掉電源。一段時間后,插上電源再次讀取之前寫入時的地址的值,串口打印結(jié)果。AT24C02的初始化程序如下
AT24CXX_Init(); // AT24C02初始化
while(AT24CXX_Check()) //檢測AT24C02是否正常
{
printf("AT24C02檢測不正常!rn");
delay_ms(500);
}
printf("AT24C02檢測正常!rn");
main函數(shù)如下
u8 gWData = 0xaa; // 準備要寫入的數(shù)據(jù)
u8 gRData = 0xaa; // 存儲讀出的數(shù)據(jù)
int main(void)
{
Med_Mcu_Iint(); // 系統(tǒng)初始化
AT24CXX_WriteOneByte(0,gWData);
printf("寫入的數(shù)據(jù)是:%drn",gWData);
gRData = AT24CXX_ReadOneByte(0);
printf("讀取的數(shù)據(jù)是:%drn",gRData);
while(1)
{
}
}
串口打印結(jié)果如下
串口打印結(jié)果
七、拓展應(yīng)用
AT24C02這種掉電數(shù)據(jù)不丟失的特性,使得它可以存儲一些重要數(shù)據(jù)。比如將一些校準數(shù)據(jù)寫入AT24C02中,再次上電之后就不會丟失?;蛘哂肁T24C02記錄開機次數(shù)等。這些原理與應(yīng)用實例中的例子原理相同,這里就不再贅述了。
-
緩沖器
+關(guān)注
關(guān)注
6文章
1922瀏覽量
45479 -
EEPROM
+關(guān)注
關(guān)注
9文章
1020瀏覽量
81587 -
ACK
+關(guān)注
關(guān)注
0文章
28瀏覽量
11146 -
I2C總線
+關(guān)注
關(guān)注
8文章
390瀏覽量
60931 -
AT24C02
+關(guān)注
關(guān)注
0文章
79瀏覽量
24297
發(fā)布評論請先 登錄
相關(guān)推薦
評論