营销型网站建设范文,百度店面定位怎么申请,网站ping值,郑州网站推广公司价格目录
一、Linux I2C 驱动简介
1、I2C 总线驱动
2、I2C 设备驱动
1、 i2c_client 结构体
2、 i2c_driver 结构体
二、硬件分析
三、设备树编写
1、pinctrl_i2c1
2、在 i2c1 节点追加 ap3216c 子节点
3、验证
四、 代码编写
1、makefile
2、ap3216c.h 3、ap3216c.c …目录
一、Linux I2C 驱动简介
1、I2C 总线驱动
2、I2C 设备驱动
1、 i2c_client 结构体
2、 i2c_driver 结构体
二、硬件分析
三、设备树编写
1、pinctrl_i2c1
2、在 i2c1 节点追加 ap3216c 子节点
3、验证
四、 代码编写
1、makefile
2、ap3216c.h 3、ap3216c.c
①、头文件
②、驱动出入口
③、 i2c驱动结构体
④、匹配函数
⑤、probe 函数
⑥、remove 函数 ⑦、函数入口出口添加注册i2c_drive
⑧、读取AP3216C的N个寄存器
⑨、向AP3216C的N个寄存器写数据
⑩、读、写AP3216C一个寄存器
⑩①、读取AP3216C的数据
⑩②完善ap3216c_open、read函数 代码如下 4、ap3216c.c 一、Linux I2C 驱动简介 在IMX6ULL开发篇中IIC实验写了四个文件 bsp_i2c.c、bsp_i2c.h、 bsp_ap3216c.c 和 bsp_ap3216c.h。其中前两个是 I.MX6U 的 IIC 接口驱动后两个文件是 AP3216C 这个 I2C 设备驱动文件。相当于有两部分驱动 ①、 I2C 主机驱动。 ②、 I2C 设备驱动。 对于 I2C 主机驱动一旦编写完成就不需要再做修改其他的 I2C 设备直接调用主机驱动提供的 API 函数完成读写操作即可。这个正好符合 Linux 的驱动分离与分层的思想因此 Linux内核也将 I2C 驱动分为两部分 ①、 I2C 总线驱动 I2C 总线驱动就是 SOC 的 I2C 控制器驱动也叫做 I2C 适配器驱动。 ②、 I2C 设备驱动 I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动
1、I2C 总线驱动 在”platform驱动实验的时候就说过 platform 是虚拟出来的一条总线目的是为了实现总线、设备、驱动框架。对于 I2C 而言不需要虚拟出一条总线直接使用 I2C总线即可。 I2C 总线驱动重点是 I2C 适配器(也就是 SOC 的 I2C 接口控制器)驱动Linux 内核将 SOC 的 I2C 适配器(控制器)抽象成 i2c_adapter i2c_adapter 结构体定义在 include/linux/i2c.h 文件中,i2c_algorithm 类型的指针变量 algo对于一个 I2C 适配器肯定要对外提供读写 API 函数设备驱动程序可以使用这些 API 函数来完成读写操作。 i2c_algorithm 就是 I2C 适配器与 IIC 设备进行通信的方法i2c_algorithm 结构体定义在 include/linux/i2c.h 文件中其中的master_xfer 就是 I2C 适配器的传输函数可以通过此函数来完成与 IIC 设备之间的通信smbus_xfer 是 SMBUS 总线的传输函数 一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的比如 I.MX6U 的 I2C 适配器驱动 NXP 已经编写好了这个不需要用户去编写。因此 I2C 总线驱动对我们这些 SOC 使用者来说是被屏蔽掉的我们只要专注于 I2C 设备驱动即可。
2、I2C 设备驱动 I2C 设备驱动重点关注两个数据结构 i2c_client 和 i2c_driveri2c_client 就是描述设备信息的 i2c_driver 描述驱动内容类似于 platform_driver
1、 i2c_client 结构体
i2c_client 结构体定义在 include/linux/i2c.h 文件中内容如下
struct i2c_client {unsigned short flags; /* 标志 */unsigned short addr; /* 芯片地址 7 位存在低 7 位*/
......char name[I2C_NAME_SIZE]; /* 名字 */struct i2c_adapter *adapter; /* 对应的 I2C 适配器 */struct device dev; /* 设备结构体 */int irq; /* 中断 */struct list_head detected;
......};
一个设备对应一个 i2c_client每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client
2、 i2c_driver 结构体
i2c_driver 类似 platform_driveri2c_driver 结构体定义在 include/linux/i2c.h 文件中部分如下 struct i2c_driver {........./* Standard driver model interfaces */
1 int (*probe)(struct i2c_client *, const struct i2c_device_id *);......
2 struct device_driver driver;
3 const struct i2c_device_id *id_table;..........}
第 1 行当 I2C 设备和驱动匹配成功以后 probe 函数就会执行和 platform 驱动一样。 第 2行 device_driver 驱动结构体如果使用设备树的话需要设置 device_driver of_match_table 成员变量也就是驱动的兼容(compatible)属性。 第3行 id_table 是传统的、未使用设备树的设备匹配 ID 表
重点工作就是构建 i2c_driver构建完成以后需要向Linux 内核注册这个 i2c_driver
二、硬件分析
这部分在在IMX6ULL开发篇中IIC实验有详细介绍下面就简单介绍
打开AP3216C原理图这是一个IIC接口的器件是一个环境光传感器 I2C1_SCL: 使用的是UART4_TXD这个IOI2C1_SDA: 使用的是UART4_RXD这个IO
打开ap3216数据手册 地址为0x1e
三、设备树编写
1、pinctrl_i2c1
打开设备树找到i2c1,pinctrl_i2c1 就是 I2C1 的 IO 节点 这里将 UART4_TXD 和 UART4_RXD 这两个 IO 分别复用为 I2C1_SCL 和 I2C1_SDA电气属性都设置为 0x4001b8b0
2、在 i2c1 节点追加 ap3216c 子节点 AP3216C 是连接到 I2C1 上的因此需要在 i2c1 节点下添加 ap3216c 的设备子节点默认内容如下 305-316行NXP 官方的 EVK 开发板上接了 mag3110和fxls8471这里使用的开发板并没有这这两个器件所以删掉并添加上我们使用的 300行clock-frequency 属性为 I2C 频率这里设置为 100KHz
302行inctrl-0 属性指定 I2C 所使用的 IO 为上面的pinctrl_i2c1
305行ap3216c 子节点 后面的“1e”是 ap3216c 的器件地址
306行设置 compatible 值为“my,ap3216c”
307行reg 属性也是设置 ap3216c 器件地址的因此 reg 设置为 0x1e
3、验证
改完成以后使用“make dtbs”重新编译一下然后使用新的设备树启动 Linux 内核
/sys/bus/i2c/devices 目录下存放着所有 I2C 设备如果设备树修改正确的话会在/sys/bus/i2c/devices 目录下看到一个名为“0-001e”的子目录,如下
“0-001e”就是 ap3216c 的设备目录“1e”就是 ap3216c 器件地址。进入0-001e 目录可以看到“name”文件 name 问价就保存着此设备名字在这里就是“ap3216c”如下 四、 代码编写
1、makefile
需要的文件如下图左修改makefile 2、ap3216c.h
定义器件及其寄存器地址 3、ap3216c.c
①、头文件 ②、驱动出入口 ③、 i2c驱动结构体 46行当 I2C 设备和驱动匹配成功以后 probe 函数就会执行和 platform 驱动一样
47行当关闭 驱动的时候remove 函数就会执行和 platform 驱动一样
49行name是在无设备树时用于和设备进行匹配也就是上面写的实验也作为驱动名字 使用设备树就设置of_match_table变量也就是驱动的兼容(compatible)属性 53行id_table 是传统的、未使用设备树的设备匹配 ID 表
④、匹配函数
i2c驱动结构体 里对应的函数 236行无设备树的时候匹配 ID 表 241行设备树所使用的匹配表
⑤、probe 函数
在这里面实现基本的字符设备
#define AP3216C_CNT 1
#define AP3216C_NAME ap3216cstruct ap3216c_dev{int devid;int major;int minor;struct cdev cedv;struct class *class;struct device *device;void *private_data;/*私有数据*/unsigned short ir, als, ps; /* 三个光传感器数据 */
};
static struct ap3216c_dev ap3216cdev;
static int ap3216c_open(struct inode *inode, struct file *filp)
{return 0;
}static int ap3216c_release(struct inode *inode, struct file *filp)
{printk(ap3216c_release\r\n);return 0;
}
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{return 0;
}static struct file_operations ap3216c_fops {.owner THIS_MODULE,.open ap3216c_open,.read ap3216c_read,.release ap3216c_release,
};static int ap3216c_probe(struct i2c_client *client,const struct i2c_device_id *id)
{int ret 0;/*搭建字符设备驱动框架*//*1、创建字符设备*/ap3216cdev.major0;if(ap3216cdev.major){ap3216cdev.devid MKDEV(ap3216cdev.major,0);ret register_chrdev_region(ap3216cdev.devid,AP3216C_CNT,AP3216C_NAME);}else{ret alloc_chrdev_region(ap3216cdev.devid,0,AP3216C_CNT,AP3216C_NAME);ap3216cdev.major MAJOR(ap3216cdev.devid);ap3216cdev.minor MINOR(ap3216cdev.devid);}if(ret 0){printk(ap3216cdev_chrdev_region error!\r\n);goto fail_devid;}printk(ap3216cde major: %d minor: %d\r\n,ap3216cdev.major,ap3216cdev.minor);/*2、注册字符设备*/ap3216cdev.cedv.owner THIS_MODULE;cdev_init(ap3216cdev.cedv,ap3216c_fops);ret cdev_add(ap3216cdev.cedv,ap3216cdev.devid,AP3216C_CNT);if(ret 0){printk(cdev_add error!\r\n);goto fail_cdev;}/*3、自动创建设备节点*/ap3216cdev.class class_create(THIS_MODULE,AP3216C_NAME);if(IS_ERR(ap3216cdev.class)){ret PTR_ERR(ap3216cdev.class);goto fail_class;}ap3216cdev.device device_create(ap3216cdev.class, NULL,ap3216cdev.devid, NULL,AP3216C_NAME);if(IS_ERR(ap3216cdev.device)){ret PTR_ERR(ap3216cdev.device);goto fail_device;}printk(ap3216c_probe\r\n);ap3216cdev.private_data client;return 0;
fail_device:class_destroy(ap3216cdev.class);
fail_class:cdev_del(ap3216cdev.cedv);
fail_cdev:unregister_chrdev_region(ap3216cdev.devid,AP3216C_CNT);
fail_devid:return ret;
}return 0 前一行将此函数的第一 个参数 client 传递给 ap3216cdev 的 private_data 成员变量
当设备和驱动匹配成功后probe函数执行传递的第一个参数就是i2c_client i2c_client 每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client这里用 ap3216cdev结构体中的private_data 成员接收
⑥、remove 函数 ⑦、函数入口出口添加注册i2c_drive 50行调用 i2c_add_driver 来向 Linux 内 核注册 i2c_driver也就是 ap3216c_driver
i2c_add_driver 是一个宏定义如下 #define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver) i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装只有一个参数就是要注册的 i2c_driver。
66行调用 i2c_del_driver 来注销掉前面 注册的 ap3216c_driver函数原型如下 void i2c_del_driver(struct i2c_driver *driver) driver要注销的 i2c_driver。返回值 无。 ⑧、读取AP3216C的N个寄存器 这里先看61行一般需要在 probe 函数里面初始化 I2C 设备要初始化 I2C 设备就必须能够对 I2C 设备寄存器进行读写操作这里就要用到 i2c_transfer 函数了i2c_transfer 函数最终会调用 I2C 适配器中 i2c_algorithm 里面的 master_xfer 函数对于 I.MX6U 而言就是i2c_imx_xfer 这个函数。 i2c_transfer 函数原型如下 int i2c_transfer(struct i2c_adapter *adap struct i2c_msg *msgs int num) adap 所使用的 I2C 适配器 i2c_client 会保存其对应的 i2c_adapter。 msgs I2C 要发送的一个或多个消息。 num 消息数量也就是 msgs 的数量。 返回值 负值失败其他非负值发送的 msgs 数量 从上面介绍i2c_client结构体就能看到有adapter成员在probe函数里面已经获取到ap3216cdev设备的private_data成员中
48行就是把private_data成员中把私有数据提出来就在61行第一个参数中使用i2c_client中的adapter成员
继续看 i2c_transfer 函数msgs 这个参数这是一个 i2c_msg 类型的指针参数 I2C 进行数据收发 说白了就是消息的传递 Linux 内核使用 i2c_msg 结构体来描述一个消息。 i2c_msg 结构体定义 在 include/uapi/linux/i2c.h 文件中 69行从机地址 70行标志
72行read data读数据
79行要发送消息(本 msg)长度
80行发送消息的数据
47行定义结构体数组按照I2C读时序把操作分为两部分 前面两段作为一部分就是50-54行后面两段作另一部分就是56-59行
50-54行发送器件寄存器地址。从i2c_client中获取从机地址标志为0表示发送数据要发送的数据也就是器件寄存器地址寄存器地址长度为1个字节
56-59行读取数据。从i2c_client中获取从机地址标志表示读数据读出来的数据保存在val最后为读取的长度
再回到61行第二个参数就是结构体数组msg第三个参数就是2结构体数组msg的长度
⑨、向AP3216C的N个寄存器写数据 76行这里根据I2C写时序寄存器地址和数据一次可以发完所以只需要一个msg即可 79行寄存器地址保存在b[0]先发寄存器地址
80行把要发送的数据拷贝到b数组里面
⑩、读、写AP3216C一个寄存器 就是调用函数
⑩①、读取AP3216C的数据 ⑩②完善ap3216c_open、read函数 代码如下 #include linux/module.h
#include linux/kernel.h
#include linux/init.h
#include linux/fs.h
#include linux/uaccess.h
#include linux/io.h
#include linux/cdev.h
#include linux/device.h
#include linux/of.h
#include linux/of_address.h
#include linux/of_irq.h
#include linux/slab.h
#include linux/of_address.h
#include linux/of_gpio.h
#include linux/atomic.h
#include linux/timer.h
#include linux/jiffies.h
#include linux/string.h
#include linux/irq.h
#include linux/interrupt.h
#include linux/input.h
#include linux/i2c.h
#include linux/delay.h
#include ap3216c.h#define AP3216C_CNT 1
#define AP3216C_NAME ap3216cstruct ap3216c_dev{int devid;/* 设备号 */int major;int minor;struct cdev cedv;/* cdev */struct class *class;/* 类 */struct device *device;/* 设备 */void *private_data;/*私有数据*/unsigned short ir, als, ps; /* 三个光传感器数据 */
};static struct ap3216c_dev ap3216cdev;/*读取AP3216C的N个寄存器*/
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;/*要发送的寄存器地址长度为1*//*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;
}/*向AP3216C的N个寄存器写数据*/
static int 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;/*msg[0]发送要读取的寄存器首地址*/memcpy(b[1], buf, len);msg.addr client-addr;/*从机地址也就是ap3216c*/msg.flags 0;/*表示为要发送的数据*/msg.buf b;/*要发送的数据寄存器首地址实际的数据*/msg.len len 1;/*要发送的数据长度寄存器首地址实际的数据*/return i2c_transfer(client-adapter,msg,1);
}
/*读取AP3216C一个寄存器*/
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev,u8 reg)
{u8 data0;ap3216c_read_regs(dev,reg,data,1);return data;
}
/*向AP3216C一个寄存器写数据*/
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg , u8 data)
{u8 buf data;ap3216c_write_regs(dev,reg,buf,1);
}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);
}static int ap3216c_open(struct inode *inode, struct file *filp)
{unsigned char value 0;filp-private_data ap3216cdev;printk(ap3216c_open\r\n);/*初始化ap3216c*/ap3216c_write_reg(ap3216cdev,AP3216C_SYSTEMCONG,0x4);/*复位*/mdelay(50);/*50ms*/ap3216c_write_reg(ap3216cdev,AP3216C_SYSTEMCONG,0x3);/*复位*/value ap3216c_read_reg(ap3216cdev,AP3216C_SYSTEMCONG);printk(AP3216C_SYSTEMCONG %#x\r\n,value);return 0;
}static int ap3216c_release(struct inode *inode, struct file *filp)
{printk(ap3216c_release\r\n);return 0;
}
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{long err0;short data[3];struct ap3216c_dev *dev (struct ap3216c_dev *)filp-private_data;/*向应用返回AP3216C的原始数据*/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;
}static struct file_operations ap3216c_fops {.owner THIS_MODULE,.open ap3216c_open,.read ap3216c_read,.release ap3216c_release,
};static int ap3216c_probe(struct i2c_client *client,const struct i2c_device_id *id)
{int ret 0;/*搭建字符设备驱动框架*//*1、创建字符设备*/ap3216cdev.major0;if(ap3216cdev.major){ap3216cdev.devid MKDEV(ap3216cdev.major,0);ret register_chrdev_region(ap3216cdev.devid,AP3216C_CNT,AP3216C_NAME);}else{ret alloc_chrdev_region(ap3216cdev.devid,0,AP3216C_CNT,AP3216C_NAME);ap3216cdev.major MAJOR(ap3216cdev.devid);ap3216cdev.minor MINOR(ap3216cdev.devid);}if(ret 0){printk(ap3216cdev_chrdev_region error!\r\n);goto fail_devid;}printk(ap3216cde major: %d minor: %d\r\n,ap3216cdev.major,ap3216cdev.minor);/*2、注册字符设备*/ap3216cdev.cedv.owner THIS_MODULE;cdev_init(ap3216cdev.cedv,ap3216c_fops);ret cdev_add(ap3216cdev.cedv,ap3216cdev.devid,AP3216C_CNT);if(ret 0){printk(cdev_add error!\r\n);goto fail_cdev;}/*3、自动创建设备节点*/ap3216cdev.class class_create(THIS_MODULE,AP3216C_NAME);if(IS_ERR(ap3216cdev.class)){ret PTR_ERR(ap3216cdev.class);goto fail_class;}ap3216cdev.device device_create(ap3216cdev.class, NULL,ap3216cdev.devid, NULL,AP3216C_NAME);if(IS_ERR(ap3216cdev.device)){ret PTR_ERR(ap3216cdev.device);goto fail_device;}printk(ap3216c_probe\r\n);ap3216cdev.private_data client;return 0;
fail_device:class_destroy(ap3216cdev.class);
fail_class:cdev_del(ap3216cdev.cedv);
fail_cdev:unregister_chrdev_region(ap3216cdev.devid,AP3216C_CNT);
fail_devid:return ret;
}
static int ap3216c_remove(struct i2c_client *client)
{cdev_del(ap3216cdev.cedv);unregister_chrdev_region(ap3216cdev.devid,AP3216C_CNT);device_destroy(ap3216cdev.class, ap3216cdev.devid);class_destroy(ap3216cdev.class);printk(ap3216c_remove\r\n);return 0;
}
/*传统匹配表*/
static struct i2c_device_id ap3216c_id[] {{my,ap3216c,0},{}
};
/*设备树匹配*/
static struct of_device_id ap3216c_of_match[] {{.compatible my,ap3216c},{}
};
/*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 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);
}module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE(GPL);
MODULE_AUTHOR(ba che kai qi lai);
4、ap3216c.c
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include stdio.h
#include unistd.h
#include stdlib.h
#include string.h
#include sys/ioctl.h
#include linux/input.h/*argc:应用程序参数个数argv数组元素个数argv:具体参数,也可以写作char **argv./ap3216cAPP filename ./ap3216cAPP /dev/ap3216c
*/int main(int argc, char *argv[])
{int fd,err;char *filename;unsigned short ir, als, ps, data[3];/*判断命令行输入参数是否正确*/if(argc ! 2){printf(error usage!\r\n);return -1;}/*用指针指向文件*/filename argv[1];/*打开文件*/fd open(filename , O_RDWR);if(fd 0){printf(file open failed\r\n,filename);return -1;}while(1){err read(fd,data,sizeof(data));if(err 0){irdata[0];alsdata[1];psdata[2] ;printf(ap3216c ir %d,als %d,ps %d\r\n,ir,als,ps);}usleep(200000);/*200ms*/}/*关闭文件*/close(fd);return 0;
} 用APP测试驱动 用手接近和用灯照ap3216c传感器数值都会发生变化