资讯详情

I2C总线原理和应用实例(51单片机和AT24C02的I2C通讯)

1. I2C简介

I2C(Inter-Integrated Circuit:内部集成电路)总线由Philips公司开发的简单双向二线系统同步串行总线。(来自百度百科)

总结其主要特点如下:

  1. 只有两条总线:串行数据线(SDA)串行时钟线(SCL),数据线用于表示数据,时钟线用于数据收发同步。
  2. 每个连接到总线的设备都有一个独立的地址,主机可以使用该地址访问不同的设备。I2C理论上,总线支持127个非保留地址(7位寻址,112个非保留地址;10位寻址,1008个非保留地址)。
  3. 两条线都是泄漏输出,所以总线通过上拉电阻连接到单元。I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。通常在标准模式下,使用10K上拉电阻;快速模式下使用2K上拉电阻(速度与电阻成反比)。
  4. 当多个主机同时使用总线时,为了防止数据冲突(仲裁:多个主机试图同时控制总线,但只允许其中一个控制总线 使传输不受损坏的过程)决定哪种设备占用总线。
  5. 传输方式有三种:标准模式传输速率为 100kbit/s,快速模式为 400kbit/s,高速模式下可达 3.4Mbit/s(大多 I2C 设备不支持高速模式),超快速模式,5Mbit/s。
  6. 芯片内集成了总线接口,无需特殊接口电路。

图(1)I2C通信设备常用的连接方式

1.1 I2C数据传输协议与格式

I2C协议定义了通信的开始、停止信号、数据有效性、响应、仲裁、时钟同步和地址广播

1.1.1 起始信号

SCL(串行时钟线)为高电平期,SDA(串行数据线)起始信号由高电平向低电平的下降边缘表示。起始信号由主机发出,起始信号生成后,总线被占用。如下图所示:

图(2)I2C通信启动信号

1.1.2 规定数据的有效性

I2C当总线传输数据时,SCL(串行时钟线)在高电平时,数据线上的数据必须保持稳定,只有在SCL(串行时钟线)上的信号是,数据线上的高低电平,如下图所示:

图(3)I2C信号数据有效性示意图

1.1.3应答响应

每当发送器件传输一个字节(八位二进制)的数据时,必须跟上一个验证位,即接收端控制SDA(串行数据线)来实现的,提醒发送端数据接收完成,可以继续进行下一字节传输。响应包括了 “ 应答(ACK:Acknowledgement)” 和 “ 非应答(NACK:Negative ACKnowledgment)两个信号。

在接收到字节数据或地址后,到字节数据或地址后,如果要求对方继续发送下一个字节数据,则需要向对方发送响应(ACK)”信号;

相反,如果要求对方结束数据传输,则应将数据传输发送给对方 “ 非应答(ACK)” 信号。

其中 “ 应答(ACK)” 信号定义为发送数据端释放SDA(串行数据线)此时,由于外部上拉电阻,上拉电平较高。数据接收器通过拉底SDA(串行数据线)SDA置0发送端接收接收端ACK响应信号后,数据可以继续传输。

图(4)I2C通讯ACK信号

“ 非应答(ACK)信号定义为:

图(5)I2C通讯NACK信号

1.1.4停止信号

SCL(串行时钟线)为高电平期,SDA(串行数据线)停止信号由低电平变为高电平。如下图所示:

图(6)I2C通讯停止信号

1.2 I2C数据传输和数据帧

1.2.1 I2C数据传输的基本格式

I2C数据传输必须从开始信号开始。传输数据的每个字节必须确保长度为8位,并首先传输到最高位置。每个传输必须遵循一个响应位置,即每帧有9位。

I2C数据帧有三种基本格式:

1.2.2发送一帧数据

图(7)I2C通信发送数据格式

根据数据帧组合前面的数据传输格式,可以获得I2C如果将一帧数据的时序图发送到地址为1010101的大写字母,V(对应ASCII代码:10101100)时,对应时序图如下:

图(8)I2C通指定地址发送字节数据

1.2.3接收一帧数据

图(8)I2C通信接收数据格式

需要注意的是,当读写控制位置为1时,相当于控制位置I2C总线控制权交给从机,因此在收到从机发送的数据后,应注意向从机发送响应,从机接收ACK继续传输信号后的下一个字节。因此,在传输最后一个字节时,主机可以发送响应或非响应。假设从机器上读出大写字母,地址为1010101V(对应ASCII代码:10101100)时,对应时序图如下:

图(9)I2C通信接收指定地址从机一字节数据

1.2.4 复合格式

图(10)I2C通信复合格式

当我们使用I2C通信时,复合格式是先发送再接收数据帧,其本质可以理解为接收数据,时序图略。

1.3 I2C的硬件电路

1.3.1 漏极开路输出OD输出(Open Drain)

漏极开路输出,即OD输出(Open Drain)主要示意图如下:

图(11)泄漏输出示意图

从上图可以看出,控制场效应关在内部电路中的导通和关闭。当导通时,输出电路直接连接到地面,直接输出低电平。当内部电路控制关闭时,场效应管成高电阻状态,从外部拉电阻输出高电平。另一方面,如果

图(11)左为场效应管关闭时,右为场效应管导通时

1.3.2为何选择漏极开漏输出?

比较普通单片机的推拉输出和泄漏输出

(12)推拉输出电路示意图

可以知道,推拉输出电路具有输出高低电平的能力,无需上拉电阻。当分析以下情况时,有两个主机,一个主机输出高电平,一个输出低电平。如下图所示:

此外,还可以实现泄漏输出 “ ” 。(关于 “ ” 内容可以找到更详细的信息)

2. AT24C02简介

2.1.1 2.1.1 AT24C02硬件特点

  • 宽工作电压1.8V~5.5V
  • 存储组织结构

- 24C02, 256 X 8 (2K bits) - 24C04, 512 X 8 (4K bits) - 24C08, 1024 X 8 (8K bits) - 24C16, 2048 X 8 (16K bits) - 24C32, 4096 X 8 (32K bits) - 24C64, 8192 X 8 (64K bits)

  • 两线 串行接口完全兼容I2C总线

  • I2C时钟频率位1MHz(5V),400kHz(1.8V,2.5V,2.7V)
  • 内部写周期(最大5ms)

2.1.2 AT24C02引脚排列与说明

 图(13)AT24C02引脚排列与说

2.1.2 AT24C02的电气特性

  图(14)AT24C02的交流电气特性

2.1.3 AT24C02的器件地址

  图(15)AT24CXX的器件地址

由以上可知AT24C02的器件地址高四位固定为1010,低三位可自定义

2.2 AT24C02写操作

2.2.1 字节写

字节写即写入一个字节,其格式如下:

 图(16)字节写

2.2.2 页写

在字节写基础上,不发送停止信号,而是接收到AT24C02应答后,继续发送7个数据,且没接收到一个数据,字地址的低3位,自动加1,,其格式如下:

 图(17)页写

2.3 AT24C02读操作

2.3.1 当前地址读

AT24C02内部带有地址计数器保存上一次访问时

 图(18)当前地址读

 2.3.2 随机地址读

随机读需要先写一个目标地址,发送完后,ATC24C02发送ACK信号,然后再重复一个开始信号,然后主机发送器件地址,再接收目标地址的数据,其格式类比于前文的复合格式。格式如下:

 图(19)随机地址读

此外还有顺序读,这里不再描述。

3. 51单片机与AT24C02的I2C通讯

3.1 准备工作

首先要知道AT24C02的器件地址,与SCL、SDA和51单片机的连接方式,我使用的51单片机连接方式如下:

  图(20)ATC24C02连线图

由上图与前文ATC24C02器件地址可知,高四位固定为1010,而低三位这里对应E0,E1,E2为000,因此该器件地址为1010000。其SCL,SDA连接再51单片机的P21,P20引脚。

3.2 代码实现与讲解

3.2.1 编写I2C.c文件

 首先定义SCL与SDA引脚,可根据实际连线修改,代码如下:

#include <REGX52.h>

sbit I2C_SCL = P2^1; 
sbit I2C_SDA = P2^0; 

根据I2C通讯的基本格式,我们可以拆分成6个独立的函数。

I2C_Start()对应I2C_开始信号

/**
  * @brief	I2C开始
  *	@param	无   
  *	@retval	无
  * @notes  I2C为推挽输出,因此将引脚置1实际上是关断mos管,由上拉电阻拉高至高电平
  *         置0时对应,mos管导通,将输出拉底
 */
void I2C_Start(void)
{
	I2C_SDA = 1;
    I2C_SCL = 1;//在开始之前,确保SDA、SCL为高电平状态,即空闲状态

    I2C_SDA = 0;//在SCL高电平期间,SDA由高电平置0表示I2C起始信号
    I2C_SCL = 0;//将SCL拉底,方便接下来的数据传输
}

I2C_SendByte()对应I2C发送一字节数据

/**
  * @brief	I2C发送一个字节
  *	@param	Byte 要发送的字节 
  *	@retval	无
  * @notes  
 */
void I2C_SendByte(unsigned char Byte)
{
    unsigned char i = 0;
    for (i=0;i<8;i++)         //循环8次,发送8位数据
    {
        I2C_SDA = Byte&(0x80>>i); //I2C数据传输由高位开始,0x80对应1000 0000
                                  //相与之后只保留最高位,右移i位
                                  //以此方法从高到低取出每一位
        I2C_SCL = 1;
        I2C_SCL = 0;
    }
}

I2C_ReceiveByte()对应I2C接收一字节数据

/**
  * @brief	I2C接收一个字节
  *	@param	无  
  *	@retval	Byte 返回接收的字节
  * @notes  
 */
unsigned char I2C_ReceiveByte(void)
{
    unsigned char i ,Byte = 0x00;
	I2C_SDA = 1;				//将SDA控制权交于从机
    for(i=0;i<8;i++)            //循环8次,读取八位数据
    {            
        I2C_SCL = 1;
		if (I2C_SDA)            //如果SDA线上是高电平
        { 
            Byte |= (0x80>>i);  //0x80对应1000 0000,
                                // |= 将数据存入Byte中
        }
		I2C_SCL = 0;
    }
    return Byte;
}

I2C_SendAck()对应I2C发送ACK应答信号

/**
  * @brief	I2C发送应答
  *	@param	AckBit 应答位 0为应答,1为不应达
  *	@retval	无
  * @notes  
 */
void I2C_SendAck(unsigned char AckBit)
{
    I2C_SDA = AckBit; 
    I2C_SCL = 1;
    I2C_SCL = 0;
}

I2C_ReceiveAck()对应接收应答

/**
  * @brief	I2C接收应答
  *	@param	无
  *	@retval	Ackbit 接收应答位
  * @notes  
 */
unsigned char I2C_ReceiveAck(void)
{
    unsigned char Ackbit;
    I2C_SDA = 1;        
    I2C_SCL = 1;       //先将SDA,SCL释放 
    Ackbit = I2C_SDA;  //在SCL高电平期间,SDA为0则表示接收到了从机应答
    I2C_SCL = 0;
    return Ackbit;     
}

I2C_Stop()对应I2C停止信号

/**
  * @brief	I2C停止
  *	@param	无 
  *	@retval	无
  * @notes  
 */
void I2C_Stop(void)
{
    I2C_SDA = 0;//保证SDA为0
    I2C_SCL = 1;
    I2C_SDA = 1;//SCL高电平期间,将SDA从0置1为I2C停止信号,且结束后, 
                //SDA、SCL均为高电平,表示为空闲状态
}

3.2.2 编写I2C.h文件

包含I2C.c各函数方便调用

#ifndef __I2C_H__
#define __I2C_H__
#include "REGX51.H"
void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(unsigned char Byte);
unsigned char I2C_GetByte(void);
void I2C_SendAck(unsigned char AckBit);
unsigned char I2C_GetAck(void);

#endif

3.2.3 编写AT24C02.c文件

首先包含I2C.h头文件与定义AT24C02器件地址

#include "I2C.h"
#define AT24C02_ADDRESS 0xa0 //0xa0对应1010 000 0,最后一位置0,为写模式

AT24C02_WriteByte()AT24C02写一字节数据

/**
  * @brief	AT24C02写一字节数据
  *	@param	WordAddress 写入字节的地址
  * @param	Data 写入的数据   
  *	@retval	无
  * @notes  
 */
void AT24C02_WriteByte(unsigned char WordAddress, unsigned char Data)
{
    I2C_Start();                   //I2C开始
    I2C_SendByte(AT2402_ADDRESS);  //找到ATAT24C02的器件地址
    I2C_ReceiveAck();              //接收AT24C02应答  
    I2C_SendByte(WordAddress);     //写入数据的地址
    I2C_ReceiveAck();              //接收AT24C02应答    
    I2C_SendByte(Data);            //写入数据
    I2C_ReceiveAck();              //接收AT24C02应答    
    I2C_Stop();                    //I2C停止
}

ATC2402_ReadByte()读取一字节

/**
  * @brief	AT24C02读取一字节数据
  *	@param	WordAddress	 读取字节的8位地址 
  *	@retval	Data 读取的字节
  * @notes  
 */
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
    unsigned char Data;
    I2C_Start();					//I2C开始
    I2C_SendByte(AT2402_ADDRESS);	//找到ATAT24C02的器件地址
    I2C_ReceiveAck();				//接收AT24C02应答  
    I2C_SendByte(WordAddress);		//写入数据的地址
    I2C_ReceiveAck();				//接收AT24C02应答
    I2C_Start();               		//I2C开始        
    I2C_SendByte(AT2402_ADDRESS | 0x01);//7位器件地址,第八位置1,表示读模式
    I2C_ReceiveAck();				//接收AT24C02应答 	
    Data = I2C_ReceiveByte();			//读取AT24C02的数据
    I2C_SendAck(1);                 //不发送应答,发送与不发生都可以
    I2C_Stop();						//I2C停止
    return Data;
}

3.2.4 编写AT24C02.h文件

#ifndef ___AT24C02_H__
#define __AT24C02_H__
void AT24C02_WriteByte(unsigned char WordAddress, unsigned char Data);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);
#endif

4. 代码验证

在main.c中编写程序,通过LCD1602验证是否能读取出存入AT24C02的数据,此部分可以自行修改,这里只作为验证。

#include <REGX52.H>
#include "LCD1602.h"
#include "AT24C02.h"
unsigned char Data;
void main()
{
	LCD_Init();
	AT24C02_WriteByte(1,66);
	Data = AT24C02_ReadByte(1);
	LCD_ShowNum(2,1,Data,3);//在LCD第1行,第1列显示显示读出的数据,长度为3
	while(1)
	{
	}
}

实验现象: 

  图(21)实验现象

程序本应该显示的是写入的66,但是却显示错误,这里就需要回到AT24C02的电气特性了,其规定了写周期最大为5ms,而写入之后马上读取,可以理解为,数据还没有真正存进AT24C02中,所以要加入延时环节,这里延时5ms后才读取。

修改main.c程序如下:

#include <REGX52.H>
#include "LCD1602.h"
#include "AT24C02.h"
#include "Delay.h"
unsigned char Data;
void main()
{
	LCD_Init();
	AT24C02_WriteByte(1,66);
	Delayms(5);
	Data = AT24C02_ReadByte(1);
	LCD_ShowNum(1,1,Data,3);
	while(1)
	{
	}
}

实验现象: 

   图(21)实验现象

实验成功。

本文到此结束,本文如有错误之处,希望大家不吝赐教,欢迎批评指正!

标签: 2402集成电路

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台