基于AP3216C芯片的三合一整合型光感测器设计

1、前言
开发板上有ap3216三合一整合型光感测器,看了看出厂sdk包中并未添加相关驱动。本次我们就一起来学习一下。
2、ap3216简介
ap3216c 芯片集成了光强传感器( als: ambient light sensor),接近传感器( ps: proximity sensor),还有一个红外 led( ir led)。
这个芯片设计的用途是给手机之类的使用,比如:返回当前环境光强以便调整屏幕亮度;用户接听电话时,将手机放置在耳边后,自动关闭屏幕避免用户误触碰 。
该芯片通过 i2c 接口与主控制器相连, 如:
3、iic驱动简介
linux下iic有两种驱动方式:一种是按照字符设备驱动方式来驱动iic;另一种是走linux下iic的框架。按照字符设备驱动的方式可以查阅这一篇文章:linux iic 字符设备 驱动例子。
这里我们浅浅地(真的很浅~~)了解学习一下第二种方式,因为找到的ap3216的驱动就是基于iic驱动框架的,哈哈。
iic驱动框架图如
iic驱动框架可大体分为两大部分:
①  i2c 总线驱动:soc 的 i2c 控制器驱动,也叫做 i2c 适配器驱动。
②  i2c 设备驱动:针对具体的 i2c 设备而编写的驱动。
其中,访问抽象层与i2c核心层数据i2c 总线驱动部分;driver驱动层属于i2c设备驱动部分。
上面框图对应的代码调用层次图如:
下面的ap3216驱动可以对照这张图来看看。
4、ap3216实验
我们使用设备树来描述ap3216设备信息,首先我们没有在设备树中添加ap3216相关节点时,我们系统的i2c设备如:
添加i2c pinctrl,板子上ap3216接的是i2c1:
配置寄存器的值都设为0x4001b8b0,这一段是什么意思我们在什么是pinctrl子系统及gpio子系统?这篇笔记中也有写到,就是几个寄存器及其配置。
接下来在i2c1节点下添加ap3216节点:
编译设备树,传到开发板上,重启。此时我们系统的i2c设备有:
可见,新增的ap3216 i2c设备名就是我们设备树里设置的。
下面编写ap3216驱动(以下代码来源于网络):
ap3216.c:
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ap3216creg.h/***************************************************************文件名  : ap3216c.c描述     : ap3216c驱动程序***************************************************************/#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 = ®     /* 读取的首地址 */ 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; #if 0 struct i2c_client *client = (struct i2c_client *)dev->private_data; return i2c_smbus_read_byte_data(client, reg);#endif} /* * @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 ir = 0;      else     /* 读取ir传感器的数据     */  dev->ir = ((unsigned short)buf[1] 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[] = { {iot,ap3216c, 0},   {}}; /* 设备树匹配列表 */static const struct of_device_id ap3216c_of_match[] = { { .compatible = iot,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(pjw); 驱动详解可查阅注释及配合上诉的i2c驱动框架的框图及数据手册理解。
ap3216creg.h:
#ifndef ap3216c_h#define ap3216c_h/***************************************************************文件名  : ap3216creg.h描述     : ap3216c寄存器地址描述头文件***************************************************************/#define ap3216c_addr     0x1e /* ap3216c器件地址  *//* 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 ap3216应用:
ap3216capp.c:
#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 #include #include #include #include /***************************************************************文件名  : ap3216capp.c描述     : ap3216c设备测试app。使用方法  :./ap3216capp /dev/ap3216c***************************************************************/ /* * @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;} 编写makefile,从之前的文章=======拷贝过来修改:
kern_dir = /home/book/100ask_imx6ull-sdk/linux-4.9.88all: make -c $(kern_dir) m=`pwd` modules  $(cross_compile)gcc -o ap3216capp ap3216capp.c clean: make -c $(kern_dir) m=`pwd` modules clean rm -rf modules.order rm -f ap3216capp# 参考内核源码drivers/char/ipmi/makefile# 要想把a.c, b.c编译成ab.ko, 可以这样指定:# ab-y := a.o b.o# obj-m += ab.oobj-m += ap3216.o 编译得到ap3216.ko及ap3216capp,传到板子上运行:
以上就是本次的实验分享,如果文章对你有帮助,欢迎转发,谢谢!
参考资料:
1、https://blog.csdn.net/weixin_34032792/article/details/85582751?utm_medium=distribute.pc_relevant.none-task-blog-blogcommendfrommachinelearnpai2-2.control&dist_request_id=1328690.367.16165120737124801&depth_1-utm_source=distribute.pc_relevant.none-task-blog-blogcommendfrommachinelearnpai2-2.control
2、https://blog.csdn.net/p1279030826/article/details/106459333
3、《嵌入式linux应用开发完全手册》


半导体器件产生多少热量以及如何顺利散发热量
华为P10领衔,荣耀V9、OPPOr9s、一加3T、小米5s、vivoX9拍照手机盘点
爱特梅尔公司扩展CryptoAuthentication产品组合
今年春季苹果没有硬件,谁能想到华为这么皮!
ISO4229安全访问规范分析,ISO4229安全访问漏洞分析
基于AP3216C芯片的三合一整合型光感测器设计
digilent全尺寸SD卡槽模块简介
人工智能主持人的出现 给人类带来了巨大的恐惧
出行充电宝要选什么样的最好,可安全带上飞机的充电宝推荐
Modbus转Profinet网关在污水处理系统中连接PLC和变频器Modbus通信配置案例
为什么USB4接口如此受大家的欢迎
新功能发布 | TSMaster如何实现checksum和rolling counter的本地化支持
疫情下的供应链产生危机 电动化的转型对于传统车企而言并不简单
博越Pro和博越的本质区别对比分析
MAX17633同步降压DC-DC转换器的功能特点介绍
英特尔、索尼和NTT将联手开发6G新技术
MediaTek 展示突破性的 5G NTN 双向卫星通信技术
一文看懂静电和漏电怎么区别
三星Galaxy Note LTE白色版将上市
美国FCC将对供应商是否使用华为和中兴设备的提案进行投票