一般一条I2C总线上,主设备即I2C控制器,一般由i2c_adapter对其进行描述,一般有nr描述它是第几个I2C控制器(第几条总线),而大家都知道I2C控制器提供读写能力,它内部一定会有数据传输的函数(包含在i2c_algorithm算法结构体中)。
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 */
unsigned long locked_flags; /* owned by the I2C core */
#define I2C_ALF_IS_SUSPENDED 0
#define I2C_ALF_SUSPEND_REPORTED 1
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 irq_domain *host_notify_domain;
};
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 (*master_xfer_atomic)(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);
int (*smbus_xfer_atomic)(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 *adap);
#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总线上(在i2c_adapter *adapter中被记录)
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 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 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
};
另外在i2c_algorithm并没有包含i2c_adapter结构体,那它又是如何找到设备地址信息的呢?这就得提到i2c_msg结构体了,其中包含了I2C设备的设备地址等信息。flag表示设备是读还是写。
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_RD 0x0001 /* read data, from slave to master */
/* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */
/* makes only sense in kernelspace */
/* userspace buffers are copied anyway */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
总的来说就是,i2c_adapter与i2c_client间传输i2c_msg,并且i2c_adapter中有一个i2c_algorithm定义了一些传输函数。
i2c_transfer
有如下函数i2c_transfer:该函数会从i2c_adapter中找到根本的传输函数:algo.master_xfer进行调用,然后从i2c_msg中找到设备的设备地址以及设备读写标志位信息,num则是传输多少个数据消息。
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
int ret;
if (!adap->algo->master_xfer) {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
/* 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;
}
源代码文件自取,这里用的是4.2版本的I2C_Tools
链接:https://pan.baidu.com/s/1iHfuwMQwFppnCzdmo76DQA
提取码:1234
访问硬件的方式最普遍的思路是通过编写自己的驱动程序进而访问I2C设备,而I2C_Tools可以直接对I2C控制器上的I2C设备进行访问,我们可以通过编写APP(应用程序)来操作I2C_Tools进而来访问I2C设备,对I2C设备进行驱动。
在这里,大家可以参考这位大佬的博客:【全志T113-S3_100ask】6-编写IIC驱动GY-302(twi)_t113 i2c_第四维度4的博客-CSDN博客
在这里不多赘述,因为他写的真的很好,这里也主要是涉及一个操作的过程。
后续可能贴出I2C_Tools的命令:
i2cdetect //i2c检测命令
//命令示例:
i2cdetect -y 0 //检测第0号总线
i2cdetect -l //列出设备中有多少条I2C总线
使用SMBus协议:
i2cset //I2C写操作命令(SMBus)
命令示例:
i2cset -f -y 0 0x1e 0 0x4 //-f强制访问I2C设备,-y不提示是否访问,0号总线,0x1e设备地址,0号寄存器地址,写入0x4
i2cset -f -y 0 0x1e 0 0x3
i2cget //I2C读操作命令(SMBus)
命令示例:
i2cget -f -y 0 0x1e 0xc w //-f强制访问I2C设备,-y不提示是否访问,0号总线,0x1e设备地址,0xc寄存器地址,w模式(read word data读取两个字节),b模式(read byte data读取一个字节),c模式(write byte/read byte)
使用I2C协议:
i2ctransfer//I2C写操作命令
使用示例:
i2ctransfer -f -y 0 w2@0x1e 0 0x4 //-f强制访问I2C设备,-y不提示是否访问,0号总线,w2@0x1e为描述符,w是写操作,表示写两个字节,0x1e是设备地址,0号寄存器,写入0x4
i2ctransfer//I2C读操作命令
使用示例:
i2ctransfer -f -y 0 w1@0x1e 0xc r2 //-f强制访问I2C设备,-y不提示是否访问,0号总线,w1@0x1e为描述符,w是写操作,表示写1个字节,0x1e是设备地址,0xc寄存器地址,r2读出两个字节,(因为读取数据需要指定寄存器地址,所以需要先写才能读)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "i2cbusses.h"
#include
//应用程序的使用格式
/* ./at24c02 w "100ask.taobao.com"
* ./at24c02 r
*/
int main(int argc, char **argv)
{
unsigned char dev_addr = 0x50;//设备地址,在这里是以AT24C02为例,且A1,A2,A3都接地设备地址为1010000=0x50
unsigned char mem_addr = 0;//存储空间的地址,从0开始读
unsigned char buf[32];//数据缓冲区
int file;
char filename[20];
unsigned char *str;
int ret;
struct timespec req;
//如果用户输入的参数不正确,则打印一下用法
if (argc != 3 && argc != 4)
{
printf("Usage:\n");
printf("write eeprom: %s w string\n", argv[0]);
printf("read eeprom: %s r\n", argv[0]);
return -1;
}
//打开I2C设备
file = open_i2c_dev(argv[1][0]-'0', filename, sizeof(filename), 0);
if (file < 0)
{
printf("can't open %s\n", filename);
return -1;
}
if (set_slave_addr(file, dev_addr, 1))
{
printf("can't set_slave_addr\n");
return -1;
}
//如果第二个参数是w
if (argv[2][0] == 'w')
{
// write str: argv[3]
str = argv[3];
req.tv_sec = 0;
req.tv_nsec = 20000000; /* 20ms */
while (*str)
{
// mem_addr, *str
// mem_addr++, str++
ret = i2c_smbus_write_byte_data(file, mem_addr, *str);
if (ret)
{
printf("i2c_smbus_write_byte_data err\n");
return -1;
}
// wait tWR(10ms)
nanosleep(&req, NULL);
mem_addr++;
str++;
}
ret = i2c_smbus_write_byte_data(file, mem_addr, 0); // string end char
if (ret)
{
printf("i2c_smbus_write_byte_data err\n");
return -1;
}
}
else
{
// read
ret = i2c_smbus_read_i2c_block_data(file, mem_addr, sizeof(buf), buf);
if (ret < 0)
{
printf("i2c_smbus_read_i2c_block_data err\n");
return -1;
}
buf[31] = '\0';
printf("get data: %s\n", buf);
}
return 0;
}
因为编译是用到了别的依赖库,光是上面的代码无法运行,完整代码如下:
链接:https://pan.baidu.com/s/1vGiLMWplEYwmkx2X8otLUQ
提取码:1234