• I2C子系统-应用视角


    I2C子系统层次

    在这里插入图片描述

    LINUX-I2C关键结构体与函数

    i2c.h

    1. i2c控制器 - 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
    };
    

    2. i2c设备 - 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	*/
    };
    

    3. i2c消息 - i2c_msg

    在上面的i2c_algorithm结构体中可以看到要传输的数据被称为:i2c_msg

    • flags用来表示传输方向:bit 0等于I2C_M_RD表示读,bit 0等于0表示写。其余bit可以使用下面定义的宏来设置一些特殊配置。
    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;
    

    4. i2c传输数据概括

    在这里插入图片描述

    • 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)
        

    5. i2c读写函数

    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使用i2ctools访问EEPROM

    一句话概括:APP通过I2C Controller与I2C Device传输数据

    在APP里,有这三个问题:

    (1) 怎么指定I2C控制器?

    • i2c-dev.c提供为每个I2C控制器(I2C Bus、I2C Adapter)都生成一个设备节点:/dev/i2c-0、/dev/i2c-1等待
    • open某个/dev/i2c-X节点,就是去访问该I2C控制器下的设备

    (2) 怎么指定I2C设备?

    • 通过ioctl指定I2C设备的地址
    • ioctl(file, I2C_SLAVE, address)
      • 如果该设备已经有了对应的设备驱动程序,则返回失败
    • ioctl(file, I2C_SLAVE_FORCE, address)
      • 如果该设备已经有了对应的设备驱动程序
      • 但是还是想通过i2c-dev驱动来访问它
      • 则使用这个ioctl来指定I2C设备地址

    (3) 怎么传输数据?

    • 两种方式
      • 一般的I2C方式:ioctl(file, I2C_RDWR, &rdwr)
      • SMBus方式:ioctl(file, I2C_SMBUS, &args)

    编写程序:

    • file = open_i2c_dev(i2c总线编号, 返回的文件名, 文件名大小, 0 - 不打印信息)
    • set_slave_addr(file, dev_addr, 1 - 强制) - 设置从机设备地址(AT24C02的设备地址是0x50)
    • 写:i2c_smbus_write_byte_data(file, command - 寄存器地址,value) - 按字节写入
    • 读:i2c_smbus_read_i2c_block_data(file, mem_addr, sizeof(data), data) - I2C连续读
    #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;
    

    }

  • 相关阅读:
    【算法】【二叉树模块】求一个二叉树是否包含另一个二叉树的拓扑结构
    PHP 图像处理组件:Intervention/image
    一段音频驱动照片唱歌,EMO模型上线通义APP
    投稿时要求注册ORCID,这张学术界身份证到底有哪些用处?
    SSM+中小型企业绩效管理系统毕业设计-附源码081536
    CSS基础:插入CSS样式的3种方法
    java基于ssm的汽车维修保养管理系统
    每章一篇博客带你拿下吉林大学JAVAEE期末(三:JSP)
    springBoot集成mybatis-plus
    PythonAppium自动化测试环境搭建
  • 原文地址:https://blog.csdn.net/ObviouslyCheng/article/details/140450009