4.6 EEPROM存储芯片(AT24C02)
4.6.1 原理图介绍
图4-6-1
图4-6-2
实验板上的EEPROM型号是AT24C02N,通信接口是IIC,接在单片机的P2.1(SCL)和P2.2(SDA) 口上。
AT24C02是一颗2kbit(256字节)的EEPROM。
实验板的原理图上描述是24C16,实际实验板上使用的具体芯片型号是AT24C02。
4.6.2 AT24C02介绍
AT24C02是一个2K位串行EEPROM,内部含有256个字节空间。AT24C02内部有一个8字节页写缓冲器。该器件通过IIC总线接口进行操作,有一个专门的写保护功能。
I2C总线是由 PHILIPS(飞利浦) 公司开发的两线式串行总线,用于连接微控制器及其外围设备。是微电子通信控制领域广泛采用的一种总线标准。它是同步通信的一种特殊形式,具有接口线少,控制方式简单,器件封装形式小,通信速率较高等优点(这个速率高指的是和它同时代的通信总线)。
特性:
(1)、传送速率为400KHz和IIC总线兼容
(2)、2.7V至7V的工作电压
(3)、8字节页写缓冲区
(4)、100万次擦写周期
(5)、数据保存可达100年
图4-6-3 AT24C02封装图与引脚功能介绍
管脚上的A0、A1、A2是地址配置引脚,以组成000~111八种情况,通过器件地址输入端A0、A1和A2可以实现将最多8个AT24C02器件连接到总线上。 SDA是IIC总线的数据线,SCL是IIC总线的时钟线。
AT24C02内部的设备地址是:1010 ,下面图片里2K就是AT2C02的对应地址。
图4-6-4 AT24C02设备地址
AT24C02的一个写周期时间为10ms,单片机向AT24C02传输数据时,AT24C02是将数据暂存在8字节的缓冲区里的,单片机发送完数据之后需要等待10ms的时间,等待AT24C02将缓冲区的数据存到内部EEPROM里,存放到EEPROM之后,掉电才不会丢失。
图4-6-5 写周期时间
4.6.3 AT24C02时序介绍
(1) 总线时序图
图4-6-6 总线时序
(2) 起始信号与停止信号时序图
图4-6-7 起始信号与停止信号时序
当时钟线为高电平的时候,数据线由高电平变为低电平的过程。
IIC_SCL=1;
IIC_SDA=1;
IIC_SDA=0;
IIC_SCL=0; //总线开始工作、开始读写数据
当时钟线为高电平的时候,数据线由低电平变为高电平的过程。
IIC_SCL=0;
IIC_SDA=0;
IIC_SCL=1;
IIC_SDA=1;
(3) 应答信号时序图
图4-6-8 应答时序图
有效应答信号是低电平。
(4) 数据位的传输时序图
图4-6-9 数据的有效性
输出传输从高位开始。
/*函数功能: 发送一个字节数据*/
void IIC_SendOneByte(u8 dat)
{
u8 j=0,i=0;
for(j=0;j<8;j++)
{
IIC_SCL=0;
if(dat&0x80)IIC_SDA=1;
else IIC_SDA=0;
IIC_SCL=1;
dat<<=1;
}
IIC_SCL=0;
}
(5) 向AT24C02写一个字节的总时序
图4-6-10
(6) 向AT24C02写一页数据的总时序
图4-6-11
(7) 从AT24C02当前地址读取一个字节的总时序
图4-6-12
(8). 从AT24C02指定位置读取指定长度数据的总时序
图4-6-13
4.6.4 AT24C02读写数据示例代码
下面代码演示了AT24C02写入数据和读取数据的过程,在主函数里,调用AT24C02写字节函数向指定空间存入数据,再调用读函数读出来,打印到串口终端。
(硬件平台说明:CPU是STC90C516RD 、晶振频率12MHZ 、工作在12T模式下、一个机器周期为1us时间)
示例代码:
#include <reg51.h>
/*
说明: 在12MHZ晶振下,12T模式下,i++消耗的时间差不多是12us
*/
/*
硬件连接:
SCL---P2.1
SDA---P2.0
*/
//数据线
sbit IIC_SDA=P2^0;
//时钟线
sbit IIC_SCL=P2^1;
/*
函数功能: 发送起始信号
当时钟线为高电平的时候,数据线由高电平变为低电平的过程
*/
void IIC_SendStart(void)
{
u8 i=0;
IIC_SCL=1;
IIC_SDA=1;
IIC_SDA=0;
IIC_SCL=0; //总线开始工作、开始读写数据
}
/*
函数功能: 停止信号
当时钟线为高电平的时候,数据线由低电平变为高电平的过程
*/
void IIC_SendStop(void)
{
u8 i=0;
IIC_SCL=0;
IIC_SDA=0;
IIC_SCL=1;
IIC_SDA=1;
}
/*
函数功能: 获取应答信号
返 回 值: 0表示获取到应答 1表示没有获取到应答
*/
u8 IIC_GetAck(void)
{
u8 i=0;
IIC_SDA=1; //上拉
IIC_SCL=0;
IIC_SCL=1;
while(IIC_SDA)
{
i++;
if(i>250)return 1; //获取不到应答
}
IIC_SCL=0;
return 0;
}
/*
函数功能: 发送应答信号
函数参数:0表示应答 1表示非应答
*/
void IIC_SendAck(u8 ack)
{
u8 i=0;
IIC_SCL=0;
if(ack)IIC_SDA=1; //发送非应答
else IIC_SDA=0; //发送应答
IIC_SCL=1;
IIC_SCL=0;
}
/*
函数功能: 发送一个字节数据
*/
void IIC_SendOneByte(u8 dat)
{
u8 j=0,i=0;
for(j=0;j<8;j++)
{
IIC_SCL=0;
if(dat&0x80)IIC_SDA=1;
else IIC_SDA=0;
IIC_SCL=1;
dat<<=1;
}
IIC_SCL=0;
}
/*
函数功能: 接收一个字节数据
*/
u8 IIC_RecvOneByte(void)
{
u8 i=0,j=0;
u8 dat=0;
for(j=0;j<8;j++)
{
IIC_SCL=0;
IIC_SCL=1;
dat<<=1; //表示默认收到0
if(IIC_SDA)dat|=0x01;
}
IIC_SCL=0;
return dat;
}
/*
函数功能: 写一个字节
函数参数:
u8 addr 数据的位置(0~255)
u8 dat 数据范围(0~255)
*/
void AT24C02_WriteOneByte(u16 addr,u8 dat)
{
IIC_SendStart();//起始信号
IIC_SendOneByte(AT24C02_WRITE_ADDR);//发送设备地址
IIC_GetAck();//获取应答
IIC_SendOneByte(addr); //数据存放的地址
IIC_GetAck();//获取应答
IIC_SendOneByte(dat); //发送将要存放的数据
IIC_GetAck();//获取应答
IIC_SendStop(); //停止信号
DelayMs(10); //等待写
}
/*
函数功能: 读一个字节
函数参数:
u8 addr 数据的位置(0~255)
返回值: 读到的数据
*/
u8 AT24C02_ReadOneByte(u16 addr)
{
u8 dat=0;
IIC_SendStart();//起始信号
IIC_SendOneByte(AT24C02_WRITE_ADDR);//发送设备地址
IIC_GetAck();//获取应答
IIC_SendOneByte(addr); //将要读取数据的地址
IIC_GetAck();//获取应答
IIC_SendStart();//起始信号
IIC_SendOneByte(AT24C02_READ_ADDR);//发送设备地址
IIC_GetAck();//获取应答
dat=IIC_RecvOneByte();//读取数据
IIC_SendAck(1); //发送非应答
IIC_SendStop(); //停止信号
return dat;
}
/*
函数功能: 从指定位置读取指定长度的数据
函数参数:
u16 addr 数据的位置(0~255)
u16 len 读取的长度
u8 *buffer 存放读取的数据
返回值: 读到的数据
*/
void AT24C02_ReadByte(u16 addr,u16 len,u8 *buffer)
{
u16 i=0;
IIC_SendStart();//起始信号
IIC_SendOneByte(AT24C02_WRITE_ADDR);//发送设备地址
IIC_GetAck();//获取应答
IIC_SendOneByte(addr); //将要读取数据的地址
IIC_GetAck();//获取应答
IIC_SendStart();//起始信号
IIC_SendOneByte(AT24C02_READ_ADDR);//发送设备地址
IIC_GetAck();//获取应答
for(i=0;i<len;i++)
{
buffer[i]=IIC_RecvOneByte();//读取数据
if(i<len-1)IIC_SendAck(0); //发送应答
else IIC_SendAck(1); //发送非应答
}
IIC_SendStop(); //停止信号
}
/*
函数功能: AT24C02页写函数
函数参数:
u16 addr 写入的位置(0~1023)
u8 len 写入的长度(每页16字节)
u8 *buffer 存放读取的数据
*/
void AT24C02_PageWrite(u16 addr,u16 len,u8 *buffer)
{
u16 i=0;
IIC_SendStart();//起始信号
IIC_SendOneByte(AT24C02_WRITE_ADDR);//发送设备地址
IIC_GetAck();//获取应答
IIC_SendOneByte(addr); //数据存放的地址
IIC_GetAck();//获取应答
for(i=0;i<len;i++)
{
IIC_SendOneByte(buffer[i]); //发送将要存放的数据
IIC_GetAck();//获取应答
}
IIC_SendStop(); //停止信号
DelayMs(10); //等待写
}
/*
函数功能: 从指定位置写入指定长度的数据
函数参数:
u16 addr 数据的位置(0~255)
u16 len 写入的长度
u8 *buffer 存放即将写入的数据
返回值: 读到的数据
*/
void AT24C02_WriteByte(u16 addr,u16 len,u8 *buffer)
{
u8 page_byte=8-addr%8; //得到当前页剩余的字节数量
if(page_byte>len) //判断当前页剩余的字节空间是否够写
{
page_byte=len; //表示一次性可以写完
}
while(1)
{
AT24C02_PageWrite(addr,page_byte,buffer); //写一页
if(page_byte==len)break; //写完了
buffer+=page_byte; //指针偏移
addr+=page_byte;//地址偏移
len-=page_byte;//得到剩余没有写完的长度
if(len>8)page_byte=8;
else page_byte=len; //一次可以写完
}
}
//u8 at24c02_w[8]="1234567";
//u8 at24c02_r[8];
int main()
{
u8 dat;
UART_Init(); //初始化串口波特率为4800
while(1)
{
//单个字节读写测试
AT24C02_WriteOneByte(10,34);
dat=AT24C02_ReadOneByte(10);
printf("dat=%d\r\n",(int)dat);
//多个字节读写测试
//AT24C02_WriteByte(0,8,at24c02_w);
// AT24C02_ReadByte(0,8,at24c02_r);
// printf("at24c02_r=%s\r\n",at24c02_r);
DelayMs(1000);
}
}
图4-6-13
图4-6-14 逻辑分析仪检测的写时序过程
图4-6-15 逻辑分析仪检测的读时序过程