全球最实用的IT互联网信息网站!

AI人工智能P2P分享&下载搜索网页发布信息网站地图

当前位置:诺佳网 > 电子/半导体 > MEMS传感器 >

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

时间:2021-03-26 16:07

人气:

作者:admin

标签: 接近  光感测器  I2C总线  I2C 

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

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* 
温馨提示:以上内容整理于网络,仅供参考,如果对您有帮助,留下您的阅读感言吧!
相关阅读
本类排行
相关标签
本类推荐

CPU | 内存 | 硬盘 | 显卡 | 显示器 | 主板 | 电源 | 键鼠 | 网站地图

Copyright © 2025-2035 诺佳网 版权所有 备案号:赣ICP备2025066733号
本站资料均来源互联网收集整理,作品版权归作者所有,如果侵犯了您的版权,请跟我们联系。

关注微信