i.MX283開發板I2C驅動——DS2460

i.MX283開發板有兩個I2C接口,其中I2C0接了一個DS2460加密芯片,本文介紹Linux下如何編寫I2C驅動程序讀寫DS2460。

Linux上I2C架構可以分爲I2C核心I2C總線驅動I2C設備驅動三個部分:

I2C核心:主要爲總線驅動和設備驅動提供各種API,比如設備探測、註冊、註銷,設備和驅動匹配等函數。它在I2C架構中處於中間的位置。

I2C總線驅動:I2C總線驅動維護了I2C適配器數據結構(i2c_adapter)和適配器的通信方法數據結構(i2c_algorithm)。所以I2C總線驅動可控制I2C適配器產生start、stop、ACK等。I2C總線驅動在整個架構中是處於最底層的位置,它直接和真實的物理設備相連,同時它也是受CPU控制。

I2C設備驅動:I2C設備驅動主要負責和用戶層交互,此處的設備是一個抽象的概念,並非真實的物理設備,它是掛在I2C適配器上,通過I2C適配器與真實的物理設備通信。

下圖是整個I2C驅動框架:

實際上,Linux經過這麼多年的發展,已經形成了一套完善的I2C驅動框架,現在編寫I2C驅動,我們只需要完成上面所說的I2C設備驅動部分就可以,其他的芯片廠商已經爲我們做好了。

根據bus-dev-drv框架模型,我們的主要工作是實現設備文件驅動文件,也就是上圖中的i2c_client和i2c_driver,i2c_client作用是完成設備和適配器的綁定 ,以確定設備驅動需要和哪個適配器下面的真實物理設備通信,i2c_driver的作用就是實現用戶層的open、write、read等調用。

下面將詳細說明整個過程:

注意:i2c適配器就是cpu中的i2c接口,cpu有幾個i2c接口,就代表有幾個適配器,又稱i2c主機。 

由於i.mx283開發板有兩個i2c接口,所以這裏就有兩個適配器。首先假設有4個E2PROM掛在兩個適配器下面,現在用戶想要調

用設備驅動2來讀寫E2PROM3,根據上面提到的設備驅動模型,設備驅動2分爲i2c_client2和i2c_driver2,首先client要爲自己取

一個名字,假設叫做“E2PROM3”,然後它需要把自己和適配器2綁定(因爲E2PROM3是掛在適配器2下面的),最後向內核注

冊自己,I2c總線就知道自己下面多了一個設備——“E2PROM3”,i2c_client2部分的工作就做完了。

接着,需要實現i2c_driver2部分的功能,首先,i2c_driver2也需要爲自己取一個名字,也必須叫“E2PROM3”,然後它需要實現

open、close、write、read等這些文件接口,對於write和read,需要使用I2C核心層提供的I2C讀寫數據API接口。接着,需要 

實現probe和remove函數接口,probe函數裏面實現就是字符設備驅動註冊的那一套流程,remove函數正好相反,最後,它也

需要向內核註冊自己,也就是告訴I2C總線,有一個新的驅動需要添加——“E2PROM3”,i2c_driver2部分的工作就做完了。

i2c總線:當向內核註冊i2c驅動時,會將i2c驅動添加到總線的鏈表中,遍歷總線上所有設備,通i2c_client>namei2c_driver-

>i2c_device_id->name進行字符串匹配,如果匹配,就調用驅動程序的probe函數。上面已經將client和driver的名字都設置爲

"E2PROM3",所以它們是匹配的,然後,I2C總線會調用驅動的probe函數,並把client結構體通過形參傳給driver,然後執行

probe函數,註冊字符設備驅動,client結構體裏主要保存了適配器的信息,這個非常重要,當用戶APP進行read和write調用

時,首先會進入driver裏面的write和read函數,剛剛提到driver的write和read,需要使用I2C核心層提供的I2C讀寫數據API接

口,這些接口是用來和適配器通信的,所以需要指定哪個適配器,適配器信息就在剛剛保存的client結構體裏面,這樣,用戶層

和適配器就是通了,最後,適配器再和掛在它下面的真實物理設備通信,這個部分是不需要我們操心的,芯片廠家已經做好了這

部分的工作,至此,整個I2C通信流程就走完了。

Linux編寫I2C驅動程序的一般流程爲:

  1. 創建i2c_client,並向內核註冊這個client,註冊的方法有圖一中提到的4種方法。
  2. 創建i2c_driver,並向內核註冊driver,一般使用i2c_add_driver註冊driver。
  3. 註冊driver時,總線會自動調用match函數,匹配client和driver,如果匹配成功,會調用driver裏面的probe函數。
  4. probe函數裏面完成字符設備驅動那些工作,一個I2C驅動程序基本完成。

一、設備註冊

圖一中提到4種註冊方法,我們這裏僅介紹第一種,利用i2c_new_probed_device或者i2c_new_device註冊,這兩個函數的區別是後者必須指定真實設備的從機地址,前者是指定一個地址範圍,內核會一個個探測(發送起始信號,看是否有ACK)地址是否有效,若探測成功,則內核記錄這個地址,再調用i2c_new_device註冊設備。

這裏會使用一個重要的結構體i2c_board_info

struct i2c_board_info {
	char		type[I2C_NAME_SIZE];
	unsigned short	flags;
	unsigned short	addr;
	void		*platform_data;
	struct dev_archdata	*archdata;
#ifdef CONFIG_OF
	struct device_node *of_node;
#endif
	int		irq;
};

它的作用是描述物理設備信息,主要是nameaddr,但內核不會探測這個addr的真實性,這個結構體是你已知真實物理設備的從機地址的情況下,可以直接指定設備信息,然後調用i2c_new_device註冊設備。

我們今天使用的是i2c_new_probed_device註冊設備,所以還需要給定一個地址範圍。

static const unsigned short addr_list[] = 
{
    0x30,0x35,0x40,0x50,I2C_CLIENT_END,
};

addr_list數組裏面就定義了地址範圍,這裏的0x40是ds2460的真實地址,內核會從0x30一直探測到0x50,若某個地址探測成功,它會把這個地址保存到i2c_board_info.addr成員中,然後調用i2c_new_device註冊設備。

注意:無論使用哪種方式,都需指定設備的名稱,即i2c_board_info.type成員。使用i2c_new_probed_device註冊的好處是當

設備地址不正確時,設備是無法註冊成功的!

下面是ds2460_dev.c:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>


static struct i2c_board_info ds2460_info=
{
	I2C_BOARD_INFO("ds2460",0x40),//設備名稱+設備地址7bit
};

static const unsigned short addr_list[] = 
{
    0x30,0x35,0x40,0x50,I2C_CLIENT_END,
};

struct i2c_client *ds2460_client = NULL;//定義一個client

static int ds2460_dev_init(void)
{
  struct i2c_adapter *adapter=NULL;

  /*獲取i2c適配器0 */
  adapter = i2c_get_adapter(0);

  /*創建一個client 並綁定適配器*/
  ds2460_client = i2c_new_probed_device(adapter,&ds2460_info,addr_list);
  //ds2460_client = i2c_new_device(adapter,&ds2460_info);
  if(ds2460_client != NULL)
  {
	  i2c_put_adapter(adapter);
	  printk("module init ok\n");
	  return 0;
  }
  else
  {
     printk("device not exist\n");
	 return -EPERM;
  }
  
}


static void ds2460_dev_exit(void)
{
  i2c_unregister_device(ds2460_client);
  
  printk("module exit ok\n");
}


module_init(ds2460_dev_init);
module_exit(ds2460_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xzx2020");

二、驅動註冊

驅動實際就是字符設備驅動那一套流程,open、read、write等文件接口以及設備號申請、自動創建設備節點等操作。

只不過設備號申請、自動創建設備節點等操作需要放到probe函數裏面去,註銷操作則需要放到remove函數裏面去。

這裏主要講講read和write調用的實現:

驅動是和適配器通信的,而I2C核心爲我們提供了很多和適配器通信的接口函數,具體可見/linux-2.6.35.3/Documentation/i2c

裏面i2c-protocolsmbus-protocol兩則文檔。

下面是標準I2C協議的通信函數:

int i2c_master_send(struct i2c_client *client,const char *buf ,int count)  
int i2c_master_recv(struct i2c_client *client, char *buf ,int count)  
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 

除此之外,還有SMBUS協議的通信函數,SMBUS是I2C協議的子集:

SMBus Receive Byte:  i2c_smbus_read_byte()
SMBus Send Byte:  i2c_smbus_write_byte()
SMBus Read Byte:  i2c_smbus_read_byte_data()
SMBus Read Word:  i2c_smbus_read_word_data()

實際上很多I2C器件用的協議都是SMBus協議,它們的時序和SMBus完全一樣,所以這裏我們選擇SMBus的通信函數與DS2460通信。這裏主要用到i2c_smbus_read_byte_data()和i2c_smbus_read_word_data()兩個函數。

s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value)

/*寫一個字節數據到指定的地址,地址通過command字節傳送*/

/*    S Addr Wr [A] Comm [A] Data [A] P         */

/*===========================================================================*/

s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command)

/*從指定的地址讀取一個字節,地址通過command字節傳送,返回值是讀到的字節*/

/* S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P  */

i2c_driver驅動還有個非常重要的結構體i2c_driver :

struct i2c_driver {
	unsigned int class;

	/* Notifies the driver that a new bus has appeared or is about to be
	 * removed. You should avoid using this if you can, it will probably
	 * be removed in a near future.
	 */
	int (*attach_adapter)(struct i2c_adapter *);
	int (*detach_adapter)(struct i2c_adapter *);

	/* 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 *);
	int (*suspend)(struct i2c_client *, pm_message_t mesg);
	int (*resume)(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;
};

其中,成員const struct i2c_device_id *id_table記錄了驅動的名字和私有數據,驅動的名字必須和設備的名字一致,否則內核會匹配失敗。

struct i2c_device_id {
	char name[I2C_NAME_SIZE];
	kernel_ulong_t driver_data	/* Data private to the driver */
			__attribute__((aligned(sizeof(kernel_ulong_t))));
};

i2c_driver 結構體填充如下:

/*配置驅動的名稱和私有數據*/
static const struct i2c_device_id ds2460_id_table=
{
    "ds2460",0//名稱爲“ds2460”需要和設備保持一致內核纔會調用驅動的probe函數
              //0 表示沒有私有數據
};


/*創建i2c_driver結構體*/
static struct i2c_driver ds2460_driver =
{
  .driver={
  	   .name ="ds2460_driver",//這個名字無所謂
	   .owner=THIS_MODULE,
     },
  .probe = ds2460_probe,
  .remove= __devexit_p(ds2460_remove),
  .id_table = &ds2460_id_table,//這裏的名字纔是和設備進行匹配的

};

最後,在probe函數裏註冊常規字符設備驅動,在remove函數裏註銷字符設備驅動,i2c_driver工作就基本結束了。

ds2460_drv.c:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>
#include<linux/fs.h>
#include<asm/uaccess.h> 
#include <linux/cdev.h>
#include <linux/delay.h>

#define SEQUENT_RW    0  //讀寫模式 0:讀寫不連續           1:連續讀寫
#define DEVICE_NAME	"ds2460_drv"//驅動名稱
static struct i2c_client *ds2460_client =NULL;
static struct cdev *ds2460_cdev=NULL;
static struct class *ds2460_class = NULL;
static struct device *ds2460_device = NULL;
static dev_t device_id;


static int ds2460_open(struct inode *inode, struct file *fp)
{
  return 0;
} 


static int ds2460_release(struct inode * inode, struct file * file)
{
  return 0;
}


static int ds2460_read(struct file *filp,  char __user *buf, size_t count,
                loff_t *f_pos)
{
    unsigned char address;//讀取的地址
	unsigned char *data = NULL;//需要讀取的數據
	unsigned char i;
    int ret;	
#if (SEQUENT_RW == 1)

	else if(count == 1)
	{
	    ret = copy_from_user(&address, buf, 1);
	    if(ret < 0)
	    { 
	       printk("read param num error ret = %d ,buf = %d\n",ret,(int)buf[0]);
		   return -EINVAL;     
		}
	    ret = i2c_smbus_read_byte_data(ds2460_client,address);
		if(ret < 0)
		{
	      printk("i2c read error %d\n",ret);
		  return ret;
		}
		copy_to_user(buf, &ret, 1);
	}
	else
	{
	   data = kmalloc(count, GFP_KERNEL);//申請count字節內存 
       ret = copy_from_user(data, buf, 1);//第1個字節是地址
	   if(ret < 0)
	   {
	       printk("write param num error \n");
		   kfree(data);
		   return -EINVAL;     
	   }
       address = data[0];	   
       ret = i2c_smbus_read_i2c_block_data(ds2460_client,address,count,data);
       if(ret < 0)
	   {
	      printk("i2c_smbus_read_i2c_block_data error %d\n",ret);
		  kfree(data);
		  return ret;
	   }	   
       copy_to_user(buf, data, count);  
	   kfree(data);

	}
#else
	 /*申請內存*/
     data = kmalloc(count,GFP_KERNEL);  
	 ret = copy_from_user(data, buf, 1);
	 if(ret < 0)
	 { 
       printk("read param num error ret = %d ,buf = %d\n",ret,(int)buf[0]);
	   return -EINVAL;     
     } 
	 
	 /*用戶buf第一個字節是需要讀取的地址*/
     address = data[0];

	 /*依次讀取數據*/
	 for(i=0;i<count;i++,address++)
	 {
	    /*非連續讀取 每次讀取產生一次完整的I2
C通信*/
	    data[i] = i2c_smbus_read_byte_data(ds2460_client,address);
		if(data[i] < 0)
		{
	      printk("i2c read error %d\n",data[i]);
		  return data[i];
		}  
	 }
	 
	 /*將數據拷貝到用戶層buf*/
     copy_to_user(buf, data, count); 
	 
	 /*釋放內存*/
	 kfree(data);
#endif
	 return 0;
   
}

ssize_t ds2460_write(struct file *filp, const char __user *buf, size_t count,
								loff_t *f_pos)
{

   unsigned char address;//需要寫入的地址
   unsigned char *data = NULL;//需要寫入的數據
   unsigned char i;
   int ret;
   
   /*至少寫入1個數據 即count至少等於2*/
   if(count < 2)
   {
       printk("write param num error count = %d\n",count);
	   return -EINVAL;     
   }
#if (SEQUENT_RW == 1)
   else if(count == 2)
   {
       data = kmalloc(2,GFP_KERNEL);  
	   ret = copy_from_user(data, buf, 2);
	   if(ret < 0)
	   {
	       printk("write param num error \n");
		   kfree(data);
		   return -EINVAL;     
	   }
	   address = data[0];
	   ret=i2c_smbus_write_byte_data(ds2460_client,address,data[1]);
	   if(ret < 0)
	   {
	      printk("i2c_smbus_write_byte_data error %d\n",ret);
		  kfree(data);
		  return ret;
	   }
	   kfree(data);
   }
   else 
   {
       data = kmalloc(count,GFP_KERNEL);  
	   ret = copy_from_user(data, buf, count);
	   if(ret < 0)
	   {
	       printk("write param num error %d\n",ret);
		   kfree(data);
		   return -EINVAL;     
	   }
	   
	   address = data[0];
	   
	   ret=i2c_smbus_write_i2c_block_data(ds2460_client,address,count-1,&data[1]);
	   if(ret < 0)
	   {
	      printk("i2c_smbus_write_i2c_block_data error %d\n",ret);
		  kfree(data);
		  return ret;
	   }
	   kfree(data);
   }
#else
       /*申請內存*/
       data = kmalloc(count,GFP_KERNEL); 

       /*拷貝count個字節到剛剛申請的內存中*/
	   ret = copy_from_user(data, buf, count);
	   if(ret < 0)
	   {
	       printk("write param num error %d\n",ret);
		   kfree(data);
		   return -EINVAL;     
	   }

	   /*用戶buf第一個字節是需要寫入的地址*/
       address = data[0];

	   /*依次寫入數據*/
	   for(i=1;i<=count-1;i++,address++)
	   {
	       /*非連續讀寫      每寫入1個字節產生一次完整的I2C通信*/
           ret=i2c_smbus_write_byte_data(ds2460_client,address,data[i]);
		   if(ret < 0)
		   {
		      printk("i2c_smbus_write_byte_data error %d\n",ret);
			  kfree(data);
			  return ret;
		   }
		   /*E2PROM寫入需要延時10ms*/
           mdelay(10);
	   }
	   
	   /*釋放內存*/
	   kfree(data);
#endif
       return 0;

}

static struct file_operations ds2460_fops = 
{

  .owner   = THIS_MODULE,
  .open    = ds2460_open,
  .release = ds2460_release,
  .read    = ds2460_read,
  .write   = ds2460_write,
};


static int __devinit ds2460_probe(struct i2c_client *client,
				        const struct i2c_device_id *id)
{
     int ret;

	 /*獲取當前操作的設備    */
     ds2460_client = client;
   
	 /*申請設備號*/
     ret = alloc_chrdev_region(&device_id, 0, 1, DEVICE_NAME);
	 if(ret < 0)
	 {
		 printk(KERN_ERR "alloc dev_id error %d \n", ret);
		 return ret;
	 }
	 
     /*分配一個cdev結構體*/
	 ds2460_cdev = cdev_alloc(); 
	 if(ds2460_cdev != NULL)
	 {
	     /*初始化cdev結構體*/
         cdev_init(ds2460_cdev, &ds2460_fops);

		 /*向內核添加該cdev結構體*/
		 ret = cdev_add(ds2460_cdev, device_id, 1);
		 if(ret != 0)
		 {
	       printk("cdev add error %d \n",ret);
		   goto error;
		 }
	 }
	 else
    {    
	     printk("cdev_alloc error \n");
	     return -1;
    }
	 
	 /*創建一個class*/
	ds2460_class = class_create(THIS_MODULE, "ds2460_class"); 
	if(ds2460_class != NULL)
	{
        ds2460_device = device_create(ds2460_class, NULL, device_id, NULL, DEVICE_NAME);  
	}
    else
    {
       	 printk("class_create error\n");
	     return -1;
	}
	
	return 0;
error:
	cdev_del(ds2460_cdev);
	unregister_chrdev_region(device_id, 1);
	return -1;
}


static int __devexit ds2460_remove(struct i2c_client *client)
{
    /*刪除cdev結構體*/
    cdev_del(ds2460_cdev);

	/*釋放設備號*/
	unregister_chrdev_region(device_id, 1);

	/*刪除設備*/
	device_del(ds2460_device);

	/*刪除類*/
	class_destroy(ds2460_class);
	return 0;
}

/*配置驅動的名稱和私有數據*/
static const struct i2c_device_id ds2460_id_table=
{
    "ds2460",0//名稱爲“ds2460”需要和設備保持一致內核纔會調用驅動的probe函數
              //0 表示沒有私有數據
};


/*創建i2c_driver結構體*/
static struct i2c_driver ds2460_driver =
{
  .driver={
  	   .name ="ds2460_driver",//這個名字無所謂
	   .owner=THIS_MODULE,
     },
  .probe = ds2460_probe,
  .remove= __devexit_p(ds2460_remove),
  .id_table = &ds2460_id_table,//這裏的名字纔是和設備進行匹配的

};
	

static int ds2460_drv_init(void)
{
  /*向內核註冊驅動 如果和設備匹配 會執行probe函數*/
  i2c_add_driver(&ds2460_driver);
  
  printk("module init ok \n");
  return 0;
}


static void ds2460_drv_exit(void)
{  
   /*向內核註銷驅動 如果和設備匹配 會執行remove函數*/
   i2c_del_driver(&ds2460_driver);
   
   printk("module exit ok \n");
}


module_init(ds2460_drv_init);
module_exit(ds2460_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xzx2020");

最後,編寫測試函數:

DS2460是一個SHA加密芯片,但是它內部包含有一個112字節的E2PROM區域,其E2PROM區域的首地址是0x80,我們首先向這個位置寫入50個數據,再讀取50個數據,對比看下是否正確,最後再讀下芯片ID。

ds2460_test.c:

#include<stdio.h>	/* using printf()        */
#include<stdlib.h>      /* using sleep()         */
#include<fcntl.h>       /* using file operation  */
#include<sys/ioctl.h>   /* using ioctl()         */
#include <asm/ioctls.h>
#include <unistd.h> //sleep  write read close


int main(int argc, const char * argv [ ])
{
  int fd,i;
  int value = 0;
  unsigned char txbuf[51],rxbuf[50];
  fd = open("/dev/ds2460_drv",O_RDWR);
  if(fd < 0)
  {
     printf("open ds2460_drv error %d\n",fd);
	 return 0;
  }

  txbuf[0] = 0x80;//需要寫入的地址
  
  for(i = 1;i<=50;i++)
  {
    txbuf[i] = i;//填充寫入的數據
  }

  printf("write:\n");
  for(i=0;i<=50;i++)
  {
	 printf("%d ",txbuf[i]);
  }
  printf("\n");//打印要寫入的數據
  
  write(fd,txbuf,51);//寫入芯片0x80的位置
 
  rxbuf[0] = 0x80;//讀取的地址
  
  read(fd,rxbuf,50);//從0x80讀取50個字節

  printf("read ds2460:\n");
  
  for(i = 0; i < 50;i++)
  {
    printf("%d ",rxbuf[i]);//打印讀到的數據
  }
  printf("\n");


  rxbuf[0] = 0xF0;//讀芯片ID
  read(fd,rxbuf,8);  
  printf("ID:\n"); 
  for(i = 0; i < 8;i++)
  {
    printf("%x ",rxbuf[i]);
  }
  printf("\n");
  
  
  return 0;
}

編譯ds2460_dev.c,ds2460_drv.c,ds2460_test,得到三個文件:ds2460_dev.ko /ds2460_drv.ko /ds2460_test.

在開發板上加載前面兩個驅動模塊,再執行最後一個測試程序:

可以看到,驅動加載成功,寫入和讀取的數據也是一致的,芯片ID(低字節在前)爲:3C 53 7F 3e 0 0 0 39

我們編寫的I2C驅動沒有問題。

後記:

1.在linux系統下編寫I2C驅動,目前主要有兩種方法,一種是把I2C設備當作一個普通的字符設備來處理,另一種是利用linux下I2C驅動體系結構來完成。下面比較下這兩種方法:
第一種方法:
優點:思路比較直接,不需要花很多時間去了解linux中複雜的I2C子系統的操作方法。
缺點: 要求工程師不僅要對I2C設備的操作熟悉,而且要熟悉I2C的適配器(I2C控制器)操作。
       要求工程師對I2C的設備器及I2C的設備操作方法都比較熟悉,最重要的是寫出的程序可以移植性差。
       對內核的資源無法直接使用,因爲內核提供的所有I2C設備器以及設備驅動都是基於I2C子系統的格式。

第一種方法的優點就是第二種方法的缺點,
第一種方法的缺點就是第二種方法的優點。

2.E2PROM支持連續取,對於DS2460,發送一次起始信號最多可連續讀取8字節,但是本文沒有采用這一方式(ds2460_drv.c文件 中有宏定義開關可以打開),本文采用的是最原始的方式,即每讀寫一個字節產生一次完整的I2C通信,這種方式會大大影響速度。

3.linux內核源碼/linux-2.6.35.3/drivers/i2c中有I2C相關代碼

busses/i2c-mxs.c:總線驅動文件即I2C適配器的驅動文件,包含I2C基本通信函數。

i2c-core.c:I2C核心文件,主要提供API,與硬件無關。

i2c-dev.c:通用設備驅動文件,它不針對某一款I2C芯片,它提供通用的方式讓用戶操作I2C設備,同時也是一個字符設備驅動。
用戶直接open這個設備驅動就是上面講的把I2C設備當作一個普通的字符設備來處理,I2C所有通信細節都需要用戶自己完成。

下面推薦幾篇寫的比較好的Linux I2C驅動框架文章:

Linux I2C驅動框架(超詳細)

Linux3.5下I2C設備驅動程序

linux下I2C驅動架構全面分析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章