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

为什么linux中不支持重复启动的i2c *** 作?,第1张

概述我想从i2c slave读取,需要多次启动 *** 作才能读取其寄存器值. 在某种程度上,我已经在Linux内核3.18.21中跟踪了I2C驱动程序,我发现它不支持多启动 *** 作,我无法读取此I2C从设备(以太网供电管理器PD69104B1). 如果需要这个i2c从站或其他任何需要,我仍然可以找到扩展驱动程序的方法. 我使用i2c-tools 3.2.1. 我试着 $i2cdump -y 0 0x20 但我可 我想从i2c slave读取,需要多次启动 *** 作才能读取其寄存器值.

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

如果需要这个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()
>写()
>读()

但是大部分时间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

解决方法

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

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

如果您正在寻找一种在用户空间中执行重复启动条件的方法,您可能需要使用I2C_RDWR请求执行ioctl(),如here所述(参见原始问题中的最后一个代码段)和here(相关代码).

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

在linux内核中,对于组合(写入/读取)消息,默认情况下会执行repeated start condition的I2C读取 *** 作.

以下是如何执行组合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 *val){    return i2c_read_regs(clIEnt,reg,1,val);}

以下是i2c_read_regs(clIEnt,val)调用的说明:

>设备地址是客户端 – > addr
>注册地址是reg
> 1表示我们要读取1个字节的数据(图片上的粉红色矩形)
>读取数据将驻留在val

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

如果没有任何作用,你可以尝试下一个作为最后的手段.出于某种原因,我不记得了,为了重复开始工作,我需要将I2C_M_NOSTART添加到第一条消息的.flags中,如下所示:

struct i2c_msg msg[2] = {    {        .addr = clIEnt->addr,.flags = I2C_M_NOSTART,{        .addr = clIEnt->addr,}};

如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中不支持重复启动的i2c *** 作?全部内容,希望文章能够帮你解决为什么linux中不支持重复启动的i2c *** 作?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/1021208.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存