I2C协议总结
- 两个方面
- 物理层电气特性
- 协议层
- I2C基本读写过程
- #写过程
- 通讯复合格式
- 通讯信号的判断
- 通讯的起始和停止信号
- 数据有效性
- 地址及数据方向
- 响应信号
- 通讯过程
- 部分代码讲解
- 总结
- **顺便找一下玩过stm32+ESP8266+onenet的大佬**
两个方面
不知道大家是不是有我这种情况,学完STM32之后,感觉学了个寂寞。
大佬说的话听都听不懂,所以复习一波深入了解一下原理
今天要说的就是I2C通讯协议,刚学不知道这些协议是干嘛用的,了解了之后才发现,不得不会呀,就比如OLED mpu6050 EEPROM巴拉巴拉巴拉。。。总之就是很常用。好了废话说完了。
我们从两个方面来介绍I2C协议
1–>物理层
2–>协议层
物理层电气特性
1: 它是一个支持多设备的总线,总线的意思就是多个设备共用的信号线,一个总线中可以挂载多个设备。
2: 包括两条总线,即串行时钟线(SCL)C代表clock的意思就是时钟,以及串行数据线(SDA)D代表Data也就是数据的意思。顾名思义,SDA用来传输数据,而SCL用来保持收发同步。
3: I2C总线上可以挂载多个设备,那么是怎么来确认发到哪个从机上呢?**其实每个设备在I2C总线上都有自己的地址,来确保不同设备之间访问的准确性。**地址可以是7位或者11位。
4: 总线通过上拉电阻接到电源。这一点可能很多人不了解,我对照着上面的图详细说一下。在计算机逻辑里:
高电平代表 1 (高阻态)
低电平代表 0
在设备空闲或者输出1时,我们输出的并不是高电平而是高阻态!
高阻态,顾名思义嘛,电阻很大,那么就取无穷大喽。
表示空闲状态时:假设图中触摸屏空闲时输出零电压, SCL总线就会被拉低,此时如果传感器 工作,输出一个高电平,此时的触摸屏是0V,传感器是高电平,就会造成短路。如果我们给触摸屏一个高阻态,相当于断路。此时我们令传感器的电压为高电平,那么整个SCL总线就被拉高了。
**表示高电平时:**假设其他设备空闲(高阻态),就可以忽略了,只有传感器工作,如果输出高阻态,又由于存在上拉电阻,所以整个SCL线也就是1。
5: 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 I 2C 设备尚不支持高速模式。我们一般就使用标准模式就可以了。
6: 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制 。
PS: 纯属被迫营业,没学过电路
协议层
I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。通过总线的状态来表述不同的信号。
I2C
I2C基本读写过程
先放一张图:
#写过程
先不用了解具体怎么产生的信号,先来抽象地理解一下,拿写来说:
主机先说一句开始传输(S),传输到哪呢??很明显就是从机了(ADRESS),主机再次确认是读别人数据还是写数据呢(R/W),然后从机表示晓得了你要读/写(A),主机发送数据巴拉巴拉巴拉,发送完成之后,从机表示不接受数据了(非应答 那个符号不会打).然后主机停止写数据(P)。
--------------------------------------- 原谅我的文笔,太感人了。----------------------------------------------
S: 表示开始传输,先说一声开始。
SLAVE_ADDRESS: 这个图上也有说,就是从机地址
R/W: 0为写,1为读
DATA: 发送的数据
A: 应答信号
P: 停止信号。结束了
通讯复合格式
这个过程其实和前面讲的写过程差不多,假设第一个R/W是写,那么主机先产生开始信号,找到从机地址,开始写,从机应答,此时后边的这个DATA表示的是要写的从机的地址,比如EEPROM,写到哪里呢,就是由这个DATA决定的,后边的都差不多就不啰嗦啦。
通讯信号的判断
通讯的起始和停止信号
1 当SCL为高电平,SDA从高变为低,表示起始信号
2 当SCL为高电平,SDA从低变高,表示停止信号
数据有效性
1: 当SCL为高电平时,如果SDA为高电平,表示数据1,如果SDA为低电平,表示数据0.
2 当SCL为低,SDA进行电平转换,表示传输下一个数据。
地址及数据方向
前面我们说到地址为7位或者10位,一般都用七位。
八位设备地址=7位从机地址+读/写地址,以EEPROM为例,他的七位设备地址为1111000十六进制转换之后就是0x78
八位设备的读地址=1111 0001=0xF1
八位设备的写地址=1111 0000=0xF0
响应信号
在传输时,主机产生时钟,在产生的第九个时钟时(8个时钟8个字节,就穿输了一个位(byte)了)
数据发送端就会放弃对SDA的控制权,转为数据接收端控制SDA,此时若为低电平则为应答,高电平为非应答
通讯过程
这里就不多说了,了解完前面看这里就很轻松,上面的和读写过程基本一样。
需要特别说的就是下面的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的初始化之类的东西我就不说了哈,大家有兴趣可以采用I2C通讯协议与EEPROM进行通讯。如果学到的话,麻烦各位看官老爷点个赞再走吧,ball ball 你们了。
顺便找一下玩过stm32+ESP8266+onenet的大佬