IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 基于百问网IMX6ULL_PRO开发板驱动AP3216实验 -> 正文阅读

[嵌入式]基于百问网IMX6ULL_PRO开发板驱动AP3216实验

查看原理图确定引脚号

首先查看原理图得知此器件挂载在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); /* 将要写入的数据拷贝到数组 b 里面 */
	 
	msg.addr = client->addr; /* ap3216 地址 */
	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; /* ap3216 地址 */
	msg[0].flags = 0; /* 标记为发送数据 */
	msg[0].buf = &reg; /* 读取的首地址 */
	msg[0].len = 1; /* reg 长度 */

	/* msg[1]读取数据 */
	msg[1].addr = client->addr; /* ap3216c 地址 */
	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"},
	{}
};

/* 传统匹配方式 ID 列表 */
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
/* 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 数据高字节 */

#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){
	/* 初始化 AP3216C */
	ap3216_write_byte(dev, AP3216C_SYSTEMCONG, 0x04); 
	mdelay(50); /* AP3216C 复位最少 10ms */
	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) /* IR_OF 位为 1,则数据无效 */
	dev->dat.ir = 0; 
	else /* 读取 IR 传感器的数据 */
	dev->dat.ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); 

	dev->dat.als = ((unsigned short)buf[3] << 8) | buf[2];/* ALS 数据 */ 

	if(buf[4] & 0x40) /* IR_OF 位为 1,则数据无效 */
	dev->dat.ps = 0; 
	else /* 读取 PS 传感器的数据 */
	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);
	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子系统将引脚设置为普通GPIO4 */
             pinctrl-names = <default>;
             pinctrl-0 = <&pinctrl_ap3216_int>;
             /* 设置引脚中断属性 这样可以直接访问i2c->irq获取终端号 */
             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;
	/* 为1的时候有人 为0没人*/
	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;
	

	//printk("AP3216C_INTSTATUS: 0x%x\n",ap3216_read_byte(&ap3216dev, AP3216C_INTSTATUS));
	int_status = ap3216_read_byte(dev,AP3216C_INTSTATUS);
	printk("int_status:0x%x\n",int_status);
	/* 如果是ps中断的 */
	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;
	/* 每次读取先清0 等待中断置1 */
	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);
	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;
}

把手靠近传感器上方,再挪开,可以看到输出有人和没人两种状态。
在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 19:03:54  更:2022-07-20 19:04:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:30:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码