I2C之AT24C04总结.docx

上传人:b****9 文档编号:24946530 上传时间:2023-06-03 格式:DOCX 页数:23 大小:271.84KB
下载 相关 举报
I2C之AT24C04总结.docx_第1页
第1页 / 共23页
I2C之AT24C04总结.docx_第2页
第2页 / 共23页
I2C之AT24C04总结.docx_第3页
第3页 / 共23页
I2C之AT24C04总结.docx_第4页
第4页 / 共23页
I2C之AT24C04总结.docx_第5页
第5页 / 共23页
点击查看更多>>
下载资源
资源描述

I2C之AT24C04总结.docx

《I2C之AT24C04总结.docx》由会员分享,可在线阅读,更多相关《I2C之AT24C04总结.docx(23页珍藏版)》请在冰豆网上搜索。

I2C之AT24C04总结.docx

I2C之AT24C04总结

 

I2C之AT24C04总结

I2C之AT24C04总结

济南职业学院电子工程系朱志强

 

1、AT24C04介绍

2、AT24C04之准备工作

3、AT24C04之小试牛刀

4、对应源程序

 

2010年7月28日

 

1、AT24C04介绍

关于I2C的介绍,这里就不用说了,直接介绍24C04了。

24C04是4K位串行CMOSE2PROM。

引脚的认识:

SCL串行时钟引脚

SDA串行数据/地址

A0、A1、A2器件地址输入端

WP写保护

(WP管脚连接到Vcc,所有的内容都被写保护(只能读)。

当WP管脚连接到Vss或悬空,允许器件进行正常的读/写操作。

2、AT24C04之准备工作

首先,我们先查看一下实验板上面的接线图。

如图1所示。

图124c04连接图

我们要注意的第一点是器件地址全部是0,即接地处理。

第二点是读写保护WP接地,意味着我们可以随意存取。

第三点是我们要用到的引脚连接到了P3^6和P3^7上。

在这里还要提醒一下,就是引脚上一定要有上拉电阻!

阻值在470~1k都可以的,具体的数值可以参考相关的手册。

在程序里我们需要先做以下定义:

sbitAT24C04_SCL=P3^7;

sbitAT24C04_SDA=P3^6;

在写这个程序的时候,要使用到键盘,不用太多按键,我们暂时只用四个。

把实验板上面的跳线JP8接到“-”端上,使第一行的按键变为独立键盘就可以了。

线路图如图2所示。

图2键盘部分电路图

键盘这部分我就不说了吧,直接附上我用到的这部分程序,在我的程序中,并没有判断按键是否松开,而是使用的延时,这样的好处是一直按着按键,数据会一直在变化,要不然,频繁的按真的很累人。

转到按键程序

对于里面用到的延时函数,一个是US级延时函数,一个是ms级延时函数,分别调用一下是延时2us和1ms。

对于显示部分吧,使用的就是LCD1602显示了。

这部分程序参见这里。

显示程序

说完了这些,准备的就差不多了,我们可以对着PDF写AT24C04程序了。

3、AT24C04之小试牛刀

我们打破PDF中的介绍顺序,按照实际写程序时的顺序分开分析。

第一项是我们要用她,那就要知道她是怎么开始吧?

这就是I2C总线时序中的开始和停止。

时序图如下。

图3开始停止时序

有一个需要注意的地方,就是当SCL为高电平时,所有的SDA的变化都会被认为是开始或停止信号。

所以,我们必须注意,在对SDA进行操作之前,一定要注意SCL的值。

例如在我们写开始信号之前,我们没法判断两个信号的具体电位,那么我们就要做最坏的打算。

首先SDA=1,然后SDA=0,此间要让SCL保持在高电平。

为保证SCL为高电平,我们要用SCL=1指令使SCL保持在高电平。

SCL放在什么位置就成了重点。

如果我们SDA变为高电平之前,这样却成了SCL=1后SDA=1,形成了停止信号!

这个是我们要避免的,那么我们就要让SDA变化的时候,全部避开SCL为高电平段。

程序如下。

voidI2C_START()

{

//AT24C04_SCL=0;delayus

(1);

//这样做有些繁琐,我们可以直接不用,因为我们跳出

//所有的子函数时,都会让SCL=0!

AT24C04_SDA=1;delayus

(1);//注意先后顺序

AT24C04_SCL=1;delayus

(1);

AT24C04_SDA=0;delayus

(1);//下降沿开始

AT24C04_SCL=0;delayus

(1);

}

我们以同样的思维,可以得到停止子函数,如下。

voidI2C_STOP()

{

AT24C04_SCL=0;delayus

(1);

AT24C04_SDA=0;delayus

(1);

AT24C04_SCL=1;delayus

(1);

AT24C04_SDA=1;delayus

(1);//上升沿停止

}

下面我们来看一下写的时序。

图4写时序

时序的第一部分给我们展现了一个第八位数据写完后的情况,也就是说要有一个ACK应答信号。

具体关于ACK应答信号的内容,可以参考器件手册。

第二部分是从开始到停止。

至于时序图中给出的时间我们可以不用考虑。

只需要记住,当SDA变化时SCL为0,SDA变化完后,SCL在变为1来告知24C04接收数据线上的信号。

具体的实现函数如下所示。

voidI2C_Wdata(unsignedchardata4)

{

unsignedchari;

unsignedchartemp;

temp=data4;

for(i=0;i<8;i++)

{

AT24C04_SCL=0;

temp=temp<<1;//先发送高位

AT24C04_SDA=CY;delayus

(1);

AT24C04_SCL=1;delayus

(1);

}

AT24C04_SCL=0;delayus

(1);

}

这里还需要注意的一点是我们每当SDA或者SCL变化一次,就会调用一下delayus

(1)来延时2us,是用来稳定信号和保证信号保持的时间。

上面是写的时序,我们还要有读的子函数啊……其实,读得时序和写的时序差不多,只不过是反过来的。

既然是读,发送方就是24C04,接收方式单片机。

每次都是让SCL为1,使得24C04掌握对SDA的控制权。

而单片机的任务就是在把SCL拉高后,监视SDA的变化并读出SDA上的数据。

下面就是读的子函数。

unsignedcharI2C_Rdata()

{

unsignedchari;

unsignedchartemp;

unsignedchark;

AT24C04_SCL=0;delayus

(1);

AT24C04_SDA=1;//等待24C02发回来的信号

for(i=0;i<8;i++)

{

AT24C04_SCL=1;delayus

(1);

if(AT24C04_SDA==1)

k=1;

else

k=0;

temp=temp<<1;

temp=temp|k;//读数据时高位在前

AT24C04_SCL=0;

}

delayus

(1);

returntemp;

}

此时,我们还需要添加上ACK回应信号。

我们先来看看它的时序图。

图5ACK应答信号

从时序图我们可以看出,ACK就是24C04还给单片机的一个低电平信号,它发生在第九个时钟脉冲上。

如果我们用不到哪个ACK的话,我们只需要给出第九个时钟脉冲就可以了。

示例程序如下。

voidI2C_ACK()

{

unsignedchari;//可以去掉

AT24C04_SCL=1;delayus

(1);

while((AT24C04_SDA==1)&&(i<511))//这部分可以去掉

i++;//可以去掉

AT24C04_SCL=0;delayus

(1);

}

以上三个是读写的重要子函数,加上开始和停止,有关于AT24C04的函数部分,都是由这五个组成的。

下面我们来分析一下可操作的和写。

先来分析写字节函数吧。

下面是它的时序图。

图6字节写时序

从时序图中我们可以看到,第一部分是开始(START),这部分调用开始函数就可以了。

第二部分器件地址,这个是由单片机发送给24C04的,选用写子函数就可以了。

别忘了还有一个ACK应答。

第三部分是字节地址,就是你想把要存储的数据存储到哪个单元里。

第四部分是发送要存储的数据。

第五部分就是停止部分(STOP)。

现在我们来分析一下什么是器件地址吧。

在I2C总线的协议中,某些器件被指定为特定的地址,这个特定的地址占用了器件地址的前四位。

具体的怎么定义的可以参考部分资料。

I2C器件的手册里也给出了自己的器件地址。

我们以AT24C04的器件地址为例分析吧。

图7AT24C04的器件地址

A2,A1是我们自己可以选择的,对应的是可以接地或者接高电平。

a8是可以用单片机控制的,但是我们的实验板上直接当做A0给接地处理了。

这样做虽然是使器件地址变简单了,但是,24c04有一半的存储区域我们没有用到,也就相当于用了一个AT24C02了。

最后一位读写控制位,从非号“¯”我们看出,此位是1表示读,此位是0则表示写!

那么我们就可以得到对24C04写的器件地址是0XA0,读的器件地址是0XA1(见图1)。

由此我们就可以按顺序写出字节写的子函数了。

如下所示。

voidI2C_xie(unsignedchardata5,unsignedchardata6)

{

I2C_START();

I2C_Wdata(0xa0);//写入型总线

I2C_ACK();

I2C_Wdata(data5);//写入数据的存储地址

I2C_ACK();

I2C_Wdata(data6);//待写入的数据

I2C_ACK();

I2C_STOP();

}

我们在用同样的方法分析一下选择性读的时序。

时序图如下。

图8选择性读时序图

有这个图我们可以分析到如下的选择性读子函数。

unsignedcharI2C_du(unsignedchardata7)

{

unsignedchardata8;

I2C_START();

I2C_Wdata(0xa0);//写入型总线

I2C_ACK();

I2C_Wdata(data7);//需要读出数据的地址

I2C_ACK();

I2C_START();//再一次重新开始

I2C_Wdata(0xa1);//读出型总线

I2C_ACK();

data8=I2C_Rdata();//转存读出的数据

I2C_STOP();

returndata8;//返回读出的数据

}

现在我们基本上就把AT24C04的读写的部分完成了。

至于PDF中提到的其他的用法,大家可以自己琢磨琢磨。

比如,下面是页写的时序图。

图9页写时序图

我们就得到了如下的页写程序。

/************************************************************

AT24C04页写函数

变量:

data5页写首地址

data6页写的数据的个数

************************************************************/

voidI2C_YExie(unsignedchardata5,unsignedchardata6)

{

unsignedchar*p;

unsignedchari;

unsignedchark;

p=tab;

//unsignedchartab[]={

//"0123456789"

//};

k=data6;

I2C_START();

I2C_Wdata(0xa0);

I2C_ACK();

I2C_Wdata(data5);

I2C_ACK();

for(i=0;i

{

I2C_Wdata(*p++);

I2C_ACK();

}

I2C_STOP();

}

4、对应源程序

最后附上全体函数,有利于大家临摹学习。

使用实验板的时候,先调节跳线帽的位置并添加上1602液晶,程序使用的是1602A(即普通16脚液晶)显示。

按照按键电路图的顺序定义K1,K2,K3和K4。

K1是显示的数据加1,K2是显示的数据减1,K3是将显示的数据存储进AT24C04中,K4是将存储在AT24C04中的数据显示出来。

这个函数没有使用到页写和一些其他的功能,大家可以自己写一个更好的,将存储的字节位置也能够用户定义。

网上有些不错的例子,比如通过每次复位初始化的时候给AT24C04中的一个数据加1来统计单片机复位的次数。

大家可以试着写一下,无非就是对一个存储的数据先读,加1,在写回到原来的存储字节中。

#include

/************************************************************/

sbitLCD1602_RS=P2^7;

sbitLCD1602_RW=P2^6;

sbitLCD1602_E=P2^5;

sbitAT24C04_SCL=P3^7;

sbitAT24C04_SDA=P3^6;

/************************************************************/

unsignedcharx;//用于统计计数,存入

unsignedchartab[]={

"0123456789"

};

/************************************************************/

voiddelayus(unsignedintus)

{

while(us--);

}

voiddelayms(unsignedintms)

{

unsignedinti;

while(ms--)

{

for(i=0;i<125;i++)

;

}

}

/***

液晶显示程序返回

*********************************************************/

voidLCD_WByte(unsignedchardata1,unsignedchardata2)

{

P0=data2;

LCD1602_RS=data1;//0为指令,1为数据

LCD1602_RW=0;//写入

LCD1602_E=1;//下降沿有效

LCD1602_E=0;

delayus(20);//执行指令用40us

}

/************************************************************/

voidLCD_chushihua()

{

LCD_WByte(0,0x38);

LCD_WByte(0,0x0c);

LCD_WByte(0,0x06);

LCD_WByte(0,0x01);//清屏

delayms

(2);//等待2毫秒到清屏结束

}

/************************************************************

单个字符显示函数

变量:

x在1602第一行的第x个位置显示

data3需要显示的字符

*************************************************************/

voidLCD_disp(unsignedcharx,unsigneddata3)

{

unsignedcharadd;

add=0x80+x;

LCD_WByte(0,add);

LCD_WByte(1,*tab+data3);//使用表格来显示数字字符

}

voidLCD_disps(unsignedchar*dat4)//字符串显示函数

{

unsignedchar*p;

p=dat4;

LCD_WByte(0,0);//显示的位置为初始坐标

while(*p!

='\0')

{

LCD_WByte(1,*p++);

}

}

/*

************************************************************/

voidxianshi(unsignedchardata9)

{

unsignedchari;

i=data9/100;

LCD_disp(10,i);

i=(data9%100)/10;

LCD_disp(11,i);

i=data9%10;

LCD_disp(12,i);

}

/*************************************************************

**************************************************************/

voidI2C_START()

{

//AT24C04_SCL=0;delayus

(1);//这样做有些繁琐,我们可以直接不用

AT24C04_SDA=1;delayus

(1);//注意先后顺序

AT24C04_SCL=1;delayus

(1);

AT24C04_SDA=0;delayus

(1);//下降沿开始

AT24C04_SCL=0;delayus

(1);

}

voidI2C_STOP()

{

AT24C04_SCL=0;delayus

(1);

AT24C04_SDA=0;delayus

(1);

AT24C04_SCL=1;delayus

(1);

AT24C04_SDA=1;delayus

(1);//上升沿停止

}

/*

************************************************************/

voidI2C_Wdata(unsignedchardata4)

{

unsignedchari;

unsignedchartemp;

temp=data4;

for(i=0;i<8;i++)

{

AT24C04_SCL=0;

temp=temp<<1;//先发送高位

AT24C04_SDA=CY;delayus

(1);

AT24C04_SCL=1;delayus

(1);

}

AT24C04_SCL=0;delayus

(1);

}

/*

************************************************************/

unsignedcharI2C_Rdata()

{

unsignedchari;

unsignedchartemp;

unsignedchark;

AT24C04_SCL=0;delayus

(1);

AT24C04_SDA=1;//等待24C02发回来的信号

for(i=0;i<8;i++)

{

AT24C04_SCL=1;delayus

(1);

if(AT24C04_SDA==1)

k=1;

else

k=0;

temp=temp<<1;

temp=temp|k;//读数据时高位在前

AT24C04_SCL=0;

}

delayus

(1);

returntemp;

}

/*

************************************************************/

voidI2C_ACK()

{

//unsignedchari;

AT24C04_SCL=1;delayus

(1);

//while((AT24C04_SDA==1)&&(i<511))

//i++;

AT24C04_SCL=0;delayus

(1);

}

/***********************************************************

给AT24C04写数据

变量:

data5要写入的地址data6要写入的数据

________________

AT24C04|1|0|1|0|A2|A1|A0|RW|

器件地址 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄

***********************************************************/

voidI2C_xie(unsignedchardata5,unsignedchardata6)

{

I2C_START();

I2C_Wdata(0xa0);

I2C_ACK();

I2C_Wdata(data5);

I2C_ACK();

I2C_Wdata(data6);

I2C_ACK();

I2C_STOP();

}

/************************************************************

AT24C04页写函数

变量:

data5页写首地址

data6页写的数据的个数

************************************************************/

/*voidI2C_YExie(unsignedchardata5,unsignedchardata6)

{

unsignedchar*p;

unsignedchari;

unsignedchark;

p=tab;

//unsignedchartab[]={

//"0123456789"

//};

k=data6;

I2C_START();

I2C_Wdata(0xa0);

I2C_ACK();

I2C_Wdata(data5);

I2C_ACK();

for(i=0;i

{

I2C_Wdata(*p++);

I2C_ACK();

}

I2C_STOP();

}*/

/***********************************************************

读AT24C04中的数据

变量:

data7要读出数据的地址

返回读出的数据data8

************************************************************/

unsignedcharI2C_du(unsignedchardata7)

{

unsignedchardata8;

I2C_START();

I2C_Wdata(0xa0);

I2C_ACK();

I2C_Wdata(data7);

I2C_ACK();

I2C_START();

I2C_Wdata(0xa1);

I2C_ACK();

data8=I2C_Rdata();

I2C_STOP();

returndata8;

}

/*

按键程序返回

************************************************************/

unsignedcharscan_key(void)

{

unsignedchartemp;

temp=P1;

returntemp;

}

unsignedcharKEY_pad()

{

unsignedcharkey_val;

key_val=scan

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 其它

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1