-
首先对于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 = ®,
},
{
.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 = ®,
},
{
.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结构变量,因此整个函数是一次完整单向的数据传输,使用
数组是为了让数据连续。