gpt4 book ai didi

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

转载 作者:IT王子 更新时间:2023-10-29 01:05:26 26 4
gpt4 key购买 nike

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

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

如果此 i2c 从设备或任何其他需要,我仍在寻找扩展驱动程序的方法。

我使用 i2c-tools 3.2.1。我努力

$ i2cdump -y 0 0x20 

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

$ i2cget -y 0 0x20 0x12

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

本slave支持两种读操作:

  • byte read-write address get its value but this need multi start
  • block 读取 - 开始读取,i2c 从机将按顺序给出寄存器值,如 0x00 0x01....(第一个寄存器,第二个,第三个,第四个....等)

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

  • i2c_smbus_access()
  • i2c_smbus_write_byte()
  • i2c_smbus_read_block_data()
  • write()
  • 读取()

但大多数时候 i2c 总线会进入超时错误和挂起情况。

有人知道如何在 Linux 中实现这一点吗?

更新0:

此 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 可以由主机根据需要拉伸(stretch)。 Linux 绝对不是实时操作系统,因此无法保证实现此超时,并且可能会发生 i2c 从属 SCL 超时重置。这就是我目前的结论!

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

最佳答案

why repeated start based i2c operation are not supported in linux?

事实上,它们是受支持的。

如果您正在寻找一种在用户空间中执行重复启动条件的方法,您可能需要使用I2C_RDWR执行ioctl() > 请求,如描述的那样here (请参阅原始问题中的最后一个代码片段)和 here (有问题的代码)。

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


在 Linux 内核中,I2C 读取操作 repeated start condition默认情况下对组合(写/读)消息执行。

这是一个如何执行组合 I2C 传输的示例:

/**
* 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;
}

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

/**
* 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);
}

下面是 i2c_read_regs(client, reg, 1, val) 调用的说明:

  • 设备地址为client->addr
  • 注册地址是reg
  • 1表示我们要读取1个字节的数据(图中粉色矩形)
  • 读取数据将驻留在val

enter image description here


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


如果没有任何效果,您可以在万不得已的情况下尝试下一步。出于某种原因,我不太记得了,为了重复启动工作,我需要将 I2C_M_NOSTART 添加到第一条消息的 .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 中所述:

If you set the I2C_M_NOSTART variable for the first partial message, we do not generate Addr, but we do generate the startbit S.


引用资料:

[1] I2C on STLinux

关于linux - 为什么 linux 不支持基于重复启动的 i2c 操作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35078131/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com