设备树
底板原理图
核心板原理图
由上面原理图可知,AP3216C设备挂在I2C1总线上:
在内核源码目录下搜索:
芯片数据手册
修改设备树
在I2C1控制器节点添加ap3216c设备节点:
驱动代码编写
ap3216c数据手册
根据ap3216c芯片手册编写驱动代码,芯片手册的主要数据如下:
驱动代码参考
//ap3216c.h
#ifndef __AP3216C_H
#define __AP3216C_H
#include <linux/ioctl.h>
typedef struct _ap3216c_data{
unsigned short ir;
unsigned short als;
unsigned short ps;
}ap3216c_data;
#define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */
#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 MAGIC_NUM 'k'
#define AP3216C_PATH "/dev/ap3216c_dev"
#define AP3216C_GET_IR _IOR(MAGIC_NUM , 0, ushort)
#define AP3216C_GET_ALS _IOR(MAGIC_NUM , 1, ushort)
#define AP3216C_GET_PS _IOR(MAGIC_NUM , 2, ushort)
#endif // !__AP3216C_H
//ap3216c.c
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include "ap3216c.h"
#include <linux/uaccess.h>
#define DEV_NAME "ap3216c_dev"
#define DEV_NUM 1
typedef struct ap3216c{
int major;
dev_t dev;
struct cdev cdev;
struct i2c_client *client;
struct class *class;
struct device *device;
}AP3216C_DEV;
static AP3216C_DEV dev;
static int ap3216c_reg_read(struct i2c_client *client, unsigned char reg)
{
int ret;
unsigned char w_buf[1] = {reg};
unsigned char r_buf[1];
struct i2c_msg msg[2] = {
{ client->addr,0,1,w_buf},
{ client->addr,I2C_M_RD,1,r_buf}
};
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if(ret < 0){
printk("i2c_transfer failed: %d\n", ret);
return ret;
}
return r_buf[0];
}
static int ap3216c_reg_write(struct i2c_client *client, unsigned char reg,unsigned char val)
{
int ret;
unsigned char w_buf[2] = {reg,val};
struct i2c_msg msg[1] = {
{ client->addr,0,2,w_buf},
};
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if(ret < 0){
printk("i2c_transfer failed: %d\n", ret);
return ret;
}
return 0;
}
long ap3216c_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
AP3216C_DEV *ap3216c_dev = (AP3216C_DEV *)filp->private_data;
unsigned short data = 0;
unsigned char tmp;
unsigned short __user *argp = (unsigned short *)arg;
if(_IOC_TYPE(cmd) != MAGIC_NUM)
return -ENOTTY;
switch(cmd){
case AP3216C_GET_IR:
tmp = ap3216c_reg_read(ap3216c_dev->client, AP3216C_IRDATALOW );
if(tmp & 0X80){
printk(KERN_WARNING "AP3216C read IR data failed\n");
return -ENOTTY;
}
tmp &= 0X3;
data = ap3216c_reg_read(ap3216c_dev->client, AP3216C_IRDATAHIGH);
data = (data << 2) | tmp;
if(copy_to_user(argp, &data, sizeof(data))){
printk(KERN_WARNING " failed to copy ap3216c data to user\n");
return -EINVAL;
}
break;
case AP3216C_GET_ALS:
tmp = ap3216c_reg_read(ap3216c_dev->client, AP3216C_ALSDATALOW );
data = ap3216c_reg_read(ap3216c_dev->client, AP3216C_ALSDATAHIGH);
data = (data << 8) | tmp;
if(copy_to_user(argp, &data, sizeof(data))){
printk(KERN_WARNING " failed to copy ap3216c data to user\n");
return -EINVAL;
}
break;
case AP3216C_GET_PS:
tmp = ap3216c_reg_read(ap3216c_dev->client, AP3216C_PSDATALOW );
if(tmp & 0X40){
printk(KERN_WARNING "AP3216C read PS data failed\n");
return -ENOTTY;
}
tmp &= 0X0F;
data = ap3216c_reg_read(ap3216c_dev->client, AP3216C_PSDATAHIGH);
data = (data << 4) | tmp;
if(copy_to_user(argp, &data, sizeof(data))){
printk(KERN_WARNING " failed to copy ap3216c data to user\n");
return -EINVAL;
}
break;
default:
printk(KERN_WARNING "Unknown AP3216C CMD: 0x%d\n", cmd);
return -EINVAL;
}
return sizeof(data);
}
static int ap3216c_open(struct inode *node, struct file *filp){
filp->private_data = &dev;
ap3216c_reg_write(dev.client, AP3216C_SYSTEMCONG, 0X04);
msleep(15);
ap3216c_reg_write(dev.client, AP3216C_SYSTEMCONG, 0X03);
msleep(125);
return 0;
}
int ap3216c_release(struct inode *node, struct file *filp){
return 0;
}
static const struct file_operations ap3216c_fops = {
.owner = THIS_MODULE,
.open = ap3216c_open,
.unlocked_ioctl = ap3216c_ioctl,
.release = ap3216c_release,
};
static void ap3216c_reg_init(struct i2c_client *client){
ap3216c_reg_write(dev.client, AP3216C_SYSTEMCONG, 0X04);
msleep(15);
ap3216c_reg_write(dev.client, AP3216C_SYSTEMCONG, 0X03);
msleep(125);
}
static int ap3216c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
dev.client = client;
printk(KERN_INFO "ap3216c_probe\n");
ret = alloc_chrdev_region(&dev.dev, 0, DEV_NUM, DEV_NAME);
dev.major = MAJOR(dev.dev);
if (ret < 0) {
printk(KERN_ERR "alloc_chrdev_region() failed for ap3216c\n");
goto out_err_2;
}
cdev_init(&dev.cdev, &ap3216c_fops);
ret = cdev_add(&dev.cdev, dev.dev, DEV_NUM);
if (ret) {
printk(KERN_ERR "cdev add failed for ap3216c\n");
goto out_err;
}
dev.class = class_create(THIS_MODULE, DEV_NAME);
if (IS_ERR(dev.class)){
printk(KERN_ERR "cdev class creation failed\n");
goto out_err;
}
dev.device = device_create(dev.class, NULL, dev.dev, NULL, DEV_NAME);
if (IS_ERR(dev.device)) {
printk(KERN_ERR "cdev device creation failed\n");
goto put_dev;
}
ap3216c_reg_init(client);
return 0;
put_dev:
class_destroy(dev.class);
out_err:
unregister_chrdev_region(dev.dev, DEV_NUM);
out_err_2:
return -EFAULT;
}
static int ap3216c_remove(struct i2c_client *client)
{
printk(KERN_ERR "ap3216c_remove\n");
device_destroy(dev.class, dev.dev);
class_destroy(dev.class);
unregister_chrdev_region(dev.dev, DEV_NUM);
return 0;
}
static const struct of_device_id ap3216c_dt_ids[] = {
{ .compatible = "LITE-ON,ap3216c", },
{},
};
static const struct i2c_device_id ap3216c_id[] = {
{ "ap3216c", 0 },
{ }
};
static struct i2c_driver ap3216c_driver = {
.driver = {
.name = "ap3216c",
.owner = THIS_MODULE,
.of_match_table = ap3216c_dt_ids,
},
.probe = ap3216c_probe,
.remove = ap3216c_remove,
.id_table = ap3216c_id,
};
static int __init ap3216c_init(void)
{
return i2c_add_driver(&ap3216c_driver);
}
static void __exit ap3216c_exit(void)
{
i2c_del_driver(&ap3216c_driver);
}
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
应用层测试代码参考
//ap3216c_test.c
#include "ap3216c.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd,ret;
unsigned short data[3];
fd = open(AP3216C_PATH, O_RDONLY);
if(fd < 0){
perror("open AP3216C_PATH");
return EXIT_FAILURE;
}
while(1){
ret = ioctl(fd, AP3216C_GET_IR, &data[0]);
if(ret < 0){
fprintf(stderr, "AP3216C_GET_IR failed: %d\n", ret);
close(fd);
return EXIT_FAILURE;
}
printf("AP3216C_GET_IR: %d\n", data[0]);
ret = ioctl(fd, AP3216C_GET_ALS , &data[1]);
if(ret < 0){
fprintf(stderr, "AP3216C_GET_ALS failed: %d\n", ret);
close(fd);
return EXIT_FAILURE;
}
printf("AP3216C_GET_ALS: %d\n", data[1]);
ret = ioctl(fd, AP3216C_GET_PS, &data[2]);
if(ret < 0){
fprintf(stderr, "AP3216C_GET_PS failed: %d\n", ret);
close(fd);
return EXIT_FAILURE;
}
printf("AP3216C_GET_PS: %d\n\n", data[2]);
sleep(2);
}
close(fd);
return 0;
}
Makefile文件参考
ARCH = arch
CROSS_COMPILE = arm-linux-gnueabihf
CC = ${CROSS_COMPILE}-gcc
KERNELDIR := /home/linux_0
CURRENT_PATH := $(shell pwd)
obj-m := ap3216c.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
$(CC) -o test ap3216c_test.c
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
@rm test ap3216c.ko -f
驱动代码验证
将make生成的ap3216c.ko文件与test文件拷贝到开发板,进行功能验证:
标签:AP3216C,dev,client,ret,Linux,驱动,I2C,data,ap3216c From: https://blog.csdn.net/m0_53955705/article/details/145653914