用户空间使用i2c_dev

<!--[if !supportLists]-->第1章 <!--[endif]-->用户空间使用i2c_dev

对于注册的i2c适配器,用户空间也可以使用它们。在Linux内核代码文件/include/linux/i2c-dev.c中针对每个适配器生成一个主设备号为89的设备节点,实现了文件操作接口,用户空间可以通过i2c设备节点访问i2c适配器。适配器的编号从0开始,和适配器的设备节点的次设备号相同。

i2c适配器的设备节点是/dev/i2c-x,其中x是数字,代表适配器的编号。由于适配器编号是动态分配的(和注册次序有关),所以想了解哪一个适配器对应什么编号,可以查看/sys/class/i2c-dev/目录下的文件内容。

<!--[if !supportLists]-->1.1 <!--[endif]-->前期准备

为了在用户空间的程序当中操作i2c适配器,必须在程序中包含以下两句:

#include <linux/i2c-dev.h>

#include <linux/i2c.h>

这两个头文件中定义了之后需要用到的结构体和宏。

然后就可以打开设备节点了。但是打开哪一个呢?因为适配器的编号并不固定。为此我们在中端中运行以下命令:

[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

如果我们想打开第二个适配器,刚好它的编号是1,对应的设备节点是/dev/i2c-1。那么可以用下面的方法打开它:

int fd;

if ((fd = open(/dev/i2c-1,O_RDWR)) < 0) {

/* 错误处理 */

exit(1);

}

打开适配器对应的设备节点,i2c-dev为打开的线程建立一个i2c_client,但是这个i2c_client并不加到i2c_adapterclient链表当中。当用户关闭设备节点时,它自动被释放。

<!--[if !supportLists]-->1.2 <!--[endif]-->IOCTL控制

查看include/linux/i2c-dev.h文件,可以看到i2c-dev支持的IOCTL命令。如<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283302932 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 3.1<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003300300032003900330032000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->所示。

程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283302932'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283302932'><span lang=EN-US><span style='mso-spacerun:yes'>&nbsp;</span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->3<!--[if supportFields]><span style='mso-bookmark: _Ref283302932'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283302932'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283302932'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283302932'><span style='font-family:黑体;mso-ascii-font-family: Arial'>程序清单</span><span lang=EN-US> \* ARABIC \s 1 <span style='mso-element: field-separator'></span></span></span><![endif]-->1<!--[if supportFields]><span style='mso-bookmark:_Ref283302932'></span><span style='mso-element:field-end'></span><![endif]--> 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 7 bit , != 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 */

下面进行一一解释。

<!--[if !supportLists]-->1. <!--[endif]-->设置重试次数

ioctl(fd, I2C_RETRIES, m);

这句话设置适配器收不到ACK时重试的次数为m。默认的重试次数为1

<!--[if !supportLists]-->2. <!--[endif]-->设置超时

ioctl(fd, I2C_TIMEOUT, m);

设置SMBus的超时时间为m,单位为jiffies

<!--[if !supportLists]-->3. <!--[endif]-->设置从机地址

ioctl(fd, I2C_SLAVE, addr);

ioctl(fd, #define I2C_SLAVE_FORCE, addr);

在调用read()write()函数之前必须设置从机地址。这两行都可以设置从机的地址,区别是第二行无论内核中是否已有驱动在使用这个地址都会成功,第一行则只在该地址空闲的情况下成功。由于i2c-dev创建的i2c_client不加入i2c_adapterclient列表,所以不能防止其它线程使用同一地址,也不能防止驱动模块占用同一地址。

<!--[if !supportLists]-->4. <!--[endif]-->设置地址模式

ioctl(file,I2C_TENBIT, select)

如果select不等于0选择10比特地址模式,如果等于0选择7比特模式,默认7比特。只有适配器支持I2C_FUNC_10BIT_ADDR,这个请求才是有效的。

<!--[if !supportLists]-->5. <!--[endif]-->获取适配器功能

ioctl(file,I2C_FUNCS,unsigned long *funcs)

获取的适配器功能保存在funcs中。各比特的含义如<!--[if supportFields]><span lang=EN-US style='mso-font-kerning:0pt'><span style='mso-element:field-begin'></span> REF _Ref283305554 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 3.2<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003300300035003500350034000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US style='mso-font-kerning:0pt'><span style='mso-element:field-end'></span></span><![endif]-->所示。具体的含义可以参考<!--[if supportFields]><span lang=EN-US style='mso-font-kerning:0pt'><span style='mso-element:field-begin'></span> REF _Ref283456550 \r \h <span style='mso-element:field-separator'></span></span><![endif]-->4<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003400350036003500350030000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US style='mso-font-kerning:0pt'><span style='mso-element:field-end'></span></span><![endif]-->

程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283305554'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283305554'><span lang=EN-US><span style='mso-spacerun:yes'>&nbsp;</span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->3<!--[if supportFields]><span style='mso-bookmark: _Ref283305554'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283305554'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283305554'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283305554'><span style='font-family:黑体;mso-ascii-font-family: Arial'>程序清单</span><span lang=EN-US> \* ARABIC \s 1 <span style='mso-element: field-separator'></span></span></span><![endif]-->2<!--[if supportFields]><span style='mso-bookmark:_Ref283305554'></span><span style='mso-element:field-end'></span><![endif]--> I2C FUNCTIONALILTY

/* include/linux/i2c.h */

#define I2C_FUNC_I2C 0x00000001

#define I2C_FUNC_10BIT_ADDR 0x00000002

#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /*I2C_M_{REV_DIR_ADDR,NOSTART,..}*/

#define I2C_FUNC_SMBUS_PEC 0x00000008

#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */

#define I2C_FUNC_SMBUS_QUICK 0x00010000

#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000

#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000

#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000

#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000

#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000

#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000

#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000

#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000

#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000

#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */

#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */

#define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 0x10000000 /* I2C-like block xfer */

#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 0x20000000 /* w/ 2-byte reg. addr. */

<!--[if !supportLists]-->6. <!--[endif]-->I2C层通信

ioctl(file,I2C_RDWR,(struct i2c_rdwr_ioctl_data *)msgset);

这一行代码可以使用I2C协议和设备进行通信。它进行连续的读写,中间没有间歇。只有当适配器支持I2C_FUNC_I2C此命令才有效。参数是一个指针,指向一个结构体,它的定义如<!--[if supportFields]><span lang=DE style='mso-ansi-language:DE'><span style='mso-element:field-begin'></span> REF _Ref283305956 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 3.3<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003300300035003900350036000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=DE style='mso-ansi-language: DE'><span style='mso-element:field-end'></span></span><![endif]-->所示。其中i2c_msg的定义参考<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283227534 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 1.7<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003200320037003500330034000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->

程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283305956'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283305956'><span lang=EN-US><span style='mso-spacerun:yes'>&nbsp;</span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->3<!--[if supportFields]><span style='mso-bookmark: _Ref283305956'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283305956'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283305956'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283305956'><span style='font-family:黑体;mso-ascii-font-family: Arial'>程序清单</span><span lang=EN-US> \* ARABIC \s 1 <span style='mso-element: field-separator'></span></span></span><![endif]-->3<!--[if supportFields]><span style='mso-bookmark:_Ref283305956'></span><span style='mso-element:field-end'></span><![endif]--> i2c_rdwr_ioctl_data

struct i2c_rdwr_ioctl_data {

struct i2c_msg __user *msgs; /* 指向i2c_msgs数组 */

__u32 nmsgs; /* 消息的个数 */

};

msgs[] 数组成员包含了指向各自缓冲区的指针。这个函数会根据是否在消息中的flags置位I2C_M_RD来对缓冲区进行读写。从机的地址以及是否使用10比特地址模式记录在每个消息中,忽略之前ioctl设置的结果。

<!--[if !supportLists]-->7. <!--[endif]-->设置SMBus PEC

ioctl(file,I2C_PEC,(long )select);

如果select不等于0选择SMBus PEC (packet error checking),等于零则关闭这个功能,默认是关闭的。

这个命令只对SMBus传输有效。这个请求只在适配器支持I2C_FUNC_SMBUS_PEC时有效;如果不支持这个命令也是安全的,它不做任何工作。

<!--[if !supportLists]-->8. <!--[endif]-->SMBus通信

ioctl(file, I2C_SMBUS, (i2c_smbus_ioctl_data *)msgset);

这个函数和I2C_RDWR类似,参数的指针指向i2c_smbus_ioctl_data类型的变量,它的定义如<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283308161 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 3.4<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003300300038003100360031000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->所示。如何填写i2c_smbus_ioctl_data的各个成员,参考<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283455290 \r \h <span style='mso-element:field-separator'></span></span><![endif]-->4.3<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003400350035003200390030000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->节。

程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283308161'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283308161'><span lang=EN-US><span style='mso-spacerun:yes'>&nbsp;</span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->3<!--[if supportFields]><span style='mso-bookmark: _Ref283308161'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283308161'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283308161'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283308161'><span style='font-family:黑体;mso-ascii-font-family: Arial'>程序清单</span><span lang=EN-US> \* ARABIC \s 1 <span style='mso-element: field-separator'></span></span></span><![endif]-->4<!--[if supportFields]><span style='mso-bookmark:_Ref283308161'></span><span style='mso-element:field-end'></span><![endif]--> i2c_smbus_ioctl_data

struct i2c_smbus_ioctl_data {

__u8 read_write;

__u8 command;

__u32 size;

union i2c_smbus_data __user *data;

};

<!--[if !supportLists]-->1.3 <!--[endif]-->i2c_dev使用例程

要想在用户空间使用i2c适配器,首先要如3.1<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003300360034003500310038000000</w:data> </xml><![endif]-->节所示,选择某个适配器的设备节点打开,然后才能进行通信。

<!--[if !supportLists]-->1.3.1 <!--[endif]-->read()/write()

通信的方式有两种,一种是使用操作普通文件的接口read()write()。这两个函数间接调用了i2c_master_recvi2c_master_send。但是在使用之前需要使用I2C_SLAVE设置从机地址,设置可能失败,需要检查返回值。这种通信过程进行I2C层的通信,一次只能进行一个方向的传输。

下面的程序是ARME2PROM芯片通信的例子,如<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283651035 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 3.5<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003600350031003000330035000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->所示。

程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283651035'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283651035'><span lang=EN-US><span style='mso-spacerun:yes'>&nbsp;</span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->3<!--[if supportFields]><span style='mso-bookmark: _Ref283651035'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283651035'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283651035'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283651035'><span style='font-family:黑体;mso-ascii-font-family: Arial'>程序清单</span><span lang=EN-US> \* ARABIC \s 1 <span style='mso-element: field-separator'></span></span></span><![endif]-->5<!--[if supportFields]><span style='mso-bookmark:_Ref283651035'></span><span style='mso-element:field-end'></span><![endif]--> 使用read()/write()i2c设备通信

#include <stdio.h>

#include <sys/ioctl.h>

#include <fcntl.h>

#include <linux/i2c-dev.h>

#include <linux/i2c.h>

#define CHIP "/dev/i2c-0"

#define CHIP_ADDR 0x50

int main()

{

printf("hello, this is i2c tester\n");

int fd = open(CHIP, O_RDWR);

if (fd < 0) {

printf("open "CHIP"failed\n");

goto exit;

}

if (ioctl(fd, I2C_SLAVE_FORCE, CHIP_ADDR) < 0) { /* 设置芯片地址 */

printf("oictl:set slave address failed\n");

goto close;

}

struct i2c_msg msg;

unsigned char rddata;

unsigned char rdaddr[2] = {0, 0}; /* 将要读取的数据在芯片中的偏移量 */

unsigned char wrbuf[3] = {0, 0, 0x3c}; /* 要写的数据,头两字节为偏移量 */

printf("input a char you want to write to E2PROM\n");

wrbuf[2] = getchar();

printf("write return:%d, write data:%x\n", write(fd, wrbuf, 3), wrbuf[2]);

sleep(1);

printf("write address return: %d\n",write(fd, rdaddr, 2)); /* 读取之前首先设置读取的偏移量 */

printf("read data return:%d\n", read(fd, &rddata, 1));

printf("rddata: %c\n", rddata);

close:

close(fd);

exit:

return 0;

}

<!--[if !supportLists]-->1.3.2 <!--[endif]-->I2C_RDWR

还可以使用I2C_RDWR实现同样的功能,如<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283651333 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 3.6<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003600350031003300330033000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->所示。此时ioctl返回的值为执行成功的消息数。

程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283651333'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283651333'><span lang=EN-US><span style='mso-spacerun:yes'>&nbsp;</span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->3<!--[if supportFields]><span style='mso-bookmark: _Ref283651333'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283651333'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283651333'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283651333'><span style='font-family:黑体;mso-ascii-font-family: Arial'>程序清单</span><span lang=EN-US> \* ARABIC \s 1 <span style='mso-element: field-separator'></span></span></span><![endif]-->6<!--[if supportFields]><span style='mso-bookmark:_Ref283651333'></span><span style='mso-element:field-end'></span><![endif]--> 使用I2C_RDWRI2C设备通信

#include <stdio.h>

#include <sys/ioctl.h>

#include <fcntl.h>

#include <linux/i2c-dev.h>

#include <linux/i2c.h>

#define CHIP "/dev/i2c-0"

#define CHIP_ADDR 0x50

int main()

{

printf("hello, this is i2c tester\n");

int fd = open(CHIP, O_RDWR);

if (fd < 0) {

printf("open "CHIP"failed\n");

goto exit;

}

struct i2c_msg msg;

unsigned char rddata;

unsigned char rdaddr[2] = {0, 0};

unsigned char wrbuf[3] = {0, 0, 0x3c};

printf("input a char you want to write to E2PROM\n");

wrbuf[2] = getchar();

struct i2c_rdwr_ioctl_data ioctl_data;

struct i2c_msg msgs[2];

msgs[0].addr = CHIP_ADDR;

msgs[0].len = 3;

msgs[0].buf = wrbuf;

ioctl_data.nmsgs = 1;

ioctl_data.msgs = &msgs[0];

printf("ioctl write,return :%d\n", ioctl(fd, I2C_RDWR, &ioctl_data));

sleep(1);

msgs[0].addr = CHIP_ADDR;

msgs[0].len = 2;

msgs[0].buf = rdaddr;

msgs[1].addr = CHIP_ADDR;

msgs[1].flags |= I2C_M_RD;

msgs[1].len = 1;

msgs[1].buf = &rddata;

ioctl_data.nmsgs = 1;

ioctl_data.msgs = msgs;

printf("ioctl write address, return :%d\n", ioctl(fd, I2C_RDWR, &ioctl_data));

ioctl_data.msgs = &msgs[1];

printf("ioctl read, return :%d\n", ioctl(fd, I2C_RDWR, &ioctl_data));

printf("rddata: %c\n", rddata);

close:

close(fd);

exit:

return 0;

}

你可能感兴趣的:(数据结构,linux,xml)