i2c 设备驱动

  这篇博客分析i2c的普通设备驱动,像往常的字符驱动一样,先module_init(),和module_exit(),来完成模块的加载与卸载,然后在初始化函数中注册设备号和关联文件结构体file_operaion,最后再去实现file_operation中的read,write,open,release等操作,在操作read,write,open,release时,直接控制s3c2440的i2c相关的寄存器来与i2c设备进行通信,而没有记住linux2.6.32.2内核提供的i2c-core.c文件里面的i2c_transfer方法来实现数据通信。
  1. #include <linux/module.h>  
  2. #include <linux/kernel.h>  
  3. #include <linux/init.h>  
  4. #include <linux/sched.h>  
  5. #include <mach/hardware.h>  
  6. #include <linux/semaphore.h>  
  7. #include <asm/uaccess.h>  
  8. #include <linux/kernel.h>   /* printk() */  
  9. #include <linux/slab.h>   /* kmalloc() */  
  10. #include <linux/fs.h>       /* everything... */  
  11. #include <linux/errno.h>    /* error codes */  
  12. #include <linux/types.h>    /* size_t */  
  13. #include <linux/mm.h>  
  14. #include <linux/kdev_t.h>  
  15. #include <linux/cdev.h>  
  16. #include <linux/delay.h>  
  17. #include <asm/io.h>  
  18. #include <asm/uaccess.h>  
  19. #include <linux/errno.h>  
  20. #include <linux/kernel.h>  
  21. #include <linux/module.h>  
  22. #include <linux/slab.h>  
  23. #include <linux/input.h>  
  24. #include <linux/init.h>  
  25. #include <linux/serio.h>  
  26. #include <asm/irq.h>  
  27. #include <mach/map.h>  
  28. #include <mach/regs-gpio.h>  
  29. #include <mach/regs-irq.h>  
  30. #include <mach/regs-clock.h>  
  31. #include <asm/signal.h>  
  32.   
  33. volatile int f_nGetACK;  
  34. #define UINT unsigned int  
  35. #define I2C_MAGIC 'k'    
  36. #define I2C_set _IO(I2C_MAGIC,1)  
  37. #define I2C_MAJOR 259  
  38. #define DEVICE_NAME "s3c2410_I2C"  
  39.   
  40. MODULE_LICENSE("GPL");  
  41. MODULE_AUTHOR("farsight");  
  42. MODULE_DESCRIPTION("s3c2410 I2C");  
  43.   
  44. char data[128]="\0";  
  45. #define rGPECON  *(volatile unsigned int *)S3C2410_GPECON  
  46.   
  47. #if 0  
  48. #define S3C2410_I2C(x) (S3C2410_IICREG(x))  
  49. #define S3C2410_IICCON     S3C2410_I2C(0x00)  
  50. #define S3C2410_IICSTAT    S3C2410_I2C(0x04)  
  51. #define S3C2410_IICADD     S3C2410_I2C(0x08)  
  52. #define S3C2410_IICDS      S3C2410_I2C(0x0c)  
  53.   
  54. #define rIICCON  *(volatile unsigned int *)S3C2410_IICCON  
  55. #define rIICSTAT *(volatile unsigned int *)S3C2410_IICSTAT  
  56. #define rIICADD  *(volatile unsigned int *)S3C2410_IICADD  
  57. #define rIICDS   *(volatile unsigned int *)S3C2410_IICDS  
  58. #define rGPECON  *(volatile unsigned int *)S3C2410_GPECON  
  59. #else  
  60.   
  61. #define rIICCON  *(volatile unsigned int *)i2c_base  
  62. #define rIICSTAT *(volatile unsigned int *)((unsigned int)i2c_base + 4)  
  63. #define rIICADD *(volatile unsigned int *)((unsigned int)i2c_base + 8)  
  64. #define rIICDS *(volatile unsigned int *)((unsigned int)i2c_base + 0xc)  
  65.   
  66. static volatile void __iomem *i2c_base;  
  67. static struct resource      *area = NULL;  
  68. #endif  
  69.   
  70. #define CLKCON 0x4c00000c  
  71. static volatile unsigned int *clkcon;  
  72. static int I2C_major = I2C_MAJOR;  
  73. static struct cdev I2C_cdev;  
  74.   
  75. /********************************************************************************************* 
  76.  * name:        iic_int_24c04() 
  77.  * func:        IIC interrupt handler 
  78.  * para:        none 
  79.  * ret:     none 
  80.  * modify: 
  81.  * comment:      
  82.  *********************************************************************************************/  
  83. static irqreturn_t  iic_int_24c04(int irq,void *dev_id,struct pt_regs *regs)  
  84. {  
  85.     f_nGetACK = 1;  
  86.     return IRQ_HANDLED ;  
  87. }  
  88.   
  89. /********************************************************************************************* 
  90.  * name:        iic_write_24c040  
  91.  * func:        write data to 24C040 
  92.  * para:        unSlaveAddr --- input, chip slave address 
  93.  *          unAddr      --- input, data address 
  94.  *          ucData      --- input, data value 
  95.  * ret:     none 
  96.  * modify: 
  97.  * comment:      
  98.  *********************************************************************************************/  
  99. void iic_write_24c040(UINT unSlaveAddr,UINT unAddr,UINT ucData)  
  100. {  
  101.     f_nGetACK = 0;  
  102.   
  103.     // Send control byte  
  104.     rIICDS = unSlaveAddr;           // 0xa0  
  105.     rIICSTAT = 0xf0;            // Master Tx,Start  
  106.     while(f_nGetACK == 0);// Wait ACK                     
  107.     f_nGetACK = 0;  
  108.     //Send address  
  109.     rIICDS = unAddr;  
  110.     rIICCON = 0xaf;             // Resumes IIC operation.  
  111.     while(f_nGetACK == 0);// Wait ACK  
  112.     f_nGetACK = 0;  
  113.   
  114.     // Send data   
  115.     rIICDS = ucData;  
  116.     rIICCON = 0xaf;             // Resumes IIC operation.  
  117.     while(f_nGetACK == 0);// Wait ACK  
  118.     f_nGetACK = 0;  
  119.   
  120.     // End send  
  121.     rIICSTAT = 0xd0;            // Stop Master Tx condition  
  122.     rIICCON = 0xaf;             // Resumes IIC operation.  
  123.     mdelay(10);             // Wait until stop condtion is in effect.  
  124. }  
  125.   
  126. /********************************************************************************************* 
  127.  * name:        iic_read_24c040 
  128.  * func:        read data from 24C040 
  129.  * para:        unSlaveAddr --- input, chip slave address 
  130.  *          unAddr      --- input, data address 
  131.  *          pData       --- output, data pointer 
  132.  * ret:     none 
  133.  * modify: 
  134.  * comment:      
  135.  *********************************************************************************************/  
  136. void iic_read_24c040(UINT unSlaveAddr,UINT unAddr,unsigned char *pData)  
  137. {  
  138.     char cRecvByte;  
  139.   
  140.     f_nGetACK = 0;  
  141.   
  142.     //Send control byte   
  143.     rIICDS = unSlaveAddr;               // 0xa0  
  144.     rIICSTAT = 0xf0;                // Master Tx,Start  
  145.     while(f_nGetACK == 0);// Wait ACK  
  146.     f_nGetACK = 0;  
  147.   
  148.     // Send address  
  149.     rIICDS = unAddr;  
  150.     rIICCON = 0xaf;                 // Resumes IIC operation.  
  151.     while(f_nGetACK == 0);// Wait ACK  
  152.     f_nGetACK = 0;  
  153.   
  154.     //Send control byte   
  155.     rIICDS = unSlaveAddr;               // 0xa0  
  156.     rIICSTAT = 0xb0;                // Master Rx,Start  
  157.     rIICCON = 0xaf;                 // Resumes IIC operation.     
  158.     mdelay(100);  
  159.     while(f_nGetACK == 0);// Wait ACK  
  160.     f_nGetACK = 0;  
  161.   
  162.     //Get data   
  163.     rIICCON = 0x2f;  
  164.     mdelay(1);  
  165.   
  166.     // Get data   
  167.     cRecvByte = rIICDS;  
  168.   
  169.     // End receive   
  170.     rIICSTAT = 0x90;                // Stop Master Rx condition   
  171.     rIICCON = 0xaf;                 // Resumes IIC operation.  
  172.     mdelay(10);                 // Wait until stop condtion is in effect.  
  173.   
  174.     *pData = cRecvByte;  
  175. }  
  176.   
  177. ssize_t I2C_read (struct file *filp, char *buff, size_t count, loff_t *offp)  
  178. {  
  179.     ssize_t result = 0;  
  180.     int i;  
  181.   
  182.     for(i=0; i<count; i++)   
  183.         data[i]=0;     
  184.     // Read 16 byte from 24C04  
  185.     for(i=0; i<count; i++)  
  186.     {  
  187.         iic_read_24c040(0xa0, i, &(data[i]));   
  188.     }  
  189.     data[count]='\0';  
  190.   
  191.     if (copy_to_user (buff, data, count))   
  192.         result = -EFAULT;  
  193.       
  194.     result=count;  
  195.     return result;  
  196. }  
  197.   
  198. ssize_t I2C_write (struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)  
  199. {  
  200.     int i;  
  201.     ssize_t ret = 0;  
  202.     //printk ("Writing %d bytes\n", count);  
  203.     if (count>127) return -ENOMEM;  
  204.     if (count<0) return -EINVAL;  
  205.     if (copy_from_user (data, buf, count))  
  206.     {  
  207.         ret = -EFAULT;  
  208.     }  
  209.     else {  
  210.         data[127]='\0';  
  211.         //printk ("Received: %s\n", data);  
  212.   
  213.   
  214.         // Write 0 - 16 to 24C04  
  215.         for(i=0; i<count; i++)  
  216.         {  
  217.             iic_write_24c040(0xa0, i, data[i]);   
  218.             //mdelay(100);  
  219.         }  
  220.         //printk("write end\n");  
  221.         ret = count;  
  222.     }  
  223.     return ret;  
  224. }  
  225.   
  226. static int I2C_open(struct inode *inode ,struct file *file)   
  227. {  
  228.     int result;  
  229.     // Initialize iic  
  230.     rIICADD = 0x10;                 // S3C2410X slave address   
  231.     rIICCON = 0xaf;                 // Enable ACK, interrupt, SET IICCLK=MCLK/16  
  232.     rIICSTAT = 0x10;                // Enable TX/RX   
  233.   
  234.     rGPECON =(rGPECON&((~0xf)<<28))+(0xa<<28);  
  235.     //printk("rGPECON=%x\n",rGPECON);  
  236.   
  237.     result = request_irq (IRQ_IIC, iic_int_24c04, IRQF_DISABLED, DEVICE_NAME, NULL);  
  238.     if (result) {  
  239.         printk(KERN_INFO "I2C: can't get assigned irq\n");  
  240.     }  
  241.   
  242.     //printk(KERN_NOTICE"open the I2C now!\n");  
  243.     return 0;  
  244. }  
  245.   
  246. static int I2C_release(struct inode *inode,struct file *file)  
  247. {  
  248.     free_irq(IRQ_IIC, NULL);//ÊÍ·ÅÖжÏ×ÊÔŽ  
  249.     //printk("I2C closed\n");  
  250.     return 0;  
  251. }  
  252.   
  253. static int I2C_ioctl(struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg)  
  254. {     
  255.     return 0;     
  256. }  
  257. //œ«É豞ע²áµœÏµÍ³Ö®ÖР 
  258. static void I2C_setup_dev(struct cdev *dev,int minor,struct file_operations *fops)  
  259. {  
  260.     int err;  
  261.     int devno=MKDEV(I2C_major,minor);  
  262.     cdev_init(dev,fops);   
  263.     dev->owner=THIS_MODULE;  
  264.     dev->ops=fops;   
  265.     err=cdev_add(dev,devno,1);  
  266.     if(err)  
  267.         printk(KERN_INFO"Error %d adding I2C %d\n",err,minor);  
  268. }  
  269.   
  270. static struct file_operations I2C_remap_ops={  
  271.     .owner=THIS_MODULE,  
  272.     .open=I2C_open,   
  273.     .write = I2C_write,  
  274.     .read = I2C_read,  
  275.     .release=I2C_release,   
  276.     .ioctl=I2C_ioctl,  
  277. };  
  278.   
  279. //×¢²áÉ豞Çý¶¯³ÌÐò£¬Ö÷ÒªÍê³ÉÖ÷É豞ºÅµÄ×¢²á  
  280. static int __init s3c2410_I2C_init(void)  
  281. {  
  282.     int result;  
  283.   
  284.     dev_t dev = MKDEV(I2C_major,0);  
  285.     if(I2C_major)  
  286.         result = register_chrdev_region(dev,1,DEVICE_NAME);  
  287.     else  
  288.     {     
  289.         result = alloc_chrdev_region(&dev,0,1,DEVICE_NAME);  
  290.         I2C_major = MAJOR(dev);  
  291.     }  
  292.   
  293.     if(result<0)  
  294.     {  
  295.         printk(KERN_WARNING"I2C:unable to get major %d\n",I2C_major);         
  296.         return result;  
  297.     }  
  298.   
  299.     if(I2C_major == 0)  
  300.         I2C_major = result;  
  301.   
  302.     printk(KERN_NOTICE"[DEBUG] I2C device major is %d\n",I2C_major);  
  303.   
  304.     __raw_writel( (__raw_readl(S3C2410_CLKCON) | (1 << 16)), S3C2410_CLKCON);  
  305.   
  306. #if 0  
  307.     printk("\n  S3C2410_CLKCON = %x \n", __raw_readl(S3C2410_CLKCON));  
  308.     area = request_mem_region(0x54000000, 16,"I2C");  
  309. #endif  
  310.   
  311.     i2c_base = ioremap(0x54000000, 16);  
  312.     clkcon = ioremap(CLKCON, 0x4);  
  313.   
  314.     printk(KERN_INFO"i2c clock = %d\n", *clkcon & (0x1 << 16));  
  315.     *clkcon |= 0x1 << 16;  
  316.   
  317.     I2C_setup_dev(&I2C_cdev,0,&I2C_remap_ops);  
  318.   
  319.     return 0;  
  320. }  
  321. //Çý¶¯Ä£¿éжÔØ  
  322. static void s3c2410_I2C_exit(void)  
  323. {  
  324. #if 0  
  325.     if (area) {  
  326.         release_resource(area);  
  327.         kfree(area);  
  328.     }  
  329. #endif  
  330.     cdev_del(&I2C_cdev);  
  331.     unregister_chrdev_region(MKDEV(I2C_major,0),1);  
  332.     printk("I2C device uninstalled\n");  
  333. }  
  334.   
  335. module_init(s3c2410_I2C_init);  
  336. module_exit(s3c2410_I2C_exit);  
在编译驱动的时候提示出错:I2C_drv.c:7:26: error: asm/hardware.h: No such file or directory        
        原因是是早期版本的头文件,在该版本位置已经换掉了。
        解决办法是使用find -name "xxx.h"查找到头文件对应的位置,然后将头文件添加进去。

       另外一个错误:2410_I2C/I2C_drv.c:285: error: 'SA_INTERRUPT' undeclared (first use in this function
       解决办法是SA_INTERRUPT定义在早期版本的内核,当前的内核没有定义该变量,而采用IRQF_DISABLE来代替

       以上归根结底是版本的问题。


测试应用程序

  1. #include <stdio.h>  
  2. #include <sys/types.h>  
  3. #include <unistd.h>  
  4. #include <fcntl.h>  
  5. #include <time.h>  
  6. #include <sys/ioctl.h>  
  7.   
  8. #define WATCHDOG_MAGIC 'k'   
  9. #define FEED_DOG _IO(WATCHDOG_MAGIC,1)  
  10.   
  11. int main(int argc,char **argv)  
  12. {  
  13.     int fd;  
  14.     char buff[]="farsight";  
  15.     //Žò¿ªI2C  
  16.     fd=open("/dev/i2c",O_RDWR);  
  17.        
  18.     if(fd<0)  
  19.     {  
  20.         printf("cannot open the I2C device\n");  
  21.         return -1;  
  22.     }  
  23.       
  24.     sleep(1);  
  25.     printf("buff_write=%s\n",buff);  
  26.     write (fd, buff, sizeof(buff));  
  27.   
  28.     //printf(" read  now!\n");  
  29.     memset (buff, '\0'sizeof(buff));  
  30.     //printf ("Read returns %d\n", read (fd, buff, sizeof(buff)));  
  31.     read (fd, buff, sizeof(buff));  
  32.     //read (fd, buff, 3);  
  33.     printf ("buff_read = %s\n", buff);  
  34.       
  35.     close(fd);  
  36.         //  while(1);  
  37.     //   printf("end\n");  
  38.     return 0;  
  39.   
  40. }  


    与前面普通设备驱动1方法不同,这里的普通设备驱动利用i2c-core.c提供的i2c_transfer方法来实现设备驱动,而不是普通设备驱动1里面的通过操纵s3c2440的i2c寄存器来与设备通信


 一、采用友善之臂的2.6.32.2内核,需要修改/linux-2.6.32.2/arch/arm/mach-s3c2440/mach-mini2440.c文件

1)添加 #include<linux/i2c.h>

2)在static struct platform_device *mini2440_devices[]前添加struct i2c_board_info i2c_devices[]结构体

//add
static struct i2c_board_info i2c_devices[] __initdata={
	{I2C_BOARD_INFO("at24c08", 0x50), },
};
/* devices we initialise */
static struct platform_device *mini2440_devices[] __initdata = {
	&s3c_device_usb,
	&s3c_device_rtc,
	&s3c_device_lcd,
	&s3c_device_wdt,
	&s3c_device_i2c0,//没有修改
	&s3c_device_iis,
	&mini2440_device_eth,
	&s3c24xx_uda134x,
	&s3c_device_nand,
	&s3c_device_sdi,
	&s3c_device_usbgadget,
};

3)在mini2440_machine_init(void)函数内添加i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));函数

static void __init mini2440_machine_init(void)
{
i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices)); //add

#if defined (LCD_WIDTH)
	s3c24xx_fb_set_platdata(&mini2440_fb_info);
#endif
	s3c_i2c0_set_platdata(NULL);

	s3c2410_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);

	s3c_device_nand.dev.platform_data = &friendly_arm_nand_info;
	s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg;
	platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
	s3c_pm_init();
}

二、在linux2.6.32.2/drivers/i2c/chips/目录下添加at24c08.c设备驱动文件,并对应的修改Kconfig和Makefile文件

#include  <linux/init.h>
#include  <linux/module.h>
#include  <linux/i2c.h>
#include  <linux/fs.h>
#include  <linux/cdev.h>
#include  <linux/device.h>
#include  <linux/kdev_t.h>
#include  <asm/uaccess.h>

#define   major  155
static  struct  i2c_driver  at24c08_driver;
static  struct  i2c_adapter  *at24c08_adapter;
static  unsigned  short  addr;
static  struct  class  at24c08_cls  =  {
					.name =  "at24c08_cls",
				};

ssize_t  at24c08_read  (struct  file  *filp,  char  __user  *buf,  size_t  sz,  loff_t  *off)
{
	struct  i2c_msg  msg[2];
	unsigned  char  args,  data;

	if  (sz  !=  1)
		return  -EINVAL;
	copy_from_user((void  *)&args,  buf,  1);
	/* 先传读地址 */
	msg[0].addr =  addr;
	msg[0].buf =  &args;
	msg[0].len =  1;
	msg[0].flags =  0;

	/* 再 读 */
	msg[1].addr =  addr;
	msg[1].buf =  &data;
	msg[1].len =  1;
	msg[1].flags =  1; /* 读 */
	if  (2  ==  i2c_transfer(at24c08_adapter,  msg,  2))  {
		/* 读成功 */
		copy_to_user((void  *)buf,  &data,  1);
		return  1;
	}
	else
		return  -EIO;
}

ssize_t  at24c08_write  (struct  file  *filp,  const  char  __user  *buf,  size_t  sz,  loff_t  *off)
{
	struct  i2c_msg  msg;
	unsigned  char  args[2];

	copy_from_user((void  *)&args,  buf,  2);

	/* args[0] = addr, args[1] = val */
	msg.addr  =  addr;
	msg.buf =  args;
	msg.len =  2;
	msg.flags  =  0; /* 写 */
	if(1  ==  i2c_transfer(at24c08_adapter,&msg,  1))
		return  2;
	else
		return  -EIO;

}

static  struct  file_operations  at24c08_fops  =  {
	.owner  =  THIS_MODULE,
	.read    =  at24c08_read,
	.write  =  at24c08_write,
};

static  struct  cdev  cdev;
static  int  at24c08_probe(struct  i2c_client  *client,  const  struct  i2c_device_id  *dev_id)
{
	register_chrdev_region(major,  1,  "at24c08");
	cdev_init(&cdev,  &at24c08_fops);
	cdev_add(&cdev,  MKDEV(major,  0),  1);
	class_register(&at24c08_cls);
	device_create(&at24c08_cls,  NULL,  MKDEV(major,  0),  NULL,  "at24c08_dev");
	return    0;
}

static  int  at24c08_remove(struct  i2c_client  *client)
{
	device_destroy(&at24c08_cls,  MKDEV(major,  0));
	class_destroy(&at24c08_cls);
	cdev_del(&cdev);
	unregister_chrdev_region(MKDEV(major,  0),  1);

	return    0;
}

static  int  at24c08_detect(struct  i2c_client  *client,  int  kind,  struct  i2c_board_info  *bd_info)
{
	strcpy(bd_info->type,    "at24c08");
	addr  =  bd_info->addr  =  client->addr;
	at24c08_adapter  =  client->adapter;
	return    0;
}

static  const  struct  i2c_device_id  at24c08_id[]  =  {
	{"at24c08",  0},
	{}
};

static  unsigned  short  ignore[]            =  {  I2C_CLIENT_END  };

static  const  unsigned  short  normal_i2c[]  =  {0x50,  I2C_CLIENT_END};

static  const  struct  i2c_client_address_data  addr_data  =  {
	.normal_i2c =  normal_i2c,
	.probe =  ignore,
	.ignore =  ignore,
};
static  struct  i2c_driver  at24c08_driver=  {
	.probe =  at24c08_probe,
	.remove =  at24c08_remove,
	.driver  =  {
			.name  =  "at24c08",
			},
	.id_table =  at24c08_id,
	.detect =  at24c08_detect,
	.address_data =  &addr_data,
	.class      =  I2C_CLASS_HWMON  |  I2C_CLASS_SPD,
};

static  int  at24c08_init(void)
{
	i2c_add_driver(&at24c08_driver);
	return  0;
}

static  void  at24c08_exit(void)
{
	i2c_del_driver(&at24c08_driver);
	return  ;
}

module_init(at24c08_init);
module_exit(at24c08_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("100ask.net Young");

以上要说明的是为什么insmod at24c08.ko之后会在/dev/目录下自动创建/dev/at24c08_dev文件节点

主要归功于两个函数

static  struct  class  at24c08_cls  =  { .name =  "at24c08_cls", };

class_register(&at24c08_cls);
device_create(&at24c08_cls,  NULL,  MKDEV(major,  0),  NULL,  "at24c08_dev");

内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

与class_create等价的函数是class_register,只是他们对应的注销函数不同class_unregister和class_destory。


三、在linux2.6.32.2目录下make menuconfig进行内核定制之后再make则会生成i2c-core.ko,i2c-s3c2440.ko,at24c08.ko等文件,将三个文件都insmod到内核中,在/dev/目录下会自动生成at24c08_dev文件节点,但是在重启之后会消失,因为insmod是动态加载的,如果在重启后还要有文件节点,则要将加载命令写到启动脚本中去。


  i2c的设备驱动可以直接利用内核提供的i2c-dev.c文件提供的ioctl函数接口在应用层实现对i2c设备的读写,但是在应用层使用ioctl函数对应用程序员要求较高,需要自行构建msg结构体,必须了解设备的操作流程,时序之类的。

        另外i2c设备的驱动也可以通过普通的设备驱动实现,像往常的驱动一样实现,然后在应用层就可以像读取普通文件一样操作,无需再考虑读写时序。其实普通的设备驱动也可以用两种方法实现,1)构建字符设备驱动,在open,read,write等函数中直接操作i2c总线的相关寄存器来读写i2c设备,但是这种方法因平台不同,设备不同都要重新写驱动,2)在设备驱动中调用i2c-core.c提供的i2c_transfer函数来实现和i2c设备的通信,这样只要对不同的设备写不同的驱动就行了。

        下面就分别对i2c-dev驱动、普通设备驱动方法1和普通设备驱动方法2来介绍一下,共分为博客的三篇文章:1)i2c驱动之i2c-dev驱动,2)普通设备驱动1,3)普通设备驱动方法2(推荐方法)。

        关于i2c设备驱动,自己在理解的过程中感觉比较好的资料的连接:

        linux下I2C驱动架构全面分析 、 深入源代码设计i2c驱动 、 宋宝华老师i2c驱动架构视频


        在/linux-2.6.32.2/drivers/i2c目录下

        ----Algos/                  一些i2c总线适配器通信的算法,个人感觉是用I/O口模拟实现i2c通信的算法

        ----Busses/               I2C总线驱动的方法,对应于s3c2440适配器驱动的文件是I2c-s3c2410.c

        ----Chips/                 I2C设备驱动,具体到某个设备,比如at24c08等

        ----I2c-boardinfo.c   

        ----I2c-core.c           I2C核心文件,用于联系设备驱动和总线驱动,作为一个桥梁,有用的函数i2c_add_addapter

                                       i2c_add_driver,和i2c_transfer函数

        ----I2c-dev.c            通用的i2c设备驱动

        ----Kconfig

        ----Makefile

       

开始在内核编译i2c-dev通用驱动

1)在linux-2.6.32.2/内核目录下make menuconfig,选择如下Device Drivers



2)进入Device Drivers目录,选择I2C Support,表示编译I2C驱动模块,会将i2c-core.c编译成模块文件i2c-core.ko


3)进入I2C support



4)选择模块化编译I2C device interface "M",则会将i2c-dev.c编译成i2c-dev.ko



5)选择I2C Hardware Bus support,并进入


选择s3c2410 I2c Driver则会将i2c-s3c2410.c编译成i2c-s3c2410.ko驱动模块


6)选择Miscellaneous I2c Chip Support,并进入


会提示让你选择编译具体的设备驱动,因为这里我们采用内核提供的通用设备驱动i2c-dev,所以这里的驱动就暂时不编译了



以上编译总共得到了3个驱动文件i2c-core.ko,i2c-dev.ko,i2c-s3c2410.ko

将这三个模块insmod到内核中,顺序是i2c-core.ko,i2c-s3c2410.ko,i2c-dev.ko,会自动创建/dev/i2c/0设备节点,然后就可以直接调用/dev/i2c/0文件节点进行访问设备了

之后应用程序员可以利用下面两种ioctl函数

ioctl(fd, I2C_RDWR, (unsigned long)&work_queue);或者

ioctl(file,I2C_SMBUS,&args);

进行与i2c设备通信了。



验证i2c应用程序:

  1. /* 
  2. //作者:王磊 
  3. //日期:2013.11.17 
  4. //文件功能:实现ioctl函数调用,并操作i2c设备/dev/i2c/0进行读写数据 
  5. //可以用i2c -r来检验数据是否已写入 
  6. */  
  7. #include<stdio.h>  
  8. #include<linux/types.h>  
  9. #include<fcntl.h>  
  10. #include<unistd.h>  
  11. #include<stdlib.h>  
  12. #include<sys/types.h>  
  13. #include<sys/ioctl.h>  
  14. #include<errno.h>  
  15. #include<assert.h>  
  16. #include<string.h>  
  17. #include<linux/i2c.h>  
  18. #include<linux/i2c-dev.h>  
  19.   
  20. int main(int argc, char** argv)  
  21. {  
  22.     struct i2c_rdwr_ioctl_data work_queue;  
  23.   
  24.     unsigned int slave_address,reg_address,dat;  
  25.     unsigned int fd;  
  26.     int ret;  
  27.     char select;  
  28.   
  29.     fd=open("/dev/i2c/0",O_RDWR);  
  30.     if(!fd)  
  31.     {  
  32.         printf("error on opening the device file\n");  
  33.         exit(1);  
  34.     }  
  35.     ioctl(fd,I2C_TIMEOUT,2);//超时时间  
  36.     ioctl(fd,I2C_RETRIES,1);//重复次数  
  37.       
  38.     //nmsgs决定了有多少start信号,一个msgs对应一个start信号,但这个start信号又不能适用于repeat start  
  39.     //在nmsg个信号结束后总线会产生一个stop  
  40.     work_queue.nmsgs = 1;  
  41.     work_queue.msgs = (struct i2c_msg *)malloc(work_queue.nmsgs * sizeof(work_queue.msgs));  
  42.     if(!work_queue.msgs)  
  43.     {  
  44.         printf("memory alloc failed");  
  45.         close(fd);  
  46.         exit(1);  
  47.     }  
  48.   
  49.     slave_address = 0x50;//24c08的访问地址是101000b  
  50.     printf("please select:w or r?\n");  
  51.     scanf("%c", &select);  
  52.     if('w' == select)  
  53.     {  
  54.         printf("please input:address,dat?(example:0x00,0x00)\n");  
  55.         scanf("%x,%x", &reg_address, &dat);  
  56.         //往i2c里面写数据  
  57.         printf("began to write\n");  
  58.         work_queue.nmsgs  = 1;    
  59.         (work_queue.msgs[0]).len = 2;//buf的长度  
  60.         (work_queue.msgs[0]).flags = 0;//write  
  61.         (work_queue.msgs[0]).addr = slave_address;//设备地址  
  62.         (work_queue.msgs[0]).buf = (unsigned char *)malloc(2);  
  63.         (work_queue.msgs[0]).buf[0] = reg_address;//写的地址  
  64.         (work_queue.msgs[0]).buf[1] = dat;//你要写的数据  
  65.   
  66.         ret = ioctl(fd, I2C_RDWR, (unsigned long)&work_queue);  
  67.         if(ret < 0)  
  68.             printf("error during I2C_RDWR ioctl with error code %d\n", ret);  
  69.     }  
  70.     else if('r' == select)  
  71.     {  
  72.         printf("please input:address?(example:0x00)\n");  
  73.         scanf("%x", &reg_address);  
  74.         //从i2c里面读出数据  
  75.         printf("began to read:");  
  76.         work_queue.nmsgs  = 1;  
  77.         //先设定一下地址     
  78.         (work_queue.msgs[0]).flags = 0;//write  
  79.         (work_queue.msgs[0]).addr = slave_address;  
  80.         (work_queue.msgs[0]).len = 1;  
  81.         (work_queue.msgs[0]).buf = (unsigned char *)malloc(1);  
  82.         (work_queue.msgs[0]).buf[0] = reg_address;//因为上面buf已经分配过了  
  83.         ret = ioctl(fd, I2C_RDWR, (unsigned long)&work_queue);  
  84.         if(ret < 0)  
  85.             printf("error during I2C_RDWR ioctl with error code %d\n", ret);  
  86.   
  87.     //因为i2c-dev不支持repeat start,所以只能将读数据操作中的写地址和读数据分为两次消息。     
  88.         //然后从刚才设定的地址处读  
  89.         work_queue.nmsgs  = 1;  
  90.         (work_queue.msgs[0]).flags = I2C_M_RD;  
  91.         (work_queue.msgs[0]).addr = slave_address;  
  92.         (work_queue.msgs[0]).len = 1;  
  93.         (work_queue.msgs[0]).buf[0] = 0;//初始化读缓冲  
  94.         ret = ioctl(fd, I2C_RDWR, (unsigned long)&work_queue);  
  95.         if(ret < 0)  
  96.             printf("error during I2C_RDWR ioctl with error code %d\n", ret);  
  97.         printf("reg_address=0x%2x,dat=0x%2x\n", reg_address, work_queue.msgs[0].buf[0]);  
  98.     }  
  99.     close(fd);  
  100.     free((work_queue.msgs[0]).buf);  
  101.     free(work_queue.msgs);  
  102.     return 0;   
  103.       
  104. }  





猜你喜欢

转载自blog.csdn.net/boyemachao/article/details/51749686