linux i2c 设备驱动开发

i2c读时序

读时序
i2c读时序如图所示,通过时序图可以发现,i2c进行读操作可以概括为两大步,读和写。
1.先发送寄存器地址
2.读取寄存器值

i2c写时序

写时序图
i2c写时序如图所示,通过时序图可以发现,i2c进行写操作一步完成。

i2c 设备驱动相关结构体

  1. i2c_client
    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
    };
    
  2. i2c_driver
    struct i2c_driver {
    	unsigned int class;
    
    	/* Notifies the driver that a new bus has appeared. You should avoid
    	 * using this, it will be removed in a near future.
    	 */
    	int (*attach_adapter)(struct i2c_adapter *) __deprecated;
    
    	/* Standard driver model interfaces */
    	int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    	int (*remove)(struct i2c_client *);
    
    	/* driver model interfaces that don't relate to enumeration  */
    	void (*shutdown)(struct i2c_client *);
    
    	/* Alert callback, for example for the SMBus alert protocol.
    	 * The format and meaning of the data value depends on the protocol.
    	 * For the SMBus alert protocol, there is a single bit of data passed
    	 * as the alert response's low bit ("event flag").
    	 */
    	void (*alert)(struct i2c_client *, unsigned int data);
    
    	/* a ioctl like command that can be used to perform specific functions
    	 * with the device.
    	 */
    	int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
    
    	struct device_driver driver;
    	const struct i2c_device_id *id_table;
    
    	/* Device detection callback for automatic device creation */
    	int (*detect)(struct i2c_client *, struct i2c_board_info *);
    	const unsigned short *address_list;
    	struct list_head clients;
    };
    
    

i2c_msg结构体

一个i2c_msg结构的变量,代表着一次单方向的传输。

struct i2c_msg {
	__u16 addr;					/* 从机地址			*/
	__u16 flags;				/* 标志位,指定进行的操作 0:write 1:read*/
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_RD		0x0001	/* read data, from slave to master */
#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
	__u16 len;		/* msg length(单位为字节,需要注意)				*/
	__u8 *buf;		/* pointer to msg data			*/
};

i2c api

函数实现位置:drivers/i2c/i2c-core.c

int i2c_master_send(const struct i2c_client *client, 
			const char *buf,
			int count);
int i2c_master_recv(const struct i2c_client *client, 
			char *buf,
			int count);

注意count要小于64KB,因为i2c_msg的len成员变量是一个u16类型的数据

i2c设备驱动读函数编写

由于读操作分为写和读两个方向,因此采用两个i2c_msg结构体变量。
/* 
 * 入口参数@client:从机地址
 *		  @reg	 :寄存器地址
 *		  @buffer:保存读取数据
 *		  @length:reg/buffer的长度
 */
static int  i2c_read_regs(struct i2c_client *client,u8 reg, u8 *buffer, int length)
{
    int err = 0;

    /* msg[0]是发送要读取的寄存器首地址 */
    struct i2c_msg msg[] = {
        {
            .addr  = client->addr,
            .flags = 0,
            .len   = 1,     //表示寄存器地址字节长度,是以byte为单位
            .buf   = &reg,
        },
        {
            .addr  = client->addr,
            .flags = I2C_M_RD,
            .len   = length, //表示期望读到数据的字节长度(寄存器长度),是以byte为单位
            .buf   = buffer, //将读取到的数据保存在buffer中
        },
    };

    err = i2c_transfer(client->adapter, msg,2);
    if(err != 2)C
    {
        err = -EINVAL;
        printk("read regs from ap3216c has been failed\n\r");
    }
    
    return err;
}

i2c设备驱动写函数编写

static int ap3216c_write_regs(struct i2c_client *client,u8 reg, u8 *buffer, int length)
{
    int err = 0;

    u8 b[256];
    struct i2c_msg msg;
    b[0] = reg;
    memcpy(&b[1],buffer,length);

    msg.addr = client->addr,
    msg.flags = 0;
    msg.len   = length + 1; /* +1是因为还有一个b[0]所存储的寄存器 */
    msg.buf = b;

    err = i2c_transfer(client->adapter, &msg,1);
    if(err != 1)
    {
        err = -EINVAL;
        printk("write data to ap3216c has been failed\n\r");
    }
    
    return err;
}

i2c设备树配置

i2c设备树配置,最重要的信息是设备地址的配置。

&i2c1{
        status = "okay";
        rn6752: rn6752@2c {
                compatible = "richnex,rn6752";//匹配名称
                reg = <0x2c>;//设备地址
        };
};

设备树修改完成以后使用 make dts 重新编译,然后使用新的设备树启动Linux内核。如果设备树修改正确的话,在/sys/bus/i2c/devices目录下看到一个0-002c的子目录。说明修改成功。

i2c板级信息设置

板级信息注册,系统会根据该信息生成对应的i2c_client结构

static struct i2c_board_info i2c_devices[] __initdata = {  
    {I2C_BOARD_INFO("24c02", 0x50), },  
     {}  
}; 
i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));

i2c驱动框架实例

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "rn6752V1_configuration.h"

#define rn6752_CNT 1
#define rn6752_NAME "rn6752"

struct rn6752_dev {
	dev_t devid; /* 设备号 	 */
	struct cdev cdev; /* cdev 	*/
	struct class *class; /* 类 		*/
	struct device *device; /* 设备 	 */
	struct device_node *nd; /* 设备节点 */
	int major; /* 主设备号 */
	void *private_data; /* 私有数据 */
	unsigned short ir, als, ps; /* 三个光传感器数据 */
};

static struct rn6752_dev rn6752dev;

/*
 * @description	: 从rn6752读取多个寄存器数据
 * @param - dev:  rn6752设备
 * @param - reg:  要读取的寄存器首地址
 * @param - val:  读取到的数据
 * @param - len:  要读取的数据长度
 * @return 		: 操作结果
 */
static int rn6752_read_regs(struct rn6752_dev *dev, u8 reg, void *val, int len)
{
	int ret;
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	printk("rn6752_read_regs add 0x%x,0x%x\n", client->addr, reg);
	/* msg[0]为发送要读取的首地址 */
	msg[0].addr = client->addr; /* rn6752地址 */
	msg[0].flags = 0; /* 标记为发送数据 */
	msg[0].buf = &reg; /* 读取的首地址 */
	msg[0].len = 1; /* reg长度*/

	/* msg[1]读取数据 */
	msg[1].addr = client->addr; /* rn6752地址 */
	msg[1].flags = I2C_M_RD; /* 标记为读取数据*/
	msg[1].buf = val; /* 读取数据缓冲区 */
	msg[1].len = len; /* 要读取的数据长度*/

	ret = i2c_transfer(client->adapter, msg, 2);
	// printk("buf:0x%x,len:%d\n", *(msg[1].buf), msg[1].len);
	// printk("0x%x\n", (*(msg[1].buf)) & 0xF1);
	if (ret == 2) {
		ret = 0;
	} else {
		printk("i2c rd failed=%d reg=%06x len=%d\n", ret, reg, len);
		ret = -EREMOTEIO;
	}
	return ret;
}

/*
 * @description	: 向rn6752多个寄存器写入数据
 * @param - dev:  rn6752设备
 * @param - reg:  要写入的寄存器首地址
 * @param - val:  要写入的数据缓冲区
 * @param - len:  要写入的数据长度
 * @return 	  :   操作结果
 */
static s32 rn6752_write_regs(struct rn6752_dev *dev, u8 reg, u8 *buf, u8 len)
{
	u8 b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->private_data;

	b[0] = reg; /* 寄存器首地址 */
	memcpy(&b[1], buf, len); /* 将要写入的数据拷贝到数组b里面 */

	msg.addr = client->addr; /* rn6752地址 */
	msg.flags = 0; /* 标记为写数据 */
	msg.buf = b; /* 要写入的数据缓冲区 */
	msg.len = len + 1; /* 要写入的数据长度 */

	return i2c_transfer(client->adapter, &msg, 1);
}

/*
 * @description	: 读取rn6752指定寄存器值,读取一个寄存器
 * @param - dev:  rn6752设备
 * @param - reg:  要读取的寄存器
 * @return 	  :   读取到的寄存器值
 */
static unsigned char rn6752_read_reg(struct rn6752_dev *dev, u8 reg)
{
	u8 data = 0;
	rn6752_read_regs(dev, reg, &data, 1);
	return data;

#if 0
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	return i2c_smbus_read_byte_data(client, reg);
#endif
}

/*
 * @description	: 向rn6752指定寄存器写入指定的值,写一个寄存器
 * @param - dev:  rn6752设备
 * @param - reg:  要写的寄存器
 * @param - data: 要写入的值
 * @return   :    无
 */
static void rn6752_write_reg(struct rn6752_dev *dev, u8 reg, u8 data)
{
	u8 buf = 0;
	buf = data;
	rn6752_write_regs(dev, reg, &buf, 1);
}

/*
 * @description	: 读取rn6752的数据,读取原始数据,包括ALS,PS和IR, 注意!
 *				: 如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms
 * @param - ir	: ir数据
 * @param - ps 	: ps数据
 * @param - ps 	: als数据 
 * @return 		: 无。
 */
void rn6752_readdata(struct rn6752_dev *dev)
{
	unsigned char buf[6] = "";

	/* 循环读取所有传感器数据 */
	// for (i = 0; i < 6; i++) {
	// 	buf[i] = rn6752_read_reg(dev, rn6752_IRDATALOW + i);
	// }

	if (buf[0] & 0X80) {
		/* IR_OF位为1,则数据无效 */
		dev->ir = 0;
	} else {
		/* 读取IR传感器的数据*/
		dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);
	}

	/* 读取ALS传感器的数据*/
	dev->als = ((unsigned short)buf[3] << 8) | buf[2];

	/* IR_OF位为1,则数据无效*/
	if (buf[4] & 0x40) {
		dev->ps = 0;
	} else {
		/* 读取PS传感器的数据*/
		dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) |
			  (buf[4] & 0X0F);
	}
}

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int rn6752_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &rn6752dev;

	/* 初始化rn6752 */
	return 0;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t rn6752_read(struct file *filp, char __user *buf, size_t cnt,
			   loff_t *off)
{
	short data[3];
	long err = 0;

	struct rn6752_dev *dev = (struct rn6752_dev *)filp->private_data;

	rn6752_readdata(dev);

	data[0] = dev->ir;
	data[1] = dev->als;
	data[2] = dev->ps;
	err = copy_to_user(buf, data, sizeof(data));
	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int rn6752_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* rn6752操作函数 */
static const struct file_operations rn6752_ops = {
	.owner = THIS_MODULE,
	.open = rn6752_open,
	.read = rn6752_read,
	.release = rn6752_release,
};

/*
  * @description     : i2c驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * @return          : 0,成功;其他负值,失败
  */
static int rn6752_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	unsigned char reg_addr = 0x00;
	int nLoop = 0;

	/* 1、构建设备号 */
	if (rn6752dev.major) {
		rn6752dev.devid = MKDEV(rn6752dev.major, 0);
		register_chrdev_region(rn6752dev.devid, rn6752_CNT,
				       rn6752_NAME);
	} else {
		alloc_chrdev_region(&rn6752dev.devid, 0, rn6752_CNT,
				    rn6752_NAME);
		rn6752dev.major = MAJOR(rn6752dev.devid);
	}

	/* 2、注册设备 */
	cdev_init(&rn6752dev.cdev, &rn6752_ops);
	cdev_add(&rn6752dev.cdev, rn6752dev.devid, rn6752_CNT);

	/* 3、创建类 */
	rn6752dev.class = class_create(THIS_MODULE, rn6752_NAME);
	if (IS_ERR(rn6752dev.class)) {
		return PTR_ERR(rn6752dev.class);
	}

	/* 4、创建设备 */
	rn6752dev.device = device_create(rn6752dev.class, NULL, rn6752dev.devid,
					 NULL, rn6752_NAME);
	if (IS_ERR(rn6752dev.device)) {
		return PTR_ERR(rn6752dev.device);
	}

	rn6752dev.private_data = client;

	/*读取寄存器状态,测试链接是否正确*/
	rn6752_read_reg(&rn6752dev, reg_addr);

	// unsigned char buf;
	// int ret = 0;
	// printk("0----------0x%x,reg:0x%x\n", client->addr, reg_addr);
	// ret = i2c_master_send(client, &reg_addr, 1); // 发送寄存器地址
	// printk("1----------ret:%d\n", ret);
	// ret = i2c_master_recv(client, &buf, 1); // 接收寄存器的值
	// printk("2----------ret:%d,0x%x\n", ret,buf);

	/*写寄存器,配置rn6752输出格式*/
	for (nLoop = 0; nLoop < sizeof(HD_720P25_video) / sizeof(char);) {
		rn6752_write_reg(&rn6752dev, HD_720P25_video[nLoop],
				 HD_720P25_video[nLoop + 1]);
		nLoop += 2;
	}

	return 0;
}

/*
 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
 * @param - client 	: i2c设备
 * @return          : 0,成功;其他负值,失败
 */
static int rn6752_remove(struct i2c_client *client)
{
	/* 删除设备 */
	cdev_del(&rn6752dev.cdev);
	unregister_chrdev_region(rn6752dev.devid, rn6752_CNT);

	/* 注销掉类和设备 */
	device_destroy(rn6752dev.class, rn6752dev.devid);
	class_destroy(rn6752dev.class);
	return 0;
}

/* 传统匹配方式ID列表 */
static const struct i2c_device_id rn6752_id[] = { { "richnex,rn6752", 0 }, {} };

/* 设备树匹配列表 */
static const struct of_device_id rn6752_of_match[] = {
	{ .compatible = "richnex,rn6752" },
	{ /* Sentinel */ }
};

/* i2c驱动结构体 */
static struct i2c_driver rn6752_driver = {
	.probe = rn6752_probe,
	.remove = rn6752_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "rn6752",
		   	.of_match_table = rn6752_of_match, 
		   },
	.id_table = rn6752_id,
};

/*
 * @description	: 驱动入口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init rn6752_init(void)
{
	int ret = 0;
	ret = i2c_add_driver(&rn6752_driver);
	return ret;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit rn6752_exit(void)
{
	i2c_del_driver(&rn6752_driver);
}

/* module_i2c_driver(rn6752_driver) */

module_init(rn6752_init);
module_exit(rn6752_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("songweishuai@foxmail.com");

i2c测试工具-i2cdetect

  1. i2cdetect -l
    查询系统中i2c总线
  2. i2cdetect -r -y 1
    检测挂载在i2c总线上的器件。
  3. i2cdump -f -y 1 0x20
    查看器件所有寄存器的值
  4. i2cget
    ./i2cget -f -y 1 0x20 0x77  读取i2c总线1上0x20器件的0x77寄存器
    
  5. i2cset
    ./i2cset -f -y 1 0x20 0x77 0x3f 设置i2c总线1上地址为0x20器件的0x77寄存器地址为0x3f 
    

i2c通讯错误

  1. 通信失败,出现这种 log: “timeout, ipd: 0x00, state: 1” 该如何调试?
    检查硬件上拉是否给电
  2. 调用 i2c_transfer 返回值为 -6
    返回值-6表示为nack错误,即对方设备无应答响应,这种情况下一般为外设问题,常见如下:
    1. i2c 地址错误,解决方法是测量i2c波形,确认是否i2c设备地址错误。
    2. i2c slave 设备部处于正常工作状态,
    3. 时序不符合i2c slave设备所要求也会产生nack信号
  3. 当外设对于读时序要求中间是 stop 信号不是 repeat start 信号的时候,该如何处理?
    这时需要调用两次 i2c_transfer, I2C read 拆分成两次,修改如下
    static int i2c_read_bytes(struct i2c_client *client, u8 cmd, u8 *data, u8 data_len) {
       struct i2c_msg msgs[2];
    int ret;
    u8 *buffer;
    buffer = kzalloc(data_len, GFP_KERNEL);
    if (!buffer)
    	return -ENOMEM;;
    msgs[0].addr = client->addr;
    msgs[0].flags = client->flags;
    msgs[0].len = 1;
    msgs[0].buf = &cmd;
    ret = i2c_transfer(client->adapter, msgs, 1);
    if (ret < 0) {
    	dev_err(&client->adapter->dev, "i2c read failed\n");
           kfree(buffer);
    		return ret;
       }
       msgs[1].addr = client->addr;
       msgs[1].flags = client->flags | I2C_M_RD;
       msgs[1].len = data_len;
       msgs[1].buf = buffer;
       ret = i2c_transfer(client->adapter, &msgs[1], 1);
       if (ret < 0)
    	dev_err(&client->adapter->dev, "i2c read failed\n");
       else
           memcpy(data, buffer, data_len);
       kfree(buffer);
       return ret;
    }
    

用户态实现设备驱动方式

把i2c设备当作一个普通的字符设备来处理,用i2c-dev.c文件提供的API,封装设备时序数据。直接操作i2c设备器驱动对应的设备文件,实现与设备通讯。
i2c-dev.c源码位置

drivers/i2c/i2c-dev.c

i2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read(),write(),和ioctl()等文件操作接口,在用户空间的应用层可以借用这些接口访问挂接在适配器上的i2c设备的存储空间货寄存器。并控制i2c设备的工作方式。
i2c适配器的设备节点是/dev/i2c-x,其中x是数字,代表适配器的编号,适配器编号是动态分配的,所以想了解哪一个适配器对应什么编号,可以查看/sys/class/i2c-dev/目录下的文件内容。

[root@zlg /]# cat /sys/class/i2c-dev/i2c-0/name
PNX4008-I2C0
[root@zlg /]# cat /sys/class/i2c-dev/i2c-1/name
PNX4008-I2C1
[root@zlg /]# cat /sys/class/i2c-dev/i2c-2/name
USB-I2C

ioctl 命令

查看include/linux/i2c-dev.h文件,可以看到i2c-dev支持的IOCTL命令。如i2c-dev IOCTL命令
#define I2C_RETRIES                    0x0701     /*设置收不到ACK时的重试次数*/
#define I2C_TIMEOUT                 0x0702 /* 设置超时时限的jiffies */
#define I2C_SLAVE                       0x0703 /*设置从机地址 */
#define I2C_SLAVE_FORCE        0x0706  /* 强制设置从机地址 */
#define I2C_TENBIT                      0x0704 /*选择地址位长:=0 for 7bit , != 0 for 10 bit */
#define I2C_FUNCS                      0x0705 /*获取适配器支持的功能 */
#define I2C_RDWR                        0x0707                                 /*Combined R/W transfer (one STOP only)  */
#define I2C_PEC                            0x0708                                  /* != 0 to use PEC with SMBus */
#define I2C_SMBUS                      0x0720                                   /*SMBus transfer  */

使用例程

#include <stdio.h>   
#include <linux/types.h>   
#include <fcntl.h>   
#include <unistd.h>   
#include <stdlib.h>   
#include <sys/types.h>   
#include <sys/ioctl.h>   
#include <errno.h>   
#include <assert.h>   
#include <string.h>   
#include <linux/i2c.h>   
#include <linux/i2c-dev.h>   
   
int main()  
{  
         intfd, ret;  
         unsignedchar rdwr_addr = 0x42;   /* e2prom 读写地址 */  
         unsignedchar device_addr = 0x50; /* e2prom 设备地址 */  
         unsignedchar data = 0x12;  /* 向e2prom写的数据 */  
         structi2c_rdwr_ioctl_data e2prom_data;  
   
         fd= open("/dev/i2c/0", O_RDWR);  
         if(fd < 0) {  
                   perror("openerror");  
                   exit(1);  
         }  
   
         e2prom_data.msgs= (struct i2c_msg *)malloc(e2prom_data.nmsgs * \  
                                               sizeof(structi2c_msg));  
         if(e2prom_data.msgs == NULL) {  
                   perror("mallocerror");  
                   exit(1);  
         }  
   
         ioctl(fd,I2C_TIMEOUT, 1); /* 设置超时 */  
         ioctl(fd,I2C_RETRIES, 2); /* 设置重试次数 */  
   
          
         /*向e2prom的rdwr_addr地址写入数据data*/  
         e2prom_data.nmsgs= 1;  
         e2prom_data.msgs[0].len= 2;  
         e2prom_data.msgs[0].addr= device_addr;  
         e2prom_data.msgs[0].flags= 0;     /* write */  
   
          
         e2prom_data.msgs[0].buf= (unsigned char *)malloc(2);  
         e2prom_data.msgs[0].buf[0]= rdwr_addr;    /* write address */  
         e2prom_data.msgs[0].buf[1]= data;      /* write data */  
   
         ret= ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);  
         if(ret < 0) {  
                   perror("writedata error");  
                   exit(1);  
         }  
         printf("writedata: %d to address: %#x\n", data, rdwr_addr);  
         data= 0;  /* be zero*/  
   
   
         /*从e2prom的rdwr_addr地址读取数据存入buf*/  
         e2prom_data.nmsgs= 2;  
         e2prom_data.msgs[0].len= 1;  
         e2prom_data.msgs[0].addr= device_addr;  
//      e2prom_data.msgs[0].flags= 0;     /* write */   
         e2prom_data.msgs[0].buf= &rdwr_addr;  
   
         e2prom_data.msgs[1].len= 1;  
         e2prom_data.msgs[1].addr= device_addr;  
         e2prom_data.msgs[1].flags= 1;     /* read */  
         e2prom_data.msgs[1].buf= &data;  
   
         ret= ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);  
         if(ret < 0) {  
                   perror("readerror");  
                   exit(1);  
         }  
         printf("read  data: %d from address: %#x\n", data,rdwr_addr);  
          
         free(e2prom_data.msgs);  
         close(fd);  
   
         return0;  
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值