4.6 EEPROM存储芯片(AT24C02)
4.6.1 原理图介绍
IIC_SCL=0; //总线开始工作、开始读写数据
实验板上的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年
管脚上的A0、A1、A2是地址配置引脚,以组成000~111八种情况,通过器件地址输入端A0、A1和A2可以实现将最多8个AT24C02器件连接到总线上。 SDA是IIC总线的数据线,SCL是IIC总线的时钟线。
AT24C02内部的设备地址是:1010 ,下面图片里2K就是AT2C02的对应地址。
AT24C02的一个写周期时间为10ms,单片机向AT24C02传输数据时,AT24C02是将数据暂存在8字节的缓冲区里的,单片机发送完数据之后需要等待10ms的时间,等待AT24C02将缓冲区的数据存到内部EEPROM里,存放到EEPROM之后,掉电才不会丢失。
4.6.3 AT24C02时序介绍
(1) 总线时序图
(2) 起始信号与停止信号时序图
当时钟线为高电平的时候,数据线由高电平变为低电平的过程。
1.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) 数据位的传输时序图
输出传输从高位开始。
1./*函数功能: 发送一个字节数据*/ 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写一个字节的总时序
(6) 向AT24C02写一页数据的总时序
(7) 从AT24C02当前地址读取一个字节的总时序
(8). 从AT24C02指定位置读取指定长度数据的总时序
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); } }