0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

电子设计 来源:面板板社区 作者:嵌入式大杂烩 2021-03-26 16:07 次阅读

1、前言

开发板上有AP3216三合一整合型光感测器,看了看出厂SDK包中并未添加相关驱动。本次我们就一起来学习一下。

2、AP3216简介

AP3216C 芯片集成了光强传感器( ALS: Ambient Light Sensor),接近传感器( PS: Proximity Sensor),还有一个红外 LED( IR LED)。

这个芯片设计的用途是给手机之类的使用,比如:返回当前环境光强以便调整屏幕亮度;用户接听电话时,将手机放置在耳边后,自动关闭屏幕避免用户误触碰 。

该芯片通过 I2C 接口与主控制器相连, 如:

pIYBAGBdlbOAIL6PAADkkj4qYmo904.png

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驱动程序
***************************************************************/
#defineAP3216C_CNT1
#defineAP3216C_NAME"ap3216c"

structap3216c_dev{
dev_tdevid;/*设备号*/
structcdevcdev;/*cdev*/
structclass*class;/*类*/
structdevice*device;/*设备*/
structdevice_node*nd;/*设备节点*/
intmajor;/*主设备号*/
void*private_data;/*私有数据*/
unsignedshortir,als,ps;/*三个光传感器数据*/
};

staticstructap3216c_devap3216cdev;

/*
*@description:从ap3216c读取多个寄存器数据
*@param-dev:ap3216c设备
*@param-reg:要读取的寄存器首地址
*@param-val:读取到的数据
*@param-len:要读取的数据长度
*@return:操作结果
*/
staticintap3216c_read_regs(structap3216c_dev*dev,u8reg,void*val,intlen)
{
intret;
structi2c_msgmsg[2];
structi2c_client*client=(structi2c_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("i2crdfailed=%dreg=%06xlen=%d\n",ret,reg,len);
ret=-EREMOTEIO;
}
returnret;
}

/*
*@description:向ap3216c多个寄存器写入数据
*@param-dev:ap3216c设备
*@param-reg:要写入的寄存器首地址
*@param-val:要写入的数据缓冲区
*@param-len:要写入的数据长度
*@return:操作结果
*/
statics32ap3216c_write_regs(structap3216c_dev*dev,u8reg,u8*buf,u8len)
{
u8b[256];
structi2c_msgmsg;
structi2c_client*client=(structi2c_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;/*要写入的数据长度*/

returni2c_transfer(client->adapter,&msg,1);
}

/*
*@description:读取ap3216c指定寄存器值,读取一个寄存器
*@param-dev:ap3216c设备
*@param-reg:要读取的寄存器
*@return:读取到的寄存器值
*/
staticunsignedcharap3216c_read_reg(structap3216c_dev*dev,u8reg)
{
u8data=0;

ap3216c_read_regs(dev,reg,&data,1);
returndata;

#if0
structi2c_client*client=(structi2c_client*)dev->private_data;
returni2c_smbus_read_byte_data(client,reg);
#endif
}

/*
*@description:向ap3216c指定寄存器写入指定的值,写一个寄存器
*@param-dev:ap3216c设备
*@param-reg:要写的寄存器
*@param-data:要写入的值
*@return:无
*/
staticvoidap3216c_write_reg(structap3216c_dev*dev,u8reg,u8data)
{
u8buf=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 :无。
*/
voidap3216c_readdata(structap3216c_dev*dev)
{
unsignedchari=0;
unsignedcharbuf[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=((unsignedshort)buf[1]<< 2) | (buf[0] & 0X03);    
 
 dev->als=((unsignedshort)buf[3]<< 8) | buf[2]; /* 读取ALS传感器的数据     */  
 
    if(buf[4] & 0x40) /* IR_OF位为1,则数据无效    */
  dev->ps=0;
else/*读取PS传感器的数据*/
dev->ps=((unsignedshort)(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*/
return0;
}

/*
*@description:从设备读取数据
*@param-filp:要打开的设备文件(文件描述符)
*@param-buf:返回给用户空间的数据缓冲区
*@param-cnt:要读取的数据长度
*@param-offt:相对于文件首地址的偏移
*@return:读取的字节数,如果为负值,表示读取失败
*/
staticssize_tap3216c_read(structfile*filp,char__user*buf,size_tcnt,loff_t*off)
{
shortdata[3];
longerr=0;

structap3216c_dev*dev=(structap3216c_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));
return0;
}

/*
*@description:关闭/释放设备
*@param-filp:要关闭的设备文件(文件描述符)
*@return:0成功;其他失败
*/
staticintap3216c_release(structinode*inode,structfile*filp)
{
return0;
}

/*AP3216C操作函数*/
staticconststructfile_operationsap3216c_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,成功;其他负值,失败
*/
staticintap3216c_probe(structi2c_client*client,conststructi2c_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)){
returnPTR_ERR(ap3216cdev.class);
}

/*4、创建设备*/
ap3216cdev.device=device_create(ap3216cdev.class,NULL,ap3216cdev.devid,NULL,AP3216C_NAME);
if(IS_ERR(ap3216cdev.device)){
returnPTR_ERR(ap3216cdev.device);
}

ap3216cdev.private_data=client;

return0;
}

/*
*@description:i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
*@param-client:i2c设备
*@return:0,成功;其他负值,失败
*/
staticintap3216c_remove(structi2c_client*client)
{
/*删除设备*/
cdev_del(&ap3216cdev.cdev);
unregister_chrdev_region(ap3216cdev.devid,AP3216C_CNT);

/*注销掉类和设备*/
device_destroy(ap3216cdev.class,ap3216cdev.devid);
class_destroy(ap3216cdev.class);
return0;
}

/*传统匹配方式ID列表*/
staticconststructi2c_device_idap3216c_id[]={
{"iot,ap3216c",0},
{}
};

/*设备树匹配列表*/
staticconststructof_device_idap3216c_of_match[]={
{.compatible="iot,ap3216c"},
{/*Sentinel*/}
};

/*i2c驱动结构体*/
staticstructi2c_driverap3216c_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:无
*/
staticint__initap3216c_init(void)
{
intret=0;

ret=i2c_add_driver(&ap3216c_driver);
returnret;
}

/*
*@description:驱动出口函数
*@param:无
*@return:无
*/
staticvoid__exitap3216c_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:

#ifndefAP3216C_H
#defineAP3216C_H
/***************************************************************
文件名:ap3216creg.h
描述:AP3216C寄存器地址描述头文件
***************************************************************/
#defineAP3216C_ADDR0X1E/*AP3216C器件地址*/
/*AP3316C寄存器*/
#defineAP3216C_SYSTEMCONG0x00/*配置寄存器*/
#defineAP3216C_INTSTATUS0X01/*中断状态寄存器*/
#defineAP3216C_INTCLEAR0X02/*中断清除寄存器*/
#defineAP3216C_IRDATALOW0x0A/*IR数据低字节*/
#defineAP3216C_IRDATAHIGH0x0B/*IR数据高字节*/
#defineAP3216C_ALSDATALOW0x0C/*ALS数据低字节*/
#defineAP3216C_ALSDATAHIGH0X0D/*ALS数据高字节*/
#defineAP3216C_PSDATALOW0X0E/*PS数据低字节*/
#defineAP3216C_PSDATAHIGH0X0F/*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成功;其他失败
*/
intmain(intargc,char*argv[])
{
intfd;
char*filename;
unsignedshortdatabuf[3];
unsignedshortir,als,ps;
intret=0;

if(argc!=2){
printf("ErrorUsage!\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.88

all:
make-C$(KERN_DIR)M=`pwd`modules
$(CROSS_COMPILE)gcc-oap3216cAppap3216cApp.c

clean:
make-C$(KERN_DIR)M=`pwd`modulesclean
rm-rfmodules.order
rm-fap3216cApp

#参考内核源码drivers/char/ipmi/Makefile
#要想把a.c,b.c编译成ab.ko,可以这样指定:
#ab-y:=a.ob.o
#obj-m+=ab.o

obj-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应用开发完全手册》

编辑:hfy

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接近传感器
    +关注

    关注

    5

    文章

    178

    浏览量

    24238
  • I2C
    I2C
    +关注

    关注

    28

    文章

    1346

    浏览量

    120789
  • I2C总线
    +关注

    关注

    8

    文章

    356

    浏览量

    60395
  • 光感测器
    +关注

    关注

    0

    文章

    5

    浏览量

    7117
收藏 人收藏

    评论

    相关推荐

    LabVIEW边干边学系列三合一

    LabVIEW边干边学系列三合一
    发表于 08-12 18:02

    三合一移动电源方案

    本帖最后由 eehome 于 2013-1-5 10:06 编辑 三合一(无充电IC和升压IC)移动电源方案:专利电路,低...
    发表于 11-13 16:13

    LabVIEW边干边学系列三合一

    三合一
    发表于 01-03 09:58

    LabVIEW边干边学系列三合一

    LabVIEW边干边学系列三合一 ,与大家分享.
    发表于 04-05 09:40

    求助移动电源三合一芯片有哪些?各有什么功能

    求助目前主流移动电源三合一芯片有哪些?各有什么功能?是硬件板方案还是软件板方案?谢谢提供
    发表于 07-11 22:11

    移动电源方案究竟硬件三合一还是软件三合一?

    弄清楚。实际上移动电源三合一,分为硬件移动电源三合一和软件移动电源三合一两种技术路线。硬件三合一芯片主要存在的问题是:1.发热严重,因为主
    发表于 09-15 22:57

    同步整流 移动电源三合一问题

    事件,真相如何?要解答这个问题,其根本是要把三合一移动电源解决方案不稳定的原因弄清楚。 实际上移动电源三合一,分为硬件移动电源三合一和软件移动电源三合一两种技术路线。 硬件
    发表于 09-18 14:32

    移动电源硬件三合一方案和软件三合一方案对比

    事件,真相如何?要解答这个问题,其根本是要把三合一移动电源解决方案不稳定的原因弄清楚。实际上移动电源三合一,分为硬件移动电源三合一和软件移动电源三合一两种技术路线。硬件
    发表于 10-21 10:40

    “芯海杯”参考芯片选型方案——移动电源三合一芯片

    ,非同步整流的移动电源三合一芯片相比,具有效率高,发热低的优点。该款芯片内带64 Byte SRAM、1路数字比较、外围配置简单,可在节约电路成本前提下满足多功能设计的需求。该款单片
    发表于 01-08 18:01

    新人提问:移动电源方案软件三合一好还是硬件三合一好?

    新人提问:移动电源方案软件三合一好还是硬件三合一好?刚开始做移动电源这行,时间不长,对于这个移动电源方案软件三合一还是硬件三合一两者的把握
    发表于 10-23 14:05

    三合一温湿传感有什么优点?

    本设计采用SHT11温湿度传感芯片款集成了ADC的环境传感MAX9635,实现温、湿度及光照
    发表于 03-11 06:01

    LabVIEW边干边学系列三合一

    LabVIEW边干边学系列三合一
    发表于 05-16 09:51

    最新电动牙刷三合一智能芯片方案

    `我司自主研发的电动牙刷三合一芯片,智能电动牙刷方案开发,性能强大稳定,成本低!及其他消费类电子方案开发技术支持`
    发表于 09-12 15:41

    移动电源三合一方案都有哪些_移动电源三合一方案哪种最稳定

    、ZS6288/A三合一同步升压移动电源专用芯片,ZS6599三合一移动电源专用芯片这几种方案都是可以的,稳定性有保障。
    发表于 12-29 15:22 1.6w次阅读
    移动电源<b class='flag-5'>三合一</b>方案都有哪些_移动电源<b class='flag-5'>三合一</b>方案哪种最稳定

    AP3216C三合一环境传感器使用指南

    博主最近在调试 AP3216C 三合一环境传感器,由敦南科技推出的一款传感器,支持环境光强度(ALS)、接近距离(PS)和红外线强度(IR)这三个环境参数检测。
    的头像 发表于 04-17 18:01 6206次阅读
    <b class='flag-5'>AP3216C</b><b class='flag-5'>三合一</b>环境传感器使用指南