I2C通信FAQ 集锦
I2C的从机是如何应答的(回复ACK)?
问:我有一个疑问,就是在 I2C 通信中,ESP32-S3 作为主机,现在要读取 AHT30 温湿度传感器的温湿度数据。目前我的 ESP32-S3 已经发送了一个设备地址,那么从机 AHT30 传感器要发送一个 ACK(也就是应答),表示接收到了设备地址。
这个应答是怎么做到的呢?
- 是由 ESP32-S3 开发板将 SDA 这条数据线的电平拉高吗?
- 然后由 AHT30 传感器回应一个 ACK,也就是将电平拉低吗?
我不太理解在主机发送完地址后,从机具体是如何应答这个 ACK 的?在开漏输出模式下是怎么样的一个流程。
答:
- I2C ACK 应答流程(开漏输出模式)
- 基本原理
I2C 总线的 SDA 和 SCL 线都设置为漏极开漏(open-drain)输出。这意味着:
- 任何设备都只能将总线拉低(输出低电平)
- 总线通过外部上拉电阻保持高电平(没有设备拉低时)
[I2C 控制器]- 具体 ACK 应答流程,当 ESP32-S3(主机)发送完 7 位从机地址 + 1 位读写标志位(共 8 个时钟脉冲)后:
- 主机释放 SDA 线:主机停止驱动 SDA,SDA 线由于上拉电阻自然变为高电平。
- 主机产生第 9 个时钟脉冲:SCL 拉高。
- 从机(AHT30)拉低 SDA:如果从机地址匹配,AHT30 在第 9 个 SCL 脉冲期间将 SDA 线主动拉低,这就是 ACK 应答。
- 主机检测 SDA 电平:主机在第 9 个 SCL 高电平期间读取 SDA,若为低电平则确认 ACK 成功。
[I2C 控制器]- 回答你的两个问题
- 不是由 ESP32-S3 将 SDA 拉高。SDA 变高是因为主机释放了对 SDA 的控制,上拉电阻自动将其拉至高电平。
- 是的,AHT30 通过将 SDA 拉低来回应 ACK。这正是开漏模式的工作方式——任何设备都可以通过拉低总线来发送信号。
- ESP32-S3 主机侧的 ACK 检测配置,在 ESP32-S3 的 I2C 控制器中,主机通过 WRITE 命令的 ack_check_en 和 ack_exp 字段来处理这个 ACK:
- 当 ack_check_en = 1 时,主机会在发送完每个字节后进行 ACK 检测。
- 匹配:接收到的 ACK 值与 ack_exp 一致,传输继续。
- 不匹配:产生 I2C_NACK_INT 中断,停止发送并产生 STOP 信号。
[I2C 控制器]简而言之,在开漏模式下,ACK 的本质是:主机放手(释放 SDA),从机拉低(表示确认),这是 I2C 协议的标准机制。
I2C模式下主机能够输出也能够检测SDA总线电平(输入检测)?
我并没有理解你说的情况:主机在发送了一个数据帧之后,在第9个比特位拉高 SCLK(即 SCLK 的上升沿)。在上升沿的同时,主机将 SDA 释放,随后由从机来控制 SDA 并将其拉低。由于开漏输出的原因,加上有上拉电阻,这条 SDA 自然也就是一个低电平。
我的问题是:
- 主机是如何检测到这个 ACK 的?
- 现在这条 I2C 总线不是设置成了开漏输出吗?开漏输出不是只能输出吗?
- 主机的 SDA 引脚在输出的同时,还能监测输入的电平信号吗?也就是说,主机的 SDA 引脚能做到一边输出一边检测高低电平吗?
答:这是一个非常好的问题,涉及到开漏输出的硬件原理。让我来解释一下。
- 开漏输出的本质
- "开漏输出"(open-drain)的名字容易让人误解为"只能输出",但实际上它的工作方式更灵活:
- 拉低时:驱动器将 MOSFET 导通,将引脚拉至低电平(GND)。
- 释放时:驱动器关闭 MOSFET,引脚处于高阻抗状态,此时外部上拉电阻将总线拉至高电平。
- 关键在于:当引脚处于高阻抗状态(即"释放"状态)时,该引脚仍然可以读取总线上的电平。
- 回答你的三个问题
主机如何检测到 ACK?
主机在发送完第 8 个比特后,在第 9 个 SCL 高电平期间释放 SDA(进入高阻抗状态),然后读取 SDA 线上的电平:
- 若从机将 SDA 拉低 → 读到低电平 → ACK
- 若从机未拉低 SDA → 上拉电阻维持高电平 → NACK
在 ESP32-S3 的 I2C 控制器中,这个检测由 ack_deal 模块负责,它在 SCL_MAIN_FSM 的控制下检测 SDA 线上 ACK 位的电平。 [I2C 控制器]开漏输出只能输出吗?
不是的。开漏输出有两种状态:
- 主动拉低:输出低电平
- 高阻抗(释放):此时引脚既不输出高也不输出低,而是"浮空",由外部上拉电阻决定总线电平
- 在"释放"状态下,引脚可以感知总线上的电平,因此开漏引脚既可以输出(拉低),也可以在释放时读取输入。
SDA 引脚能否同时输出和检测电平?
可以。这正是 I2C 协议能够工作的基础。ESP32-S2 的文档中明确描述了这一机制——主机在 SCL 高电平期间,会检测 SDA 输入值与 SDA 输出值是否一致,用于仲裁检测:
- "I2C Master 在 SCL 为高电平期间,检测到 SDA 输入值与 SDA 输出值不等时,则 I2C Master 会产生 I2C_ARBITRATION_LOST_INT 中断。" [I2C 控制器]
这说明主机的 SDA 引脚在"输出"的同时,确实也在持续监测总线上的实际电平。ACK 检测正是利用了这一能力:主机释放 SDA 后,读取总线实际电平,判断从机是否拉低了 SDA。
总结:开漏输出并不是"只能输出",它在释放(高阻抗)状态下可以读取总线电平。ESP32-S3 的 I2C 控制器正是通过在第 9 个 SCL 脉冲期间释放 SDA 并读取其电平,来检测从机的 ACK 应答的。
