2.2.I2C编程实践

Posted 衾许°

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2.2.I2C编程实践相关的知识,希望对你有一定的参考价值。

linux内核I2C驱动代码中的一些重要结构体:

一般一条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;

I2C_Tools

源代码文件自取,这里用的是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读出两个字节,(因为读取数据需要指定寄存器地址,所以需要先写才能读)

基于I2C_Tools编写I2C驱动程序

#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <i2c/smbus.h>
#include "i2cbusses.h"
#include <time.h>


//应用程序的使用格式
/* ./at24c02 <i2c_bus_number> w "100ask.taobao.com"
 * ./at24c02 <i2c_bus_number> 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 <i2c_bus_number> w string\\n", argv[0]);
		printf("read  eeprom: %s <i2c_bus_number> 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 
 

以上是关于2.2.I2C编程实践的主要内容,如果未能解决你的问题,请参考以下文章

C语言精华知识:表驱动法编程实践

好书记录

C语言编程实践训练营,精心规划的一期学习计划,细致到开发环境的搭建,都是手把手传授

用C语言编程,哪个IDE最好?

用 C 语言开发一门编程语言 — 函数库的设计与实现

学生信息管理系统 C语言程序设计