i2c.h
i2c_adapter
主要的成员包括
nr
- 表示是哪个i2c控制器(i2c总线)
const struct i2c_algorithm *algo
- i2c的算法,含有传输函数,用来收发I2C数据
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* data fields that are valid for all devices */
const struct i2c_lock_operations *lock_ops;
struct rt_mutex bus_lock;
struct rt_mutex mux_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
};
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
i2c_client
主要成员:
addr
- i2c的设备地址struct i2c_adapter *adapter
- 指向i2c控制器的指针,表示连接在哪个I2C Controller上LINUX 4.9.88
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};
LINUX 6.9
struct i2c_client {
unsigned short flags; /* div., see below */
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
#define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */
/* Must equal I2C_M_TEN below */
#define I2C_CLIENT_SLAVE 0x20 /* we are the slave */
#define I2C_CLIENT_HOST_NOTIFY 0x40 /* We want to use I2C host notify */
#define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */
#define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */
/* Must match I2C_M_STOP|IGNORE_NAK */
unsigned short c; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
int init_irq; /* irq set at initialization */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
void *devres_group_id; /* ID of probe devres group */
};
在上面的i2c_algorithm
结构体中可以看到要传输的数据被称为:i2c_msg
struct i2c_msg {
__u16 addr; //设备地址
__u16 flags; //读写标志
#define I2C_M_RD 0x0001 /* guaranteed to be 0x0001! */
#define I2C_M_TEN 0x0010 /* use only if I2C_FUNC_10BIT_ADDR */
#define I2C_M_DMA_SAFE 0x0200 /* use only in kernel space */
#define I2C_M_RECV_LEN 0x0400 /* use only if I2C_FUNC_SMBUS_READ_BLOCK_DATA */
#define I2C_M_NO_RD_ACK 0x0800 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* use only if I2C_FUNC_NOSTART */
#define I2C_M_STOP 0x8000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
__u16 len; //消息长度
__u8 *buf; //消息
};
示例:设备地址为0x50的EEPROM,要读取它里面存储地址为0x10的一个字节,应该构造几个i2c_msg?
要构造2个i2c_msg
第一个i2c_msg表示写操作,把要访问的存储地址0x10发给设备
第二个i2c_msg表示读操作
代码如下
u8 data_addr = 0x10;
i8 data;
struct i2c_msg msgs[2];
msgs[0].addr = 0x50;
msgs[0].flags = 0;
msgs[0].len = 1;
msgs[0].buf = &data_addr;
msgs[1].addr = 0x50;
msgs[1].flags = I2C_M_RD;
msgs[1].len = 1;
msgs[1].buf = &data;
APP通过I2C Controller与I2C Device传输数据
APP通过i2c_adapter与i2c_client传输i2c_msg
内核函数i2c_transfer
i2c_msg里含有addr,所以这个函数里不需要i2c_client
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
i2c-core-base.c
(LINUX 6)
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
int ret;
/* REVISIT the fault reporting model here is weak:
*
* - When we get an error after receiving N bytes from a slave,
* there is no way to report "N".
*
* - When we get a NAK after transmitting N bytes to a slave,
* there is no way to report "N" ... or to let the master
* continue executing the rest of this combined message, if
* that's the appropriate response.
*
* - When for example "num" is two and we successfully complete
* the first message but get an error part way through the
* second, it's unclear whether that should be reported as
* one (discarding status on the second message) or errno
* (discarding status on the first one).
*/
ret = __i2c_lock_bus_helper(adap);
if (ret)
return ret;
ret = __i2c_transfer(adap, msgs, num);
i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
return ret;
}
EXPORT_SYMBOL(i2c_transfer);
i2c-core.c
(LINUX 4.9)
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
int ret;
/* REVISIT the fault reporting model here is weak:
*
* - When we get an error after receiving N bytes from a slave,
* there is no way to report "N".
*
* - When we get a NAK after transmitting N bytes to a slave,
* there is no way to report "N" ... or to let the master
* continue executing the rest of this combined message, if
* that's the appropriate response.
*
* - When for example "num" is two and we successfully complete
* the first message but get an error part way through the
* second, it's unclear whether that should be reported as
* one (discarding status on the second message) or errno
* (discarding status on the first one).
*/
if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev,
"master_xfer[%d] %c, addr=0x%02x, len=%d%s\n",
ret, (msgs[ret].flags & I2C_M_RD) ? 'R' : 'W',
msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
if (in_atomic() || irqs_disabled()) {
ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
}
ret = __i2c_transfer(adap, msgs, num);
i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
EXPORT_SYMBOL(i2c_transfer);
一句话概括:APP通过I2C Controller与I2C Device传输数据
在APP里,有这三个问题:
(1) 怎么指定I2C控制器?
(2) 怎么指定I2C设备?
(3) 怎么传输数据?
编写程序:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "i2cbusses.h"
/**
* Usage:
* ./at24c02 w "100ask.taobao.com"
* ./at24c02 r
*/
int main(int argc, char const *argv[])
{
unsigned char dev_addr = 0x50;
unsigned char mem_addr = 0;
unsigned char data[32];
int file, ret;
char filename[20];
unsigned char *pstr;
struct timespec req;
if (argc != 3 && argc != 4)
{
printf("Usage:\n");
printf("\t%s [write data]\n" , argv[0]);
return -1;
}
// 打开i2c设备
file = open_i2c_dev(argv[1][0] - '0', filename, sizeof(filename), 0);
if (file < 0)
{
printf("Unable to open %s\n", filename);
return -1;
}
// 设置i2c设备地址
if (set_slave_addr(file, dev_addr, 1))
{
printf("Error: Could not set address to 0x%02x\n", dev_addr);
return -1;
}
// 读or写
if (strcmp(argv[2], "w") == 0)
{
req.tv_sec = 0;
req.tv_nsec = 20000000; // 20ms
// write
pstr = (char *)argv[3];
while (*pstr)
{
// 写入mem_addr和*pstr指向要写入的数据
ret = i2c_smbus_write_byte_data(file, mem_addr++, *pstr++);
if (ret != 0)
{
printf("Error: i2c_smbus_write_byte_data failed\n");
return -1;
}
// wait TWR 20ms
nanosleep(&req, NULL);
}
ret = i2c_smbus_write_byte_data(file, mem_addr, 0); // end char
if (ret != 0)
{
printf("Error: i2c_smbus_write_byte_data failed\n");
return -1;
}
}
else if (strcmp(argv[2], "r") == 0)
{
ret = i2c_smbus_read_i2c_block_data(file, mem_addr, sizeof(data), data);
if (ret < 0)
{
printf("Error: i2c_smbus_read_i2c_block_data failed\n");
return -1;
}
data[31] = '\0';
printf("get data: %s\n", data);
}
// 关闭i2c设备
close(file);
return 0;
}
led\n");
return -1;
}
}
else if (strcmp(argv[2], “r”) == 0)
{
ret = i2c_smbus_read_i2c_block_data(file, mem_addr, sizeof(data), data);
if (ret < 0)
{
printf(“Error: i2c_smbus_read_i2c_block_data failed\n”);
return -1;
}
data[31] = ‘\0’;
printf(“get data: %s\n”, data);
}
// 关闭i2c设备
close(file);
return 0;
}