为什么linux中不支持重复启动的i2c操作?

时间:2016-01-29 06:21:57

标签: linux linux-kernel driver device i2c

我想从i2c slave读取,需要多次启动操作才能读取其寄存器值。

在某种程度上,我已经在Linux内核3.18.21中跟踪了I2C驱动程序,我发现它不支持多启动操作,我无法读取此I2C从设备(以太网供电管理器PD69104B1)。 / p>

如果需要这个i2c奴隶或其他任何需要的话,我仍然可以找到扩展驱动程序的方法。

我使用的是i2c-tools 3.2.1。 我试着

$ i2cdump -y 0 0x20 

但我可以看到相同的值,这意味着它每次都会读取第一个寄存器。

$ i2cget -y 0 0x20 0x12

或任何其他寄存器地址返回与第一个寄存器相同的值。

这个slave支持两个读操作:

  • 字节读 - 写地址获取其值,但这需要多次启动
  • 块读取 - 开始读取,i2c slave将按顺序给出寄存器值,如0x00 0x01 ....(第一个寄存器,第二个,第三个,第四个......等)

我尝试了所有可能的方法:

  • i2c_smbus_access()
  • i2c_smbus_write_byte()
  • i2c_smbus_read_block_data()
  • write()
  • read()

但是大部分时间i2c总线都会进入超时错误并挂起情况。

任何人都知道如何在Linux中实现这一目标?

Update0:

这个I2C从设备需要独特的读周期:

  • 方向变更:S Addr Wr [A] RegAddress [A] S Addr Rd [A] [RegValue] P

  • 简短阅读:S Addr Rd [A] [RegValue] P

此处从i2c slave返回的最后一个值不期望ACK。

我尝试使用I2C_M_NO_RD_ACK,但没有多大帮助。我读了一些值,然后得到FF。

这个POE I2C从器件在SCL上有超过14ms的i2c时间,这有点让人怀疑。这看起来像i2c非标准,因为i2c可以在0HZ上工作,即SCL可以由主设备拉伸,只要它想要。 Linux绝对不是实时操作系统,因此无法保证实现此时间,并且可能会发生i2c slave SCL超时复位。这就是我目前的结论!

使用的I2C消息表示法来自: https://www.kernel.org/doc/Documentation/i2c/i2c-protocol

1 个答案:

答案 0 :(得分:7)

  

为什么Linux不支持重复启动的i2c操作?

事实上,它们得到了支持。

如果您正在寻找一种在用户空间中执行重复启动条件的方法,您可能需要ioctl() I2C_RDWR请求,就像它描述的那样/** * Read set of registers via I2C using "repeated start" condition. * * Two I2C messages are being sent by this function: * 1. I2C write operation (write register address) with no STOP bit in the end * 2. I2C read operation * * @client: I2C client structure * @reg: register address (subaddress) * @len: bytes count to read * @buf: buffer which will contain read data * * Returns 0 on success or negative value on error. */ static int i2c_read_regs(struct i2c_client *client, u8 reg, u8 len, u8 *buf) { int ret; struct i2c_msg msg[2] = { { .addr = client->addr, .len = 1, .buf = &reg, }, { .addr = client->addr, .flags = I2C_M_RD, .len = len, .buf = buf, } }; ret = i2c_transfer(client->adapter, msg, 2); if (ret < 0) { dev_err(&client->dev, "I2C read failed\n"); return ret; } return 0; } 3}}(请参阅原始问题中的最后一个代码段)和here(相关代码)。

下面介绍了在内核空间中执行重复启动的方法。

在Linux内核中,对于组合(写入/读取)消息,默认情况下会执行here的I2C读取操作。

以下是如何执行组合I2C传输的示例:

/**
 * Read one register via I2C using "repeated start" condition.
 *
 * @client: I2C client structure
 * @reg: register address (subaddress)
 * @val: variable to store read value
 *
 * Returns 0 on success or negative value on error.
 */
static int i2c_read_reg(struct i2c_client *client, u8 reg, u8 *val)
{
    return i2c_read_regs(client, reg, 1, val);
}

要读取1个字节(单个寄存器值),您可以使用下一个辅助函数:

i2c_read_regs(client, reg, 1, val)

以下是client->addr电话的说明:

  • 设备地址为reg
  • 注册地址为1
  • val表示我们想要读取1个字节的数据(图片上的粉红色矩形)
  • 读取数据将驻留在I2C_M_NOSTART

repeated start condition

注意:如果您的I2C控制器(或其驱动程序)不支持在组合消息中重复启动,您仍然可以使用I2C的bit-bang实现,即i2c-gpio驱动程序。

如果无效,您可以尝试下一步作为最后的手段。由于某种原因,我不记得了,为了重复开始工作,我需要将.flags添加到第一条消息的struct i2c_msg msg[2] = { { .addr = client->addr, .flags = I2C_M_NOSTART, .len = 1, .buf = &reg, }, { .addr = client->addr, .flags = I2C_M_RD, .len = len, .buf = buf, } }; ,如下所示:

Documentation/i2c/i2c-protocol

I2C_M_NOSTART中所述:

  

如果为第一部分邮件设置Addr变量,       我们不会生成S,但我们会生成startbit ['$scope', 'ProductFactory', 'ProductListFactory','$routeParams', function ($scope, ProductFactory,ProductListFactory,$routeParams)

参考文献:

[1] enter image description here