[转]单片机I2C总线及E2PROM应用实例


一、I2C简介:

I2C(Inter Integrated Circuit) 总线I2C总线产生于在80年代是一种由 PHILIPS 公司开发的两线式串行总线用于连接微控制器及其外围设备。
一. 技术性能:
 工作速率有100K和400K两种;

 支持多机通讯; 支持多主控模块,但同一时刻只允许有一个主控; 
 由数据线SDA和时钟SCL构成的串行总线;
 每个电路和模块都有唯一的地址; 每个器件可以使用独立电源
二. 基本工作原理: 
 以启动信号START来掌管总线,以停止信号STOP来释放总线;
 每次通讯以START开始,以STOP结束; 
 启动信号START后紧接着发送一个地址字节,其中7位为被控器件的地址码,一位为读/写控制位R/W,R. /W位为0表示由主控向被控器件写数据,R/W为1表示由主控向被控器件读数据;
 当被控器件检测到收到的地址与自己的地址相同时,在第9个时钟期间反馈应答信号; 每个数据字节在传送时都是高位(MSB)在前
三.通讯过程
写通讯过程: 1. 主控在检测到总线空闲的状况下,首先发送一个START信号掌管总线; 2. 发送一个地址字节(包括7位地址码和一位R/W); 3. 当被控器件检测到主控发送的地址与自己的地址相同时发送一个应答信号(ACK); 4. 主控收到ACK后开始发送第一个数据字节; 5. 被控器收到数据字节后发送一个ACK表示继续传送数据,发送NACK表示传送数据结束; 6. 主控发送完全部数据后,发送一个停止位STOP,结束整个通讯并且释放总线;
读通讯过程: 1. 主控在检测到总线空闲的状况下,首先发送一个START信号掌管总线; 2. 发送一个地址字节(包括7位地址码和一位R/W); 3. 当被控器件检测到主控发送的地址与自己的地址相同时发送一个应答信号(ACK); 4.主控收到ACK后释放数据总线,开始接收第一个数据字节; 5. 主控收到数据后发送ACK表示继续传送数据,发送NACK表示传送数据结束; 6. 主控发送完全部数据后,发送一个停止位STOP,结束整个通讯并且释放总线;
四. 总线信号时序分析 
 1. 总线空闲状态 SDA和SCL两条信号线都处于高电平,即总线上所有的器件都释放总线,两条信号线各自的上拉电阻把电平拉高;
 2. 启动信号START 时钟信号SCL保持高电平,数据信号SDA的电平被拉低(即负跳变)。启动信号必须是跳变信号,而且在建立该信号前必修保证总线处于空闲状态;
 3. 停止信号STOP 时钟信号SCL保持高电平,数据线被释放,使得SDA返回高电平(即正跳变),停止信号也必须是跳变信号。 
 4. 数据传送 SCL线呈现高电平期间,SDA线上的电平必须保持稳定,低电平表示0(此时的线电压为地电压),高电平表示1(此时的电压由元器件的VDD决定)。只有在SCL线为低电平期间,SDA上的电平允许变化。
 5. 应答信号ACK I2C总线的数据都是以字节(8位)的方式传送的,发送器件每发送一个字节之后,在时钟的第9个脉冲期间释放数据总线,由接收器发送一个ACK(把数据总线的电平拉低)来表示数据成功接收。
6. 无应答信号NACK 在时钟的第9个脉冲期间发送器释放数据总线,接收器不拉低数据总线表示一个NACK,NACK有两种用途: a. 一般表示接收器未成功接收数据字节; b. 当接收器是主控器时,它收到最后一个字节后,应发送一个NACK信号,以通知被控发送器结束数据发送,并释放总线,以便主控接收器发送一个停止信号STOP。
五. 寻址约定
 地址的分配方法有两种: 
 1. 含CPU的智能器件,地址由软件初始化时定义,但不能与其它的器件有冲突;
 2. 不含CPU的非智能器件,由厂家在器件内部固化,不可改变。 高7位为地址码,其分为两部分: 1. 高4位属于固定地址不可改变,由厂家固化的统一地址; 2. 低三位为引脚设定地址,可以由外部引脚来设定(并非所有器件都可以设定);


1. I2C总线特点

I2C总线最主要的优点是其简单性和有效性。由于接口直接在组件之上因此I2C总线占用的空间非常小减少了电路板的空间和芯片管脚的数量降低了互联成本。总线的长度可高达25英尺并且能够以10Kbps的最大传输速率支持40个组件。I2C总线的另一个优点是它支持多主控(multimastering) 其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然在任何时间点上只能有一个主控。

2. I2C总线工作原理

2.1 总线的构成及信号类型

I2C总线是一种串行数据总线只有二根信号线一根是双向的数据线SDA另一根是时钟线SCL。在CPU与被控IC之间、ICIC之间进行双向传送最高传送速率100kbps(快速模式400k)各种被控制电路均并联在这条总线上但就像电话机一样只有拨通各自的号码才能工作所以每个电路和模块都有唯一的地址在信息的传输过程中I2C总线上并接的每一模块电路既是主控器(或被控器)又是发送器(或接收器)这取决于它所要完成的功能。CPU发出的控制信号分为地址码和控制量两部分地址码用来选址即接通需要控制的电路确定控制的种类;控制量决定该调整的类别(如对比度、亮度等)及需要调整的量。这样各控制电路虽然挂在同一条总线上却彼此独立互不相关。

2.2 位的传输

SDA 线上的数据必须在时钟的高电平周期保持稳定,数据线的高或低电平状态只有在SCL 线的时钟信号是低电平时才能改变见图1

[转]单片机I2C总线及E2PROM应用实例_第1张图片

1

2.3 开始信号

SCL为高电平时SDA由高电平向低电平跳变开始传送数据。

2.4 结束信号:

SCL为高电平时SDA由低电平向高电平跳变结束传送数据。

[转]单片机I2C总线及E2PROM应用实例_第2张图片

2

2.5 应答信号:

接收数据的IC在接收到8bit数据后向发送数据的IC发出特定的低电平脉冲表示已收到数据。CPU向受控单元发出一个信号后等待受控单元发出一个应答信号CPU接收到应答信号后根据实际情况作出是否继续传递信号的判断。若未收到应答信号由判断为受控单元出现故障。

2.6 总线基本操作

I2C规程运用主/从双向通讯。器件发送数据到总线上则定义为发送器器件接收数据则定义为接收器。主器件和从器件(本文为AT24C01)都可以工作于接收和发送状态。 总线必须由主器件(通常为微控制器CPU)控制主器件产生串行时钟(SCL)控制总线的传输方向并产生起始和停止条件。SDA线上的数据状态仅在SCL为低电平的期间才能改变SCL为高电平的期间SDA状态的改变被用来表示起始和停止条件。参见图2

3. I2C应用实例AT24C01

AT24C系列串行E2PROM具有I2C总线接口功能功耗小宽电源电压(根据不同型号2.5V6.0V)工作电流约为3mA静态电流随电源电压不同为30μA110μA

3.1 AT24C系列 E2PROM接口及地址选择

由于I2C总线可挂接多个串行接口器件I2C总线中每个器件应有唯一的器件地址I2C总线规则器件地址为7位数据(即一个I2C总线系统中理论上可挂接128个不同地址的器件)它和1位数据方向位构成一个器件寻址字节最低位D0为方向位(/)。器件寻址字节中的最高4(D7D4)为器件型号地址不同的I2C总线接口器件的型号地址是厂家给定的AT24C系列E2PROM的型号地址皆为1010器件地址中的低3位为引脚地址A2A1A0对应器件寻址字节中的D3D2D1在硬件设计时由连接的引脚电平给定。

[转]单片机I2C总线及E2PROM应用实例_第3张图片

3

对于E2PROM的片内地址容量小于256字节的芯片(AT24C01/02)8位片内寻址(A0A7)即可满足要求。然而对于容量大于256字节的芯片8位片内寻址范围不够AT24C16相应的寻址位数应为11(211=2048)。若以256字节为1则多于8位的寻址视为页面寻址。在AT24C系列中对页面寻址位采取占用器件引脚地址(A2A1A0)的办法AT24C16A2A1A0作为页地址。凡在系统中引脚地址用作页地址后该引脚在电路中不得使用作悬空处理。AT24C系列串行E2PROM的器件地址寻址字节如图4所示表中P0 P1 P2表示页面寻址位。

[转]单片机I2C总线及E2PROM应用实例_第4张图片

4

3.2 AT89S51单片机与AT24C01 E2PROM通讯的硬件实现

是用AT89S51 P2口模拟I2C总线与E2PROM连接电路图(以AT24C01为例)由于AT24C01是漏极开路图中R1R2为上拉电阻(5.1k)A0A2地址引脚脚均接地。

[转]单片机I2C总线及E2PROM应用实例_第5张图片

5 AT24C0151单片机接口

3.3 AT24C系列 E2PROM读写操作软件实现方法

AT24C系列 E2PROM的读写操作完全遵守I2C总线的主收从发和主发从收的规则。

3.3.1 AT24C01的写操作

写操作分为字节写和页面写两种操作对于页面写根据芯片的一次装载的字节不同有所不同。关于页面写的地址、应答和数据传送的时序参见图和图7

连续写操作是对E2PROM连续装载n个字节数据的写入操作n随型号不同而不同一次可装载字节数也不同。

AT24C01/02 8字节/每页

AT24C04/08/16 16字节/每页

[转]单片机I2C总线及E2PROM应用实例_第6张图片

写一个字节的时序图

7写一页的时序图

3.3.2 AT24C01的读操作

读操作有三种基本操作:当前地址读、随机读和顺序读。图10给出的是顺序读的时序图。应当注意的是:最后一个读操作的第9个时钟周期不是不关心。为了结束读操作主机必须在第9个周期间发出停止条件或者在第9个时钟周期内保持SDA为高电平、然后发出停止条件。

AT24C系列片内地址在接收到每一个数据字节地址后自动加1故装载一页以内规定数据字节时只须输入首地址若装载字节多于规定的最多字节数数据地址将上卷前面的数据被覆盖。

连续读操作时为了指定首地址需要两个伪字节写来给定器件地址和片内地址重复一次启动信号和器件地址()就可读出该地址的数据。由于伪字节写中并未执行写操作地址没有加1。以后每读取一个字节地址自动加1

在读操作中接收器接收到最后一个数据字节后不返回肯定应答(保持SDA高电平)随后发停止信号。

[转]单片机I2C总线及E2PROM应用实例_第7张图片

读当前地址内容

[转]单片机I2C总线及E2PROM应用实例_第8张图片

9读任意地址内容

[转]单片机I2C总线及E2PROM应用实例_第9张图片

10读连续地址内容

汇编写的24C01 单字节读写程序

SDA        EQU       P2.0

SCL        EQU       P2.1

Address    EQU       08H

I2CData    EQU       09H

ORG 0000H

START:

       MOV      SP,#60H

       MOV      Address,#00H

       MOV      I2CData,#55H

       CALL      I2C_WRITE   ;写入数据

       MOV      I2CDATA,#0AAH   ;

       MOV      Address,#00H

       CALL      I2C_READ

       MOV      I2CData,A      ;读出数据

       NOP

       NOP

MAIN:

       JMP        MAIN

/*=======================================================

写一个字节    Address地址  I2CDatata写入的数据

=======================================================*/

I2C_WRITE:

    I2C_WRITE_A:

       LCALL    I2C_START

       MOV      A,#10100000B

       LCALL    I2C_SEND8BIT

       LCALL    I2C_ACK

       JC   I2C_WRITE_A      ;=1,表示无确认,再次发送

       MOV      A,Address

       LCALL    I2C_SEND8BIT

       LCALL    I2C_ACK      

       MOV      A,I2CData

       LCALL    I2C_SEND8BIT

       LCALL    I2C_ACK      

       LCALL    I2C_STOP

RET

/*=======================================================

读一个字节    Address地址  Data读出的数据

=======================================================*/

I2C_READ:

    I2C_READ_A:

       LCALL    I2C_START

       MOV      A,#10100000B

       LCALL    I2C_SEND8BIT

       LCALL    I2C_ACK

       JC   I2C_READ_A ;=1,表示无确认,再次发送

       MOV      A,Address

       LCALL    I2C_SEND8BIT

       LCALL    I2C_ACK      

    I2C_READ_B:

       LCALL    I2C_START

       MOV      A,#10100001B

       LCALL    I2C_SEND8BIT

       LCALL    I2C_ACK

       JC   I2C_READ_B

       LCALL    I2C_RECEIVE8BIT

       MOV      I2CData,A

       LCALL    I2C_ACK

       LCALL    I2C_STOP

RET

;=======================================================

;发送开始信号

I2C_START:

       SETB      SCL

       SETB      SDA

       NOP

       NOP

       CLR       SDA

       NOP

       NOP

       CLR       SCL

RET

;=======================================================

;发送结束信号

I2C_STOP:

       CLR       SDA

       NOP

       NOP

       SETB      SCL

       NOP

       NOP

       SETB      SDA

RET

;=======================================================

;发送接收确认信号

I2C_ACk:

       SETB      SDA

       SETB      SCL

       NOP

       NOP

       JB        SDA,I2C_ACK0

       CLR       C    

       SJMP      I2C_ACK_END

    I2C_ACK0:

       SETB      C

    I2C_ACK_END:

       CLR       SCL

RET

;=======================================================

;送八位数据

I2C_SEND8BIT:

       MOV      B,#08H

I2C_SEND8BIT_A:      

       RLC A

       MOV      SDA,C

       SETB      SCL

       NOP

       NOP

       CLR SCL

       DJNZ      B,I2C_SEND8BIT_A

RET

;=======================================================

;接收八位数据

I2C_RECEIVE8BIT:

       MOV      B,#08H

       CLR A

       SETB      SDA

I2C_RECEIVE8IT_A:

       SETB      SCL

       NOP

       NOP

       MOV       C,SDA

       RLC       A

       CLR       SCL

       DJNZ      B,I2C_RECEIVE8IT_A

RET

END

5. C语言写的24C01 单字节读写程序

#include

#include

sbit  SDA=0x90;

sbit  SCL=0x91;

//函数声明

unsigned char i2c_read(unsigned char);

void i2c_write(unsigned char,unsigned char);

void i2c_send8bit(unsigned char);

unsigned char i2c_receive8bit(void);

void i2c_start(void);

void i2c_stop(void);

bit i2c_ack(void);

//=======================================================

void main(void)

{

       unsigned char dd;

       i2c_write(0x00,0x55);

       _nop_();

       dd=i2c_read(0x00);

    for(;;)

       {}

}

 

/*=======================================================

i2c_write(地址,数据),写一个字节

=======================================================*/

void i2c_write(unsigned char Address,unsigned char Data)

{

       do{

          i2c_start();

          i2c_send8bit(0xA0);

       }while(i2c_ack());

       i2c_send8bit(Address);

       i2c_ack();

       i2c_send8bit(Data);

       i2c_ack();

       i2c_stop();

       return;

}

/*=======================================================

i2c_read(地址,数据),写一个字节

=======================================================*/

unsigned char i2c_read(unsigned char Address)

{

       unsigned char c;

       do{

           i2c_start();

           i2c_send8bit(0xA0);

       }while(i2c_ack());                //=1,表示无确认,再次发送

       i2c_send8bit(Address);

       i2c_ack();

       do{

           i2c_start();

           i2c_send8bit(0xA1);

       }while(i2c_ack());

       c=i2c_receive8bit();

       i2c_ack();

       i2c_stop();

       return(c);

}

//=======================================================

//发送开始信号

void i2c_start(void)

{

       SDA = 1;

       SCL = 1;

       SDA = 0;

       SCL = 0;

       return;

}

//发送结束信号

void i2c_stop(void)

{

       SDA = 0;

       SCL = 1;

       SDA = 1;

       return;

}    

//发送接收确认信号

bit    i2c_ack(void)

{

       bit    ack;

       SDA = 1;

       SCL = 1;

       if     (SDA==1)

           ack = 1;

       else

           ack = 0;

       SCL = 0;

       return  (ack);

}

//送八位数据

void i2c_send8bit(unsigned char b)

{

       unsigned  char a;

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

              {

              if ((b

                 SDA = 1;

              else

                 SDA = 0;

              SCL = 1;

              SCL = 0;

              }

       return;

}

//接收八位数据

unsigned char i2c_receive8bit(void)

{

       unsigned  char a;

       unsigned char b=0;

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

              {

              SCL = 1;

              b=b<<1;

              if (SDA==1)

              b=b|0x01;       //按位或

              SCL = 0;

              }

       return      (b);

}

6.I 2 C总线的应用中应注意的事项总结为以下几点 :

1 严格按照时序图的要求进行操作

2 若与口线上带内部上拉电阻的单片机接口连接,可以不外加上拉电阻。

3 程序中为配合相应的传输速率,在对口线操作的指令后可用NOP指令加一定的延时。

4 为了减少意外的干扰信号将EEPROM内的数据改写可在EEPROM内部没有用的空间写入标志字,每次上电时或复位时做一次检测,判断EEPROM是否被意外改写。

说明:本文的前3 是由本人从网上收集的各种文章中,修改、整理而成。

4节和第5 软件部份是本人自已编写的,由于水平有限,错误之处敬请谅解。

你可能感兴趣的:(芯片)