I2C通信协议详解

(一)I2C总线简介

        I2C总线是Philips公司在八十年代初推出的一种同步串行、半双工的总线,主要用于近距离、低速的芯片之间的通信。

        I2C总线有两根双向的信号线,一根数据线SDA用于收发数据,一根时钟线SCL用于通信双方时钟的同步。

        I2C总线是一种多主机总线,连接在I2C总线上的器件分为主机和从机。主机有权发起和结束一次通信,从机只能被动呼叫。

        标准模式下,基本的I2C总线规范的规定的数据传输速率为100kb/s

        快速模式下,数据传输速率为400Kb/s

        高速模式下,数据传输速率为3.4Mb/s        

        当总线上有多个主机同时启用总线时,I2C也具备冲突检测和仲裁的功能来防止错误产生;每个连接到I2C总线上的器件都有一个唯一的地址(7bit),且每个器件都可以作为主机也可以作为从机(但同一时刻只能有一个主机),总线上的器件增加和删除不影响其他器件正常工作;I2C总线在通信时总线上发送数据的器件为发送器,接收数据的器件为接收器。

I2C协议仅需要一个SDA和SCL引脚。SDA是串行数据线的缩写,而SCL是串行时钟线的缩写。

但这两条数据线需要接上拉电阻,一般接2KΩ或4.7KΩ。这是因为I²C总线(SDASCL)内部都使用漏极开路驱动器(开漏驱动),因此SDASCL 可以被拉低为低电平,但是不能被驱动为高电平,所以每条线上都要使用一个上拉电阻,默认情况下将其保持在高电平;

(二)I2C总线的寻址

每个连接在I2C总线的器件,都具有一个唯一确定的地址。在任何时刻,I2C总线上只能有一个主机对总线实行控制权,分时地实现点对点的数据传送。
器件(从机)的地址由7位组成,它与1位方向位构成了I2C总线数据传输时起始状态Start之后第1个字节。

当主机发送了第1个字节后,系统中的每个从机(器件)都在起始状态Start之后把高7位与本机的地址比较,如果与本机地址一样,则该从机被主机选中,是接收数据还是发送数据由R/W确定

从机器件地址由固定位和可编程位组成。固定位由器件出厂时给定,用户不能自行设置,它是器件的标识码。

当系统中使用了多个相同器件时,从机地址中的可编程位,可使这些器件具有不同的地址;这些可编程位也规定了I2C总线上同类芯片的最大个数。如一个从机的7位寻址位有4位是固定位,3位是可编程位,这时仅能寻址8个同样的器件即可以有8个同样的器件接入到该I2C总线系统中。
 

(三)I2C总线通信协议使用方法

I2C总线上主设备和从设备进行数据传输时遵循以下协议格式。数据通过一条SDA数据线在主设备和从设备之间传输01的串行数据。

只有在总线空闲时才允许启动数据传送

在数据传送过程中当时钟线为高电平时数据线必须保持稳定状态不允许有跳变,时钟线为高电平时数据线的任何电平变化将被看作总线的起始或停止信号。

 整个I2C串行通信过程可以分为:起始信号,地址位,读写位,应答位,数据位,停止条件,具体如下所示: 1、起始信号

 

起始(START)状态:I2C总线传输过程中,当时钟线SCL为高电平时,数据线SDA出现高电平到低电平跳变时,标志着I2C总线传输数据开始。

当主设备决定开始通讯时,需要发送开始信号,需要执行以下动作;

  • 先将SDA线从高压电平切换到低压电平;
  • 然后将SCL从高电平切换到低电平;

在主设备发送开始条件信号之后,所有从机即使处于睡眠模式也将变为活动状态,并等待接收地址位

/*******************************************
函数:I2C_Start()
功能:I2C总线通信起始信号
参数:无
返回值:无
*******************************************/
void I2C_Start()
{
	I2C_SDA = 1;
	I2C_SCL = 1;	//总线空闲时SDA SCK都为高电平
	//时钟线保持高电平4us之后 数据线电平从高到低的跳变 作为I2C总线的起始信号
	I2CDelay();		//12MHz 延时4us
	I2C_SDA = 0;
	I2CDelay();		//12MHz 延时4us
	I2C_SCL = 0;
}

2、数据传输

I2C总线进行数据传送时,时钟信号SCL为高电平期间,数据线SDA上的数据必须保持稳定只有在时钟信号SCL信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

I2C总线上主机与从机之间一次传送的数据成为一帧,由启动信号、若干个数据字节、应答位和停止信号组成,数据传输的基本单元位一位数据。

数据传输时,每一个字节必须保证是8位长度。数据传输先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位。(即一帧数据共有九位)

I2C总线数据传送时每成功地传送一个字节数据后,接收器都必须产生一个应答信号,应答的器件在第9个时钟周期时将SDA 线拉低表示其已收到一个8 位数据,必须要跟一个应答信号,否则主机和从机都不知道数据是否传输完毕。

每写8位数据之后要读取一次ACK应答信号。应答位是从机将SDA电平拉低,应答信号为0说明有应答

/*******************************************
函数:I2C_WriteByte(unsigned char dat)
功能:I2C总线通信写入数据
参数:unsigned char dat---一个字节 8位数据
返回值:无
*******************************************/
void I2C_WriteByte(unsigned char dat)
{
	unsigned char temp;
	for(temp = 0x80; temp != 0; temp >>= 1)
	{
		if(temp & dat)
		{
			I2C_SDA = 1;	
		}
		else 
		{
			I2C_SDA = 0;	
		}
		I2CDelay();		//12MHz 延时4us 维持SCK低,写入数据
		I2C_SCL = 1;	//拉高器件读数据位
		I2CDelay();		//12MHz 延时4us	 维持SCK高,器件读取数据
		I2C_SCL = 0;	//SCK拉低,为下次数据位变化做准备
	}	
}
/*******************************************
函数:I2C_RecvAck()
功能:I2C总线通信读取应答位
参数:无
返回值:bit---0 器件有应答 1 器件无应答 
*******************************************/
bit I2C_RecvAck()
{
	bit ack;
	I2C_SDA = 1;//主机主动释放数据总线,为读ack做准备 器件存在是拉低电平
	I2CDelay();	//维持SCL低 4us
	I2C_SCL = 1;//拉高时钟线,主机读取ACK,若ACK存在,从机会将SDA拉低
	ack = I2C_SDA; 
	I2CDelay();	//维持SCL高 4us
	I2C_SCL = 0;
	
	return ack;		
}

I2C总线上传输的数据信号是广义的,既包括地址信号,有包括真正的数据信号。

在起始信号Start之后,必须首先传送的第一个字节必须是是从机的器件地址数据,只有器件地址传送完成之后,才能进行后续操作。

 I2C总线的一次数据传输过程可以有几种数据传输的组合方式:

(1)主机向从机发送数据,数据的整个传送方向在整个传送过程中保持不变。

 (2)在第一个字节后,主机立刻从从机读取数据

 (3)组合格式,在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反向。

3、停止信号

 停止(STOP)状态:I2C总线传输过程中,当时钟线SCL为高电平时,数据线SDA出现低电平到高电平跳变时,标志着IPC总线传输数据结束。

当主设备决定结束通讯时,需要发送开始信号,需要执行以下动作;

  • 先将SDA线从低电压电平切换到高电压电平;
  • 再将SCL线从高电平拉到低电平;
/*******************************************
函数:I2C_Stop()
功能:I2C总线通信结束信号
参数:无
返回值:无
*******************************************/
void I2C_Stop()
{
	I2C_SDA = 0;
	I2C_SCL = 0;//初始都给低电平	
	//时钟线保持高电平4us之后 数据线电平从低到高的跳变 作为I2C总线的结束信号
	I2CDelay();		//SCL维持4us低
	I2C_SCL = 1;
	I2CDelay();		//SCL维持4us高
	I2C_SDA = 1;	//拉高SDA
	I2CDelay();
}

(四)I2C总线完整工作过程

1、主机发送启动信号

2、主机发送从设备器件地址

        主设备向每个从机发送要与之通信的从机的7位或10位地址,以及相应的读/写位。

3、主机接收应答

        每个从设备将主设备发送的地址与其自己的地址进行比较。如果地址匹配,则从设备通过将SDA线拉低一位以表示返回一个ACK位

        如果来自主设备的地址与从机自身的地址不匹配,则从设备将SDA线拉高,表示返回一个NACK位

4、传输数据

        主设备发送或接收数据到从设备;

5、接收应答

        在传输完每个数据帧后,接收设备将另一个ACK位返回给发送方,以确认已成功接收到该帧:

6、结束信号

        为了停止数据传输,主设备将SCL切换为高电平,然后再将SDA切换为高电平,从而向从机发送停止条件;

  • 8
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Linux操作系统下,I2C是一种常见的通信协议,用于连接主机与各种外部设备,如传感器、扩展模块等。在编写Linux下的I2C代码时,需要使用Linux提供的I2C子系统,它包含了各种函数和结构体,用于管理和操作I2C总线。 首先,我们需要创建一个I2C设备的句柄,可以使用函数`open()`打开对应的设备文件,如`/dev/i2c-0`。如果成功打开设备文件,就可以使用`ioctl()`函数进行各种设置和控制操作。例如,通过`I2C_SLAVE`命令可以指定要与之通信的从设备的地址。 接下来,我们可以使用`i2c_smbus_*()`系列函数来进行I2C通信。这些函数封装了一些常用的I2C操作,如读写字节、读取寄存器等。对于特定的I2C设备,可能还需要使用其他特定的函数进行访问。 在使用`i2c_smbus_*()`函数进行通信时,需要注意传入的参数。例如,读操作需要指定要读取的字节个数,写操作需要指定要写入的数据。同时,还要注意处理函数的返回值,以便判断操作是否成功。 另外,可以使用`close()`关闭I2C设备的句柄,释放资源。 需要注意的是,编写Linux下的I2C代码需要有一定的编程基础和对Linux系统的了解。还需要查阅相关的资料,了解具体的设备地址、寄存器映射关系,以及所需的操作命令和数据格式等。 总之,编写Linux下的I2C代码需要熟悉Linux系统提供的I2C子系统和相关函数,同时还需要了解具体的设备和通信协议的细节。只有掌握了这些知识,才能有效地进行I2C通信,并与外部设备进行数据交互。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值