目录
上篇文章了解了I2C如何发送数据的,但是还不够还需要了解I2C发送的数据格式------本质就是你学会了a,b,c,d的字母,但是还没有学单词。
I2C协议
主从设备通信之前默认处于空闲状态。
空闲状态
I2C的空闲状态默认是输出高电平的,因为复用开漏输出,默认N-MOS管都是断开的,因此输出高电平。

不管是通信开始还是结束,输出的都是高电平,总是处于空闲状态。
Master发送状态和停止状态
根据上篇文章,当SCL保持高电平的时候,可以选择 获取SDA数据,低电平的时候,不允许获取SDA的数据。
但是,这里接收方为了区分SDA发送数据的起始位在哪里,以及停止位在哪里,就产生了差异。
当SCL保持高电平的时候,SDA产生高到低 电平的变化------代表告诉接收方我要开始发数据了。
当SCL保持高电平的时候,SDA产生低到高 电平的变化------代表告诉接收方我要结束发数据了。

细节:在SCL是高电平的情况下,接收方获取的数据有四种情况高到低,0,1,低到高,分别代表起始条件,低电平,高电平,停止条件
Master发送从设备地址
由于从设备有多个,因此为了确定哪一个从设备,I2C的制造厂商会统一一个地址区分多个不同的从设备。
从设备的地址数据一般由7个或者10个比特位组成,一般情况下是7个,下面都是以7个举例说明。
下面是发送0x50,的样例

地址说明
这个地址中,由7位组成,其中最高比特位用不到,因此整体左移1位,去掉了最高位。
前七位是地址数据------决定了你要通信哪个I2C从设备。
其中第八位比特位决定了这个数据是读(1)的还是写(0)的。
为了让发送方知道接收方已经收到数据了,再第九位比特位又多了一个叫做ACK------应答,会主动拉低点位。

因此这个地址数据处理相应的地址以外,还有读写比特位,应答比特位。
其中这个应答位有两种情况,一个是ACK,一个是NACK。
ACK:表示数据成功接收。
NACK:可能表示接收方未准备好、地址不匹配、接收失败或读取结束信号等,具体后续会详细介
绍。
关于ACK和NACK的细节:
I2C是同步可靠性通信,需要应答保证可靠性(这里可靠性理解为接收方不管有没有接收数据,都会应答,跟数据有没有错误没有关系)
应答ACK和NACK都是通过SDA传递的,通过将SDA线拉低,这表示ACK应答,保持SDA线为高电平,这表示无应答。
接收方接收到了ACK,可以传输,但是怎么回馈给发送方的?
接收方在发送方发完8位数据后的第9个SCL时钟周期,主动拉低SDA线,发送方在第9个SCL高电平期间采样SDA,读到低电平就表示ACK。
Master写数据Slaver读的过程


| 步骤 | 谁发 | 内容 | SCL周期 | 说明 |
|---|---|---|---|---|
| 第1步 | Master | 起始条件 | --- | SCL高时,SDA高→低 |
| 第2步 | Master | 7位地址 + 0(写) | 8位 | MSB先发,0x50<<1|0 = 0xA0 |
| 第3步 | Slave | ACK | 第9位 | 从机拉低SDA |
| 第4步 | Master | 数据0 | 8位 | MSB先发 |
| 第5步 | Slave | ACK | 第9位 | 从机拉低SDA |
| ... | ... | ... | ... | 重复发送多个字节 |
| 第m步 | Master | 数据n | 8位 | 最后一个字节 |
| 第m+1步 | Slave | ACK | 第9位 | 从机拉低SDA |
| 第m+2步 | Master | 停止条件 | --- | SCL高时,SDA低→高 |
每个字节后必须跟1位ACK,最后以停止条件结束。
最初的问题:SDA在停止条件之前,怎么保证是低电平?
发送方在发起停止条件之前,会主动把SDA拉低,确保停止条件有"低→高"的跳变。
Master读数据Slaver写的过程
场景:
Master:MCU,主动发起通信,产生SCL。
Slaver:EEPROM,被动响应,提供数据。
这里和Master写的情况不一样,EEPROM内部有存储地址,Master要先写一个内部地址,再读数据。
| 步骤 | 谁发 | 内容 | SCL周期 | 说明 |
|---|---|---|---|---|
| 第1步 | Master | 起始条件 | --- | SCL高时,SDA高→低 |
| 第2步 | Master | 7位地址 + 0(写) | 8位 | 0x50<<1|0 = 0xA0 |
| 第3步 | Slave | ACK | 第9位 | 从机拉低SDA |
| 第4步 | Master | 要读的内部地址 | 8位 | 比如0x00 |
| 第5步 | Slave | ACK | 第9位 | 从机拉低SDA |
| 第6步 | Master | 重复起始条件 | --- | 不释放总线,再发起始 |
| 第7步 | Master | 7位地址 + 1(读) | 8位 | 0x50<<1|1 = 0xA1 |
| 第8步 | Slave | ACK | 第9位 | 从机拉低SDA |
| 第9步 | Slave | 数据0 | 8位 | Slave发数据 |
| 第10步 | Master | ACK | 第9位 | Master拉低SDA,表示继续读 |
| ... | ... | ... | ... | 继续读多个字节 |
| 第m步 | Slave | 数据n | 8位 | 最后一个字节 |
| 第m+1步 | Master | NACK | 第9位 | Master不拉低,表示读完了 |
| 第m+2步 | Master | 停止条件 | --- | SCL高时,SDA低→高 |
为什么最后一次是NACK?------避免总线竞争
bash
如果Master发ACK:
→ Slave认为Master还要继续读
→ Slave继续发下一个数据
→ 同时Master想发停止条件
→ 两个设备同时控制SDA → 冲突
如果Master发NACK:
→ Slave知道Master读完了
→ Slave释放SDA
→ Master发停止条件
→ 不冲突
I2C 收发不对称:Master 写时没问题,Master 读时 SDA 归 Slave,Master 想发停止会和 Slave 抢 SDA。解决办法是约定 NACK:Master 读够了就发 NACK,让 Slave 释放 SDA,然后 Master 发停止条件。
细节问题
1、Master 接收数据会来不及吗?------一般不会
-
SCL 时钟信号由 Master 产生
-
Master 掌管数据发送的"节奏"
-
Slave 只是在这个节奏下发送数据
如果 Master 真的需要更多时间处理数据,它也可以用时钟拉伸:
-
Master 拉低 SCL,强制暂停
-
处理完再释放 SCL,继续接收
2、从设备怎么知道主机要读取什么数据?
很多时候,在发完从设备地址后,还会再发操作码或二级地址,然后才进行数据读取。
因为需要先指定读什么数据、在哪里读 ,这就是应用层面的协议约定。
设备主从的问题
Master = 主导通信过程的设备。
Slave = 被动响应的设备。
能读写数据和主导通信过程是两码事。
Master(主设备)
| 职责 | 说明 |
|---|---|
| 发起通信 | 产生起始和停止条件 |
| 产生时钟 | 控制 SCL 线 |
| 寻址从设备 | 发送从设备地址 |
| 控制读写方向 | 发读写位(0写/1读) |
| 主导整个过程 | 什么时候开始、什么时候结束,由它决定 |
典型设备: MCU、CPU、FPGA
Slave(从设备)
| 职责 | 说明 |
|---|---|
| 被寻址 | 等待 Master 发地址 |
| 响应命令 | 地址匹配后发 ACK |
| 被动收发数据 | 在 Master 的时钟节奏下收发 |
| 不能主动发起通信 | 只能等 Master 来找它 |
典型设备: 传感器、EEPROM、RTC、ADC/DAC、IO扩展芯片
能读写 ≠ 是 Master
总结:Master 是主导通信过程的设备,负责发起通信、产生时钟、发送地址、控制读写方向。Slave 是被动响应的设备,但 Slave 也能读写数据。能读写数据和主导通信是两码事,谁主导通信谁就是 Master。角色可配置,可互换