I2C总线介绍
更新时间: 2026/02/13
在Gitcode上查看源码

基本概念

定义

I2C属于两线式串行总线,由飞利浦公司开发用于微控制器(MCU)和外围设备(从设备)进行通信的一种总线,属于一主多从(多住多从时,会进行主机仲裁,同一时间下的通信只会有一个主机)的总线结构,总线上的每个设备都有一个特定的设备地址,以区分同一I2C总线上的其他设备

物理结构

I2C总线上可以连接多个设备,各设备之间通过SDA总线(数据线)和SCL总线(时钟线)进行连接,支持多主设备和多从设备,多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线,拓扑结构如下图所示。SCL和SDA空闲状态均为高天平

通信协议核心机制

I2C总线通信时采用主从模式,即主设备发起和结束通信,总线上的从设备被动响应,是一种同步、半双工、串行通信协议

起始(START)与停止(STOP)信号

SCL为高天平时,主设备控制SDA从高->低跳变产生START信号;SCL为高电平时,主设备控制SDA从低->高跳变产生STOP信号

确认(ACK)与非确认(NACK)信号

每字节传输后,接收方在第9个时钟周期拉低SDA发出ACK信号,确认接收成功,若SDA保持高电平(相当于发出NACK)则发送方会停止传输

数据传输格式

主设备发起START信号 -> 主设备发送从设备地址+读写位 -> 从设备相应ACK -> 传输n个字节,每传输完一个字节,接收方均响应ACK -> 主设备发送STOP信号结束通信并释放总线

SDA总线数据生成与采样

主从设备各有一个移位寄存器用于数据传输,两者由主设备控制的SCL总线的时钟信号进行统一驱动,一个时钟周期完成一位数据的传输。时钟信号的下降沿会触发发送方将移位寄存器最高位(MSB)传输到SDA总线,并保证在时钟信号的低电平期间完成该动作并确保数据稳定(因为数据传输有延迟,发送方不能保证在时钟信号下降沿的一瞬间完成传输),同一时钟周期的时钟上升沿会触发接收方采样SDA总线的数据并存入移位寄存器,并在SCL高电平期间完成采样。在SCL高电平期间,不能让SDA信号跳变,否则会导致采样错误

时钟延展

从设备可以通过拉低SCL暂停时钟,延长低电平时间以处理数据。主设备检查到SCL被拉低后,等待其释放后再重新接管SCL总线

Restart机制

该机制的典型应用场景之一为切换读写方向,以读EEPROM特定地址数据为例说明该机制: 主设备发起START信号 -> 主设备发送从设备地址+写位 -> 从设备响应ACK (只有与主设备发出的地址匹配的从设备会响应) -> 主设备往eeprom设备写入一个字节的偏移地址(eeprom收到该地址后会设置内部读数据的地址指针,以供后续读取数据使用,每读一个字节,该指针会自动加1)-> 从设备响应ACK -> 主设备发送Restart信号 -> 无效数据+读位(每读取一个字节就需要写一个无效数据,硬件据此判断要读取的字节数,并据此决定发送ACK的数量)-> 从设备返回1字节数据,主设备响应ACK(读取几个字节就重复该步骤几次)-> 主设备发送STOP信号结束通信并释放总线 RESTART信号的产生:在SCL高电平时,主设备释放SDA跳变为高电平,然后迅速将SDA拉低为低电平,产生RESTART信号,为了避免被误判为STOP信号,SDA跳变过程中要缩短其高电平时间,确保快速完成SDA的电平跳变

I2C支持的器件

待补充

问题定位

常见问题现象

/var/log/framework.log | grep error 在串口查看框架日志(或一键收集日志dump_info\LogDump\framework.log中),出现错误的调用函数可能不同,但如果打印:i2c read fail, ret: 5或i2c write fail, ret: 5的时候优先按以下方法定位

问题定位指南

  1. 关键字:i2c fail, ret: 5
  2. 定位手段:查看/var/log/linux_kernel_log的SDK日志(或一键收集日志dump_info\LogDump\linux_kernel_log中)
  3. 定位思路:在linux_kernel_log中搜索I2C-xx, 对应的I2C日志格式如下:

[I2C-bus-RT <xfer_ret|ret>] M <addr|flag|protocal|retries|timeout> L <wlen|rldn|cmd_len> C <wcnt|rcnt|outstanding> E <cmd_error|msg_error|status|abort_source> I <irq_status|irq_raw_status|irq_mask>

参数解释
bus出现问题的I2C通道
xfer_ret返回的错误码
ret返回的错误码
addr表示slave地址(7bit)例如:0x50,BMC侧调试I2C xfer接口传入地址0x50,调用其他接口使用传入0xA0(0x50 << 1),8bit地址
flag1 表示接收数据,0表示发送数据
protocal表示传输消息的协议类型,I2C协议为0, SMBus协议表示此处协议类型,例:8表示block write
retries可能的重传次数
timeoutBMC软件配置的传输超时时间(timeout * 10)ms
wlenI2C需要写的数据个数
rlenI2C需要读的数据个数
cmd_len不关注
wcnt软件写到芯片FIFO中的数据个数
rcnt软件读到芯片中的数据个数
outstanding软件还剩余的没有读到的数据个数
cmd_errorcmd错误码
msg_errorMsg错误码
status数据传输的状态
abort_source发生abort的原因
irq_status中断状态
irq_raw_status原始中断状态
irq_mask中断mask

主要关注点:

  1. 找到对应的I2C
  2. RT<-5 | 5>对应framework.log中的ret:5
  3. slave器件地址
  4. flag(0x1或0x0),分别对应接收数据或者发送数据
  5. 错误码(查找下方具体的错误码对应的错误信息排查
返回码错误码说明排查步骤
-50x1发生tx_abort错误,传输被中止,此时一般都是slave没有响应。
原因有:
slave地址错误;
slave不响应;
链路异常;
0x1可能和0x200,0x800一起出现,只要有0x1,请一定查阅此处

地址无响应:
abort_source=1;
1) 确认slave地址是否存在
2) 确认slave是否在活跃状态
3)确认硬件链路是否畅通,信号质量是否良好
数据无响应:
abort_source=0x8;
1) 确认slave是否在活跃状态
2) 确认硬件链路是否畅通
丢失总线控制权:
abort_source=0x1000;
1) 是否存在多master抢占?
2) 是否存在总线干扰?
3) I2C外接的slave器件是否过多
三种错误都寻求硬件帮忙测量波形搞定。
-50x2smbus时钟一直是低
原因有:
I2C的复用关系可能没有配置
1、首先联系硬件,确认该I2C通道被用作I2C功能;
2、联系SDK软件检查复用配置
-50x200或0x201发送FIFO中的有效数据个数不为0.
原因有:
接收数据过程中,从器件突然不响应;
总线干扰;
1、一般0x200会与0x1一起出现,即0x201,这时引起这样的错误的原因一般是从器件无响应导致的,请联系硬件同事排查slave不响应的原因。
2、只有0x200的情况,一般是存在总线干扰,请联系硬件同事进行总线排查
-50x800或0x801软件还有剩余的数据没有读到
原因有:
slave地址错误;
slave不响应;
链路异常;
1、一般0x800会与0x1一起出现,即0x801;或者与0x200、0x1一起出现,即0xa01,这时引起这样的错误的原因一般是从器件无响应导致的。请联系硬件同事排查slave不响应的原因。2、只有0x800的情况,或是0x800与0x200一起出现,即0xa00,一般是存在总线干扰,请联系硬件同事进行总线排查
-620x10001传输超时
原因有:
设置的超时时间较短;
从器件特性限制,在读写之间需要有一个延时;
总线挂死;
1、查看超时时间,和要读的数据长度,是否存在超时时间太短,但时间很长的情况?此时建议BMC增大超时时间重试
2、联系slave器件负责人,咨询slave器件的性质,是否存在需要在读写之间增加延时的情况?典型器件如PSU需要在读写之间增加10ms的延时
3、通过测量波形,发现状态一直保持高电平,可以尝试减小链路的上拉电阻2
-620x10000传输超时
原因有:中断被屏蔽,硬件无法发出信号。
1、查看超时时间,和要读的数据长度,是否存在超时时间太短,但时间很长的情况?此时建议BMC软件增大超时时间重试
2、联系slave器件负责人,咨询slave器件的性质,是否存在需要在读写之间增加延时的情况?典型器件如PSU需要在读写之间增加10ms的延时
3、查看此时是否有大量打印,print会屏蔽中断