博客專欄

EEPW首頁 > 博客 > Linux i2c-dev簡析

Linux i2c-dev簡析

發(fā)布人:電子禪石 時間:2024-02-03 來源:工程師 發(fā)布文章

上篇文章簡要分析了如何編寫一個Linux下的I2C設(shè)備驅(qū)動程序。

編寫驅(qū)動程序畢竟有一定的門檻,需要熟悉內(nèi)核各種相關(guān)的開發(fā)規(guī)范,

有時為了快速的測試一款I(lǐng)2C設(shè)備的功能,臨時編寫驅(qū)動程序可能會使得工期比較緊張;

并且

	libi2c.so  libi2c.so.0  libi2c.so.0.1.1

有時I2C設(shè)備

十分的簡單,為此編寫一個單獨的驅(qū)動程序未免有點“興師動眾”。


i2c-dev框架很好的解決了上面的問題,使用該框架可以使我們在用戶空間上編寫I2C通信程序。

i2c-dev在內(nèi)核中封裝了關(guān)于I2C通信所需要的所有通信細(xì)節(jié),通過ioctl接口將這些功能暴露

給用戶空間程序調(diào)用。用戶應(yīng)用程序使用open/read/write/ioctl系統(tǒng)嗲用就可實現(xiàn)與I2C設(shè)備

的通信。


基本原理

在Linux系統(tǒng)下,每個使能的I2C適配器,在/dev目錄下都會創(chuàng)建一個字符設(shè)備文件

(主設(shè)備號89),例如/dev/i2c-0,通過這個設(shè)備文件,就可以實現(xiàn)與I2C設(shè)備的通信,

當(dāng)然,I2C設(shè)備必須首先掛載在該I2C適配器之下。


i2c-dev的內(nèi)核代碼可以參考這里。其主要的功能就是創(chuàng)建I2C適配器字符設(shè)備,

并提供如下的功能:

	static const struct file_operations i2cdev_fops = {
		.owner		= THIS_MODULE,
		.llseek		= no_llseek,
		.read		= i2cdev_read,
		.write		= i2cdev_write,
		.unlocked_ioctl	= i2cdev_ioctl,
		.compat_ioctl	= compat_i2cdev_ioctl,
		.open		= i2cdev_open,
		.release	= i2cdev_release,
	};

可以看到i2c-dev提供了read、write、ioctl功能。這里需要注意的是:

read和write方法不支持RepStart模式,也就是每次調(diào)用只能發(fā)送/接收一個字節(jié)的數(shù)據(jù),

這對于操作稍微復(fù)雜點的I2C設(shè)備的局限性太大,一般不會使用這兩個接口。

ioctl提供了I2C復(fù)合數(shù)據(jù)傳輸和SMbus傳輸兩種更為通用的數(shù)據(jù)傳輸方式,

它們支持一次發(fā)送、接收多個字節(jié)數(shù)據(jù),所以,一般選擇這種方式與I2C設(shè)備進(jìn)行通信。



使用方式

Linux內(nèi)核I2C系統(tǒng)向用戶空間提供了兩個主要的i2c-dev接口文件:linux/i2c.h和linux/i2c-dev.h,這兩個文件里定義了用戶空間與I2C設(shè)備進(jìn)行通信的各種規(guī)范,比如配置設(shè)備地址,

設(shè)置超時時間,設(shè)置重試次數(shù)以及選用何種I2C通信方式等。

 

配置命令

配置命令主要是提供給ioctl系統(tǒng)調(diào)用使用,主要命令如下:

i2c-dev.h
#define I2C_RETRIES	0x0701	/*  通信未響應(yīng)時的重試次數(shù)*/
#define I2C_TIMEOUT	0x0702	/*  設(shè)置通信的超時時間,單位:jiffies */

/* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses
 * are NOT supported! (due to code brokenness)
 */
#define I2C_SLAVE	0x0703	    /* 設(shè)置從設(shè)備地址 */
#define I2C_SLAVE_FORCE	0x0706	/* 當(dāng)該設(shè)備地址被某個驅(qū)動程序使用時,強制設(shè)置設(shè)備地址*/
#define I2C_TENBIT	0x0704	    /* 0 for 7 bit addrs, != 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 */

 

上述命令的參數(shù)如下:

  • I2C_FUNCS:指向unsigned long的指針

  • I2C_RDWR:指向i2c_rdwr_ioctl_data結(jié)構(gòu)的指針

  • I2C_SMBUS:指向i2c_smbus_ioctl_data結(jié)構(gòu)的指針

  • 其他的參數(shù)都是unsigned long

具體到用戶空間的i2c通信,對于這些配置命令的使用方式一般如下:


調(diào)用ioctl(xxx_fd, I2C_SLAVE/I2C_SLAVE_FORCE, xx),設(shè)置i2c設(shè)備通信地址。

調(diào)用ioctl(xxx_fd, I2C_RETRIES, xx),設(shè)置通信未響應(yīng)時的重試次數(shù)

調(diào)用ioctl(xxx_fd, I2C_TIMEOUT, xx),設(shè)置通信超時時間

調(diào)用ioctl(xxx_fd, I2C_FUNCS, xx),查詢適配支持的功能掩碼

調(diào)用ioctl(xxx_fd, I2C_RDWR, xx)或者ioctl(xxx_fd, I2C_SMBUS, xx)進(jìn)行數(shù)據(jù)傳輸

數(shù)據(jù)傳輸

數(shù)據(jù)傳輸分為兩種方式:I2C復(fù)合數(shù)據(jù)傳輸和SMbus傳輸,下面分別介紹一下。

i2c.h頭文件里定義了I2C適配器功能定義。

#define I2C_FUNC_I2C			0x00000001
#define I2C_FUNC_10BIT_ADDR		0x00000002
#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_IGNORE_NAK etc. */
#define I2C_FUNC_SMBUS_PEC		0x00000008
#define I2C_FUNC_NOSTART		0x00000010 /* I2C_M_NOSTART */
#define I2C_FUNC_SLAVE			0x00000020
#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_HOST_NOTIFY	0x10000000

I2C復(fù)合數(shù)據(jù)傳輸

所謂的I2C復(fù)合數(shù)據(jù)傳輸,就是基于I2C通信時序,發(fā)送復(fù)合的數(shù)據(jù),該模式支持RepStart,

可以改變數(shù)據(jù)的發(fā)送方向,每次傳輸過程只有一個結(jié)束信號。

該功能需要I2C適配器對于I2C_FUNC_I2C功能的支持。

發(fā)送和接收的數(shù)據(jù)通過i2c_msg進(jìn)行封裝,

關(guān)于i2c_msg我們在如何編寫一個Linux下的I2C設(shè)備驅(qū)動程序有過介紹,

然后將i2c_msg數(shù)組加入到i2c_rdwr_ioctl_data結(jié)構(gòu)中,

struct i2c_rdwr_ioctl_data {
	struct i2c_msg __user *msgs;	/* pointers to i2c_msgs */
	__u32 nmsgs;			/* number of i2c_msgs */
};

最后,通過ioctl(xxx_fd, I2C_RDWR, xx)進(jìn)行數(shù)據(jù)傳輸。

下面是使用i2c_msg傳輸數(shù)據(jù)的示例。

_u8 _buf[] = {0x01, 0x02, 0x03};
_u8 write_buf[16] = {0};

struct i2c_msg[2] = msgs{
	{
		.addr = 0x48,
		.flags = 0, //寫數(shù)據(jù)
		.len = 3,
		.buf = write_buf,
	}

	{
		.addr = 0x0f,
		.flags = I2C_M_RD,//讀數(shù)據(jù)
		.len = 6,
		.buf = read_buf,
};

struct i2c_rdwr_ioctl_data ioctl_data = {
	.msgs = msgs,
	.nmsgs = 2,
};

/**/
fd = open("/dev/i2c-x", O_RDWR);

/**/
ioctl(fd, I2C_SLAVE, addr);

/**/
ioctl(fd, I2C_RDWR, &ioctl_data);

————————————————


上面完成一次復(fù)合數(shù)據(jù)傳輸,先寫入3字節(jié)數(shù)據(jù),然后讀取6個字節(jié)數(shù)據(jù)。通過查看ioctl的返回值,可以知道發(fā)送狀態(tài)。

SMbus傳輸

SMbus在如何編寫一個Linux下的I2C設(shè)備驅(qū)動程序同樣有介紹,如果I2C適配器支持SMbus協(xié)議,

那么就可以使用SMbus協(xié)議與設(shè)備進(jìn)行通信。查看I2C適配器關(guān)于SMbus支持情況,

可以通過ioctl的I2C_FUNCS命令查詢。


使用SMbus進(jìn)行數(shù)據(jù)傳輸,需要將數(shù)據(jù)封裝到i2c_smbus_ioctl_data中

struct i2c_smbus_ioctl_data {
	__u8 read_write;
	__u8 command;
	__u32 size;
	union i2c_smbus_data __user *data;
};

#define I2C_SMBUS_BLOCK_MAX	32	/* As specified in SMBus standard */
union i2c_smbus_data {
	__u8 byte;
	__u16 word;
	__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; 
					/* block[0] is used for length */
			       /* and one more for user-space compatibility */
};
/* i2c_smbus_xfer read or write markers */
#define I2C_SMBUS_READ	1
#define I2C_SMBUS_WRITE	0

  • read_write:0表示寫,1表示讀

  • command:設(shè)備寄存器地址

  • i2c_smbus_data:block[0]代表數(shù)據(jù)的讀取和寫入長度

  • size:SMbus傳輸類型,具體定義如下:

  #define I2C_SMBUS_QUICK             0
  #define I2C_SMBUS_BYTE              1
  #define I2C_SMBUS_BYTE_DATA         2
  #define I2C_SMBUS_WORD_DATA         3
  #define I2C_SMBUS_PROC_CALL         4
  #define I2C_SMBUS_BLOCK_DATA        5
  #define I2C_SMBUS_I2C_BLOCK_BROKEN  6
  #define I2C_SMBUS_BLOCK_PROC_CALL   7 /* SMBus 2.0 */
  #define I2C_SMBUS_I2C_BLOCK_DATA    8

最后,通過ioctl(xxx_fd, I2C_SMBUS, xx)進(jìn)行數(shù)據(jù)傳輸。

SMbus的數(shù)據(jù)傳輸協(xié)議比較復(fù)雜,

i2ctools中的smbus.c對每個傳輸協(xié)議都進(jìn)行了封裝,我們可以直接借鑒,參考i2ctools一節(jié)。

i2ctools

i2ctools就是基于i2c-dev實現(xiàn)的i2c相關(guān)的工具集合,其功能十分的強大,

i2cdetect可以實現(xiàn)系統(tǒng)中i2c設(shè)備的基本探測,i2ctransfer可以進(jìn)行i2c數(shù)據(jù)傳輸。

在嵌入式開發(fā)中,我們可以借助這些工具來對I2C設(shè)備進(jìn)行基本管理和通信協(xié)議測試。

編譯

下載i2ctools源碼,i2ctools一般應(yīng)用于嵌入式Linux環(huán)境,所以需要交叉編譯。

	tar zxvf i2c-tools-4.1.tar.gz
	cd i2c-tools-4.1

修改Makefile文件編譯器相關(guān)配置。

CC      := arm-linux-gcc

AR      := arm-linux-ar

STRIP   := arm-linux-strip

編譯完成之后,可以到tools目錄下獲得i2cdetect和i2ctransfer工具,

lib和include目錄分別是smbus二次開發(fā)庫,我們可以直接使用。

i2cdetect

i2cdetect可以查看當(dāng)前系統(tǒng)i2c適配器和設(shè)備相關(guān)一些配置信息。

使用幫助


 Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
    i2cdetect -F I2CBUS
    i2cdetect -l
   I2CBUS is an integer or an I2C bus name
   If provided, FIRST and LAST limit the probing range.

查詢系統(tǒng)i2c適配器列表

 root~$ i2cdetect -l
 i2c-0   i2c             21a0000.i2c                             I2C adapter

查詢i2c bus的支持的功能

root@zpd ~$ i2cdetect -F 21a0000.i2c 或者是 0

Functionalities implemented by /dev/i2c-0:

I2C yes

SMBus Quick Command yes

SMBus Send Byte yes

SMBus Receive Byte yes

SMBus Write Byte yes

SMBus Read Byte yes

SMBus Write Word yes

SMBus Read Word yes

SMBus Process Call yes

SMBus Block Write yes

SMBus Block Read no

SMBus Block Process Call no

SMBus PEC yes

I2C Block Write yes

I2C Block Read yes


根據(jù)這些,可以確定該i2c bus所支持的I2C通信方式。


查詢i2c bus下的設(shè)備地址

 root@zpd ~$ i2cdetect -y 0
      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
 00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
 30: -- -- UU -- -- -- -- -- -- -- -- -- -- -- -- -- 
 40: -- -- -- -- -- -- -- -- UU UU -- -- -- -- -- -- 
 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
 70: -- -- -- -- -- -- -- --

可以看到,目前編號為0的i2c bus下有三個設(shè)備,地址分別是:0x32, 0x48, 0x49。

i2ctransfer

i2ctransfer的功能就是基于SMbus協(xié)議,完成數(shù)據(jù)的傳輸。

基本格式

 

 Usage: i2ctransfer I2CBUS DESC [DATA] [DESC [DATA]]...
  I2CBUS is an integer or an I2C bus name

   

其中,I2CBUS表示i2c bus

DESC表示傳輸格式,{r|w}LENGTH[@address],比如,

w2@0x48,表示向地址為0x48的設(shè)備寫入2個字節(jié),DATA表示字節(jié)序列

可以沒有DATA字段,比如DESC為r2@0x48,從地址為0x48的設(shè)備中,讀取2個字節(jié)。

DESC和DATA可以多次出現(xiàn),比如,i2ctransfer 0 w1@0x32 0x10 r1,

表示,首先向地址為0x32的設(shè)備寫入一個字節(jié)0x10,這個 0x10表示設(shè)備一個寄存器地址,

然后,從該寄存器讀取一個字節(jié)數(shù)據(jù)。

讀取實例


我們通過一個實例演示如何通過,i2ctransfer與i2c設(shè)備通信。

rtc-rx8010是一個通過I2C通信的RTC芯片,

我們可以通過i2ctransfer直接讀取當(dāng)前的時間。具體命令如下:

 i2ctransfer 0 w1@0x32 0x10 r7
 
 0x28 0x13 0x15 0x02 0x04 0x08 0x20 //2020-08-04 15:13:28 Tue

其中, w1@0x32 0x10表示首先寫入RTC代表的SEC的寄存器地址,

r7表示以SEC寄存開始,連續(xù)讀取7個數(shù)據(jù),分別為SEC MIN HOUR WEEK DAY MONTH YEAR

smbus二次開發(fā)

i2ctools提供了完整的SMbus通信方式,如果,我們需要以SMbus方式與設(shè)備進(jìn)行通信,那我們可以利用將lib庫和smbus.h文件加入到我們的工程中完成smbus協(xié)議支持。

   

	libi2c.so  libi2c.so.0  libi2c.so.0.1.1

         smbus.h中封裝的smbus通信接口。           

	extern __s32 i2c_smbus_access(int file, char read_write, __u8 command,
                          int size, union i2c_smbus_data *data);

	extern __s32 i2c_smbus_write_quick(int file, __u8 value);
	extern __s32 i2c_smbus_read_byte(int file);
	extern __s32 i2c_smbus_write_byte(int file, __u8 value);
	extern __s32 i2c_smbus_read_byte_data(int file, __u8 command);
	extern __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
	extern __s32 i2c_smbus_read_word_data(int file, __u8 command);
	extern __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
	extern __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);
	 
	/* Returns the number of read bytes */
	extern __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values);
	extern __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
	                                        const __u8 *values);
	 
	/* Returns the number of read bytes */
	/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
	   ask for less than 32 bytes, your code will only work with kernels
	   2.6.23 and later. */
	extern __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
	                                           __u8 *values);
	extern __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
	                                            const __u8 *values);
	 
	/* Returns the number of read bytes */
	extern __s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
	                                          __u8 *values);


————————————————


                        

 


    原文鏈接:https://blog.csdn.net/lhl_blog/article/details/107787881


*博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點,如有侵權(quán)請聯(lián)系工作人員刪除。



關(guān)鍵詞: i2c-dev

相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉