良许Linux教程网 干货合集 debug通信协议 I2C 子系统

debug通信协议 I2C 子系统

在应该收到 ACK 信号的时候没有收到 ACK 信号,i2c controller 就会产生一个 ACK error 的中断,告诉 i2c driver 发生了 ACK error。通常情况是 slave 本身的问题。

I2C 常见有两种错误:I2C ACK error、I2C timeout

一、I2C ACK error

在应该收到 ACK 信号的时候没有收到 ACK 信号,i2c controller 就会产生一个 ACK error 的中断,告诉 i2c driver 发生了 ACK error。通常情况是 slave 本身的问题。

如何debug通信协议 I2C 子系统如何debug通信协议 I2C 子系统

1、检查 device 是否存在,i2c bus number 和 device address 是否正确。示例如下:i2c number 为 6,addr 为 0x28:

[31.092951][xxx]i2c i2c-6:addr:0x28,ACK error

2、检查 device 是否已经上电使能,以及正确的 init。

3、检查 i2c speed 是否适配,speed 大于 device 支持的 max speed 也会造成 ACK Error。降低速度,如果仍然可以工作的话就说明是 clk 相关问题。

4、检查 i2c device 信号电平是否与 AP 匹配。

5、GPIO check 以下几个部分:

GPIO 电流驱动能力。

GPIO 工作模式是否是 I2C 模式。

GPIO 是否有内部上拉电阻。

GPIO 默认电平状态。

从 i2c spec 看,如下情况 NACK 是正常的。

I2C 写

如何debug通信协议 I2C 子系统如何debug通信协议 I2C 子系统

主机向从机发送数据时,最后一个 Byte 数据时,从机可能应答也可能非应答,但不怎样主机都可以产生停止条件,如果主机在向从机发送数据(甚至包括从机地址在内)时检测到从机非应答,则会及时停止传输。

I2C 读

如何debug通信协议 I2C 子系统如何debug通信协议 I2C 子系统

主机从从机接收数据时,最后一个 Byte 数据时,主机不回应该从机,即 NACK。

二、I2C timeout

当 I2C 传输发生 timeout 时,一般 kernel log 会有类似如下打印:

[48.197718][xxx]i2c i2c-1: addr:0xa,transfer timeout
如何debug通信协议 I2C 子系统如何debug通信协议 I2C 子系统

1、GPIO check 以下几个部分。

GPIO 电流驱动能力。

GPIO 工作模式是否是 I2C 模式。

GPIO 是否有内部上拉电阻。

GPIO 默认电平状态。

2、排查 slave 顺序。

log 中第一个发生 timeout 的 slave。

有 power 控制和 reset 控制的 slave。

other slave。

复现问题后,可以手动将相应外设去掉,确认是哪个外设将 i2c bus 拉住,再与供应商沟通,debug 一下该 IC 状态,理清拉住 i2c bus 原因。

三、i2c-tools

i2c-tools 也很好用,这个工具工具博主上次写过,参考以下文章:

手把手教你使用 i2c-tools。

四、常见问题

1、同一条 i2c bus 上所有的外设的 i2c addr 要不同

(1)相同 address 注册时冲突.

[2.059184][xxx]i2c i2c-1:Failed to register i2c client 24c02 at 0x51(-16)
[2.059189][xxx]i2c i2c-1:Can't create device at 0x51

对应的错误码是 -16。

/kernel-5.10/include/uapi/asm-generic/errno-base.h
#define EPERM   1 /* Operation not permitted */
#define ENOENT   2 /* No such file or directory */
#define ESRCH   3 /* No such process */
#define EINTR   4 /* Interrupted system call */
#define EIO   5 /* I/O error */
#define ENXIO   6 /* No such device or address */
#define E2BIG   7 /* Argument list too long */
#define ENOEXEC   8 /* Exec format error */
#define EBADF   9 /* Bad file number */
#define ECHILD  10 /* No child processes */
#define EAGAIN  11 /* Try again */
#define ENOMEM  12 /* Out of memory */
#define EACCES  13 /* Permission denied */
#define EFAULT  14 /* Bad address */
#define ENOTBLK  15 /* Block device required */
#define EBUSY  16 /* Device or resource busy */
......

可以执行 ls /sys/bus/i2c/devices 查看对应的 i2c-1 下是否已经有注册相同的 address 的外设。

若返回 -11,-EAGAIN。代表总线正忙,或无法申请到总线锁。如果总线正忙,请 retry 等待,或查看是哪个 device 一直在发送。如果无法申请到总线锁,请查看是否在中断函数或原子上下文调用了 i2c_transfer。

(2)隐藏的 i2c address,即外设存在多个 i2c addr 或外设 HW bug,导致 i2c 通讯异常。

示例:eeprom 在 i2c-1 上注册了 0x50 地址,而 type c 虽然注册到 0x60 地址,但对 0x50 也能产生响应,type c 拉低了 SDA ,从而 timeout 。

debug 方法:

软件上确认数据及对应的 driver 是否正确。

硬件上逐一去掉外设,确认是哪个外设导致。

2、示波器 ACK 的地方有毛刺

slave 在第 9 个 clk 产生 ACK 应答后,换成 mater 端来控制时产生的毛刺。此毛刺不会影响 I2C 总线读写时序,无需处理。

即 slave 和 master 控制总线切换间隔,没有人控制总线,带来的毛刺。

3、半高电平

在外接上拉电阻的情况下,有 enable 内部下拉电阻,导致总线上有半高电平。

4、总线上电平拉不到地

master 端发送数据时电平拉不到地,可以增大驱动电流或者上拉电阻。

slave 端拉不到地,可以咨询供应商看是否能增大 slave 端驱动电流或者上拉电阻。

5、RK 平台 I2C debug

来源于 firefly。

https://wiki.t-firefly.com/zh_CN/Firefly-RK3399/driver_i2c.html

I2C 通信失败,出现 log: “timeout, ipd: 0x00, state: 1”

请检查硬件上拉是否给电。

调用 i2c_transfer 返回值为 -6?

返回值为 -6 表示为 NACK 错误,即对方设备无应答响应,这种情况一般为外设的问题,常见的有以下几种情况:

I2C 地址错误,解决方法是测量 I2C 波形,确认是否 I2C 设备地址错误;

I2C slave 设备不处于正常工作状态,比如未给电,错误的上电时序等;

时序不符合 I2C slave 设备所要求也会产生 Nack 信号。

当外设对于读时序要求中间是 stop 信号不是 repeat start 信号的时候,该如何处理?

这时需要调用两次 i2c_transfer, I2C read 拆分成两次,修改如下:

static int i2c_read_bytes(struct i2c_client *client, u8 cmd, u8 *data, u8 data_len) {struct i2c_msg msgs[2];
    int ret;
    u8 *buffer;
    buffer = kzalloc(data_len, GFP_KERNEL);
    if (!buffer)
        return -ENOMEM;
    msgs[0].addr = client->addr;
    msgs[0].flags = client->flags;
    msgs[0].len = 1;
    msgs[0].buf = &cmd;
    ret = i2c_transfer(client->adapter, msgs, 1);
    if (ret adapter->dev, "i2c read failed\n");
           kfree(buffer);
      return ret;
     }
     msgs[1].addr = client->addr;
     msgs[1].flags = client->flags | I2C_M_RD;
     msgs[1].len = data_len;
     msgs[1].buf = buffer;
     ret = i2c_transfer(client->adapter, &msgs[1], 1);
     if (ret adapter->dev, "i2c read failed\n");
     else
       memcpy(data, buffer, data_len);
     kfree(buffer);
     return ret;
}

相信以上的 I2C debug 方法已经能为大家解决大部分问题,如果还是没解决,一般是芯片问题或者原厂底层 code bug,可以找芯片原厂支持。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

邮箱地址不会被公开。 必填项已用*标注

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部