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

    文章

    189

    浏览量

    24427
  • I2C
    I2C
    +关注

    关注

    28

    文章

    1487

    浏览量

    123782
  • I2C总线
    +关注

    关注

    8

    文章

    391

    浏览量

    60943
  • 光感测器
    +关注

    关注

    0

    文章

    5

    浏览量

    7135
收藏 人收藏

    评论

    相关推荐

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

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

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

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

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

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

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

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

    鸡、狗、猫三合一声光玩具电路图

    鸡、狗、猫三合一声光玩具电路图
    发表于 05-21 13:31 905次阅读
    鸡、狗、猫<b class='flag-5'>三合一声光</b>玩具电路图

    Vishay推出首款三合一环境传感 VCNL4000

    Vishay推出首款三合一环境传感 VCNL4000 带有I2C总线界面的短距离接近/环境传感
    发表于 09-30 12:05 995次阅读
    Vishay推出首款<b class='flag-5'>三合一</b>环境<b class='flag-5'>光</b>传感<b class='flag-5'>器</b> VCNL4000

    三合一(温湿)传感设计

    本设计采用SHT11温湿度传感芯片款集成了ADC的环境传感MAX9635,实现温、湿度及光照
    发表于 07-16 10:38 4427次阅读
    <b class='flag-5'>三合一</b>(温湿<b class='flag-5'>光</b>)传感<b class='flag-5'>器</b>设计

    环境温湿度及光照三合一传感器设计

    设计了基于I 2 C总线接口的全校准数字式相对湿度、温度传感和环境三合一传感器。其具有数字式输出、免调试、免标定、免外围电路,提供I 2 C
    发表于 12-07 14:25 99次下载
    环境温湿度及光照<b class='flag-5'>三合一传感器</b>设计

    移动电源三合一方案解析_移动电源三合一方案的优点介绍

    本文主要介绍移动电源三合一方案解析_移动电源三合一方案的优点介绍。移动电源三合一方案,是指主控芯片移动电源MCU无需外挂DC-DC芯片和充电
    发表于 12-29 15:00 9978次阅读
    移动电源<b class='flag-5'>三合一</b>方案解析_移动电源<b class='flag-5'>三合一</b>方案的优点介绍

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

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

    MPort 100IE三合一隔离Modbus网关

    电子发烧友网站提供《MPort 100IE三合一隔离Modbus网关.pdf》资料免费下载
    发表于 10-17 09:23 0次下载
    MPort 100IE<b class='flag-5'>三合一</b>隔离<b class='flag-5'>型</b>Modbus网关

    三合一智能音箱演示

    三合一智能音箱演示
    发表于 03-15 19:54 0次下载
    <b class='flag-5'>三合一</b>智能音箱演示

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

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

    三合一智能音箱演示

    三合一智能音箱演示
    发表于 07-06 19:03 0次下载
    <b class='flag-5'>三合一</b>智能音箱演示

    WH4530A三合一 接近传感芯片

    由工采网代理的台湾旺泓-WH4530A是款集成了环境传感(PS)接近传感(ALS)和红外LED灯三合一
    的头像 发表于 02-26 10:58 523次阅读
    WH4530A<b class='flag-5'>三合一</b><b class='flag-5'>光</b>距<b class='flag-5'>感</b> 接近传感<b class='flag-5'>芯片</b>