【Linux驱动模块】十、AP3216C感光传感器
【Linux驱动模块】十、AP3216C感光传感器
本文最后更新于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


测试验证

💡商业转载请联系作者获得授权,非商业转载请注明出处。
协议(License):署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)。
使用这些免费资源的时候应遵守版权意识,切勿非法利用,售卖,尊重原创内容知识产权。未经允许严禁转载。

评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇