AP3216C是敦南科技退出的一款三合一环境传感器:
通信接口:
应用:
寄存器表:


寄存器描述:

I2C写寄存器时序:

I2C读寄存器时序:

imx6ull-mmc-npi.dts: 向i2c1 节点追加节点ap3216c
&i2c1 { clock-frequency = <100000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c1>; status = "okay"; #address-cells = <1>;#size-cells = <0>; /* 设备树修改查看是否修改成功,查看内核目录/sys/bus/i2c/devices/看是否存在0x1E地址的设备 */ ap3216c@1E { compatible = "xgj-ap3216c"; // compatible = "fire,i2c_ap3216c"; reg = <0x1E>; interrupt-parent = <&gpio5>; interrupts = <2 IRQ_TYPE_EDGE_BOTH>; //IRQ_TYPE_EDGE_RISING status = "okay"; };};ap3216c接在i2c1下,在i2c1节点下追加设备的子节点ap3216c
设备树编译:
ares@ubuntu:~/work/ebf_linux_kernel-ebf_4.19.35_imx6ul$ cat make_dtb.sh#!/bin/shmake ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs将设备树拷贝系统目录:
debian@npi:~/nfs_root/driver$ cat cp_dtb_to_linux.sh#!/bin/shsudo cp imx6ull-mmc-npi.dtb /usr/lib/linux-image-4.19.35-carp-imx6/重启系统设备树生效:
sudo reboot通过查看系统目录 /sys/firmware/devicetree/base/ 看是否存在ap3216c的设备树节点判定设备树是否生效
ap3216c.h:寄存器相关的定义
#ifndef _AP3216C_H_#define _AP3216C_H_#include<linux/i2c.h>/* System Registers define */#define AP3216C_REG_SYS_CONFIG 0x00 /* Control of basic functions */#define AP3216C_REG_INT_STATUS 0x01 /* ALS and PS interrupt status output */#define AP3216C_REG_INT_CLEAR_MANNER 0x02 /* Auto/semi clear INT pin selector */#define AP3216C_REG_IR_DATA_LOW 0x0A /* Lower byte for IR ADC channel output */#define AP3216C_REG_IR_DATA_HIGH 0x0B /* Higher byte for IR ADC channel output */#define AP3216C_REG_ALS_DATA_LOW 0x0C /* Lower byte for ALS ADC channel output */#define AP3216C_REG_ALS_DATA_HIGH 0x0D /* Higher byte for ALS ADC channel output */#define AP3216C_REG_PS_DATA_LOW 0x0E /* Lower byte for PS ADC channel output */#define AP3216C_REG_PS_DATA_HIGH 0x0F /* Higher byte for PS ADC channel output *//* ALS rAP3216C_egisters define */#define AP3216C_REG_ALS_CONFIG 0x10 /* Control of gain, conversion time of persist for ALS */#define AP3216C_REG_ALS_CALIBRATION 0X19 /* ALS window loss calibration */#define AP3216C_REG_ALS_LOW_THRESHOLD_LOW 0x1A /* Lower byte of ALS low threshold */#define AP3216C_REG_ALS_LOW_THRESHOLD_HIGH 0x1B /* Higher byte of ALS low threshold */#define AP3216C_REG_ALS_HIGH_THRESHOLD_LOW 0x1C /* Lower byte of ALS high threshold */#define AP3216C_REG_ALS_HIGH_THRESHOLD_HIGH 0x1D /* Higher byte of ALS high threshold *//* PS reAP3216C_gisters define */#define AP3216C_REG_PS_CONFIG 0x20 /* Control of gain, integrated time and persist for PS */#define AP3216C_REG_PS_LED_DRIVER 0x21 /* Control of LED pulses number and driver current */#define AP3216C_REG_PS_INT_FORM 0x22 /* Interrupt algorithms style select of PS */#define AP3216C_REG_PS_MEAN_TIME 0x23 /* PS average time selector */#define AP3216C_REG_PS_LED_WAITING_TIME 0x24 /* Control PS LED waiting time */#define AP3216C_REG_PS_CALIBRATION_L 0x28 /* Offset value to eliminate cross talk */#define AP3216C_REG_PS_CALIBRATION_H 0x29 /* Offset value to eliminate cross talk */#define AP3216C_REG_PS_LOW_THRESHOLD_LOW 0x2A /* Lower byte of PS low threshold */#define AP3216C_REG_PS_LOW_THRESHOLD_HIGH 0x2B /* Higher byte of PS low threshold */#define AP3216C_REG_PS_HIGH_THRESHOLD_LOW 0x2C /* Lower byte of PS high threshold */#define AP3216C_REG_PS_HIGH_THRESHOLD_HIGH 0x2D /* Higher byte of PS high threshold */enumsys_config_value { AP3216C_SYS_CONF_POWER_DOWN = 0x000, AP3216C_SYS_CONF_ALS_ACTIVE = 0x001, AP3216C_SYS_CONF_PS_IR_ACTIVE = 0x002, AP3216C_SYS_CONF_ALS_PS_IR_ACTIVE = 0x03, AP3216C_SYS_CONF_SW_RESET = 0x04, /* software reset */ AP3216C_SYS_CONF_ALS_ONCE = 0x05, AP3216C_SYS_CONF_PS_IR_ONCE = 0x06, AP3216C_SYS_CONF_ALS_PS_IRONCE = 0x07,};#endif/* _AP3216C_H_ */使用内核i2c子系统去驱动ap3216c:
staticintap3216c_i2c_write_reg(struct i2c_client *client, uint8_t reg_addr, uint8_t data){int ret = 0;#if 0 structi2c_msgmsgs[2]; msgs[0].addr = client->addr; msgs[0].buf = ®_addr; /* 发送寄存器地址 */ msgs[0].flags = 0; /* I2C方向 :写数据 */ msgs[0].len = sizeof(reg_addr); msgs[1].addr = client->addr; msgs[1].buf = &data; /* 写数据 */ msgs[1].flags = 0; /* I2C方向 :写数据 */ msgs[1].len = sizeof(data);#elseuint8_t buf[2];structi2c_msgmsg; buf[0] = reg_addr; buf[1] = data; msg.addr = client->addr; msg.buf = buf; /* 发送寄存器地址 */ msg.flags = 0; /* I2C方向 :写数据 */ msg.len = 2;#endif ret = i2c_transfer(client->adapter, &msg, 1);if (ret < 0)return ret;elseif (ret != 1)return -EIO;return0;}staticintap3216c_i2c_read_reg(struct i2c_client *client, uint8_t reg_addr, uint8_t *data){int ret = 0;structi2c_msgmsgs[2]; msgs[0].addr = client->addr; msgs[0].buf = ®_addr; /* 发送寄存器地址 */ msgs[0].flags = 0; /* I2C方向 :写数据 */ msgs[0].len = sizeof(reg_addr); msgs[1].addr = client->addr; msgs[1].buf = data; /* 读取寄存器数据 */ msgs[1].flags = I2C_M_RD; /* I2C方向 :读数据 */ msgs[1].len = 1; ret = i2c_transfer(client->adapter, msgs, 2);if (ret < 0)return ret;elseif (ret != 2)return -EIO;return0;}staticintap3216c_init(void){int ret; /* 软复位 */ ret = ap3216c_i2c_write_reg(ap3216c_dev->client, AP3216C_REG_SYS_CONFIG, AP3216C_SYS_CONF_SW_RESET); msleep(50);/* 开启ALS、PS、IR*/ ret = ap3216c_i2c_write_reg(ap3216c_dev->client, AP3216C_REG_SYS_CONFIG, AP3216C_SYS_CONF_ALS_PS_IR_ACTIVE); return ret;}staticintap3216c_read_als(shortint *als){int ret;uint8_t low, high; ret = ap3216c_i2c_read_reg(ap3216c_dev->client, AP3216C_REG_ALS_DATA_LOW, &low); ret = ap3216c_i2c_read_reg(ap3216c_dev->client, AP3216C_REG_ALS_DATA_HIGH, &high); *als = (high << 8) + low;return ret;}staticintap3216c_read_ps(shortint *ps){int ret;uint8_t low, high; ret = ap3216c_i2c_read_reg(ap3216c_dev->client, AP3216C_REG_PS_DATA_LOW, &low); ret = ap3216c_i2c_read_reg(ap3216c_dev->client, AP3216C_REG_PS_DATA_HIGH, &high); *ps = (high << 8) + low;return ret;}staticintap3216c_read_ir(shortint *ir){int ret;uint8_t low, high; ret = ap3216c_i2c_read_reg(ap3216c_dev->client, AP3216C_REG_IR_DATA_LOW, &low); ret = ap3216c_i2c_read_reg(ap3216c_dev->client, AP3216C_REG_IR_DATA_HIGH, &high); *ir = (high << 8) + low;return ret;}熟透于心的linux驱动的框架(套路):
#define AP3216C_DTS_NODE_PATH "xgj_ap3216c"#define AP3216C_DTS_COMPATIBLE "xgj-ap3216c"#define AP3216C_READY_GPIO_NAME "ready-gpios"#define DEV_NAME "ap3216c"structap3216c_data {shortint als; //环境光亮度传感器数据shortint ps; //接近传感器数据shortint ir; //红外LED};structap3216c_device {int irq; /* 中断号 */int gpio;dev_t dev_no; /* 设备号 */structcdevchrdev;structclass *class;structmutexm_lock;wait_queue_head_t wq; /* 等待队列 */structap3216c_datadata;structi2c_client *client;};staticstructap3216c_device *ap3216c_dev;staticint _drv_open (struct inode *node, struct file *file){int ret = 0; file->private_data = ap3216c_dev;if ((ret = ap3216c_init()) != 0) { printk("ap3216c init failed %d\r\n", ret); } printk("%s open\r\n", DEV_NAME);return ret;}staticssize_t _drv_read(struct file *filp, char __user *buf, size_t size, loff_t *offset){int ret = 0;structap3216c_device *tmp_ap3216c = filp->private_data;/* 中断屏蔽 */if (size != sizeof(struct ap3216c_data)) return -EINVAL; ret = ap3216c_read_als(&ap3216c_dev->data.als); ret = ap3216c_read_ps(&ap3216c_dev->data.ps); ret = ap3216c_read_ir(&ap3216c_dev->data.ir);if (copy_to_user(buf, &tmp_ap3216c->data, sizeof(struct ap3216c_data))) { ret = -EFAULT; } else { ret = sizeof(struct ap3216c_data); }return ret;}/* 使驱动支持多路复用IO */static__poll_t _drv_poll(struct file *filp, struct poll_table_struct *wait){__poll_t mask = 0;// mutex_lock(&ap3216c_dev->m_lock);// poll_wait(filp, &ap3216c_dev->wq, wait); // mutex_unlock(&sr501.m_lock);return mask;}staticlong _drv_ioctl(struct file *filp, unsignedint cmd, unsignedlong arg){int ret = 0;return ret;}staticint _drv_release(struct inode *node, struct file *filp){structap3216c_device *tmp_ap3216c = filp->private_data; printk("%s close\n", DEV_NAME);return0;}staticstructfile_operationsap3216c_drv_ops = { .owner = THIS_MODULE, .open = _drv_open, .read = _drv_read,// .poll = _drv_poll, .release = _drv_release,};staticirqreturn_tap3216c_isr(int irq, void *dev){ printk("%s %d %s\n", __FILE__, __LINE__, __FUNCTION__);return IRQ_RETVAL(IRQ_HANDLED); }staticint _driver_probe(struct i2c_client *client, conststruct i2c_device_id *id){ int err = 0;int ret;structdevice *_dev;structdevice_node *_dts_node; ap3216c_dev = (struct ap3216c_device *) kzalloc(sizeof(struct ap3216c_device), GFP_KERNEL);if (!ap3216c_dev) { printk("can't kzalloc ap3216c\n");return -ENOMEM; } _dts_node = client->dev.of_node; if (!_dts_node) { printk("AP3216C dts node can not found!\r\n"); err = -EINVAL; goto out_free_dev; } ap3216c_dev->client = client; printk("AP3216C irq %d !\r\n", client->irq); #if 1 ap3216c_dev->irq = client->irq; err = request_irq(ap3216c_dev->irq, ap3216c_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, DEV_NAME, NULL); /* 申请中断 */if (err) { printk(KERN_INFO"failed to request irq %d\r\n", ap3216c_dev->irq);goto out_free_dev; }/* 内核自动分配设备号 */ err = alloc_chrdev_region(&ap3216c_dev->dev_no, 0, 1, DEV_NAME); if (err < 0) { pr_err("Error: failed to register mbochs_dev, err: %d\n", err);goto out_free_irq; } cdev_init(&ap3216c_dev->chrdev, &ap3216c_drv_ops); err = cdev_add(&ap3216c_dev->chrdev, ap3216c_dev->dev_no, 1);if (err) {goto out_unregister; } ap3216c_dev->class = class_create(THIS_MODULE, DEV_NAME);if (IS_ERR(ap3216c_dev->class)) { err = PTR_ERR(ap3216c_dev->class);goto out_cdev_del; }/* 创建设备节点 */ _dev = device_create(ap3216c_dev->class , NULL, ap3216c_dev->dev_no, NULL, DEV_NAME); if (IS_ERR(_dev)) { /* 判断指针是否合法 */ err = PTR_ERR(_dev);goto out_class_del; }// mutex_init(&ap3216c_dev->m_lock); /* 初始化互斥锁 */ printk("ap3216c probe success\r\n");goto out;out_class_del: class_destroy(ap3216c_dev->class);out_cdev_del: cdev_del(&ap3216c_dev->chrdev);out_unregister: unregister_chrdev_region(ap3216c_dev->dev_no, 1); /* 注销设备 */out_free_irq: free_irq(ap3216c_dev->irq, NULL);out_free_dev: kfree(ap3216c_dev); ap3216c_dev = NULL;out:#endifreturn err;}staticint _driver_remove(struct i2c_client *client){ device_destroy(ap3216c_dev->class, ap3216c_dev->dev_no); class_destroy(ap3216c_dev->class); unregister_chrdev_region(ap3216c_dev->dev_no, 1); /* 注销设备 */ cdev_del(&ap3216c_dev->chrdev); free_irq(ap3216c_dev->irq, NULL); /* 释放中断*/ kfree(ap3216c_dev); printk(KERN_INFO"%s success\n", DEV_NAME);return0;}/* 设备树的匹配列表 */staticstructof_device_iddts_match_table[] = { {.compatible = AP3216C_DTS_COMPATIBLE, }, /* 通过设备树来匹配 */};/* 传统匹配方式 ID 列表 ,即使不使用也要添加,不然probe匹配不成功 */staticconststructi2c_device_idap3216c_id[] = { {AP3216C_DTS_COMPATIBLE, 0}, }; staticstructi2c_driverap3216c_driver = { .probe = _driver_probe, .remove = _driver_remove, .driver = { .name = AP3216C_DTS_COMPATIBLE, .owner = THIS_MODULE, .of_match_table = dts_match_table, /* 通过设备树匹配 */ }, .id_table = ap3216c_id,};/* 入口函数 */staticint __init _driver_init(void){int ret = 0; ret = i2c_add_driver(&ap3216c_driver); /* 注册I2C驱动 */ printk("%s %s\n", DEV_NAME, __FUNCTION__);return ret;}/* 出口函数 */staticvoid __exit _driver_exit(void){ printk("%s driver %s\n", DEV_NAME, __FUNCTION__); i2c_del_driver(&ap3216c_driver); /* 注销I2C驱动 */}module_init(_driver_init);module_exit(_driver_exit);MODULE_AUTHOR("Ares");MODULE_LICENSE("GPL");必须注意的地方:
I2C总线驱动内核缺陷,即使不用i2c_device_id也要加上,不然设备树匹配方式of_device_id 无法匹配成功测试程序:
#include"stdio.h"#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<stdio.h>#include<string.h>#include<sys/ioctl.h>#include<poll.h>#include<stdint.h>#define DEV_NAME "/dev/ap3216c"structap3216c_data {shortint als; /* 环境光亮度传感器数据 */shortint ps; /* 接近传感器数据 */shortint ir; /* 红外LED */};voidsleep_ms(unsignedint ms){structtimevaldelay; delay.tv_sec = 0; delay.tv_usec = ms * 1000; select(0, NULL, NULL, NULL, &delay);}intmain(int argc, char **argv){int fd;int ret;structpollfdfds[1];/* 2. 打开文件 */ fd = open(DEV_NAME, O_RDWR ); // | O_NONBLOCKif (fd < 0) {printf("can not open file %s, %d\n", DEV_NAME, fd);return-1; }structap3216c_datadata;while (1) {if (read(fd, &data, sizeof(data)) == sizeof(data)) {printf("als %d ps %d ir %d\r\n", data.als, data.ps, data.ir); } sleep_ms(1000); } close(fd);printf("%s ok!\n", DEV_NAME);return0;}安装、卸载and执行测试程序:
sudo insmod ap3216c_drv.kosudo ./ap3216c_testsudo rmmod ap3216c_drv.ko