基本概念
定义
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的时候优先按以下方法定位
问题定位指南
- 关键字:i2c fail, ret: 5
- 定位手段:查看/var/log/linux_kernel_log的SDK日志(或一键收集日志dump_info\LogDump\linux_kernel_log中)
- 定位思路:在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地址 |
| flag | 1 表示接收数据,0表示发送数据 |
| protocal | 表示传输消息的协议类型,I2C协议为0, SMBus协议表示此处协议类型,例:8表示block write |
| retries | 可能的重传次数 |
| timeout | BMC软件配置的传输超时时间(timeout * 10)ms |
| wlen | I2C需要写的数据个数 |
| rlen | I2C需要读的数据个数 |
| cmd_len | 不关注 |
| wcnt | 软件写到芯片FIFO中的数据个数 |
| rcnt | 软件读到芯片中的数据个数 |
| outstanding | 软件还剩余的没有读到的数据个数 |
| cmd_error | cmd错误码 |
| msg_error | Msg错误码 |
| status | 数据传输的状态 |
| abort_source | 发生abort的原因 |
| irq_status | 中断状态 |
| irq_raw_status | 原始中断状态 |
| irq_mask | 中断mask |
主要关注点:
- 找到对应的I2C
- RT<-5 | 5>对应framework.log中的ret:5
- slave器件地址
- flag(0x1或0x0),分别对应接收数据或者发送数据
- 错误码(查找下方具体的错误码对应的错误信息排查
| 返回码 | 错误码 | 说明 | 排查步骤 |
|---|---|---|---|
| -5 | 0x1 | 发生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器件是否过多 三种错误都寻求硬件帮忙测量波形搞定。 |
| -5 | 0x2 | smbus时钟一直是低 原因有: I2C的复用关系可能没有配置 | 1、首先联系硬件,确认该I2C通道被用作I2C功能; 2、联系SDK软件检查复用配置 |
| -5 | 0x200或0x201 | 发送FIFO中的有效数据个数不为0. 原因有: 接收数据过程中,从器件突然不响应; 总线干扰; | 1、一般0x200会与0x1一起出现,即0x201,这时引起这样的错误的原因一般是从器件无响应导致的,请联系硬件同事排查slave不响应的原因。 2、只有0x200的情况,一般是存在总线干扰,请联系硬件同事进行总线排查 |
| -5 | 0x800或0x801 | 软件还有剩余的数据没有读到 原因有: slave地址错误; slave不响应; 链路异常; | 1、一般0x800会与0x1一起出现,即0x801;或者与0x200、0x1一起出现,即0xa01,这时引起这样的错误的原因一般是从器件无响应导致的。请联系硬件同事排查slave不响应的原因。2、只有0x800的情况,或是0x800与0x200一起出现,即0xa00,一般是存在总线干扰,请联系硬件同事进行总线排查 |
| -62 | 0x10001 | 传输超时 原因有: 设置的超时时间较短; 从器件特性限制,在读写之间需要有一个延时; 总线挂死; | 1、查看超时时间,和要读的数据长度,是否存在超时时间太短,但时间很长的情况?此时建议BMC增大超时时间重试 2、联系slave器件负责人,咨询slave器件的性质,是否存在需要在读写之间增加延时的情况?典型器件如PSU需要在读写之间增加10ms的延时 3、通过测量波形,发现状态一直保持高电平,可以尝试减小链路的上拉电阻2 |
| -62 | 0x10000 | 传输超时 原因有:中断被屏蔽,硬件无法发出信号。 | 1、查看超时时间,和要读的数据长度,是否存在超时时间太短,但时间很长的情况?此时建议BMC软件增大超时时间重试 2、联系slave器件负责人,咨询slave器件的性质,是否存在需要在读写之间增加延时的情况?典型器件如PSU需要在读写之间增加10ms的延时 3、查看此时是否有大量打印,print会屏蔽中断 |