用戶空間使用i2c_dev


============================================
作者:yuanlulu
http://blog.csdn.NET/yuanlulu


版權沒有,但是轉載請保留此段聲明
============================================


第1章 用戶空間使用i2c_dev

對於注冊的i2c適配器,用戶空間也可以使用它們。在Linux內核代碼文件/include/linux/i2c-dev.c中針對每個適配器生成一個主設備號為89的設備節點,實現了文件操作接口,用戶空間可以通過i2c設備節點訪問i2c適配器。適配器的編號從0開始,和適配器的設備節點的次設備號相同。

i2c適配器的設備節點是/dev/i2c-x,其中x是數字,代表適配器的編號。由於適配器編號是動態分配的(和注冊次序有關),所以想了解哪一個適配器對應什么編號,可以查看/sys/class/i2c-dev/目錄下的文件內容。

1.1     前期准備

       為了在用戶空間的程序當中操作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_adapter的client鏈表當中。當用戶關閉設備節點時,它自動被釋放。

1.2     IOCTL控制

查看include/linux/i2c-dev.h文件,可以看到i2c-dev支持的IOCTL命令。如<!--[if supportFields]> REF _Ref283302932 /h <![endif]-->程序清單 3.1<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。

程序清單 <!--[if supportFields]> STYLEREF 1 /s <![endif]-->3<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清單 /* ARABIC /s 1 <![endif]-->1<!--[if supportFields]><![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 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                                            */

       下面進行一一解釋。

1.  設置重試次數

ioctl(fd, I2C_RETRIES,m);

這句話設置適配器收不到ACK時重試的次數為m。默認的重試次數為1。

2.  設置超時

ioctl(fd, I2C_TIMEOUT,m);

設置SMBus的超時時間為m,單位為jiffies。

3.  設置從機地址

ioctl(fd, I2C_SLAVE,addr);

ioctl(fd, #defineI2C_SLAVE_FORCE, addr);

在調用read()和write()函數之前必須設置從機地址。這兩行都可以設置從機的地址,區別是第二行無論內核中是否已有驅動在使用這個地址都會成功,第一行則只在該地址空閑的情況下成功。由於i2c-dev創建的i2c_client不加入i2c_adapter的client列表,所以不能防止其它線程使用同一地址,也不能防止驅動模塊占用同一地址。

4.  設置地址模式

ioctl(file,I2C_TENBIT,select)

如果select不等於0選擇10比特地址模式,如果等於0選擇7比特模式,默認7比特。只有適配器支持I2C_FUNC_10BIT_ADDR,這個請求才是有效的。

5.  獲取適配器功能

ioctl(file,I2C_FUNCS,(unsignedlong *)funcs)

       獲取的適配器功能保存在funcs中。各比特的含義如<!--[if supportFields]>REF _Ref283305554 /h <![endif]-->程序清單 3.2<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。具體的含義可以參考<!--[if supportFields]>REF _Ref283456550 /r /h <![endif]-->第4章<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->。

程序清單 <!--[if supportFields]> STYLEREF 1 /s <![endif]-->3<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清單 /* ARABIC /s 1 <![endif]-->2<!--[if supportFields]><![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.                           */

6.  I2C層通信

ioctl(file,I2C_RDWR,(structi2c_rdwr_ioctl_data *)msgset);

這一行代碼可以使用I2C協議和設備進行通信。它進行連續的讀寫,中間沒有間歇。只有當適配器支持I2C_FUNC_I2C此命令才有效。參數是一個指針,指向一個結構體,它的定義如<!--[if supportFields]>REF _Ref283305956 /h <![endif]-->程序清單 3.3<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。其中i2c_msg的定義參考<!--[if supportFields]> REF _Ref283227534 /h <![endif]-->程序清單 1.7<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->。

程序清單 <!--[if supportFields]> STYLEREF 1 /s <![endif]-->3<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清單 /* ARABIC /s 1 <![endif]-->3<!--[if supportFields]><![endif]-->  i2c_rdwr_ioctl_data

struct i2c_rdwr_ioctl_data {

         structi2c_msg __user *msgs;                                                        /* 指向i2c_msgs數組                          */

         __u32nmsgs;                                                                                /* 消息的個數                                       */

};

msgs[] 數組成員包含了指向各自緩沖區的指針。這個函數會根據是否在消息中的flags置位I2C_M_RD來對緩沖區進行讀寫。從機的地址以及是否使用10比特地址模式記錄在每個消息中,忽略之前ioctl設置的結果。

7.  設置SMBus PEC

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

如果select不等於0選擇SMBus PEC (packet error checking),等於零則關閉這個功能,默認是關閉的。

這個命令只對SMBus傳輸有效。這個請求只在適配器支持I2C_FUNC_SMBUS_PEC時有效;如果不支持這個命令也是安全的,它不做任何工作。

8.  SMBus通信

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

這個函數和I2C_RDWR類似,參數的指針指向i2c_smbus_ioctl_data類型的變量,它的定義如<!--[if supportFields]> REF _Ref283308161 /h <![endif]-->程序清單 3.4<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。如何填寫i2c_smbus_ioctl_data的各個成員,參考<!--[if supportFields]> REF _Ref283455290 /r/h <![endif]-->4.3<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->節。

程序清單 <!--[if supportFields]> STYLEREF 1 /s <![endif]-->3<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清單 /* ARABIC /s 1 <![endif]-->4<!--[if supportFields]><![endif]-->  i2c_smbus_ioctl_data

struct i2c_smbus_ioctl_data {

         __u8read_write;

         __u8command;

         __u32size;

         unioni2c_smbus_data __user *data;

};

1.3     i2c_dev使用例程

要想在用戶空間使用i2c適配器,首先要如3.1<!--[if gte mso 9]><![endif]-->節所示,選擇某個適配器的設備節點打開,然后才能進行通信。

1.3.1   read()/write()

通信的方式有兩種,一種是使用操作普通文件的接口read()和write()。這兩個函數間接調用了i2c_master_recv和i2c_master_send。但是在使用之前需要使用I2C_SLAVE設置從機地址,設置可能失敗,需要檢查返回值。這種通信過程進行I2C層的通信,一次只能進行一個方向的傳輸。

下面的程序是ARM與E2PROM芯片通信的例子,如<!--[if supportFields]> REF _Ref283651035 /h <![endif]-->程序清單 3.5<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。

程序清單 <!--[if supportFields]> STYLEREF 1 /s <![endif]-->3<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清單 /* ARABIC /s 1 <![endif]-->5<!--[if supportFields]><![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");

                   gotoexit;

         }

 

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

                   printf("oictl:setslave address failed/n");

                   gotoclose;

         }

 

         struct                   i2c_msg msg;

         unsignedchar      rddata;

         unsignedchar      rdaddr[2] = {0, 0};                                            /* 將要讀取的數據在芯片中的偏移量          */

         unsignedchar      wrbuf[3] = {0, 0, 0x3c};                                   /* 要寫的數據,頭兩字節為偏移量    */

        

         printf("inputa char you want to write to E2PROM/n");

         wrbuf[2]= getchar();

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

         sleep(1);

         printf("writeaddress return: %d/n",write(fd, rdaddr, 2));       /* 讀取之前首先設置讀取的偏移量    */

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

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

 

close:

         close(fd);

exit:

         return0;

}

1.3.2  I2C_RDWR

還可以使用I2C_RDWR實現同樣的功能,如<!--[if supportFields]> REF _Ref283651333 /h <![endif]-->程序清單 3.6<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。此時ioctl返回的值為執行成功的消息數。

程序清單 <!--[if supportFields]> STYLEREF 1 /s <![endif]-->3<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清單 /* ARABIC /s 1 <![endif]-->6<!--[if supportFields]><![endif]-->   使用I2C_RDWR與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");

                   gotoexit;

         }

 

         struct                   i2c_msg msg;

         unsignedchar      rddata;

         unsignedchar      rdaddr[2] = {0, 0};                                           

         unsignedchar      wrbuf[3] = {0, 0, 0x3c};                         

        

         printf("inputa char you want to write to E2PROM/n");

         wrbuf[2]= getchar();

        

         structi2c_rdwr_ioctl_data ioctl_data;

         structi2c_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("ioctlwrite,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("ioctlwrite address, return :%d/n", ioctl(fd, I2C_RDWR, &ioctl_data));

         ioctl_data.msgs= &msgs[1];

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

 

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

 

close:

         close(fd);

exit:

         return0;

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM