本文最后更新于301 天前,其中的信息可能已经过时,如有错误请发送邮件到273925452@qq.com
AP3216C简介
AP3216C集成了一个光照强度(ALS)、接近距离(PS)和红外线强度(IR)这三个传感器为一体。
AP3216C具有以下特性:
- I2C接口,支持400K波特率
- 宽工作温度范围(-30℃ – +80℃)
- 环境光传感器有16位分辨率
- 接近传感器和红外传感器具有10位分辨率
AP3216C内置的接近传感器可以用于检测是否有物体接近,比如手机可以用来检测耳朵是否接触听筒。也可以用环境光传感器检测光照强度,实现自动背光亮度调节。
AP3216C的I2C地址为0x1E,引脚如下图:
功能使用
AP3216C的常用寄存器列表如下:
接线
imx6ull_pro 扩展板原理图
这里使用I2C1_SCL和I2C1_SDA
AP3216C读写时序
读取AP3216C寄存器:
AP3216C写入寄存器:
编写驱动
修改设备树
设置I2C1引脚
首先设置I2C1引脚的复用功能和电气属性,找到 pinctrl_i2c1 节点:
设备树已经有了无需修改。
pinctrl_i2c2: i2c2grp {
fsl,pins = <
MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0
MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0
>;
};
添加新的i2c设备
找到i2c1子节点的补充描述
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
ap3216c@1e {
compatible = "fire,ap3216c";
reg = <0x1e>;
};
};
重新编译设备树
make dtbs
编译完成后,使用新的设备树启动内核,查看设备树是否有新添加的节点:
查看是否有新添加的总线设备:
编写AP3216C设备驱动
ap3216c_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/mod_devicetable.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/delay.h>
static int major;
static struct class *ap3216c_class;
static struct i2c_client *ap3216c_client;
static int ap3216c_open(struct inode *node, struct file *filp)
{
/* reset: write 0x4 to reg 0 */
i2c_smbus_write_byte_data(ap3216c_client, 0, 0x4);
/* delay for reset */
mdelay(15);
/* enable: write 0x3 to reg 0 */
i2c_smbus_write_byte_data(ap3216c_client, 0, 0x3);
return 0;
}
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
int var, err;
char data[6];
if (size != 6)
return -EINVAL;
/* read IR */
var = i2c_smbus_read_word_data(ap3216c_client, 0xa);
data[0] = (var >> 8) & 0xff;
data[1] = var & 0xff;
/* read light */
var = i2c_smbus_read_word_data(ap3216c_client, 0xc);
data[2] = (var >> 8) & 0xff;
data[3] = var & 0xff;
/* read dis */
var = i2c_smbus_read_word_data(ap3216c_client, 0xe);
data[4] = (var >> 8) & 0xff;
data[5] = var & 0xff;
err = copy_to_user(buf, data, size);
return 0;
}
// 用于和设备树匹配
static const struct of_device_id ap3216c_dt_match[] = {
{
.compatible = "fire,ap3216c",
},
{},
};
// 用于和一般的i2c设备匹配,不管i2c设备来自设备树还是手工创建
static const struct i2c_device_id ap3216c_i2c_id[] = {
{
"ap3216c",
},
{}};
static struct file_operations ap3216c_fops = {
.owner = THIS_MODULE,
.open = ap3216c_open,
.read = ap3216c_read,
};
static int ap3216c_i2c_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id)
{
struct device *result;
ap3216c_client = client;
printk("====%s====\n", __FUNCTION__);
/* register chrdev */
major = register_chrdev(0, "ap3216c", &ap3216c_fops);
ap3216c_class = class_create(THIS_MODULE, "ap3216c_class");
if (IS_ERR(ap3216c_class))
{
printk("ap3216c class_create failed!\n");
unregister_chrdev(major, "ap3216c");
return PTR_ERR(ap3216c_class);
}
result = device_create(ap3216c_class, NULL, MKDEV(major, 0), NULL, "ap3216c"); /* /dev/ap3216c */
if (IS_ERR(result))
{
printk("ap3216c device_create failed\n");
class_destroy(ap3216c_class);
unregister_chrdev(major, "ap3216c");
return -ENODEV;
}
dev_info(&pdev->dev, "=======ap3216c initialized successfully=====\n");
return 0;
}
static int ap3216c_i2c_remove(struct i2c_client *client)
{
printk("======%s=======\n", __FUNCTION__);
device_destroy(ap3216c_class, MKDEV(major, 0));
class_destroy(ap3216c_class);
unregister_chrdev(major, "ap3216c");
return 0;
}
static struct i2c_driver ap3216c_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "ap3216c",
.of_match_table = ap3216c_dt_match,
},
.probe = ap3216c_i2c_probe,
.remove = ap3216c_i2c_remove,
.id_table = ap3216c_i2c_id,
};
static int __init ap3216c_i2c_init(void)
{
int ret;
printk("====%s====\n", __FUNCTION__);
ret = i2c_add_driver(&ap3216c_i2c_driver);
if (ret != 0)
pr_err("Failed to register ap3216c I2C driver: %d\n", ret);
return 0;
}
static void __exit ap3216c_i2c_exit(void)
{
printk("====%s====\n", __FUNCTION__);
i2c_del_driver(&ap3216c_i2c_driver);
}
module_init(ap3216c_i2c_init);
module_exit(ap3216c_i2c_exit);
MODULE_LICENSE("GPL");
ap3216c_test.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/*
* ./ap3216c_test /dev/ap3216c
*
*/
int main(int argc, char *argv[])
{
char data[6];
int fd;
/* 1. 判断参数 */
if (argc != 2)
{
printf("Usage: %s /dev/ap3216c \n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR);
if (fd < 0)
{
printf("open /dev/ap3216c failed\n");
return -1;
}
while (1)
{
read(fd, data, 6);
printf("IR = %d, light = %d, dis = %d\n\n",
(data[0] << 8) | data[1],
(data[2] << 8) | data[3],
(data[4] << 8) | data[5]);
sleep(1);
}
return 0;
}
Makefile
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH, 比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
# 请参考各开发板的高级用户使用手册
KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88 # 板子所用内核源码的目录
all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o ap3216c_test ap3216c_test.c
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order ap3216c_test
# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o
obj-m += ap3216c_drv.o
评论