I2C 总线精要

来自Jack's Lab
(版本间的差异)
跳转到: 导航, 搜索
(传输距离)
(参考)
 
(未显示1个用户的3个中间版本)
第108行: 第108行:
 
<br>
 
<br>
  
 +
 +
<br><br>
 +
 +
== 实例 ==
 +
 +
Touch the head, send i2c data (No other device):
 +
 +
* Write 0x00 (no ack)
 +
* Write 0x28 (no ack) 0x44 (no ack)
 +
* Write 0x28 (no ack) 0x44 (no ack)
 +
 +
[[文件:Touch-iKair-head-then-send-data.png]]
 +
 +
<br>
 +
 +
Send i2c data (with pm2.5 sensor):
 +
 +
  0x00 0x12 0x1E 0x03 0x02 0x00 0x00 0x1A
 +
 +
[[文件:Touch-iKair-head-then-send-data2.png]]
 +
 +
 +
第一个字节 0x00 是目标传感部件环灯广播地址,改为 0x60 就只点亮 PM2.5 的环灯 (i2c addr = 0x30), 0x50 就只点亮甲醛传感部件上的环灯 (i2c addr = 0x28)
  
 
<br><br>
 
<br><br>
第133行: 第156行:
  
 
<source lang=cpp>
 
<source lang=cpp>
SDA = SCL = 1;
 
SCL_IN = SDA_IN = 0;
 
  
void i2c_dly(void)
+
#define I2C_DELAY 4 /* us delay */
{
+
#define I2C_MAXWAIT 5000
}
+
 
 +
#define TWI_SDA 11 /* PIN14_PD7 */
 +
#define TWI_SCL 16 /* PIN21_PF2 */
 +
 
 +
#define SDA_LOW() digitalWrite(TWI_SDA, LOW)
 +
#define SDA_HIGH() digitalWrite(TWI_SDA, HIGH)
 +
#define SCL_LOW() digitalWrite(TWI_SCL, LOW)
 +
#define SCL_HIGH() digitalWrite(TWI_SCL, HIGH)
 +
 
 +
#define SDA_READ() digitalRead(TWI_SDA)
 +
#define SCL_READ() digitalRead(TWI_SCL)
 +
 
 +
#define i2c_delay() delayMicroseconds(4)
  
 
void i2c_start(void)
 
void i2c_start(void)
 
{
 
{
  SDA = 1;             // i2c start bit sequence
+
SDA_HIGH(); // i2c start bit sequence
  i2c_dly();
+
i2c_delay();
  SCL = 1;
+
 
  i2c_dly();
+
SCL_HIGH();
  SDA = 0;
+
i2c_delay();
  i2c_dly();
+
 
  SCL = 0;
+
SDA_LOW();
  i2c_dly();
+
i2c_delay();
 +
 
 +
SCL_LOW();
 +
i2c_delay();
 
}
 
}
  
 
void i2c_stop(void)
 
void i2c_stop(void)
 
{
 
{
  SDA = 0;             // i2c stop bit sequence
+
SDA_LOW(); // i2c stop bit sequence
  i2c_dly();
+
i2c_delay();
  SCL = 1;
+
 
  i2c_dly();
+
SCL_HIGH();
  SDA = 1;
+
i2c_delay();
  i2c_dly();
+
 
 +
SDA_HIGH();
 +
i2c_delay();
 
}
 
}
  
unsigned char i2c_rx(char ack)
+
uint8_t i2c_rx(uint8_t ack)
 
{
 
{
char x, d=0;
+
uint8_t x, d = 0;
  SDA = 1;  
+
 
  for(x=0; x<8; x++) {
+
SDA_HIGH();
    d <<= 1;
+
 
    do {
+
for (x = 0; x < 8; x++) {
      SCL = 1;
+
 
    }
+
d <<= 1;
    while(SCL_IN==0);   // wait for any SCL clock stretching
+
 
    i2c_dly();
+
do {
    if(SDA_IN) d |= 1;
+
SCL_HIGH();
    SCL = 0;
+
} while (SCL_IN == 0); // wait for any SCL clock stretching
  }  
+
 
  if(ack) SDA = 0;
+
i2c_delay();
  else SDA = 1;
+
 
  SCL = 1;
+
if (SDA_READ())
  i2c_dly();             // send (N)ACK bit
+
d |= 1;
  SCL = 0;
+
SCL_LOW();
  SDA = 1;
+
}
  return d;
+
 
 +
if (ack)
 +
SDA_LOW();
 +
else
 +
SDA_HIGH();
 +
 
 +
SCL_HIGH();
 +
i2c_delay(); // send (N)ACK bit
 +
SCL_LOW();
 +
SDA_HIGH();
 +
 
 +
return d;
 
}
 
}
  
bit i2c_tx(unsigned char d)
+
uint8_t i2c_tx(unsigned uint8_t d)
 
{
 
{
char x;
+
uint8_t x;
static bit b;
+
static uint8_t b;
  for(x=8; x; x--) {
+
 
    if(d&0x80) SDA = 1;
+
for (x = 8; x; x--) {
    else SDA = 0;
+
if (d & 0x80)
    SCL = 1;
+
SDA_HIGH();
    d <<= 1;
+
else
    SCL = 0;
+
SDA_LOW();
  }
+
SCL_HIGH();
  SDA = 1;
+
d <<= 1;
  SCL = 1;
+
SCL_LOW();
  i2c_dly();
+
}
  b = SDA_IN;         // possible ACK bit
+
 
  SCL = 0;
+
SDA_HIGH();
  return b;
+
SCL_HIGH();
 +
 
 +
i2c_delay();
 +
 
 +
b = SDA_READ(); // possible ACK bit
 +
 
 +
SCL_LOW();
 +
return b;
 
}
 
}
 
</source>
 
</source>
第267行: 第323行:
 
* http://wiki.csie.ncku.edu.tw/embedded/I2C
 
* http://wiki.csie.ncku.edu.tw/embedded/I2C
  
<br><br>
 
<br><br>
 
<br><br>
 
<br><br>
 
<br><br>
 
<br><br>
 
<br><br>
 
<br><br>
 
<br><br>
 
<br><br>
 
<br><br>
 
<br><br>
 
<br><br>
 
 
<br><br>
 
<br><br>
 
<br><br>
 
<br><br>

2020年6月24日 (三) 15:01的最后版本


目录

[编辑] 1 概述

由 Philips (现 NXP) 于 80 年代开发,是一种很可靠的低速外设总线,广泛应用于 PC 和嵌入式系统:各种 I2C 接口的传感器、OLED 显示屏、SMBus (System Management Bus, )、PMBus (Power Management Bus)、IPMI (Intelligent Platform Management Interface)、DDC (Display Data Channel)、ATCA (Advanced Telecom Computing Architecture)......


两根线: SDA, SCL,皆拉高 (pull-up),总线空闲时,两线皆为高电平

  • SDA : Serial Data Line, holds Data or address signal
  • SCL : Serial Clock Line, holds Clock signal
  • IO must be open drain (or open collector in TTL)


I2C data transfer 640.png

I2c com.png


  • 7bit 地址,保留 16 个,可用 112;标准模式 (100 Kbit/s)、低速模式 (10 Kbit/s)
  • 10bit 地址;快速模式 (400 Kbit/s)、高速模式 (3.4 Mbit/s)


数据传输:

  • 7bit mode: 发送 7bit 的address (MSB) 及一位的 W(0) / R(1) 后,该 address 的 slave 端会发送一个 Ack (acknowledge) 位,ACK = 0 表设定成功 (Slave 把 SDA 拉到 LOW),开始数据传送
  • 每次传一个字节,每个字节都跟著一个 ACK bit,由 Slave 端发送(把 SDA 拉 LOW),否则 Master 认为字节写失败,发出 STOP 信号或是重新发送 START 信号
  • 在每个 byte 之间 Slave 端可以把 SCL 拉低来强制传输暂停
  • SDA 只能在 SCL 为 LOW 时改变
  • 在 SCL 为 HIGH 时,SDA 必须是 stable 的,除非 Start/Stop condition


10bit mode:

用两个 byte 来传送 address。第一个 byte 的前五位需为 “1111 0” 来表示要使用 10-bit addressing

I2c-10bit-addr.gif



[编辑] 2 开始位

Start Condition

SDA,SCL 皆为高电平时,拉低 SDA

void i2c_start(void)
{
  SDA = 1;             // i2c start bit sequence
  SCL = 1;
  i2c_delay();

  SDA = 0;
  i2c_delay();

  SCL = 0;
  i2c_delay();
}


[编辑] 3 结束位

Stop Condition

SCL 上升沿后, SDA 从 0 拉到 1

void i2c_stop()
{
  SDA = 0;             // i2c stop bit sequence
  i2c_delay();
  SCL = 1;
  i2c_delay();
  SDA = 1;
  i2c_delay();
}


[编辑] 4 状态图

[编辑] 4.1 主发从收

  • 开始位后,发 7 位地址,随后读写位为 0 (Write)
  • 收到 slave 反馈的 ACK 位后,开始写字节
  • 每写一个字节,需要收到 slave 反馈的 ACK (SDA = 0) 后才开始下一个字节传输,否则传输中止,抛出出错异常
  • 写完所有字节,则发送 Stop condition,Slave 收到 Stop condition 后,才会中止数据接收。。。


I2c flow chart master tx slave rx.jpg


[编辑] 4.2 主收从发

  • 开始位后,发 7 位地址,随后读写位为 1 (Read)
  • 收到 slave 反馈的 ACK 位后,立即开始读取数据 <------
  • 每读一个字节,需要给一个 ACK (SDA = 0),如果是最后一个字节,则需给一个 NACK (SDA = 1)


I2c flow chart master rx slave tx.jpg





[编辑] 5 实例

Touch the head, send i2c data (No other device):

  • Write 0x00 (no ack)
  • Write 0x28 (no ack) 0x44 (no ack)
  • Write 0x28 (no ack) 0x44 (no ack)

Touch-iKair-head-then-send-data.png


Send i2c data (with pm2.5 sensor):

 0x00 0x12 0x1E 0x03 0x02 0x00 0x00 0x1A

Touch-iKair-head-then-send-data2.png


第一个字节 0x00 是目标传感部件环灯广播地址,改为 0x60 就只点亮 PM2.5 的环灯 (i2c addr = 0x30), 0x50 就只点亮甲醛传感部件上的环灯 (i2c addr = 0x28)



[编辑] 6 版本

The history of I²C specification releases:

  • In 1982, the original 100 kHz I²C system was created as a simple internal bus system for building control electronics with various Philips chips.
  • In 1992, Version 1 added 400 kHz Fast-mode (Fm) and a 10-bit addressing mode to increase capacity to 1008 nodes. This was the first standardized version.
  • In 1998, Version 2 added 3.4 MHz High-speed mode (Hs) with power-saving requirements for electric voltage and current.
  • In 2000, Version 2.1 clarified version 2, without significant functional changes.
  • In 2007, Version 3 added 1 MHz Fast-mode plus (Fm+) (using 20 mA drivers), and a device ID mechanism.
  • In 2012, Version 4 added 5 MHz Ultra Fast-mode (UFm) for new USDA (data) and USCL (clock) lines using push-pull logic without pull-up resistors, and added an assigned manufacturer ID table. It is only a unidirectional bus.
  • In 2012, Version 5 corrected mistakes.
  • In 2014, Version 6 corrected two graphs. This is the most recent standard


[编辑] 7 软件模拟

To initialize the ports set the output resisters to 0 and the tristate registers to 1 which disables the outputs and allows them to be pulled high by the resistors.

The following 4 functions provide the primitive start, stop, read and write sequences. All I2C transactions can be built up from these.


#define I2C_DELAY			4		/* us delay */
#define I2C_MAXWAIT			5000

#define TWI_SDA		11		/* PIN14_PD7 */
#define TWI_SCL		16		/* PIN21_PF2 */

#define SDA_LOW()		digitalWrite(TWI_SDA, LOW)
#define SDA_HIGH() 		digitalWrite(TWI_SDA, HIGH)
#define SCL_LOW()		digitalWrite(TWI_SCL, LOW)
#define SCL_HIGH()		digitalWrite(TWI_SCL, HIGH)

#define SDA_READ()		digitalRead(TWI_SDA)
#define SCL_READ()		digitalRead(TWI_SCL)

#define i2c_delay()		delayMicroseconds(4)

void i2c_start(void)
{
	SDA_HIGH();		// i2c start bit sequence
	i2c_delay();

	SCL_HIGH();
	i2c_delay();

	SDA_LOW();
	i2c_delay();

	SCL_LOW();
	i2c_delay();
}

void i2c_stop(void)
{
	SDA_LOW();		// i2c stop bit sequence
	i2c_delay();

	SCL_HIGH();
	i2c_delay();

	SDA_HIGH();
	i2c_delay();
}

uint8_t i2c_rx(uint8_t ack)
{
	uint8_t x, d = 0;

	SDA_HIGH();

	for (x = 0; x < 8; x++) {

		d <<= 1;

		do {
			SCL_HIGH();
		} while (SCL_IN == 0);	// wait for any SCL clock stretching

		i2c_delay();

		if (SDA_READ())
			d |= 1;
		SCL_LOW();
	}

	if (ack)
		SDA_LOW();
	else
		SDA_HIGH();

	SCL_HIGH();
	i2c_delay();		// send (N)ACK bit
	SCL_LOW();
	SDA_HIGH();

	return d;
}

uint8_t i2c_tx(unsigned uint8_t d)
{
	uint8_t x;
	static uint8_t b;

	for (x = 8; x; x--) {
		if (d & 0x80)
			SDA_HIGH();
		else
			SDA_LOW();
		SCL_HIGH();
		d <<= 1;
		SCL_LOW();
	}

	SDA_HIGH();
	SCL_HIGH();

	i2c_delay();

	b = SDA_READ();		// possible ACK bit

	SCL_LOW();
	return b;
}


使用示例:

The 4 primitive functions above can easily be put together to form complete I2C transactions. Here's and example to start an SRF08 ranging in cm:

i2c_start();              // send start sequence
i2c_tx(0xE0);             // SRF08 I2C address with R/W bit clear
i2c_tx(0x00);             // SRF08 command register address
i2c_tx(0x51);             // command to start ranging in cm
i2c_stop();               // send stop sequence


Now after waiting 65mS for the ranging to complete (I've left that to you) the following example shows how to read the light sensor value from register 1 and the range result from registers 2 & 3.

i2c_start();              // send start sequence
i2c_tx(0xE0);             // SRF08 I2C address with R/W bit clear
i2c_tx(0x01);             // SRF08 light sensor register address
i2c_start();              // send a restart sequence
i2c_tx(0xE1);             // SRF08 I2C address with R/W bit set
lightsensor = i2c_rx(1);  // get light sensor and send acknowledge. Internal register address will increment automatically.
rangehigh = i2c_rx(1);    // get the high byte of the range and send acknowledge.
rangelow = i2c_rx(0);     // get low byte of the range - note we don't acknowledge the last byte.
i2c_stop();               // send stop sequence



[编辑] 8 传输距离

I2C 的总线长度,只要硬件参数合适,可以做到 5~8 米,如何延长 I2C 通信线缆长度,以下几条经验可以参考:

(1)实验测试中,我们发现,在通信正常的情况下,I2C 总线的上拉电阻与线缆长度是有比例关系的:

         通信线缆越长,上拉电阻要越小。而通信线缆越短,上拉电阻要越大。比如:

        ① 通信线缆小于 0.2 米,一般可以采用 10K 的上拉电阻,这个也是很多I2C芯片的说明书中推荐的上拉电阻。

        ② 通信线缆在 0.2 到 2 米之间,一般可以采用 4.7K 上拉电阻。

        ③ 通信线缆在 2 米以上,一般可以采用 1~2.2K 上拉电阻。

        上拉电阻的范围一般在 1K~10k 之间,之所以有这个范围,可以简单的认为,电阻过小,功耗比较大,而且容易烧毁 I2C 接口,而电阻过大,会影响 信号的上升沿时间,也就是影响到了 时钟频率,会出现误码。

(2)远距离通信时,尽量使用 屏蔽 线缆,这个确实是有用的,能够很好的抗干扰。

(3)如果不考虑成本,可以采用 I2C 总线中继芯片,如 P82B715、P82B96 等,这些中继芯片的基本原理是,将 I2C 总线接口转为差分信号,所以主机和从机之间,需要一对芯片,这样主机和从机之间是差分信号传输,也就克服了 I2C 总线的线缆长度限制了。一对芯片约 30元。

(4)通信速率,在各种通信中,有一个共同的规律,通信速率与误码率成正比,通信速率越低,误码率也越低,所以我们在与 I2C 设备通信的时候,尤其是距离比较远的,可以考虑降低通信速率。


[编辑] 9 参考





个人工具
名字空间

变换
操作
导航
工具箱