1. 程式人生 > >I2C驅動及裝置節點使用講解

I2C驅動及裝置節點使用講解

裝載於:http://blog.csdn.net/yhf19881015/article/details/8188809

對於註冊的i2c介面卡,使用者空間也可以使用它們。在Linux核心程式碼檔案/include/linux/i2c-dev.c中針對每個介面卡生成一個主裝置號為89的裝置節點,實現了檔案操作介面,使用者空間可以通過i2c裝置節點訪問i2c介面卡。介面卡的編號從0開始,和介面卡的裝置節點的次裝置號相同。

i2c介面卡的裝置節點是/dev/i2c-x,其中x是數字,代表介面卡的編號。由於介面卡編號是動態分配的(和註冊次序有關),所以想了解哪一個介面卡對應什麼編號,可以檢視/sys/class/i2c-dev/目錄下的檔案內容。

       為了在使用者空間的程式當中操作i2c介面卡,必須在程式中包含以下兩句:

#include<linux/i2c-dev.h>

#include<linux/i2c.h>

這兩個標頭檔案中定義了之後需要用到的結構體和巨集。

然後就可以開啟裝置節點了。但是開啟哪一個呢?因為介面卡的編號並不固定。為此我們在中端中執行以下命令:

[[email protected] /]# cat /sys/class/i2c-dev/i2c-0/name

PNX4008-I2C0

[[email protected] /]# cat /sys/class/i2c-dev/i2c-1/name

PNX4008-I2C1

[[email protected] /]# 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連結串列當中。當用戶關閉裝置節點時,它自動被釋放。

檢視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;

};

要想在使用者空間使用i2c介面卡,首先要如3.1<!--[if gte mso 9]><![endif]-->節所示,選擇某個介面卡的裝置節點開啟,然後才能進行通訊。

通訊的方式有兩種,一種是使用操作普通檔案的介面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;

         unsigned char      rddata;

         unsigned char      rdaddr[2] = {0, 0};                                            /* 將要讀取的資料在晶片中的偏移量          */

         unsigned char      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;

}

還可以使用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;

         unsigned char      rddata;

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

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

         printf("inputa 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("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;

}