查看原理图确定引脚号
首先查看原理图得知此器件挂载在I2C1上,并且拥有一个中断引脚,此引脚为GPIO4_IO16
编写设备树
首先查看i2c1的pinctrl是否拥有,我查找到如下所示,是有的 那么这部分就不用我们来写了 继续查看i2c1节点,也是有的,并且是使能的 我们就在它下面写我们自己的ap3216节点
ap3216@1e{
compatible = "jianglin,ap3216";
reg = <0x1e>;
};
编写I2C驱动基本框架
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/poll.h>
#include <linux/signal.h>
#include "ap3216_reg.h"
#define DRV_NAME "ap3216@1e"
#define DEV_NUM 1
struct ap3216dev_info{
int major;
int minor;
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
struct device_node *node;
void* priv_dat;
};
static struct ap3216dev_info ap3216dev;
static int ap3216_write_bytes(struct ap3216dev_info *dev,u8 reg,u8 *buf,u8 len){
u8 b[256];
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)dev->priv_dat;
b[0] = reg;
memcpy(&b[1],buf,len);
msg.addr = client->addr;
msg.flags = 0;
msg.buf = b;
msg.len = len + 1;
return i2c_transfer(client->adapter, &msg, 1);
}
static int ap3216_read_bytes(struct ap3216dev_info *dev,u8 reg,u8 *buf,u8 len){
int ret;
struct i2c_msg msg[2];
struct i2c_client *client = (struct i2c_client *)dev->priv_dat;
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].buf = ®
msg[0].len = 1;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = buf;
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;
}
static int ap3216_read_byte(struct ap3216dev_info *dev,u8 reg){
u8 dat;
ap3216_read_bytes(dev,reg,&dat,1);
return dat;
}
static void ap3216_write_byte(struct ap3216dev_info *dev,u8 reg,u8 val){
u8 dat = val;
ap3216_write_bytes(dev,reg,&dat,1);
}
static int ap3216_drv_open (struct inode *node, struct file *filep){
printk("%s %s line%d\r\n",__FILE__,__FUNCTION__,__LINE__);
filep->private_data = (void*)&ap3216dev;
return 0;
}
static ssize_t ap3216_drv_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){
int ret = 0;
return ret;
}
static int ap3216_drv_close(struct inode *inode, struct file *filp){
printk("%s %s line%d\r\n",__FILE__,__FUNCTION__,__LINE__);
return 0;
}
static struct file_operations ap3216_drv_opr = {
.owner = THIS_MODULE,
.open = ap3216_drv_open,
.read = ap3216_drv_read,
.release = ap3216_drv_close,
};
static int ap3216_drv_probe(struct i2c_client *client, const struct i2c_device_id *id){
printk("%s %s line%d\r\n",__FILE__,__FUNCTION__,__LINE__);
if(ap3216dev.major){
ap3216dev.minor = 0;
ap3216dev.devid = MKDEV(ap3216dev.major,ap3216dev.minor);
register_chrdev_region(ap3216dev.devid,DEV_NUM,DRV_NAME);
}
else{
alloc_chrdev_region(&ap3216dev.devid,0,DEV_NUM,DRV_NAME);
ap3216dev.major = MAJOR(ap3216dev.devid);
ap3216dev.minor = MINOR(ap3216dev.devid);
}
ap3216dev.cdev.owner = THIS_MODULE;
cdev_init(&ap3216dev.cdev,&ap3216_drv_opr);
cdev_add(&ap3216dev.cdev,ap3216dev.devid,DEV_NUM);
ap3216dev.class = class_create(THIS_MODULE,DRV_NAME);
if(IS_ERR(ap3216dev.class)){
return PTR_ERR(ap3216dev.class);
}
ap3216dev.device = device_create(ap3216dev.class,NULL,ap3216dev.devid,NULL,DRV_NAME);
if(IS_ERR(ap3216dev.device)){
return PTR_ERR(ap3216dev.device);
}
ap3216dev.priv_dat = (void*)client;
return 0;
}
static int ap3216_drv_remove(struct i2c_client *client){
printk("%s %s line%d\r\n",__FILE__,__FUNCTION__,__LINE__);
unregister_chrdev_region(ap3216dev.devid,DEV_NUM);
cdev_del(&ap3216dev.cdev);
device_destroy(ap3216dev.class, ap3216dev.devid);
class_destroy(ap3216dev.class);
return 0;
}
static const struct of_device_id ap3216_of_match[] = {
{.compatible = "jianglin,ap3216"},
{}
};
static const struct i2c_device_id ap3216_id[] = {
{"jianglin,ap3216", 0},
{}
};
static struct i2c_driver ap3216_driver = {
.probe = ap3216_drv_probe,
.remove = ap3216_drv_remove,
.driver = {
.owner = THIS_MODULE,
.name =
"ap3216",
.of_match_table = ap3216_of_match,
},
.id_table = ap3216_id,
};
static int __init ap3216_drv_init(void){
int ret;
printk("%s %s line%d\r\n",__FILE__,__FUNCTION__,__LINE__);
ret = i2c_add_driver(&ap3216_driver);
printk("i2c_add_driver ret:%d \n",ret);
return ret;
}
static void __exit ap3216_drv_exit(void){
printk("%s %s line%d\r\n",__FILE__,__FUNCTION__,__LINE__);
i2c_del_driver(&ap3216_driver);
}
module_init(ap3216_drv_init);
module_exit(ap3216_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jianglin");
查看AP3216数据手册
从这里我们可以看出来如下内容 IR_H(0x0B) << 8 | IR_L(0X0a) = 红外值 ALS_H(0x0D) << 8 | ALS_L = 光照值 PS_H(0X0f) << 8 | PS_L(0X0E) = 距离值 还有一个SW_RESET的位,我们详细查看一下说明,这个软复位必须维持10ms以上,否则可能导致异常,也没详细说明必须软件复位,但我们还是做一下复位吧,免得出现什么不知道的问题
完善程序
添加并编写ap3216_reg.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
#define AP3216C_ALS_LOW_TRIGGER_LOW 0x1A
#define AP3216C_ALS_LOW_TRIGGER_HIGH 0x1B
#define AP3216C_ALS_HIGH_TRIGGER_LOW 0x1C
#define AP3216C_ALS_HIGH_TRIGGER_HIGH 0x1D
#define AP3216C_PS_LOW_TRIGGER_LOW 0x2A
#define AP3216C_PS_LOW_TRIGGER_HIGH 0x2B
#define AP3216C_PS_HIGH_TRIGGER_LOW 0x2C
#define AP3216C_PS_HIGH_TRIGGER_HIGH 0x2D
#endif
完善驱动
添加ap3216_init函数
static void ap3216_init(struct ap3216dev_info *dev){
ap3216_write_byte(dev, AP3216C_SYSTEMCONG, 0x04);
mdelay(50);
ap3216_write_byte(dev, AP3216C_SYSTEMCONG, 0X03);
}
添加ap3216dev_data结构体
struct ap3216dev_data{
unsigned short ir;
unsigned short als;
unsigned short ps;
};
struct ap3216dev_info{
...
struct ap3216dev_data dat;
void* priv_dat;
};
添加ap3216_read_data函数
static void ap3216_read_data(struct ap3216dev_info *dev){
unsigned char i =0;
unsigned char buf[6];
for(i = 0; i < 6; i++) {
buf[i] = ap3216_read_byte(dev, AP3216C_IRDATALOW + i);
}
if(buf[0] & 0X80)
dev->dat.ir = 0;
else
dev->dat.ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);
dev->dat.als = ((unsigned short)buf[3] << 8) | buf[2];
if(buf[4] & 0x40)
dev->dat.ps = 0;
else
dev->dat.ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
}
完善ap3216_drv_probe函数
static int ap3216_drv_probe(struct i2c_client *client, const struct i2c_device_id *id){
...
p3216_init(&ap3216dev);
}
完善ap3216_drv_read函数
static ssize_t ap3216_drv_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){
int ret;
short buff[3];
struct ap3216dev_info *dev = (struct ap3216dev_info *)filp->private_data;
ap3216_read_data(dev);
buff[0] = dev->dat.ir;
buff[1] = dev->dat.als;
buff[2] = dev->dat.ps;
ret = copy_to_user(buf, &buff, sizeof(buff));
return 0;
}
编写应用
int main(int argc,char** argv){
short buf[3];
unsigned short ir;
unsigned short als;
unsigned short ps;
if(argc != 2){
printf("Error Usage!\r\n");
return -1;
}
int fd = open(argv[1],O_RDWR | O_NONBLOCK);
if(fd < 0){
printf("open %s err\n",argv[1]);
return -1;
}
while(1){
int ret = read(fd, buf, sizeof(buf));
if(ret == 0) {
ir = buf[0];
als = buf[1];
ps = buf[2];
printf("ir: %d als: %d ps: %d\r\n", ir,als,ps);
}
sleep(1);
}
close(fd);
return 0;
}
用手捂住可以看到距离值和光照值都在发生变化
添加中断
修改设备树
ap3216@1e{
compatible = "jianglin,ap3216";
reg = <0x1e>;
pinctrl-names = <default>;
pinctrl-0 = <&pinctrl_ap3216_int>;
interrupt-parent = <&gpio4>;
interrupts = <16 IRQ_TYPE_EDGE_FALLING>;
};
&iomuxc{
...
imx6ul-evk{
pinctrl_ap3216_int: ap3216_int{
fls,pins = <
MX6UL_PAD_NAND_DQS__GPIO4_IO16 0x00010B0
>;
};
};
};
阅读数据手册
我们可以知道通过读取0x02寄存器,并且&0x02可以判断PS的中断,&0x01可以判断ALS的中断,0x02这个寄存器我们可以不管,因为上面写到,读取数据寄存器会自动清除。 这四个寄存器貌似设置中断阈值的,详细进入看一下 这里看样子是只要超过阈值就会一直触发中断,这个基本上是需要软件处理了,我们只是做个简单小demo,就不用这个,继续往下看PS的中断 在PS阈值寄存器这里看到一个中断模式 默认中断模式是2,看一下模式2的描述 发现这个模式下,只有超过阈值时会触发一次,那么就做一下这个PS的中断
更改驱动
修改ap3216dev_data结构体
struct ap3216dev_data{
char isNew;
unsigned short ir;
unsigned short als;
unsigned short ps;
char ps_status;
};
修改ap3216_drv_probe函数
static int ap3216_drv_probe(struct i2c_client *client, const struct i2c_device_id *id){
int err;
...
ap3216dev.dat.isNew = 0;
ap3216dev.dat.ps_status = 0;
err = request_threaded_irq(client->irq,__ap3216_int_isr,__ap3216_int_threaded_func,IRQF_TRIGGER_FALLING, "ap3216_int", &ap3216dev);
}
添加wq队列
static DECLARE_WAIT_QUEUE_HEAD(wait_queue_head);
添加中断处理函数
static irqreturn_t __ap3216_int_threaded_func(int irq, void *data){
unsigned char int_status = 0;
struct ap3216dev_info* dev = (struct ap3216dev_info*)data;
int_status = ap3216_read_byte(dev,AP3216C_INTSTATUS);
printk("int_status:0x%x\n",int_status);
if(int_status & 0x02){
ap3216_read_data(dev);
dev->dat.isNew = 1;
if(dev->dat.ps >= AP3216C_PS_HIGH_TRIGGER_VALUE){
dev->dat.ps_status = 1;
}
else{
dev->dat.ps_status = 0;
}
}
wake_up_interruptible(&wait_queue_head);
return IRQ_HANDLED;
}
static irqreturn_t __ap3216_int_isr(int irq, void *dev){
printk("%s %s line%d\r\n",__FILE__,__FUNCTION__,__LINE__);
return IRQ_WAKE_THREAD;
}
修改ap3216_drv_read函数
static ssize_t ap3216_drv_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){
int ret;
char ps_status;
struct ap3216dev_info *dev = (struct ap3216dev_info *)filp->private_data;
dev->dat.isNew = 0;
wait_event_interruptible(wait_queue_head,dev->dat.isNew);
ps_status = dev->dat.ps_status;
ret = copy_to_user(buf, &ps_status, 1);
return 0;
}
修改ap3216_init函数
在数据手册中我们看到,高阈值寄存器和低阈值寄存器的计算方式,高字节*4,不就是左移2位吗,那我们写出来一套宏,看起来明了一些
#define AP3216C_PS_HIGH_TRIGGER_VALUE 450
#define AP3216C_PS_LOW_TRIGGER_VALUE 100
#define MAKE_PS_HIGH_BYTE(x) ((x)>>2)
#define MAKE_PS_LOW_BYTE(x) ((x) & 0x03)
修改init函数,在其中设置阈值
static void ap3216_init(struct ap3216dev_info *dev){
...
ap3216_write_byte(dev,AP3216C_PS_LOW_TRIGGER_LOW,
MAKE_PS_LOW_BYTE(AP3216C_PS_LOW_TRIGGER_VALUE));
ap3216_write_byte(dev,AP3216C_PS_LOW_TRIGGER_HIGH,
MAKE_PS_HIGH_BYTE(AP3216C_PS_LOW_TRIGGER_VALUE));
ap3216_write_byte(dev,AP3216C_PS_HIGH_TRIGGER_LOW,
MAKE_PS_LOW_BYTE(AP3216C_PS_HIGH_TRIGGER_VALUE));
ap3216_write_byte(dev,AP3216C_PS_HIGH_TRIGGER_HIGH,
MAKE_PS_HIGH_BYTE(AP3216C_PS_HIGH_TRIGGER_VALUE));
}
修改应用
int main(int argc,char** argv){
unsigned char ps_status;
if(argc != 2){
printf("Error Usage!\r\n");
return -1;
}
int fd = open(argv[1],O_RDWR | O_NONBLOCK);
if(fd < 0){
printf("open %s err\n",argv[1]);
return -1;
}
while(1){
int ret = read(fd, &ps_status, 1);
if(ret == 0) {
printf("have%speople\r\n", ps_status ? " " : " not ");
}
}
close(fd);
return 0;
}
把手靠近传感器上方,再挪开,可以看到输出有人和没人两种状态。
|