Data&&&Video Transceivers
设计与模拟工具
ClockWorks& Configurator
1 Mb&64 Mb
1 Mb&64 Mb
64 Kb&1 Mb
512 Kb&1 Mb
耐用性(擦/写周期)
1,000,000+
I2C&、SPI、Microwire和UNI/O&总线
SPI、Dual和SQI& Flash
SPI、SDI和SQI&
时钟频率/访问时间
0.4&20 MHz
20&104 MHz
32和48引脚
数据保存时间
20年以上(使用电池)
典型待机电流
1.65V&1.95V, 2.7V&3.6V, 2.3V&3.6V
1.65V&1.95V, 2.7V&3.6V, 4.5V&5.5V
1.65V&1.95V, 2.5V&5.5V
-40&C至+125&C
-40&C至+105&C
-40&C至+85&C
-40&C至+85&C
-40&C至+85&C
Microchip Technology Inc. All rights reserved.自制单片机之六……串行I2C总线E2PROM AT24CXXX的应用
自制单片机之六……串行I2C总线E2PROM AT24CXXX的应用
[摘要:那一篇先容I2C存储器的应用。首要是先容AT24CXX系列器件,它分为两类,首要是经过被存储容量地点去分的,一类是AT24C02-AT24C16,它的存储容量从256字节到2048字节。另外一类是AT24C32-A]
这一篇介绍I2C存储器的使用。主要是介绍AT24CXX系列器件,它分为两类,主要是通过被存储容量地址来分的,一类是AT24C02-AT24C16,它的存储容量从256字节到2048字节。另一类是AT24C32-AT24C1024,容量从4K-128K。(理论上好像可以达到最高512K字节容量,但现在网上最高也就能看到AT24C1024也就是128K字节容量)原理: I2C总线是一种用于IC器件之间连接的二线制总线。它通过SDA(串行数据线)及SCL(串行时钟线)两根线在连到总线上的器件之间传送信息,并根据地址识别每个器件:不管是单片机、存储器、LCD驱动器还是键盘接口。I2C总线接口电路结构如图所示。
SDA和SCL均为双向I/O线,通过上拉电阻接正电源。当总线空闲时,两根线都是高电平。连接总线的器件的输出级必须是集电极或漏极开路,以具有线&与&功能。I2C总线的数据传送速率在标准工作方式下为100kbit/s,在快速方式下,最高传送速率可达400kbit/s。 在I2C总线技术规范中,开始和结束信号(也称启动和停止信号)的定义如图所示。当时钟线SCL为高电平时,数据线SDA由高电平跳变为低电平定义为&开始&信号;当SCL线为高电平时,SDA线发生低电平到高电平的跳变为&结束&信号。开始和结束信号都是由主器件产生。在开始信号以后,总线即被认为处于忙状态;在结束信号以后的一段时间内,总线被认为是空闲的。
I2C总线的数据传送格式是:在I2C总线开始信号后,送出的第一个字节数据是用来选择从器件地址的,其中4-7位为器件码,如1010就是代表串行E2PROM器件。1-3位为存储器的片选地址或存储器内的块地址码,如何区分?后面再做详细说明,第8位为方向位(R/W)。方向位为&0&表示发送,即主器件把信息写到所选择的从器件;方向位为&1&表示主器件将从从器件读信息。开始信号后,系统中的各个器件将自己的地址和主器件送到总线上的地址进行比较,如果与主器件发送到总线上的地址一致,则该器件即为被主器件寻址的器件,其接收信息还是发送信息则由第8位(R/W)确定。一个字节的写操作的过程:首先器件发出起始信号后,发送器件识别控制字节,即A00(最低位置0,即R/W读写控制位为低电平0),然后等待应答信号指示从器件被寻址。再发送一个AT24CXX存储器将要写入的位置地址。再次等待AT24CXX应答信号以后,将发送数据字节,AT24CXX接收到后写入到刚刚指定的存储器地址。然后主器件再次等待AT24CXX的应答信号。主器件最后发出停止信号。 在I2C总线上每次传送的数据字节数不限,但每一个字节必须为8位,而且每个传送的字节后面必须跟一个认可位(第9位),也叫应答位(ACK)。数据的传送过程如图所示。每次都是先传最高位,通常从器件在接收到每个字节后都会作出响应,即释放SCL线返回高电平,准备接收下一个数据字节,主器件可继续传送。如果从器件正在处理一个实时事件而不能接收数据时,(例如正在处理一个内部中断,在这个中断处理完之前就不能接收I2C总线上的数据字节)可以使时钟SCL线保持低电平,从器件必须使SDA保持高电平,此时主器件产生1个结束信号,使传送异常结束,迫使主器件处于等待状态。当从器件处理完毕时将释放SCL线,主器件继续传送。
当主器件发送完一个字节的数据后,接着发出对应于SCL线上的一个时钟(ACK)认可位,在此时钟内主器件释放SDA线,一个字节传送结束,而从器件的响应信号将SDA线拉成低电平,使SDA在该时钟的高电平期间为稳定的低电平。从器件的响应信号结束后,SDA线返回高电平,进入下一个传送周期。 I2C总线还具有广播呼叫地址用于寻址总线上所有器件的功能。若一个器件不需要广播呼叫寻址中所提供的任何数据,则可以忽略该地址不作响应。如果该器件需要广播呼叫寻址中提供的数据,则应对地址作出响应,其表现为一个接收器。 5.总线竞争的仲裁 总线上可能挂接有多个器件,有时会发生两个或多个主器件同时想占用总线的情况。例如,多单片机系统中,可能在某一时刻有两个单片机要同时向总线发送数据,这种情况叫做总线竞争。I2C总线具有多主控能力,可以对发生在SDA线上的总线竞争进行仲裁,其仲裁原则是这样的:当多个主器件同时想占用总线时,如果某个主器件发送高电平,而另一个主器件发送低电平,则发送电平与此时SDA总线电平不符的那个器件将自动关闭其输出级。总线竞争的仲裁是在两个层次上进行的。首先是地址位的比较,如果主器件寻址同一个从器件,则进入数据位的比较,从而确保了竞争仲裁的可靠性。由于是利用I2C总线上的信息进行仲裁,因此不会造成信息的丢失。
器件说明:AT24CXXX系列引脚图如下
现在我先来说说AT24CXX的具体使用&&&&& 假设用AT89S51的P0.0做SDA总线,P0.1做SCL总线。有若干个I2C器件挂接在SDA和SCL总线上。现在要对E2PROM_01存储器进行写字节操作看看它是如何找到的。上面说过在发送完一个开始信号后接着发送一个字节的器件识别信号。这一个字节的4-7位就是器件识别码。1010就是对应E2PROM器件,其它器件就不再理会了。1-3位是器件的物理地址,也就是说如果是E2PROM,它可以在I2C总线上挂接(000-111)8个E2PROM。在这里就得详细说说AT24CXX上的A0,A1,A2和这个器件识别字节之间的关系了。上面说过存储器的寻址范围是一个字节,也就256个,AT24C02的存储容量为256字字,刚刚好将一个字节的地址用完。器件电路上A0,A1,A2三个管脚通过接高电平或低电平来和AT89S51发送过来的器件识别控制字节相匹配,从而得以识别出AT89S51将要操作的那个存储器。现在AT24C04的容量是512个字节,那不是一个字节的地址不够用了吗?其实它是将512个字节为成两个页,每页256字节,而页地址就是器件识别控制字节的1位。前面说了这个1-3位不是和器件上的A0,A1,A2匹配来识别器件的吗?是的,但存储器容量超过256字节情况就有变了。AT24C04上的A0这时就废弃不用了,只用A1和A2,这样就只能接(00-11)四个AT24C04了,同样AT24C08容量为1K字节分为4页了,于是页地址就是器件识别控制字节的1-2位,器件上的A0,A1废弃不用,只用A2,就只能接两个AT24C08了。AT24C16容量为2K字节,分为8页。页地址是器件识别控制字节的1-3位,全用了。器件上的A0,A1,A2,就无效了,只能接一只AT24C16。
我这么说能明白吗?
对于大容量AT24C32-1024的存储器。器件的存储寻址地址为两个字节,所以它的一页为65536(64K)。AT24C32-64的容量为4K字节-8K字节,在一页范围,可以接8只器件。从AT24C128-1024的器件代号也由1010改为10100,多了一位,识别控制字节的器件物理地址就少了一位,变为1-2位,相应的在器件管脚上A2也废弃空着了,因此最多只能接四只器件。AT24C128-512只有两位器件地址所以最多只能接四只器件。而AT24C1024的容量为128K,分为两页,识别控制字节的的1位为页地址,器件的A0脚废弃不用,只用了A1。因此只能接2只器件。
&&& 下面我们进行具体的制做先准备好器件如下图 ,我用的是AT24C16
原先的板子如下图
好!下面我们进行调试:插上主电源。但AT24C16的电源短路帽不接,在短路帽两个针之间接上万用表的电流档检查是否有短路和静态电流的大小。实测静态电流几乎为零,改变两个数据线的电平时,电流会有所上升,说明电路基本正常。
现在我们接上电源短路帽把AT24C16电源接好。将两个数据线用跳线接到P1.6和P1.7口上(接到哪个口上可以自己选的)。注意分清哪个是SDA哪个是SCL。别弄错了。
把LCD12864装上,后面我们就要进行软件的调试了。
后面一篇,我们进行软件件部分的调试。
先将源代码附上。前面部分LCD12864的代码基本不变,添加上AT24C16的代码。
//LCD12864
//**********************************************************
//连线表: CPU=89S51
SysClock=12MHz
//DB0-DB7=P3.0-P3.7
/Reset=InBoard
//**********************************************************
//**********************************************************
//连线表: CPU=89S51
SysClock=12MHz
//SDA=P1.6
//**********************************************************
#include ®52.h&
#include &stdlib.h&
#include &intrins.h&
#include &stdio.h&
#include &math.h&
#define uchar unsigned char
#define uint unsigned int
/****************LCD12864引脚定义*****************/
#define DataPort P3
//LCD128*64 I/O 信号管脚
//数据指令
CSL =P2^3;
CSR =P2^4;
uchar code BMP1[];
uchar code HZK_12[];
//12&12阵点字模
uchar code ASC_5x7[];
//5&7阵点字模
uchar str[4];
/**************AT24C16引脚定义*****************/
SDA =P1^6;
SCL =P1^7;
/***********************************************/
/****************LCD12864函数定义*******************/
void BusyL(void);
//左屏检测忙
void BusyR(void);
//右屏检测忙
void CheckBusy(void);
//读取忙信号
void Delay(uint MS);
void Locatexy(void);
//将屏幕横向0-12纵向0-7转换成左、右屏的的X、Y
void WriteCommandL( uchar CommandByte );
//向左屏写入指令
void WriteCommandR( uchar CommandByte );
//向右屏写入指令
uchar ReadData( void );
void WriteData( uchar DataByte );
void LcmClear( void );
void LcmInit( void );
void LcmPutBMP( uchar *puts );
//显示一幅图
void LcmReverseBMP( void );
//将整屏反显
void LcmPutHZ_12( uchar x,uchar y,uchar HZcode ); //在屏幕上任意点显示一个12&12汉字
uchar * uchartostr(unsigned char unm);
//将值转成字符串
void LcmPutAsc( uchar asc );
//显示一个5&7的ASC字符
void LcmPutstr( uchar row,uchar y,uchar * str );
//在设定位置显示字符串
void LcmPutpoint( uchar ro,uchar lie,uchar colour );
//在设定位置显示一个点
/****************AT24C16函数定义*******************/
/****向总线上发送n字节数***/
bit write_nbyte(uchar block_addr,uchar addr,uchar *s,uchar numb);
/****从总线上读取n个字节数******/
bit read_nbyte(uchar block_addr,uchar addr,uchar *s,uchar numb);
/*****************************
block_addr
存储器的块地址选0-8
存储器的存储地址
一般是用在数组的首地址
要写入的字节数
********************************/
/***************************/
/*检查Busy
/***************************/
void BusyL(void)
CheckBusy();
void BusyR(void)
CheckBusy();
void CheckBusy(void)
DataPort= 0xFF;
//输出0xff以便读取正确
while(0);//DataPort & 0x80);
//Status Read Bit7 = BUSY
/********************************************************/
/*根据设定的坐标数据,定位LCM上的下一个操作单元位置
/********************************************************/
void Locatexy(void)
uchar x,y;
switch (Col&0xc0)
/* col.and.0xC0
/*条件分支执行
{BusyL();break;}/*左区 */
case 0x40:
{BusyR();break;}/*右区 */
x = Col&0x3F|0x40;
/* col.and.0x3f.or.Set Y Address*/
y = Page&0x07|0xB8;
/* row.and.0x07.or.set Page
CheckBusy();
/* waitting for enable */
DataPort =
//设置页面地址
CheckBusy();
/* waitting for enable */
DataPort =
//设置列地址
/***************************/
/***************************/
void WriteCommandL( uchar CommandByte )
DataPort = CommandB
void WriteCommandR( uchar CommandByte )
DataPort = CommandB
/***************************/
/***************************/
uchar ReadData( void )
uchar DataB
Locatexy();
/*坐标定位,返回时保留分区状态不变
/*数据输出*/
DataPort = 0xFF;
//输出0xff以便读取正确
/*读入到LCM*/
DataByte = DataP /*数据读出到数据口P1 */
return DataB
/***************************/
/***************************/
void WriteData( uchar DataByte )
Locatexy();
/*坐标定位,返回时保留分区状态不变
/*数据输出*/
/*写输出 */
DataPort = DataB /*数据输出到数据口 */
/*写入到LCM*/
void LcmClear( void )
for(Page=0;Page&8;Page++)
for(Col=0;Col&128;Col++)
WriteData(0);
void LcmInit( void )
Delay(200);
//等待复位
WriteCommandL(0x3f);
WriteCommandR(0x3f);
WriteCommandL(0xc0);
//设置起始地址=0
WriteCommandR(0xc0);
WriteCommandL(0x3f);
WriteCommandR(0x3f);
LcmClear();
Locatexy();
void LcmPutBMP( uchar *puts )
for(Page=0;Page&8;Page++)
for(Col=0;Col&128;Col++)
WriteData( puts[X] );
void LcmReverseBMP( void )
for(Page=0;Page&8;Page++)
for(Col=0;Col&128;Col++)
temp = ReadData();
//空读一次
temp = ReadData();
WriteData(temp);
void LcmPutHZ_12( uchar x,uchar y,uchar HZcode )
uchar offset,Rd,Wt,m,tmp,i;
if(x&117&y&53)
Page=(y & 0x38)&&3;
n = 0x18*HZ
offset=y&0x07;
if(offset&5)
for(i=12;i&0;i--)
Rd=ReadData();
Rd=ReadData();
m=HZK_12[n];
Wt=Rd&(0xff&&(8-offset))|(m&&offset);
WriteData(Wt);
m=HZK_12[n];
Rd=ReadData();
Rd=ReadData();
Wt=tmp&&(8-offset)|(m&&offset)|(Rd&(0xff&&(offset+4)));
WriteData(Wt);
for(i=12;i&0;i--)
Rd=ReadData();
Rd=ReadData();
m=HZK_12[n];
Wt=Rd&(0xff&&(8-offset))|(m&&offset);
WriteData(Wt);
m=HZK_12[n];
Wt=tmp&&(8-offset)|(m&&offset);
WriteData(Wt);
Rd=ReadData();
Rd=ReadData();
Wt=m&&(8-offset)|(Rd&(0xff&&(offset-4)));
WriteData(Wt);
Page=Page-2;//恢复位置
Col++; //修正下一个汉字的起始位置
uchar * uchartostr(uchar unm)
uchar x00,xx,x0,x,n;
x00=unm/100;
xx=unm%100;
if(x00!=0)
{ str[n]=x00+48; //值加48即为字符
if(!(x00==0&x0==0))
{ str[n]=x0+48;
str[n]=x+48;
str[n]='