i2c协议解读(附读写过程c代码)

原文链接:https://blog.csdn.net/qq_39759656/article/details/81232909

1.what is I2C?

简单讲就是用来传输数据的两根线:一根数据线(SDA)一根时钟线(SCL)

2.I2C怎么传输的?

(1)基本过程:
1.主机发出开始信号
2.主机接着发出一字节的从机地址信息,其中最低位为读写控制码(1为读、0为写), 高七位为从机器件地址
3.从机发出认可信号
4.主机开始发送信号,每发完一字节后,从机发出认可信号给主机
5.主机发出停止信号
在这里插入图片描述

(2)对以上信号的具体说明:
开始信号:在时钟线为高电平期间,数据线由高变低,将产生一个开始信号
停止信号:在时钟线为高电平期间,数据线由低变高,将产生一个停止信号
应答信号:即认可信号,主机写从机时,每写完一个字节,如果正确从机将在下一个时钟周期将数据线拉低,以告诉主机操作有效。在主机读从机的时候,正确读完一个字节后,主机在下一个时钟周期同样也要将数据线拉低,发出认可信号,告诉从机所发数据已经收妥(注:读从机时主机在最后一个字节数据接收完后,不发应答,直接发停止信号)

注意:任何在时钟线为高电平期间的数据线上的电平改变都被认为是起始和停止信号,所以数据改变必须要在时钟为低电平时改变。

(3)数据格式:

在这里插入图片描述

I2C支持两种数据格式:

7bit/10bit寻址数据格式

7bit/10bit寻址和重复开始信号的格式

从设备地址:

总线上每个设备都有自己的一个addr,共7个bit,广播地址全0.
系统中可能有多个同种芯片,为此addr分为固定部分和可编程部份,细节视芯片而定,看datasheet。
在这里插入图片描述

硬件结构:

每一个I2C总线器件内部的SDA、SCL引脚电路结构都是一样的,引脚的输出驱动与输入缓冲连在一起。其中输出为漏极开路的场效应管、输入缓冲为一只高输入阻抗的同相器。这种电路具有两个特点:

(1)由于 SDA、SCL 为漏极开路结构,借助于外部的上拉电阻实现了信号的“线与”逻辑;
(2)引脚在输出信号的同时还将引脚上的电平进行检测,检测是否与刚才输出一致。为 “时钟同步”和“总线仲裁”提供硬件基础。

3.几个I2C的疑问

(1)模拟I2C与硬件I2C有什么区别

原理上:硬件I2C(提供专门的SDA,SCL口)的时钟是系统产生的,一般由晶振分频产生。模拟I2C通过编程模拟时钟线和数据线。

控制上:硬件I2C通过硬件中断实现各种操作。模拟I2C没有中断的概念,通过IO口电平置高置低来实现写入和读取。

性能上:硬件模式更高效更稳定。

(2)MPU6050和MPU9250的I2C

之前对于这两个芯片的通过I2C的操作地址有点乱,如要获取到加速度计的原始值,首先要发送开始信号,然后是发送从机地址(I2C的设备地址),然后发送寄存器的地址(这里很多人会搞乱了,因为要发两次地址,第一次是I2C设备地址,第二次是加速度计的地址),接下来才能获取到原始数据。

(3)slave address
i2c通讯是master下可能挂多个slave,每个slave拥有不同的slave address,master通过不同的address设置来遍历下挂的slave,每个i2c接口的芯片会有不同的slave addres配置。芯片厂商在设计i2c接口时会设计多个address配置,以避免多slave访问时地址重复,如下举例tmp513温控芯片slave address的设置:
在这里插入图片描述

4.怎么实现I2C?

之前的所有铺垫都是为了实现I2C通讯,所以怎么用代码实现也是尤为关键的一个问题。

下面是是I2C的读写流程:
在这里插入图片描述

写寄存器的标准流程为:

  1. Master发起START
  2. Master发送I2C addr(7bit)和w操作0(1bit),等待ACK
  3. Slave发送ACK
  4. Master发送reg addr(8bit),等待ACK
  5. Slave发送ACK
  6. Master发送data(8bit),即要写入寄存器中的数据,等待ACK
  7. Slave发送ACK
  8. 第6步和第7步可以重复多次,即顺序写多个寄存器
  9. Master发起STOP
void Tmp513_Reg_Write(unsigned char regaddr, unsigned short regdata) 
{ 
	unsigned int i;
	unsigned int j;
	unsigned char opcode;
	unsigned char low_regdata;
	unsigned char high_regdata;
	
	low_regdata = regdata & 0xff;
	high_regdata = (regdata >> 8) & 0xff;

	TMP513_DATA_OUT;

	// START
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	opcode = TMP513_WRITE_CODE;

	// Operation code
	for(i = 0; i < 7; i++){
		(TMP513_SDA->DATA) = opcode;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
		opcode = opcode >> 1;
	}
	(TMP513_SDA->DATA) = opcode;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Reg address
	TMP513_DATA_OUT;
	for(i = 0; i < 7; i++){
		(TMP513_SDA->DATA) = (regaddr >> (7-i));
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
	}
	(TMP513_SDA->DATA) = regaddr;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Write high byte
	TMP513_DATA_OUT;
	for(j = 0; j < 7; j++){
		(TMP513_SDA->DATA) = (high_regdata >> (7 - j));
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
	}
	(TMP513_SDA->DATA) = high_regdata;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;
	
	// Write low byte
	TMP513_DATA_OUT;
	for(j = 0; j < 7; j++){
		(TMP513_SDA->DATA) = (low_regdata >> (7 - j));
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
	}
	(TMP513_SDA->DATA) = low_regdata;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Stop
	TMP513_DATA_OUT;
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SDA->DATA) = 1;

	TMP513_DATA_IN;
}

读寄存器的标准流程为:

  1. Master发送I2C addr(7bit)和w操作1(1bit),等待ACK
  2. Slave发送ACK
  3. Master发送reg addr(8bit),等待ACK
  4. Slave发送ACK
  5. Master发起START
  6. Master发送I2C addr(7bit)和r操作1(1bit),等待ACK
  7. Slave发送ACK
  8. Slave发送data(8bit),即寄存器里的值
  9. Master发送ACK
  10. 第8步和第9步可以重复多次,即顺序读多个寄存器

以上通讯流程模拟I2C跟硬件I2C都类似,不同的地方是读写:

附i2c读写过程驱动c代码:

unsigned short Tmp513_Reg_Read(unsigned char regaddr) 
{ 
	unsigned int i;
	unsigned int j;
	unsigned char opcode;
	unsigned short readdata;

	// Register pointer set
	TMP513_DATA_OUT;
	
	// START
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Operation code
	opcode = TMP513_WRITE_CODE;
	for(i = 0; i < 7; i++){
		(TMP513_SDA->DATA) = opcode;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
		opcode = opcode >> 1;
	}
	(TMP513_SDA->DATA) = opcode;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Reg address
	TMP513_DATA_OUT;
	for(i = 0; i < 7; i++){
		(TMP513_SDA->DATA) = (regaddr >> (7-i));
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
	}
	(TMP513_SDA->DATA) = regaddr;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;
	
	// Stop
	Delay(TMP513_DELAY);
	TMP513_DATA_OUT;
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SDA->DATA) = 1;
	TMP513_DATA_IN;

	// Read register
	// Start
	TMP513_DATA_OUT;
	Delay(TMP513_DELAY);
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Operation code
	opcode = TMP513_READ_CODE;
	for(i = 0; i < 7; i++){
		(TMP513_SDA->DATA) = opcode;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
		opcode = opcode >> 1;
	}
	(TMP513_SDA->DATA) = opcode;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Read reg's high byte
	readdata = 0;
	for(j = 0; j < 8; j++){
		Delay(TMP513_DELAY);
		readdata = 	readdata << 1;
		readdata = ((TMP513_SDA->DATA) & 0x00000001) | readdata;
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
	}

	// Master ACK
	Delay(TMP513_DELAY);
	TMP513_DATA_OUT;
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;
	TMP513_DATA_IN;

	// Read reg's low byte
	for(j = 0; j < 8; j++){
		Delay(TMP513_DELAY);
		readdata = 	readdata << 1;
		readdata = ((TMP513_SDA->DATA) & 0x00000001) | readdata;
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
	}

	// Master NO ACK
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Stop
	TMP513_DATA_OUT;
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SDA->DATA) = 1;

	TMP513_DATA_IN;
	return readdata;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章