关于i2c_msg的一点注意事项

  • 首先对于i2c_msg的说明:一个i2c_msg结构变量,代表着一次单方向的完整传输。

  • 正确理解上面这句话的意思,就能正确使用linux中使用i2c_msg对于i2c的读写操作。

        来看一下i2c_msg的定义(位于:linux-3.10\include\uapi\linux):

/**
 * struct i2c_msg - an I2C transaction segment beginning with START
 * @addr: Slave address, either seven or ten bits.  When this is a ten
 *	bit address, I2C_M_TEN must be set in @flags and the adapter
 *	must support I2C_FUNC_10BIT_ADDR.
 * @flags: I2C_M_RD is handled by all adapters.  No other flags may be
 *	provided unless the adapter exported the relevant I2C_FUNC_*
 *	flags through i2c_check_functionality().
 * @len: Number of data bytes in @buf being read from or written to the
 *	I2C slave address.  For read transactions where I2C_M_RECV_LEN
 *	is set, the caller guarantees that this buffer can hold up to
 *	32 bytes in addition to the initial length byte sent by the
 *	slave (plus, if used, the SMBus PEC); and this value will be
 *	incremented by the number of block data bytes received.
 * @buf: The buffer into which data is read, or from which it's written.
 *
 * An i2c_msg is the low level representation of one segment of an I2C
 * transaction.  It is visible to drivers in the @i2c_transfer() procedure,
 * to userspace from i2c-dev, and to I2C adapter drivers through the
 * @i2c_adapter.@master_xfer() method.
 *
 * Except when I2C "protocol mangling" is used, all I2C adapters implement
 * the standard rules for I2C transactions.  Each transaction begins with a
 * START.  That is followed by the slave address, and a bit encoding read
 * versus write.  Then follow all the data bytes, possibly including a byte
 * with SMBus PEC.  The transfer terminates with a NAK, or when all those
 * bytes have been transferred and ACKed.  If this is the last message in a
 * group, it is followed by a STOP.  Otherwise it is followed by the next
 * @i2c_msg transaction segment, beginning with a (repeated) START.
 *
 * Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then
 * passing certain @flags may have changed those standard protocol behaviors.
 * Those flags are only for use with broken/nonconforming slaves, and with
 * adapters which are known to support the specific mangling options they
 * need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR).
 */
struct i2c_msg {
	__u16 addr;	/* slave address(从机地址)			*/
	__u16 flags; // 标志位,指定进行的操作
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */    // 表示这个器件的器件地址是10Bit的
#define I2C_M_RD		0x0001	/* read data, from slave to master */   // 表示这是一个读操作,默认是把相应的位置1
#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */           // 表示当前i2c_msg不发送start信号
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */ // 表示把读写标志位反转,也就是读是把相应位置0
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */ // 表示当前i2c_msg忽略I2C器件的ack和nack信号
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */ // 表示在读操作时不去ACK
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */ // 表示在读操作时不去ACK
	__u16 len;		/* msg length(单位为字节)       */
	__u8 *buf;		/* pointer to msg data(数据缓冲区)*/
};
  • 看一个读函数实例

i2c的读数据操作分为:“写地址” 和 “读数据” 两步操作。

因此可以定义两个 i2c_msg 结构体变量,分别通过对结构体成员变量flag的赋值来确定传输方向,

该函数本身没啥问题,读数据也正常,只需要注意length是以字节为单位即可。函数编写如下:

/* 读取多个寄存器数据
 * 入口参数: @client: 从机地址
 *		      @reg	 : 寄存器地址
 *		      @buffer: 保存读取数据
 *		      @length: reg/buffer的长度
 */
static int i2c_read_regs(struct i2c_client *client, u8 reg, u8 *buffer, int length)
{
    int err = 0;

    /* msg[0]是发送要读取的寄存器首地址 */
    struct i2c_msg msg[] = {
        {
            .addr  = client->addr,
            .flags = 0,
            .len   = 1,     //表示寄存器地址字节长度,是以byte为单位
            .buf   = &reg,
        },
        {
            .addr  = client->addr,
            .flags = I2C_M_RD,
            .len   = length, //表示期望读到数据的字节长度(寄存器长度),是以byte为单位
            .buf   = buffer, //将读取到的数据保存在buffer中
        },
    };

    err = i2c_transfer(client->adapter, msg, 2);
    if(err != 2)C
    {
        err = -EINVAL;
        printk("read regs from i2c has been failed\n\r");
    }
    
    return err;
}
  • 再来看一个写函数实例

写数据操作也分为:“写地址” 和 “写数据” 两步操作,根据写时序图来看,没有什么问题。

函数编写如下:

/* 向i2c设备写多个寄存器数据
 * 入口参数   @client: 从机地址
 *		      @reg	 : 寄存器地址
 *		      @buffer: 需要写入的数据
 *		      @length: reg/buffer的长度
 */
static int i2c_write_regs(struct i2c_client *client,u8 reg, u8 *buffer, int length)
{
    int err = 0;
    struct i2c_msg msg[] = {
        {
            .addr  = client->addr,
            .flags = 0,    // 表示写
            .len   = 1,
            .buf   = &reg,
        },
        {
            .addr  = client->addr,
            .flags = 0,    // 表示写
            .len   = length,
            .buf   = buffer,
        },
    };

    err = i2c_transfer(client->adapter, msg, 2);
    if(err != 2)
    {
        err = -EINVAL;
        printk("write data to i2c has been failed\n\r");
    }
    
    return err;
}

但实际读出来的数据,确不对。那问题出在哪???

还是要正确理解文章开头的那一句话:一个i2c_msg结构变量,代表着一次单方向的完整传输。

  • 分析

在进行写寄存器操作时,并不是说第一个i2c_msg结构体是写了设备地址之后,第二个i2c_msg结

构体就是向设备的寄存器地址进行写数据了。

i2c_msg成员变量的buf远远没有达到这么智能的程度,它只能知道在一个单次且完整的写过程中,

第一个传递给buf的是寄存器地址。因此,尽管程序中用了结构体数组来定义了两个连续的i2c_msg

结构体变量,但是它们依旧是两个单次的写数据过程,因此i2c_msg[1]还是写地址,而不是写数

,所以在指定地址读取不到想要的值,而是其他值。

  • 正确函数写法应该是:
static int i2c_write_regs(struct i2c_client *client,u8 reg, u8 *buffer, int length)
{
    int err = 0;

    u8 data[256];
    struct i2c_msg msg;
    data[0] = reg;
    memcpy(&data[1], buffer, length);

    msg.addr = client->addr,
    msg.flags = 0;
    msg.len   = length + 1; /* +1是因为还有一个data[0]所存储的寄存器 */
    msg.buf = data;

    err = i2c_transfer(client->adapter, &msg, 1);
    if(err != 1)
    {
        err = -EINVAL;
        printk("write data to i2c has been failed\n\r");
    }
    
    return err;
}

在上述函数中,只使用一个i2c_msg结构变量,因此整个函数是一次完整单向的数据传输,使用

数组是为了让数据连续。

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值