IMX6ULL开发板使用了一个AP3216C的三合一环境光传感器,此设备连接在SOC的I2C1 接口,原理图如下:
AP3216C 用到了 I2C1 接口,I.MX6U-ALPHA 开发板上的 I2C1 接口使用到了 UART4_TXD 和UART4_RXD,因此肯定要在设备树里面设置这两个 IO。如果要用到 AP3216C 的中断功能的话还需初始化 AP_INT 对应的 GIO1_IO01 这个 IO,本次实验我们不使用中断功能。因此只需要设置 UART4_TXD 和 UART4_RXD 这两个 IO,NXP 其实已经将他这两个 IO 设置好了,打开 imx6ull-alientek-emmc.dts,然后找到如下内容:
pinctrl_i2c1: i2c1grp {
fsl,pins = <
MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
>;
};
pinctrl_i2c1 就是 I2C1 的 IO 节点,这里将 UART4_TXD 和 UART4_RXD 这两个 IO 分别复用为 I2C1_SCL 和 I2C1_SDA,电气属性都设置为 0x4001b8b0。
AP3216C 是连接到 I2C1 上的,因此需要在 i2c1 节点下添加 ap3216c 的设备子节点,在imx6ull-alientek-emmc.dts 文件中找到 i2c1 节点,将NXP 官方的 EVK 开发板上的mag3110和fxls8471子节点删除,添加ap3216c设备子节点。
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
ap3216c@1e {
compatible = "alientek,ap3216c";
reg = <0x1e>;
};
};
设备树修改完成以后使用“make dtbs”重新编译一下,然后使用新的设备树启动 Linux 内核。进入/sys/bus/i2c/devices。
图中的“0-001e”就是 ap3216c 的设备目录,“1e”就是 ap3216c 器件地址。进入0-001e 目录,可以看到“name”文件,name 问价就保存着此设备名字,在这里就是“ap3216c”。
新建iic文件夹,在文件夹下新建ap3216c.c 和 ap3216creg.h两个文件,ap3216c.c 为 AP3216C 的
驱动代码,ap3216creg.h 是 AP3216C 寄存器头文件。
在ap3216creg.h 文件中输入如下内容
#ifndef AP3216C_H
#define AP3216C_H
/* 寄存器地址 */
#define AP3216C_SYSTEMCONG 0X00
#define AP3216C_INTSTATUS 0X01
#define AP3216C_INTCLEAR 0X02
#define AP3216C_IRDATALOW 0X0A
#define AP3216C_IRDATAHIGH 0X0B
#define AP3216C_ALSDATALOW 0X0C
#define AP3216C_ALSDATAHIGH 0X0D
#define AP3216C_PSDATALOW 0X0E
#define AP3216C_PSDATAHIGH 0X0F
#endif
ap3216c.c 驱动程序编写步骤如下
第一步:添加头文件
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ap3216creg.h"
第二步:编写驱动入口函数和驱动出口函数
/*驱动入口函数*/
static int __init ap3216c_init(void)
{
return 0;
}
/*驱动出口函数*/
static void __exit ap3216c_exit(void)
{
}
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("supersmart");
第三步:添加和删除ap3216c_driver
static struct i2c_driver ap3216c_driver = {
};
/*驱动入口函数*/
static int __init ap3216c_init(void)
{
int ret = 0;
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("supersmart");
i2c_driver 注册函数原型:
int i2c_register_driver(struct module *owner,
struct i2c_driver *driver)
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
i2c_driver注销函数原型:
void i2c_del_driver(struct i2c_driver *driver)
第四步:完成ap3216c_driver
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
return 0;
}
static int ap3216c_remove(struct i2c_client *client)
{
return 0;
}
/* 无设备树的时候匹配 ID 表 */
static struct i2c_device_id ap3216c_id[] = {
{"alientek,ap3216c", 0},
{}
};
/* 设备树所使用的匹配表*/
static const struct of_device_id ap3216c_of_match[] = {
{
.compatible = "alientek,ap3216c",
},
{}
};
static struct i2c_driver ap3216c_driver = {
.probe = ap3216c_probe,//当 I2C 设备和 I2C 驱动匹配成功以后 probe 函数就会执行,这些和 platform 驱动一样,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;
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("supersmart");
第五步:完成“probe”和“remove”函数
1、ap3216c_probe// i2c 驱动的 probe 函数,当驱动与设备匹配以后此函数就会执行
2、ap3216c_remove // i2c 驱动的 remove 函数,移除 i2c 驱动此函数会执行
#define AP3216C_CNT 1
#define AP3216C_NAME "ap3216c"
struct ap3216c_dev
{
int major;
int minor;
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
void *private_data;
unsigned short ir, als, ps;
};
static struct ap3216c_dev ap3216cdev;
static const struct file_operations ap3216c_fops = {
};
/*
* @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)
{
int ret = 0;
printk("ap3216c_probe!\r\n");
/* 搭建字符设备驱动框架,在/dev/下 */
/* 1、构建设备号 */
ap3216cdev.major = 0; /* 由系统分配主设备号 */
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("ap3216c chrdev_region err!\r\n");
goto fail_devid;
}
printk("ap3216c major=%d, minor=%d\r\n", ap3216cdev.major, ap3216cdev.minor);
/* 2、注册设备 */
ap3216cdev.cdev.owner = THIS_MODULE;
cdev_init(&ap3216cdev.cdev, &ap3216c_fops);
ret = cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);
if (ret < 0)
{
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;
}
/* 4、创建设备 */
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;
}
ap3216cdev.private_data = client;
return 0;
fail_device:
class_destroy(ap3216cdev.class);
fail_class:
cdev_del(&ap3216cdev.cdev);
fail_cdev:
unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
fail_devid:
return ret;
}
/*
* @description : i2c 驱动的 remove 函数,移除 i2c 驱动此函数会执行
* @param – client : i2c 设备
* @return : 0,成功;其他负值,失败
*/
static int ap3216c_remove(struct i2c_client *client)
{
/* 1,删除字符设备 */
cdev_del(&ap3216cdev.cdev);
/* 2,注销设备号 */
unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
/* 3,摧毁设备 */
device_destroy(ap3216cdev.class, ap3216cdev.devid);
/* 4,摧毁类 */
class_destroy(ap3216cdev.class);
return 0;
}
/* 无设备树的时候匹配 ID 表 */
static struct i2c_device_id ap3216c_id[] = {
{"alientek,ap3216c", 0},
{}
};
/* 设备树所使用的匹配表*/
static const struct of_device_id ap3216c_of_match[] = {
{
.compatible = "alientek,ap3216c",
},
{}
};
static struct i2c_driver ap3216c_driver = {
.probe = ap3216c_probe, //当 I2C 设备和 I2C 驱动匹配成功以后 probe 函数就会执行,这些和 platform 驱动一样,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;
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("supersmart");
第六步:完成file_operations 文件操作集 ap3216c_fops结构体成员变量
1、ap3216c_open //打开设备
2、ap3216c_read //从设备读取数据
3、ap3216c_release //关闭/释放设备
#define AP3216C_CNT 1
#define AP3216C_NAME "ap3216c"
struct ap3216c_dev
{
int major;
int minor;
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
void *private_data;
unsigned short ir, als, ps;
};
static struct ap3216c_dev ap3216cdev;
/*
* @description : 打开设备
* @param – inode : 传递给驱动的 inode
* @param - filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
* 一般在 open 的时候将 private_data 指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int ap3216c_open(struct inode *inode, struct file *filp)
{
unsigned char value = 0;
filp->private_data = &ap3216cdev;
return 0;
}
/*
* @description : 从设备读取数据
* @param – filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param – offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
long err = 0;
short data[3];
struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int ap3216c_release(struct inode *inode, struct file *filp)
{
printk("ap3216c_release\r\n");
return 0;
}
/* AP3216C 操作函数 */
static const struct file_operations ap3216c_fops = {
.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)
{
int ret = 0;
printk("ap3216c_probe!\r\n");
/* 搭建字符设备驱动框架,在/dev/下 */
/* 1、构建设备号 */
ap3216cdev.major = 0; /* 由系统分配主设备号 */
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("ap3216c chrdev_region err!\r\n");
goto fail_devid;
}
printk("ap3216c major=%d, minor=%d\r\n", ap3216cdev.major, ap3216cdev.minor);
/* 2、注册设备 */
ap3216cdev.cdev.owner = THIS_MODULE;
cdev_init(&ap3216cdev.cdev, &ap3216c_fops);
ret = cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);
if (ret < 0)
{
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;
}
/* 4、创建设备 */
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;
}
ap3216cdev.private_data = client;
return 0;
fail_device:
class_destroy(ap3216cdev.class);
fail_class:
cdev_del(&ap3216cdev.cdev);
fail_cdev:
unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
fail_devid:
return ret;
}
/*
* @description : i2c 驱动的 remove 函数,移除 i2c 驱动此函数会执行
* @param – client : i2c 设备
* @return : 0,成功;其他负值,失败
*/
static int ap3216c_remove(struct i2c_client *client)
{
/* 1,删除字符设备 */
cdev_del(&ap3216cdev.cdev);
/* 2,注销设备号 */
unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
/* 3,摧毁设备 */
device_destroy(ap3216cdev.class, ap3216cdev.devid);
/* 4,摧毁类 */
class_destroy(ap3216cdev.class);
return 0;
}
/* 无设备树的时候匹配 ID 表 */
static struct i2c_device_id ap3216c_id[] = {
{"alientek,ap3216c", 0},
{}
};
/* 设备树所使用的匹配表*/
static const struct of_device_id ap3216c_of_match[] = {
{
.compatible = "alientek,ap3216c",
},
{}
};
static struct i2c_driver ap3216c_driver = {
.probe = ap3216c_probe, //当 I2C 设备和 I2C 驱动匹配成功以后 probe 函数就会执行,这些和 platform 驱动一样,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;
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("supersmart");
第七步:添加AP3216C的读写寄存器函数
1、ap3216c_read_regs //从 ap3216c 读取多个寄存器数据
2、ap3216c_write_regs // 向 ap3216c 多个寄存器写入数据
3、ap3216c_read_reg //读取 ap3216c 指定寄存器值,读取一个寄存器
4、ap3216c_write_reg //向 ap3216c 指定寄存器写入指定的值,写一个寄存器
重点使用的函数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 数量。
#define AP3216C_CNT 1
#define AP3216C_NAME "ap3216c"
struct ap3216c_dev
{
int major;
int minor;
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
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)
{
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 = ® /* 表示为要发送的数据,也就是寄存器地址*/
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; /* 要读取的寄存器长度*/
return i2c_transfer(client->adapter, msg, 2);
}
/*
* @description : 向 ap3216c 多个寄存器写入数据
* @param – dev : ap3216c 设备
* @param – reg : 要写入的寄存器首地址
* @param – val : 要写入的数据缓冲区
* @param – len : 要写入的数据长度
* @return : 操作结果
*/
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;
memcpy(&b[1], buf, len);
/* msg[0]发送要读取的寄存器首地址 */
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 : 打开设备
* @param – inode : 传递给驱动的 inode
* @param - filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
* 一般在 open 的时候将 private_data 指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int ap3216c_open(struct inode *inode, struct file *filp)
{
unsigned char value = 0;
filp->private_data = &ap3216cdev;
return 0;
}
/*
* @description : 从设备读取数据
* @param – filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param – offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
long err = 0;
short data[3];
struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int ap3216c_release(struct inode *inode, struct file *filp)
{
printk("ap3216c_release\r\n");
return 0;
}
/* AP3216C 操作函数 */
static const struct file_operations ap3216c_fops = {
.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)
{
int ret = 0;
printk("ap3216c_probe!\r\n");
/* 搭建字符设备驱动框架,在/dev/下 */
/* 1、构建设备号 */
ap3216cdev.major = 0; /* 由系统分配主设备号 */
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("ap3216c chrdev_region err!\r\n");
goto fail_devid;
}
printk("ap3216c major=%d, minor=%d\r\n", ap3216cdev.major, ap3216cdev.minor);
/* 2、注册设备 */
ap3216cdev.cdev.owner = THIS_MODULE;
cdev_init(&ap3216cdev.cdev, &ap3216c_fops);
ret = cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);
if (ret < 0)
{
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;
}
/* 4、创建设备 */
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;
}
ap3216cdev.private_data = client;
return 0;
fail_device:
class_destroy(ap3216cdev.class);
fail_class:
cdev_del(&ap3216cdev.cdev);
fail_cdev:
unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
fail_devid:
return ret;
}
/*
* @description : i2c 驱动的 remove 函数,移除 i2c 驱动此函数会执行
* @param – client : i2c 设备
* @return : 0,成功;其他负值,失败
*/
static int ap3216c_remove(struct i2c_client *client)
{
/* 1,删除字符设备 */
cdev_del(&ap3216cdev.cdev);
/* 2,注销设备号 */
unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
/* 3,摧毁设备 */
device_destroy(ap3216cdev.class, ap3216cdev.devid);
/* 4,摧毁类 */
class_destroy(ap3216cdev.class);
return 0;
}
/* 无设备树的时候匹配 ID 表 */
static struct i2c_device_id ap3216c_id[] = {
{"alientek,ap3216c", 0},
{}
};
/* 设备树所使用的匹配表*/
static const struct of_device_id ap3216c_of_match[] = {
{
.compatible = "alientek,ap3216c",
},
{}
};
static struct i2c_driver ap3216c_driver = {
.probe = ap3216c_probe, //当 I2C 设备和 I2C 驱动匹配成功以后 probe 函数就会执行,这些和 platform 驱动一样,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;
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("supersmart");
第八步:完善ap3216c驱动
1、增加读取 ap3216c 的数据函数
2、在ap3216c_open函数中增加初始化ap3216c部分代码
3、在 ap3216c_read函数中增加向应用层返回ap3216c原始数据代码
#define AP3216C_CNT 1
#define AP3216C_NAME "ap3216c"
struct ap3216c_dev
{
int major;
int minor;
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
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)
{
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 = ® /* 表示为要发送的数据,也就是寄存器地址*/
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; /* 要读取的寄存器长度*/
return i2c_transfer(client->adapter, msg, 2);
}
/*
* @description : 向 ap3216c 多个寄存器写入数据
* @param – dev : ap3216c 设备
* @param – reg : 要写入的寄存器首地址
* @param – val : 要写入的数据缓冲区
* @param – len : 要写入的数据长度
* @return : 操作结果
*/
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;
memcpy(&b[1], buf, len);
/* msg[0]发送要读取的寄存器首地址 */
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 buf[6] = {0};
unsigned char i = 0;
/*循环的读取数据*/
for (i = 0; i < 6; i++)
{
buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);
}
if (buf[0] & 0x80) /*为真表示IR 和PS数据无效*/
{
dev->ir = 0;
dev->ps = 0;
}
else
{
dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0x03);
dev->ps = (((unsigned short)buf[5] & 0x3F) << 4) | ((unsigned short)buf[4] & 0x0F);
}
dev->als = ((unsigned short)buf[3] << 8) | buf[2];
}
/*
* @description : 打开设备
* @param – inode : 传递给驱动的 inode
* @param - filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
* 一般在 open 的时候将 private_data 指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int ap3216c_open(struct inode *inode, struct file *filp)
{
unsigned char value = 0;
filp->private_data = &ap3216cdev;
/*初始化AP3216C*/
ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x4); /* 复位 */
mdelay(50);
ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x3); /* 复位 */
value = ap3216c_read_reg(&ap3216cdev, AP3216C_SYSTEMCONG);
printk("AP3216C_SYSTEMCONG=%#x\r\n", value);
return 0;
}
/*
* @description : 从设备读取数据
* @param – filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param – offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
long err = 0;
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;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int ap3216c_release(struct inode *inode, struct file *filp)
{
printk("ap3216c_release\r\n");
return 0;
}
/* AP3216C 操作函数 */
static const struct file_operations ap3216c_fops = {
.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)
{
int ret = 0;
printk("ap3216c_probe!\r\n");
/* 搭建字符设备驱动框架,在/dev/下 */
/* 1、构建设备号 */
ap3216cdev.major = 0; /* 由系统分配主设备号 */
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("ap3216c chrdev_region err!\r\n");
goto fail_devid;
}
printk("ap3216c major=%d, minor=%d\r\n", ap3216cdev.major, ap3216cdev.minor);
/* 2、注册设备 */
ap3216cdev.cdev.owner = THIS_MODULE;
cdev_init(&ap3216cdev.cdev, &ap3216c_fops);
ret = cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);
if (ret < 0)
{
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;
}
/* 4、创建设备 */
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;
}
ap3216cdev.private_data = client;
return 0;
fail_device:
class_destroy(ap3216cdev.class);
fail_class:
cdev_del(&ap3216cdev.cdev);
fail_cdev:
unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
fail_devid:
return ret;
}
/*
* @description : i2c 驱动的 remove 函数,移除 i2c 驱动此函数会执行
* @param – client : i2c 设备
* @return : 0,成功;其他负值,失败
*/
static int ap3216c_remove(struct i2c_client *client)
{
/* 1,删除字符设备 */
cdev_del(&ap3216cdev.cdev);
/* 2,注销设备号 */
unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
/* 3,摧毁设备 */
device_destroy(ap3216cdev.class, ap3216cdev.devid);
/* 4,摧毁类 */
class_destroy(ap3216cdev.class);
return 0;
}
/* 无设备树的时候匹配 ID 表 */
static struct i2c_device_id ap3216c_id[] = {
{"alientek,ap3216c", 0},
{}
};
/* 设备树所使用的匹配表*/
static const struct of_device_id ap3216c_of_match[] = {
{
.compatible = "alientek,ap3216c",
},
{}
};
static struct i2c_driver ap3216c_driver = {
.probe = ap3216c_probe, //当 I2C 设备和 I2C 驱动匹配成功以后 probe 函数就会执行,这些和 platform 驱动一样,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;
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("supersmart");
ap3216cAPP.c 应用测试程序内容如下
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
*argc:应用程序参数个数
* argv[]:具体的参数内容,字符串形式
* ./ap3216cAPP
* ./ap3216cAPP /dev/ap3216c
*/
int main(int argc, char *argv[])
{
int fd, err;
char *filename;
unsigned short data[3];
unsigned short ir, ps, als;
if (argc != 2)
{
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if (fd < 0)
{
printf("file %s open failed!\r\n", filename);
return -1;
}
while (1)
{
err = read(fd, data, sizeof(data));
if (err == 0)
{
ir = data[0];
als = data[1];
ps = data[2];
printf("AP3216C ir=%d,als=%d,ps=%d\r\n",ir,als,ps);
}
usleep(500000);/* 延时200ms */
}
close(fd);
return 0;
}
略
将上一小节编译出来 ap3216c.ko 和 ap3216cAPP 这两个文件拷贝到 rootfs/lib/modules/4.1.15目录中,重启开发板,进入到目录 lib/modules/4.1.15 中。加载 ap3216c.ko 这个驱动模块。
输入如下命令读取数据:
用手电筒照射数据变化如下: