参考:Linux I2C驱动框架(超详细)_JT同学的博客-CSDN博客_i2c驱动

一、几个重要的对象

1、I2C总线

struct bus_type i2c_bus_type = {.name       = "i2c",.match       = i2c_device_match,.probe      = i2c_device_probe,.remove     = i2c_device_remove,.shutdown  = i2c_device_shutdown,.pm      = &i2c_device_pm_ops,
};

I2C总线对应着/bus下的一条总线,这个i2c总线结构体管理着i2c设备I2C驱动的匹配删除等操作,I2C总线会调用i2c_device_match函数看I2C设备和I2C驱动是否匹配,如果匹配就调用i2c_device_probe函数,进而调用I2C驱动的probe函数。

2、I2C驱动

struct i2c_driver {int (*probe)(struct i2c_client *, const struct i2c_device_id *); //probe函数struct device_driver driver; //表明这是一个驱动const struct i2c_device_id *id_table; //要匹配的从设备信息(名称)int (*detect)(struct i2c_client *, struct i2c_board_info *); //设备探测函数const unsigned short *address_list; //设备地址struct list_head clients; //设备链表
};

对应的是I2C驱动程序

3、I2C设备

struct i2c_client {unsigned short addr; //设备地址char name[I2C_NAME_SIZE]; //设备名称struct i2c_adapter *adapter; //设配器,值I2C控制器struct i2c_driver *driver; //设备对应的驱动struct device dev; //表明这是一个设备int irq; //中断号struct list_head detected; //节点
};

对应的是I2C设备

4、I2C设配器

I2C适配器是什么?

经过上面的介绍,知道有I2C驱动I2C设备,我们需要通过I2C驱动去和I2C设备通讯,这其中就需要一个I2C设配器。

struct i2c_adapter {unsigned int id; //设备器的编号const struct i2c_algorithm *algo; //算法,发送时序struct device dev; //表明这是一个设备
};

其中的i2c_algorithm是算法的意思,对应的就是如何发送I2C时序

struct i2c_algorithm {/* 作为主设备时的发送函数 */int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);/* 作为从设备时的发送函数 */int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data);
};

小结:

I2C驱动有4个重要的东西,I2C总线、I2C驱动、I2C设备、I2C设备器
I2C总线:维护着两个链表(I2C驱动、I2C设备),管理I2C设备和I2C驱动的匹配和删除等
I2C驱动:对应的就是I2C设备的驱动程序
I2C设备:是具体硬件设备的一个抽象
I2C设配器:用于I2C驱动和I2C设备间的通用,是SOC上I2C控制器的一个抽象
以注册I2C驱动为例,简单讲解I2C总线的运行机制(I2C设备道理相同)

1、注册I2C驱动
2、将I2C驱动添加到I2C总线的驱动链表中
3、遍历I2C总线上的设备链表,根据i2c_device_match函数进行匹配,如果匹配成功系统则会分配一个i2c_client结构体,并当做参数传递给i2c_device_probe函数。
4、i2c_device_probe函数会调用I2C驱动的probe函数

二、驱动编写测试

对于i2c总线属于内核的内容,留着以后学~,i2c adapter已经由芯片厂商编写,因此我们只需学习设备驱动编写。

1、修改设备树

从原理图中可以看出,UART4_RXD作为I2C1_SDA,UART4_TXD作为I2C1_SCL。

因此需要修改对应的pinctrl节点信息,使管脚复用为I2C1功能 。修改后结果如下:

随后在设备树i2c1接口上添加AP3216C设备节点信息,添加结束后如下:

其中@后面的“1e”是 ap3216c 的器件地址,compatible属性用于驱动和设备的匹配,reg属性也用来保存ap3216c的器件地址。

修改完设备树后,重启开发板可以发现i2c总线上已经可以看到ap3216c这个设备了。

2、 搭建i2c驱动框架

ap3216creg.h描述相关寄存器

#ifndef AP3216C_H
#define AP3216C_H/* AP3316C 寄存器 */
#define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */
#define AP3216C_INTSTATUS 0X01 /* 中断状态寄存器 */
#define AP3216C_INTCLEAR 0X02 /* 中断清除寄存器 */
#define AP3216C_IRDATALOW 0x0A /* IR 数据低字节 */
#define AP3216C_IRDATAHIGH 0x0B /* IR 数据高字节 */
#define AP3216C_ALSDATALOW 0x0C /* ALS 数据低字节 */
#define AP3216C_ALSDATAHIGH 0X0D /* ALS 数据高字节 */
#define AP3216C_PSDATALOW 0X0E /* PS 数据低字节 */
#define AP3216C_PSDATAHIGH 0X0F /* PS 数据高字节 */#endif
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/input.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/i2c.h>
#include "ap3216creg.h"/*无设备树匹配设备*/
static struct i2c_device_id ap3216c_id[] = {{"ap3216c,idtable",0},{},
};/*设备树匹配表*/
static struct of_device_id ap3216c_of_match[] = {{.compatible = "tree,ap3216c"},{},
};static int ap3216c_probe(struct i2c_client *client,const struct i2c_device_id *id){printk("ap3216c init\r\n");return 0;
}static int ap3216c_remove(struct i2c_client *client){printk("ap3216c exit\r\n");return 0;
}/*i2c_driver*/
static struct i2c_driver ap3216c_driver = {.probe = ap3216c_probe,.remove = ap3216c_remove,.driver = {.name = "ap3216c",.owner = THIS_MODULE,.of_match_table = of_match_ptr(ap3216c_of_match),},/*id_table用于无设备树匹配设备*/.id_table = ap3216c_id,
};/*入口函数*/
static int __init ap3216c_init(void){int ret = 0;ret = i2c_add_driver(&ap3216c_driver);  /*注册i2c_driver*/return ret;
}/*出口函数*/
static void __exit ap3216c_exit(void){i2c_del_driver(&ap3216c_driver);    /*卸载i2c_driver*/
}/*模块入口和出口*/
module_init(ap3216c_init);
module_exit(ap3216c_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYC");

i2c驱动框架采用,设备树匹配方式进行。当ap3216c_of_match[]中的compatible属性与设备树节点compatible属性匹配后就会执行.probe函数,卸载驱动时会执行.remove函数。

验证结果如下:

完整驱动框架如下:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/input.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/i2c.h>
#include "ap3216creg.h"#define AP3216C_NAME "AP3216C"
#define AP3216C_COUNT 1/*i2c设备结构体*/
struct ap3216c_dev{struct cdev cdev;   /*字符设备结构体*/dev_t devid;    /*定义设备号*/struct class *class;    /*类*/struct device *device;  /*创建设备*/int major;  /*定义主设备号*/int minor;  /*定义次设备号*/
};struct ap3216c_dev ap3216c;static int ap3216c_open(struct inode *inode, struct file *filp){filp->private_data = &ap3216c;printk("ap3216c open\r\n");return 0;
}
static ssize_t ap3216c_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){printk("ap3216c read\r\n");return 0;
}static int ap3216c_release(struct inode *inode, struct file *filp){printk("ap3216c release\r\n");return 0;
}static const struct file_operations ap3216c_fops = {.owner = THIS_MODULE,.open = ap3216c_open,.read = ap3216c_read,.release = ap3216c_release,
};/*无设备树匹配设备*/
static struct i2c_device_id ap3216c_id[] = {{"ap3216c,idtable",0},{},
};/*设备树匹配表*/
static struct of_device_id ap3216c_of_match[] = {{.compatible = "tree,ap3216c"},{},
};static int ap3216c_probe(struct i2c_client *client,const struct i2c_device_id *id){int ret = 0;printk("ap3216c init\r\n");/*注册字符设备*/ap3216c.major = 0;  /*由系统分配设备号*/if(ap3216c.major){    /*给定主设备号*/ap3216c.devid = MKDEV(ap3216c.major, 0);    /*获取设备号*/ret = register_chrdev_region(ap3216c.devid, AP3216C_COUNT, AP3216C_NAME);}else{   /*s否则自动申请设备号*/ret = alloc_chrdev_region(&ap3216c.devid, 0, AP3216C_COUNT, AP3216C_NAME);ap3216c.major = MAJOR(ap3216c.devid);ap3216c.minor = MINOR(ap3216c.devid);}if(ret < 0){printk("ap3216c register error!\r\n");goto fail_devid;}printk("ap3216c major = %d, minor = %d \r\n", ap3216c.major, ap3216c.minor);//打印主次设备号ap3216c.cdev.owner = THIS_MODULE;cdev_init(&ap3216c.cdev, &ap3216c_fops);ret = cdev_add(&ap3216c.cdev, ap3216c.devid, AP3216C_COUNT);if(ret < 0){goto fail_cdev;}/*自动创建设备节点*/ap3216c.class = class_create(THIS_MODULE, AP3216C_NAME);if(IS_ERR(ap3216c.class)){ret = PTR_ERR(ap3216c.class);goto fail_class;}ap3216c.device = device_create(ap3216c.class, NULL, ap3216c.devid, NULL, AP3216C_NAME);if(IS_ERR(ap3216c.device)){ret = PTR_ERR(ap3216c.device);goto fail_device;}return 0;fail_device:class_destroy(ap3216c.class);
fail_class:cdev_del(&ap3216c.cdev);
fail_cdev:unregister_chrdev_region(ap3216c.devid, AP3216C_COUNT);
fail_devid:return ret;
}static int ap3216c_remove(struct i2c_client *client){printk("ap3216c exit\r\n");/*删除字符设备*/cdev_del(&ap3216c.cdev);/*注销设备号*/unregister_chrdev_region(ap3216c.devid, AP3216C_COUNT);/*销毁设备*/device_destroy(ap3216c.class, ap3216c.devid);/*销毁类*/class_destroy(ap3216c.class);return 0;
}/*i2c_driver*/
static struct i2c_driver ap3216c_driver = {.probe = ap3216c_probe,.remove = ap3216c_remove,.driver = {.name = "ap3216c",.owner = THIS_MODULE,.of_match_table = of_match_ptr(ap3216c_of_match),},/*id_table用于无设备树匹配设备*/.id_table = ap3216c_id,
};/*入口函数*/
static int __init ap3216c_init(void){int ret = 0;ret = i2c_add_driver(&ap3216c_driver);return ret;
}/*出口函数*/
static void __exit ap3216c_exit(void){i2c_del_driver(&ap3216c_driver);    /*卸载i2c_driver*/
}/*模块入口和出口*/
module_init(ap3216c_init);
module_exit(ap3216c_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYC");

3、 AP3216C读写函数编写

通过IIC适配器,来向AP3216C发送或读取数据

①向AP3216C寄存器写数据

如图是一个i2c的写数据时序图,从图中可以看出具体步骤:##器件地址为7位,末位0是写
开始信号-->发送i2c器件地址-->从机发送应答-->发送要写入寄存器地址-->从机发送应答-->发送要写入的数据-->从机发送应答-->停止信号。

具体函数为:

static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
{u8 b[256];struct i2c_msg msg;struct i2c_client *client = (struct i2c_client *)dev->private_data;b[0] = reg;                   /* 寄存器首地址 */memcpy(&b[1],buf,len);      /* 将要写入的数据拷贝到数组b里面 */msg.addr = client->addr;   /* ap3216c地址 */msg.flags = 0;              /* 标记为写数据 */msg.buf = b;               /* 要写入的数据缓冲区 */msg.len = len + 1;         /* 要写入的数据长度 */return i2c_transfer(client->adapter, &msg, 1);
}/** @description  : 向ap3216c指定寄存器写入指定的值,写一个寄存器* @param - dev:  ap3216c设备* @param - reg:  要写的寄存器* @param - data: 要写入的值* @return   :    无*/
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{u8 buf = 0;buf = data;ap3216c_write_regs(dev, reg, &buf, 1);
}

Linux内核采用msg这个参数描述一个消息。其中msg.addr保存i2c设备的器件地址。msg.flags是一个标志位,为0表示写数据。msg.buf保存要写数据的首地址。msg.len表示要写数据的长度,单位为字节。

注意:为什么写数据会有两个函数?因为ap3216c的寄存器长度是8位,有的i2c寄存器长度可能不止8位,因此s32 ap3216c_write_regs其实是一个通用写多个寄存器的函数,ap3216c_write_reg才是针对ap3216c的寄存器写数据的函数。

②从AP3216C寄存器读数据

如图是一个i2c的读数据时序图,从图中可以看出具体步骤:##器件地址为7位,末位1是读

开始信号-->发送i2c器件地址-->从机发送应答-->重新发送起始信号-->发送要读的寄存器地址-->从机发送应答-->重新发送起始信号-->重新发送i2c器件地址-->从机发送应答信号-->主机读取从机发送数据-->主机发送NO ACK表示读取完成-->主机发送STOP信号结束通讯

具体函数为:

static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{int ret;struct i2c_msg msg[2];struct i2c_client *client = (struct i2c_client *)dev->private_data;/* msg[0]为发送要读取的首地址 */msg[0].addr = client->addr;         /* ap3216c地址 */msg[0].flags = 0;                   /* 标记为发送数据 */msg[0].buf = &reg;                    /* 读取的首地址 */msg[0].len = 1;                        /* reg长度*//* msg[1]读取数据 */msg[1].addr = client->addr;           /* ap3216c地址 */msg[1].flags = I2C_M_RD;            /* 标记为读取数据*/msg[1].buf = val;                  /* 读取数据缓冲区 */msg[1].len = len;                 /* 要读取的数据长度*/ret = i2c_transfer(client->adapter, msg, 2);if(ret == 2) {ret = 0;} else {printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);ret = -EREMOTEIO;}return ret;
}/** @description  : 读取ap3216c指定寄存器值,读取一个寄存器* @param - dev:  ap3216c设备* @param - reg:  要读取的寄存器* @return    :   读取到的寄存器值*/
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{u8 data = 0;ap3216c_read_regs(dev, reg, &data, 1);return data;
}

i2c读数据的步骤比写数据麻烦一些。因为既包含写寄存器又包含读寄存器。因此需要建立一个msg[]数组保存两种信息。

4、完整驱动

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "ap3216creg.h"#define AP3216C_CNT    1
#define AP3216C_NAME    "ap3216c"struct ap3216c_dev {dev_t devid;         /* 设备号   */struct cdev cdev;        /* cdev     */struct class *class;  /* 类        */struct device *device;    /* 设备    */struct device_node   *nd; /* 设备节点 */int major;           /* 主设备号 */void *private_data;   /* 私有数据 */unsigned short ir, als, ps;       /* 三个光传感器数据 */
};static struct ap3216c_dev ap3216cdev;/** @description    : 从ap3216c读取多个寄存器数据* @param - dev:  ap3216c设备* @param - reg:  要读取的寄存器首地址* @param - val:  读取到的数据* @param - len:  要读取的数据长度* @return      : 操作结果*/
static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{int ret;struct i2c_msg msg[2];struct i2c_client *client = (struct i2c_client *)dev->private_data;/* msg[0]为发送要读取的首地址 */msg[0].addr = client->addr;         /* ap3216c地址 */msg[0].flags = 0;                   /* 标记为发送数据 */msg[0].buf = &reg;                    /* 读取的首地址 */msg[0].len = 1;                        /* reg长度*//* msg[1]读取数据 */msg[1].addr = client->addr;           /* ap3216c地址 */msg[1].flags = I2C_M_RD;            /* 标记为读取数据*/msg[1].buf = val;                  /* 读取数据缓冲区 */msg[1].len = len;                 /* 要读取的数据长度*/ret = i2c_transfer(client->adapter, msg, 2);if(ret == 2) {ret = 0;} else {printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);ret = -EREMOTEIO;}return ret;
}/** @description  : 向ap3216c多个寄存器写入数据* @param - dev:  ap3216c设备* @param - reg:  要写入的寄存器首地址* @param - val:  要写入的数据缓冲区* @param - len:  要写入的数据长度* @return     :   操作结果*/
static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
{u8 b[256];struct i2c_msg msg;struct i2c_client *client = (struct i2c_client *)dev->private_data;b[0] = reg;                   /* 寄存器首地址 */memcpy(&b[1],buf,len);      /* 将要写入的数据拷贝到数组b里面 */msg.addr = client->addr;   /* ap3216c地址 */msg.flags = 0;              /* 标记为写数据 */msg.buf = b;               /* 要写入的数据缓冲区 */msg.len = len + 1;         /* 要写入的数据长度 */return i2c_transfer(client->adapter, &msg, 1);
}/** @description  : 读取ap3216c指定寄存器值,读取一个寄存器* @param - dev:  ap3216c设备* @param - reg:  要读取的寄存器* @return    :   读取到的寄存器值*/
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{u8 data = 0;ap3216c_read_regs(dev, reg, &data, 1);return data;
}/** @description  : 向ap3216c指定寄存器写入指定的值,写一个寄存器* @param - dev:  ap3216c设备* @param - reg:  要写的寄存器* @param - data: 要写入的值* @return   :    无*/
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{u8 buf = 0;buf = data;ap3216c_write_regs(dev, reg, &buf, 1);
}/** @description  : 读取AP3216C的数据,读取原始数据,包括ALS,PS和IR, 注意!*                : 如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms* @param - ir : ir数据* @param - ps    : ps数据* @param - ps    : als数据 * @return      : 无。*/
void ap3216c_readdata(struct ap3216c_dev *dev)
{unsigned char i =0;unsigned char buf[6];/* 循环读取所有传感器数据 */for(i = 0; i < 6; i++) {buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);   }if(buf[0] & 0X80)  /* IR_OF位为1,则数据无效 */dev->ir = 0;                    else                /* 读取IR传感器的数据           */dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);          dev->als = ((unsigned short)buf[3] << 8) | buf[2];    /* 读取ALS传感器的数据           */  if(buf[4] & 0x40)  /* IR_OF位为1,则数据无效           */dev->ps = 0;                                                      else                /* 读取PS传感器的数据    */dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
}/** @description      : 打开设备* @param - inode     : 传递给驱动的inode* @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量*                       一般在open的时候将private_data指向设备结构体。* @return             : 0 成功;其他 失败*/
static int ap3216c_open(struct inode *inode, struct file *filp)
{filp->private_data = &ap3216cdev;/* 初始化AP3216C */ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04);     /* 复位AP3216C            */mdelay(50);                                                       /* AP3216C复位最少10ms  */ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X03);     /* 开启ALS、PS+IR         */return 0;
}/** @description      : 从设备读取数据 * @param - filp  : 要打开的设备文件(文件描述符)* @param - buf    : 返回给用户空间的数据缓冲区* @param - cnt  : 要读取的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{short data[3];long err = 0;struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;ap3216c_readdata(dev);data[0] = dev->ir;data[1] = dev->als;data[2] = dev->ps;err = copy_to_user(buf, data, sizeof(data));return 0;
}/** @description      : 关闭/释放设备* @param - filp   : 要关闭的设备文件(文件描述符)* @return             : 0 成功;其他 失败*/
static int ap3216c_release(struct inode *inode, struct file *filp)
{return 0;
}/* AP3216C操作函数 */
static const struct file_operations ap3216c_ops = {.owner = THIS_MODULE,.open = ap3216c_open,.read = ap3216c_read,.release = ap3216c_release,
};/** @description     : i2c驱动的probe函数,当驱动与*                    设备匹配以后此函数就会执行* @param - client  : i2c设备* @param - id      : i2c设备ID* @return          : 0,成功;其他负值,失败*/
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{/* 1、构建设备号 */if (ap3216cdev.major) {ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);} else {alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);ap3216cdev.major = MAJOR(ap3216cdev.devid);}/* 2、注册设备 */cdev_init(&ap3216cdev.cdev, &ap3216c_ops);cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);/* 3、创建类 */ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);if (IS_ERR(ap3216cdev.class)) {return PTR_ERR(ap3216cdev.class);}/* 4、创建设备 */ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME);if (IS_ERR(ap3216cdev.device)) {return PTR_ERR(ap3216cdev.device);}ap3216cdev.private_data = client;return 0;
}/** @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行* @param - client   : i2c设备* @return          : 0,成功;其他负值,失败*/
static int ap3216c_remove(struct i2c_client *client)
{/* 删除设备 */cdev_del(&ap3216cdev.cdev);unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);/* 注销掉类和设备 */device_destroy(ap3216cdev.class, ap3216cdev.devid);class_destroy(ap3216cdev.class);return 0;
}/* 传统匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {{"ap3216c,idtable",0}, {}
};/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {{ .compatible = "tree,ap3216c" },{ /* Sentinel */ }
};/* i2c驱动结构体 */
static struct i2c_driver ap3216c_driver = {.probe = ap3216c_probe,.remove = ap3216c_remove,.driver = {.owner = THIS_MODULE,.name = "ap3216c",.of_match_table = ap3216c_of_match, },.id_table = ap3216c_id,
};/** @description : 驱动入口函数* @param       : 无* @return       : 无*/
static int __init ap3216c_init(void)
{int ret = 0;ret = i2c_add_driver(&ap3216c_driver);return ret;
}/** @description  : 驱动出口函数* @param       : 无* @return       : 无*/
static void __exit ap3216c_exit(void)
{i2c_del_driver(&ap3216c_driver);
}/* module_i2c_driver(ap3216c_driver) */module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zyc");

5、应用

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>/** @description       : main主程序* @param - argc   : argv数组元素个数* @param - argv    : 具体参数* @return            : 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd;char *filename;unsigned short databuf[3];unsigned short ir, als, ps;int ret = 0;if (argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if(fd < 0) {printf("can't open file %s\r\n", filename);return -1;}while (1) {ret = read(fd, databuf, sizeof(databuf));if(ret == 0) {            /* 数据读取成功 */ir =  databuf[0];  /* ir传感器数据 */als = databuf[1];     /* als传感器数据 */ps =  databuf[2];    /* ps传感器数据 */printf("ir = %d, als = %d, ps = %d\r\n", ir, als, ps);}usleep(200000); /*100ms */}close(fd);  /* 关闭文件 */  return 0;
}

结果测试:

Linux驱动_i2c驱动(ap3216c)相关推荐

  1. ()shi linux字符设备,Linux字符设备驱动基础(三)

    Linux字符设备驱动基础(三) 6 创建设备节点 6.1 手动创建设备节点 查看申请的设备名及主设备号: cat /proc/devices # cat /proc/devices Characte ...

  2. Linux ALSA声卡驱动之八:ASoC架构中的Platform

    1.  Platform驱动在ASoC中的作用 前面几章内容已经说过,ASoC被分为Machine,Platform和Codec三大部件,Platform驱动的主要作用是完成音频数据的管理,最终通过C ...

  3. linux下i2c设备驱动程序,Linux I2C 设备驱动

    I2C 设备驱动要使用 i2c_driver 和 i2c_client 数据结构并填充其中的成员函数.i2c_client 一般被包含在设备的私有信息结构体yyy_data 中,而 i2c_drive ...

  4. 基于linux的驱动设计,《基于LINUX的虚拟驱动设计》-毕业论文.doc

    PAGE 40 l 摘 要 驱动程序是当前最热门.最有发展前途的IT应用技术之一.目前的驱动程序的开发主要应用在包括键盘 .鼠标.扫描仪.打印机以及存储设备等日益普及的设备之间的通讯上.但是要使这些设 ...

  5. 【驱动】linux下I2C驱动架构全面分析

    I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...

  6. 连接LilyPad之Linux平台的驱动

    连接LilyPad之Linux平台的驱动 常用的Linux发行版都自带了FTDI驱动,因此在绝大多数Linux发行版中不需要用户进行额外的操作. 在LilyPad编程器被正确驱动后,就可以将LilyP ...

  7. linux的platform驱动

    如下内容来自<[正点原子]I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf>   将设备信息从设备驱动中剥离开来,驱动使用标准方法去获取到设备信息(比如从设备树中获取到设备信息 ...

  8. Linux内核网络设备驱动

    本文首先从宏观上介绍数据包的接收过程,然后详细介绍了Linux网络设备驱动的工作过程,最后介绍网卡监控与调优,包括网络数据包总数.丢包.错包数量的相关统计. 1. 接收数据包过程概述 介绍数据包收包过 ...

  9. linux设备模型 字符设备,Linux 字符设备驱动模型之框架解说

    一.软件操作硬件设备模型 在进行嵌入式开发的过程中,在常做的事情就是驱动配置硬件设 备,然后根据功能需求使用硬件设备,实现功能的逻辑.如下图为其 相互之间的关系. 如上图所示: 驱动程序:主要作为操作 ...

最新文章

  1. C++中的友元函数friend
  2. DK云网关与普通DTU之间的区别
  3. 可以看游资的app_跟随一线游资操作,轻松收获涨停板
  4. 2019年终总结一下吧
  5. 微软VDI动手实验之应用模型及部署攻略
  6. 使用openssl生成双向加密证书(转)
  7. Android-用ListView显示SDCard文件列表
  8. JDK 11的一般可用性
  9. 封装cookie设置和获取的简易方法
  10. java list的作用_集合框架(List集合的特有功能概述和测试)
  11. 国内学生宿舍最“豪华”的4所大学,清华大学排第3,第1当之无愧
  12. 收到手机第二天就自燃?S10机主怒告三星 要求道歉并索赔1元
  13. ​​​​​​​微信、Facebook牵手合作,抗击全球新冠肺炎疫情
  14. GHOSTXPSP3系统封装网页图文教程
  15. 怎么把VC++的注释语句调成其他颜色,只调注释语句
  16. fifaol4服务器维护,新闻|FIFAOL4 5月7日停机维护公告
  17. 一阶惯性环节如何实现跟踪性能与滤波性能共存(一)
  18. Spring实战——FileSystemResource
  19. 提示“百度未授权使用地图API,可能是因为您提供的密钥不是有效的百度LBS开放平台密钥”解决方案
  20. Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day33】—— 手撸算法2

热门文章

  1. android车载娱乐系统场景,复合式娱乐综合体,共享设备集成场景化空间-迷你ktv官网...
  2. Python 优化 回溯下降算法
  3. Selenium2Library库中没有系统关键字select window by handle咋办?
  4. 阴阳师服务器维护2月20,阴阳师2月19日更新维护公告 大妖试炼超鬼王来袭
  5. C语言数据结构之树超全详解
  6. 【机器学习】线性回归之波斯顿房价预测
  7. linux中top中vsz博客,linux top 命令
  8. VPS国内各网点一键测试脚本
  9. BeautfulSoup详解
  10. 怎么将图片变成圆角矩形,2种方法可供选择