欢迎光临
我们一直在努力

I2C时钟延展:从设备如何优雅地“叫停”通信

1. I2C时钟延展:从设备的\”叫停\”艺术

大家好,我是从事嵌入式开发十多年的老工程师,今天想和大家聊聊I2C通信中一个非常实用但经常被忽视的功能——时钟延展。在实际项目中,我遇到过太多因为时钟延展处理不当导致的通信故障,特别是在STM32与各种外设芯片配合时。

简单来说,时钟延展就是I2C从设备的一种\”叫停\”机制。想象一下这样的场景:主设备像是个急性子的领导,不停地向下属(从设备)下达指令。但如果下属需要时间处理当前任务,他就会举手说\”请稍等\”。在I2C世界里,这个\”举手\”动作就是从设备拉低SCL时钟线的行为。

我清楚地记得第一次遇到时钟延展问题时的情形。当时我们在做一个智能家居项目,STM32作为从设备与一个音频解码芯片通信。调试时发现系统运行一段时间后就会死机,用示波器测量发现SCL线一直被拉低。经过仔细排查,才发现是STM32的接收缓冲区满了,自动启动了时钟延展,但主设备却不支持这个功能,导致通信死锁。

2. 时钟延展的工作原理与触发条件

2.1 时钟延展的底层机制

时钟延展的本质是从设备对总线控制权的临时获取。在标准I2C协议中,时钟线SCL完全由主设备控制,但从设备在特定情况下可以介入并延长时钟低电平的时间。

从硬件层面来看,当时钟延展发生时,从设备会在主设备释放SCL线(试图拉高)后立即将其拉低并保持。这个过程发生在纳秒级别,要求主设备必须实时检测SCL线的实际状态,而不是简单地输出时钟信号。

让我用代码来说明一下。如果是软件模拟I2C,主设备的代码应该这样写:

// 错误的做法(不检测时钟延展)
void i2c_write_byte(uint8_t data) {
for(int i = 0; i < 8; i++) {
set_scl_low();
delay_us(1);
set_sda((data >> (7-i)) & 0x01);
delay_us(1);
set_scl_high(); // 直接拉高,不检测从设备是否延展
delay_us(1);
}
}

// 正确的做法(处理时钟延展)
void i2c_write_byte(uint8_t data) {
for(int i = 0; i < 8; i++) {
set_scl_low();
delay_us(1);
set_sda((data >> (7-i)) & 0x01);
delay_us(1);

// 释放SCL并检测是否被从设备拉低
set_scl_high();
while(read_scl() == 0) {
// 等待从设备释放SCL
delay_us(1);
}

赞(0)
未经允许不得转载:171主机测评 » I2C时钟延展:从设备如何优雅地“叫停”通信
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址