一般一条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 1int 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 0x3i2cget //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号寄存器,写入0x4i2ctransfer//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=0x50unsigned 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;}//如果第二个参数是wif (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 charif (ret){printf("i2c_smbus_write_byte_data err\n");return -1;}}else{// readret = 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