AT24C02介绍
AT24C02是一个2K位串行CMOS E2PROM, 内部含有**256个8位字节**。AT24C02有一个16字节页写缓冲器。该器件通过IIC总线接口进行操作,有一个专门的写保护功能。
功能描述:AT24C02支持总线数据传送协议I2C,协议规定任何将数据传送到总线的器件作为发送器,反之从总线接收的器件则为接收器。数据传送是由产生串行时钟和所有起始停止信号的主器件控制的。主器件和从器件均可作为发送器或接收器,但由主器件控制传送数据(发送或接收)的模式,由于A0、A1和A2可以组成000~111八种情况,即通过器件地址输入端A0、A1和A2可以实现将最多8个AT24C02器件连接到总线上,通过进行不同的配置进行选择器件。
WP高电平有效,这里直接接了GND,没有使用WP。
存储器的简单介绍
AT24C02就是一直存储器,在开始写代码之前先对存储器进行一个简单的了解。
RAM随机存储器,存储速度快,但掉电数据会丢失,ROM只读存储器,掉电不丢失但掉电存储数据不丢失。RAM主要分为SRAM(static ram,存储速度最快,容量较少,成本较高),DRAM(dynamic ram,成本更低,容量更大)两种。mask rom依靠电路存储数据,掉电不丢失,只能读不能写入数据。prom(可编程rom)只能写入一次数据。eprom(erasable programmable rom可擦除可编程rom),可编程也可擦除(需紫外线照射进行擦除)。e2prom(电可擦除可编程rom)。flash(闪存)。硬盘,靠磁介质存储数据。光盘用光信号存储数据。
I2C
由于该器件通过I2C总线接口进行操作,我们需要先了解一下I2C。
I2C,一种总线结构。单片机的品种规格有限,所以只能选用某种单片机来进行扩展。扩展的方法有两种:一种是并行总线,另一种是串行总线。由于串行总线的连线少,结构简单,往往不用专门的母板和插座而直接用导线连接各个设备。因此,采用串行线可大大简化系统的硬件设计。I2C串行总线则是一种高性能的串行总线。
硬件结构
I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。
为避免总线信号的混乱,要求各设备连接到总线的输出端时必须是漏极开路(OD)输出或集电极开路(OC)输出。设备上的串行数据线SDA接口电路应该是双向的,输出电路用于向总线上发送数据,输入电路用于接收总线上的数据。串行时钟线是双向的,作为控制总线数据传送的主机,要通过SCL输出电路发送时钟信号,还要检测总线上的SCL电平,以决定什么时候发送下一个时钟脉冲电平;作为接受主机命令的从机,要按总线上的SCL信号发出或接收SDA上的信号,也可以向SCL线发出低电平信号以延长总线时钟信号周期。总线空闲时,因各设备都是开漏输出,上拉电阻使SDA和SCL线都保持高电平。任一设备输出的低电平都将使相应的总线信号线变低。
主机是发出启动信号、发出时钟信号以及传送结束时发出停止信号的设备,通常都是微处理器。被主机寻访的设备称为从机。为进行通讯,每个接到I2C总线的设备都有一个唯一的地址,以便于主机寻访。
数据传输
在I2C总线传输过程中,将两种特定的情况定义为开始和停止条件(见图):当SCL保持“高”时,SDA由“高”变为“低”为开始条件;当SCL保持“高”且SDA由“低”变为“高”时为停止条件。开始和停止条件均由主机产生。
SDA线上的数据在时钟“高”期间必须是稳定的,只有当SCL线上的时钟信号为低时,数据线上的“高”或“低”状态才可以改变。输出到SDA线上的每个字节必须是8位,每次传输的字节不受限制,但每个字节必须要有一个应答ACK。如果一接收器件在完成其他功能(如一内部中断)前不能接收另一数据的完整字节时,它可以保持时钟线SCL为低,以促使发送器进入等待状态;当接收器准备好接受数据的其它字节并释放时钟SCL后,数据传输继续进行。I2C数据总线传送时序。
数据传送必须有应答。与应答对应的时钟脉冲由主控制器产生,发送器在应答期间必须下拉SDA线。当寻址的被控器件不能应答时,数据保持为高并使主控器产生停止条件而终止传输。在传输的过程中,在用到主控接收器的情况下,主控接收器必须发出一数据结束信号给被控发送器,从而使被控发送器释放数据线,以允许主控器产生停止条件。
主机在接收前需要释放SDA:主机和从机共享SDA线,如果主机不释放SDA线,从机无法操作总线。防止数据冲突:如果主机不释放SDA线,SDA线的电平状态不确定,可能会导致数据传输错误。
I2C总线在开始条件后的首字节决定哪个被控器将被主控器选择。当主控器输出一地址时,系统中的每一器件都将开始条件后的前7位地址和自己的地址进行比较。若相同,该器件即认为自己被主控器寻址(被选中),R/W位决定是发送(读取)数据还是接收(写入)数据。
固定地址是指AT24C02地址前4位固定,地址低4位的前三位是000,低4位的最后一位为读写标志位,所以从机写地址为0xA0或者0xA1
代码部分
代码分为3个板块,一个主函数,一个用来实现读写操作的模块,一个用来实现开始停止、发送和接收字节、应答。
main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "key.h"
#include "delay.h"
#include "AT24C02.h"
unsigned char keynum;
unsigned int num;
void main()
{
LCD_Init();
LCD_ShowNum(1,1,num,5);
while(1)
{
keynum=key();
if(keynum==1)//按键1实现加
{
num++;
LCD_ShowNum(1,1,num,5);
}
if(keynum==2)//按键2实现减
{
num--;
LCD_ShowNum(1,1,num,5);
}
if(keynum==3)//按键3实现写入数据
{
AT24C02_writebyte(0,num%256);//写入数据低8位(int占2个字节)
delay(5);//每次写完必须延时5ms(写周期)
AT24C02_writebyte(1,num/256);//写入数据高8位
delay(5);
LCD_ShowString(2,1,"write ok");
delay(1000);
LCD_ShowString(2,1," ");
}
if(keynum==4)//按键4实现读出数据
{
num=AT24C02_readbyte(0);//读出低8位数据
num|=AT24C02_readbyte(1)<<8;//读出高8位数据并存取完整数据
LCD_ShowNum(1,1,num,5);
LCD_ShowString(2,1,"read ok");
delay(1000);
LCD_ShowString(2,1," ");
}
}
}
AT24C02.c
#include "I2C.h"
#include <REGX52.H>
#define AT24C02_address 0xA0//从机地址
void AT24C02_writebyte(unsigned char wordaddress,Data)
{
I2C_start();
I2C_sendbyte(AT24C02_address);//主机发送写的信号
I2C_receiveack();//接收来自从机的应答
I2C_sendbyte(wordaddress);//写入数据的地址
I2C_receiveack();
I2C_sendbyte(Data);//写入具体数据
I2C_receiveack();
I2C_stop();
}
unsigned char AT24C02_readbyte(unsigned char wordaddress)
{
unsigned char Data;
I2C_start();
I2C_sendbyte(AT24C02_address);//发送从机地址(谁)
I2C_receiveack();
I2C_sendbyte(wordaddress);//发送数据的地址(在哪)
I2C_receiveack();
I2C_start();
I2C_sendbyte(AT24C02_address|0x01);//发送读的信号(干什么)
I2C_receiveack();
Data=I2C_receivebyte();//读取c02数据
I2C_sendack(1);//不发送应答
I2C_stop();
return Data;
}
I2C.c
#include <REGX52.H>
sbit I2C_SCL=P2^1;//命名IO口
sbit I2C_SDA=P2^0;//命名IO口
void I2C_start(void)//开始信号
{
I2C_SDA=1;//scl高电平期间sda由高变为低(开始信号)
I2C_SCL=1;
I2C_SDA=0;
I2C_SCL=0;//发出开始信号后将sda恢复到低电平
}
void I2C_stop(void)//结束信号
{
I2C_SDA=0;//结束之前应答可能为1或0
I2C_SCL=1;//scl高电平期间sda由0变为1
I2C_SDA=1;
}
void I2C_sendbyte(unsigned char byte)//发送数据
{
unsigned char i;
for(i=0;i<8;i++)
{
I2C_SDA=byte&(0x80>>i);//scl低电平期间取出数据每一位放在sda上,高位在前
I2C_SCL=1;//拉高scl读取数据位
I2C_SCL=0;//拉低SCL方便后面数据放上SDA
}
}
unsigned char I2C_receivebyte(void)//接收数据
{
unsigned char i,byte=0x00;
I2C_SDA=1;//释放SDA
for(i=0;i<8;i++)
{
I2C_SCL=1;
if(I2C_SDA){byte|=(0x80>>i);}
I2C_SCL=0;
}
return byte;
}
void I2C_sendack(unsigned char ack)//发送应答
{
I2C_SDA=ack;
I2C_SCL=1;//读取ack
I2C_SCL=0;
}
unsigned char I2C_receiveack(void)//接收应答
{
unsigned char ack;
I2C_SDA=1;//释放SDA
I2C_SCL=1;//读取ack
ack=I2C_SDA;//记录读取到的数据(应答)
I2C_SCL=0;//拉低SCL方便后面数据放上SDA
return ack;
}