当前位置:网站首页>Based on Baiwen imx6ull_ Ap3216 experiment driven by Pro development board
Based on Baiwen imx6ull_ Ap3216 experiment driven by Pro development board
2022-07-25 13:17:00 【Jiang Lin】
Catalog
Check the schematic diagram to determine the pin number
First, check the schematic diagram to know that this device is attached to I2C1 On , And it has an interrupt pin , This pin is GPIO4_IO16
Write a device tree
First of all to see i2c1 Of pinctrl Owned or not , I found the following , There is Then we don't need to write this part 
Keep looking at i2c1 node , There are , And it is enabled 
We write our own under it ap3216 node
[email protected]1e{
compatible = "jianglin,ap3216";
reg = <0x1e>;
};
To write I2C Drive basic framework
#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 "[email protected]"
#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; /* Register first address */
memcpy(&b[1],buf,len); /* Copy the data to be written to the array b Inside */
msg.addr = client->addr; /* ap3216 Address */
msg.flags = 0;
msg.buf = b; /* Data buffer to write to */
msg.len = len + 1; /* The length of data to write */
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 Address */
msg[0].flags = 0; /* Mark as send data */
msg[0].buf = ® /* The first address to read */
msg[0].len = 1; /* reg length */
/* msg[1] Reading data */
msg[1].addr = client->addr; /* ap3216c Address */
msg[1].flags = I2C_M_RD; /* Mark to read data */
msg[1].buf = buf; /* Read data buffer */
msg[1].len = len; /* The length of data to read */
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__);
/* Set up private data */
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 there is a master device number */
if(ap3216dev.major){
ap3216dev.minor = 0;
ap3216dev.devid = MKDEV(ap3216dev.major,ap3216dev.minor);
register_chrdev_region(ap3216dev.devid,DEV_NUM,DRV_NAME);
}
else{
/* If there is no master equipment number */
alloc_chrdev_region(&ap3216dev.devid,0,DEV_NUM,DRV_NAME);
ap3216dev.major = MAJOR(ap3216dev.devid);
ap3216dev.minor = MINOR(ap3216dev.devid);
}
/* Initialize and add a character device */
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"},
{
}
};
/* Traditional matching ID list */
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");
see AP3216 Data manual
From here we can see the following
IR_H(0x0B) << 8 | IR_L(0X0a) = Infrared value
ALS_H(0x0D) << 8 | ALS_L = Illumination value
PS_H(0X0f) << 8 | PS_L(0X0E) = Distance value 
One more SW_RESET Bit , Let's take a detailed look at the instructions , This soft reset must be maintained 10ms above , Otherwise, it may lead to exceptions , It didn't specify that the software must be reset , But let's do a reset , So as to avoid any unknown problems 
Perfect the program
Add and write ap3216_reg.h
#ifndef AP3216C_H
#define AP3216C_H
/* AP3316C register */
#define AP3216C_SYSTEMCONG 0x00 /* Configuration register */
#define AP3216C_INTSTATUS 0X01 /* Interrupt status register */
#define AP3216C_INTCLEAR 0X02 /* Interrupt clear register */
#define AP3216C_IRDATALOW 0x0A /* IR Data low byte */
#define AP3216C_IRDATAHIGH 0x0B /* IR Data high byte */
#define AP3216C_ALSDATALOW 0x0C /* ALS Data low byte */
#define AP3216C_ALSDATAHIGH 0X0D /* ALS Data high byte */
#define AP3216C_PSDATALOW 0X0E /* PS Data low byte */
#define AP3216C_PSDATAHIGH 0X0F /* PS Data high byte */
#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
Perfect drive
add to ap3216_init function
static void ap3216_init(struct ap3216dev_info *dev){
/* initialization AP3216C */
ap3216_write_byte(dev, AP3216C_SYSTEMCONG, 0x04);
mdelay(50); /* AP3216C Reset at least 10ms */
ap3216_write_byte(dev, AP3216C_SYSTEMCONG, 0X03);
}
add to ap3216dev_data Structure
struct ap3216dev_data{
/* Three optical sensor data */
unsigned short ir;
unsigned short als;
unsigned short ps;
};
struct ap3216dev_info{
...
struct ap3216dev_data dat;
void* priv_dat;
};
add to ap3216_read_data function
static void ap3216_read_data(struct ap3216dev_info *dev){
unsigned char i =0;
unsigned char buf[6];
/* Cycle through all sensor data */
for(i = 0; i < 6; i++) {
buf[i] = ap3216_read_byte(dev, AP3216C_IRDATALOW + i);
}
if(buf[0] & 0X80) /* IR_OF Position as 1, The data is invalid */
dev->dat.ir = 0;
else /* Read IR Sensor data */
dev->dat.ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);
dev->dat.als = ((unsigned short)buf[3] << 8) | buf[2];/* ALS data */
if(buf[4] & 0x40) /* IR_OF Position as 1, The data is invalid */
dev->dat.ps = 0;
else /* Read PS Sensor data */
dev->dat.ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
}
perfect ap3216_drv_probe function
static int ap3216_drv_probe(struct i2c_client *client, const struct i2c_device_id *id){
...
p3216_init(&ap3216dev);
}
perfect ap3216_drv_read function
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;
}
Write applications
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) {
/* Data read successful */
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;
}
Covering with your hand, you can see that the distance value and illumination value are changing 
Add interrupt
Modify the device tree
[email protected]1e{
compatible = "jianglin,ap3216";
reg = <0x1e>;
/* use pinctrl The subsystem sets the pin to normal GPIO4 */
pinctrl-names = <default>;
pinctrl-0 = <&pinctrl_ap3216_int>;
/* Set pin interrupt attribute This allows direct access i2c->irq Get the terminal number */
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
>;
};
};
};
Read the data book
We can know by reading 0x02 register , also &0x02 Can be judged PS The interrupt ,&0x01 Can be judged ALS The interrupt ,0x02 We can ignore this register , Because it says , Reading the data register will automatically clear .
These four registers seem to set the interrupt threshold , Take a detailed look 
It seems that interrupts will be triggered as long as the threshold is exceeded , This basically needs software processing , We just do a simple little demo, Don't use this , Keep looking down PS The interrupt 
stay PS The threshold register sees an interrupt mode here 
The default interrupt mode is 2, Take a look at the mode 2 Description of 
Found that in this mode , It will only be triggered once when the threshold is exceeded , Then do this PS The interrupt 
Change drive
modify ap3216dev_data Structure
struct ap3216dev_data{
/* Whether the data is new */
char isNew;
/* Three optical sensor data */
unsigned short ir;
unsigned short als;
unsigned short ps;
/* by 1 When someone by 0 no one */
char ps_status;
};
modify ap3216_drv_probe function
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);
}
add to wq queue
static DECLARE_WAIT_QUEUE_HEAD(wait_queue_head);
Add interrupt handling function
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);
/* If it is ps Interrupted */
if(int_status & 0x02){
ap3216_read_data(dev);
dev->dat.isNew = 1; // There's new data
if(dev->dat.ps >= AP3216C_PS_HIGH_TRIGGER_VALUE){
dev->dat.ps_status = 1;// Someone is approaching
}
else{
dev->dat.ps_status = 0;// People left
}
}
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;
}
modify ap3216_drv_read function
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;
/* Clear every time you read 0 Wait for the interrupt to set 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;
}
modify ap3216_init function

In the data book we see , Calculation method of high threshold register and low threshold register , High byte *4, It's just moving left 2 Place? , Let's write a set of macros , It seems clear
#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)
modify init function , Set the threshold
static void ap3216_init(struct ap3216dev_info *dev){
...
/* Set the distance minimum trigger threshold */
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));
/* Set the maximum trigger threshold of distance */
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));
}
Modify application
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) {
/* Data read successful */
printf("have%speople\r\n", ps_status ? " " : " not ");
}
}
close(fd);
return 0;
}
Put your hand over the sensor , Move away again , You can see that there are two states of output, with and without people .
边栏推荐
- [today in history] July 25: IBM obtained the first patent; Verizon acquires Yahoo; Amazon releases fire phone
- 【CSDN 年终总结】结束与开始,一直在路上—— “1+1=王”的2021总结
- Django 2 ----- 数据库与Admin
- How to realize the configuration method of user password free login?
- [figure attack and Defense] backdoor attacks to graph neural networks (sacmat '21)
- 外围系统调用SAP的WebAPI接口
- Vim技巧:永远显示行号
- Detailed explanation of the training and prediction process of deep learning [taking lenet model and cifar10 data set as examples]
- yum和vim须掌握的常用操作
- Cyberspace Security penetration attack and defense 9 (PKI)
猜你喜欢

R language GLM generalized linear model: logistic regression, Poisson regression fitting mouse clinical trial data (dose and response) examples and self-test questions

Shell常用脚本:判断远程主机的文件是否存在

G027-OP-INS-RHEL-04 RedHat OpenStack 创建自定义的QCOW2格式镜像

Design and principle of thread pool

Eccv2022 | transclassp class level grab posture migration
![[CSDN year-end summary] end and start, always on the way -](/img/51/a3fc5eba0eeb22b600260ee81ff9e6.png)
[CSDN year-end summary] end and start, always on the way - "2021 summary of" 1+1= Wang "

0710RHCSA

Introduction to jupyter notebook
![[figure attack and Defense] backdoor attacks to graph neural networks (sacmat '21)](/img/d2/6be99fd194c66e4f60af38c6e52c93.png)
[figure attack and Defense] backdoor attacks to graph neural networks (sacmat '21)

程序的内存布局
随机推荐
ESP32-C3 基于Arduino框架下Blinker点灯控制10路开关或继电器组
Basic knowledge of binary tree
Zero basic learning canoe panel (15) -- CAPL output view
Emqx cloud update: more parameters are added to log analysis, which makes monitoring, operation and maintenance easier
The programmer's father made his own AI breast feeding detector to predict that the baby is hungry and not let the crying affect his wife's sleep
并发编程之AQS
In order to improve efficiency, there are various problems when using parallelstream
Concurrent programming - memory model JMM
【GCN-RS】Learning Explicit User Interest Boundary for Recommendation (WWW‘22)
Shell常用脚本:检测某域名、IP地址是否通
VIM tip: always show line numbers
web安全入门-UDP测试与防御
Excel录制宏
Online Learning and Pricing with Reusable Resources: Linear Bandits with Sub-Exponential Rewards: Li
【AI4Code】CodeX:《Evaluating Large Language Models Trained on Code》(OpenAI)
[six articles talk about scalablegnn] around www 2022 best paper PASCA
Introduction to web security UDP testing and defense
pytorch创建自己的Dataset加载数据集
Word style and multi-level list setting skills (II)
Cv2.resize function reports an error: error: (-215:assertion failed) func= 0 in function ‘cv::hal::resize‘