I2C协议总结

  • 两个方面
  • 物理层电气特性
  • 协议层
  • I2C基本读写过程
  • #写过程
  • 通讯复合格式
  • 通讯信号的判断
  • 通讯的起始和停止信号
  • 数据有效性
  • 地址及数据方向
  • 响应信号
  • 通讯过程
  • 部分代码讲解
  • 总结
  • **顺便找一下玩过stm32+ESP8266+onenet的大佬**


两个方面

不知道大家是不是有我这种情况,学完STM32之后,感觉学了个寂寞。

i2c mem 协议_i2c mem 协议


大佬说的话听都听不懂,所以复习一波深入了解一下原理

i2c mem 协议_i2c mem 协议_02


今天要说的就是I2C通讯协议,刚学不知道这些协议是干嘛用的,了解了之后才发现,不得不会呀,就比如OLED mpu6050 EEPROM巴拉巴拉巴拉。。。总之就是很常用。好了废话说完了。

我们从两个方面来介绍I2C协议

1–>物理层

2–>协议层

物理层电气特性

i2c mem 协议_单片机_03


1: 它是一个支持多设备的总线,总线的意思就是多个设备共用的信号线,一个总线中可以挂载多个设备。

2: 包括两条总线,即串行时钟线(SCL)C代表clock的意思就是时钟,以及串行数据线(SDA)D代表Data也就是数据的意思。顾名思义,SDA用来传输数据,而SCL用来保持收发同步。

3: I2C总线上可以挂载多个设备,那么是怎么来确认发到哪个从机上呢?**其实每个设备在I2C总线上都有自己的地址,来确保不同设备之间访问的准确性。**地址可以是7位或者11位。

4: 总线通过上拉电阻接到电源。这一点可能很多人不了解,我对照着上面的图详细说一下。在计算机逻辑里:

高电平代表 1 (高阻态)

低电平代表 0

在设备空闲或者输出1时,我们输出的并不是高电平而是高阻态!

i2c mem 协议_数据_04

高阻态,顾名思义嘛,电阻很大,那么就取无穷大喽。
表示空闲状态时假设图中触摸屏空闲时输出零电压, SCL总线就会被拉低,此时如果传感器 工作,输出一个高电平,此时的触摸屏是0V,传感器是高电平,就会造成短路。如果我们给触摸屏一个高阻态,相当于断路。此时我们令传感器的电压为高电平,那么整个SCL总线就被拉高了。

**表示高电平时:**假设其他设备空闲(高阻态),就可以忽略了,只有传感器工作,如果输出高阻态,又由于存在上拉电阻,所以整个SCL线也就是1。

5: 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 I 2C 设备尚不支持高速模式。我们一般就使用标准模式就可以了。

6: 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制 。
PS: 纯属被迫营业,没学过电路

i2c mem 协议_i2c mem 协议_05

协议层

I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。通过总线的状态来表述不同的信号。

I2C

I2C基本读写过程

先放一张图:

i2c mem 协议_数据_06

#写过程

先不用了解具体怎么产生的信号,先来抽象地理解一下,拿写来说:

主机先说一句开始传输(S),传输到哪呢??很明显就是从机了(ADRESS),主机再次确认是读别人数据还是写数据呢(R/W),然后从机表示晓得了你要读/写(A),主机发送数据巴拉巴拉巴拉,发送完成之后,从机表示不接受数据了(非应答 那个符号不会打).然后主机停止写数据(P)。

--------------------------------------- 原谅我的文笔,太感人了。----------------------------------------------

i2c mem 协议_数据_07

S: 表示开始传输,先说一声开始。
SLAVE_ADDRESS: 这个图上也有说,就是从机地址
R/W: 0为写,1为读
DATA: 发送的数据
A: 应答信号
P: 停止信号。结束了

通讯复合格式

i2c mem 协议_单片机_08


这个过程其实和前面讲的写过程差不多,假设第一个R/W是写,那么主机先产生开始信号,找到从机地址,开始写,从机应答,此时后边的这个DATA表示的是要写的从机的地址,比如EEPROM,写到哪里呢,就是由这个DATA决定的,后边的都差不多就不啰嗦啦。

通讯信号的判断

通讯的起始和停止信号

i2c mem 协议_数据_09


1 当SCL为高电平,SDA从高变为低,表示起始信号

2 当SCL为高电平,SDA从低变高,表示停止信号

数据有效性

i2c mem 协议_嵌入式_10


1: 当SCL为高电平时,如果SDA为高电平,表示数据1,如果SDA为低电平,表示数据0.

2 当SCL为低,SDA进行电平转换,表示传输下一个数据。

地址及数据方向

i2c mem 协议_停止信号_11


前面我们说到地址为7位或者10位,一般都用七位。

八位设备地址=7位从机地址+读/写地址,以EEPROM为例,他的七位设备地址为1111000十六进制转换之后就是0x78
八位设备的读地址=1111 0001=0xF1
八位设备的写地址=1111 0000=0xF0

响应信号

i2c mem 协议_嵌入式_12


在传输时,主机产生时钟,在产生的第九个时钟时(8个时钟8个字节,就穿输了一个位(byte)了)

数据发送端就会放弃对SDA的控制权,转为数据接收端控制SDA,此时若为低电平则为应答,高电平为非应答

通讯过程

i2c mem 协议_嵌入式_13


i2c mem 协议_i2c mem 协议_14


这里就不多说了,了解完前面看这里就很轻松,上面的和读写过程基本一样。

i2c mem 协议_单片机_15


需要特别说的就是下面的EV5,EV6之类的了,其实下面的表示的是状态,比如产生一个起始信号,就会有对应的事件产生,如果此时我们检测状态寄存器(想了解的可以参加《STM32中文参考手册》),如果检测到事件发生,就判断产生了起始信号,算是一个保障吧。

部分代码讲解

#include "myiic.h"
#include "delay.h"
 

void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); 	
}

void IIC_Start(void)
{
	SDA_OUT();     //sdaÏßÊä³ö
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	IIC_SCL=0;
}	  
//²úÉúIICÍ£Ö¹ÐźÅ
void IIC_Stop(void)
{
	SDA_OUT();//sdaÏßÊä³ö
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;
	delay_us(4);							   	
}

u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();    
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//ʱÖÓÊä³ö0 	   
	return 0;  
} 

void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}

IIC_Start(); 起始信号
IIC_Stop();停止信号
IIC_Wait_Ack(); 等待应答
IIC_Ack()应答信号
IIC_NAck 非应答信号
这些根据前面的时序图理解就可以了;

着重讲一下发送与读取字节的函数

void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//À­µÍʱÖÓ¿ªÊ¼Êý¾Ý´«Êä
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(2);   //¶ÔTEA5767ÕâÈý¸öÑÓʱ¶¼ÊDZØÐëµÄ
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	    
//¶Á1¸ö×Ö½Ú£¬ack=1ʱ£¬·¢ËÍACK£¬ack=0£¬·¢ËÍnACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDAÉèÖÃΪÊäÈë
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//·¢ËÍnACK
    else
        IIC_Ack(); //·¢ËÍACK   
    return receive;
}

先看发送数据 txd为要发送的数据,0x80就是1000 0000
如果txd第一位为1,则 txd&0x80=1000 0000
如果txd第一位为0 则 txd&0x80=0000 0000 这个与运算的作用就是提取txd最高位的数据;
(txd&0x80)>>7 表示(txd&0x80)右移七位 ,假设数据是1000 0000右移后补零就是0000 00001,
这样就得到了最高位(第八位)的数据;之后令数据左移一位,循环得到第七位的数据
接受数据的同理。

总结

经过最近学习发现通讯真的很重要,前几天做了关于OLED 和mpu6050的实验,都是用的I2C通讯协议。ESP8266连接云服务器也要用的协议(MQTT)。一定要把通讯吃透哦,不然后期很难学的。

i2c mem 协议_停止信号_16


在了解完这些之后代码大部分就可以看懂了,其中I2C的初始化之类的东西我就不说了哈,大家有兴趣可以采用I2C通讯协议与EEPROM进行通讯。如果学到的话,麻烦各位看官老爷点个赞再走吧,ball ball 你们了。

i2c mem 协议_停止信号_17

顺便找一下玩过stm32+ESP8266+onenet的大佬

i2c mem 协议_嵌入式_18