140.Linux驱动-IIC驱动(基于AP3216C)

#include <linux/types.h>			
#include <linux/module.h>           
#include <linux/init.h>			   
#include <linux/kernel.h>		   
#include <asm/io.h>               
#include <linux/cdev.h>             
#include <linux/device.h>          
#include <asm/uaccess.h>           
#include <linux/fs.h>			   
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>            /*kzalloc*/


#define debug(format, args...) do{\
        printk(KERN_ERR "[ap3216c]%s:%d:" format " \n", __FUNCTION__, __LINE__, ##args);\
}while(0)


#define AP3216C_ADDR        0x1e
#define AP3216C_NAME       "ap3216c" 
#define AP3216C_CNT			1


#define SYSTEM_CONFIG		0x00
#define INT_STATUS			0x01
#define INT_CLR_MANNER		0x02
#define IR_DATA_LOW			0x0A
#define IR_DATA_HIGH		0x0B
#define ALS_DATA_LOW        0x0C
#define ALS_DATA_HIGH       0x0D
#define PS_DATA_LOW         0x0E
#define PS_DATA_HIGH        0x0F

struct ap3216c_reg_info
{
	u8 reg_addr;
	u8 reg_val;
};

static struct i2c_client *ap3216c_client;
struct ap3216c_device {
	struct i2c_client *client;
	int major;
	int minor;
	dev_t devid;			
	struct cdev cdev;   	
	struct class *class;	
	struct device *device;  
	unsigned short ir;
	unsigned short als;
	unsigned short ps;
	bool sys_create;
};


static int ap3216c_i2c_read_regs(struct i2c_client *client,u8 addr,u8 reg,char *buf,u8 len)
{
	int ret;
	struct i2c_msg	msg[2];
	bool retry = false;

	if(!client||!buf)
	{
		debug("%s:Invaild params\n",__FUNCTION__);
		return -EINVAL;
	}

	msg[0].addr  = addr;	
	msg[0].flags = 0; 		
	msg[0].len   = 1;		
	msg[0].buf   = &reg;	
	
	
	msg[1].addr  = addr;		
	msg[1].flags = I2C_M_RD;    
	msg[1].len   = len;			
	msg[1].buf   = buf;         

retry_read:
	ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
	if(ret != ARRAY_SIZE(msg))
	{
		if(!retry)
		{
			debug("ap3216c i2c data read retry (%d)\n",ret);
			msleep(20);
			retry = true;
			goto retry_read;
		}else
		{
			debug("adapter[%s],i2c read addr[0x%02x] reg [0x%02x] failed\n",client->adapter->name,addr,reg);
			return -EIO;
		}
	}

	//debug("%s:client[%s],i2c read addr[0x%02x] reg [0x%02x] sucess\n",__FUNCTION__,client->name,addr,reg);

	return 0;
}


static int ap3216c_i2c_write_regs(struct i2c_client *client,u8 addr,u8 reg,u8 *buf,u8 len)
{
	int ret;
	struct i2c_msg msg[1];
	bool retry = false;
	u8 b[256];
	
	if(!client)
	{
		debug("%s:Invaild params\n",__FUNCTION__);
		return -EINVAL;
	}
	
	b[0] = reg;                 
	memcpy(&b[1],buf,len);      
	
	
	msg[0].addr  = addr;			
	msg[0].flags = 0;	    		
    msg[0].buf   = b; 	            
	msg[0].len   = len + 1;        
	
retry_write:
	ret = i2c_transfer(client->adapter,msg, ARRAY_SIZE(msg));
	if(ret != ARRAY_SIZE(msg))
	{
		if(!retry)
		{
			debug("ap3216c i2c data write retry (%d)\n",ret);
			msleep(20);
			retry = true;
			goto retry_write;
		}else
		{
			debug("adapter[%s],i2c write addr[0x%02x]  failed\n",client->adapter->name,addr);
			return -EIO;
		}
	}

	//debug("client[%s],i2c write addr[0x%02x] reg [0x%02x] sucess\n",client->name,addr,reg);

	return 0;
}


static int ap3216c_i2c_read_reg(struct i2c_client *client,u8 addr,u8 reg,u8 *value)
{
	return ap3216c_i2c_read_regs(client,addr,reg,value,1);
}


static int ap3216c_i2c_write_reg(struct i2c_client *client, u8 addr, u8 reg, u8 value)
{
	u8 buf[1];
	
	buf[0] = value;
	
	return ap3216c_i2c_write_regs(client,addr,reg,buf,1);
}


static void ap3216c_readdata(struct ap3216c_device *data)
{
	u8 buf[6];
	int i = 0;

	struct ap3216c_device *ap3216c_data = i2c_get_clientdata(data->client);

	for(i = 0 ;i < 6;i++)
	{
		ap3216c_i2c_read_reg(ap3216c_data->client, AP3216C_ADDR,IR_DATA_LOW + i, &buf[i]);
	}

	if(buf[0] & 0x80)
	{
		debug("ir ap3216c_data invailed\n");
		ap3216c_data->ir = 0;
	}else
	{
		ap3216c_data->ir  = ((buf[0] & 0x03)| ((unsigned short)buf[1] << 2));
		ap3216c_data->als = (((unsigned short)buf[3]<<8 )| buf[2]); 		
	}

	if(buf[4]&0x40 || buf[5]&0x40)
	{
		debug("ps data invailed\n");
		ap3216c_data->ps = 0;
	}

	ap3216c_data->ps = ((unsigned short)(buf[5] & 0x3F)<<4)|
						  (buf[4] & 0x0F);	
}


static ssize_t ap3216c_read(struct file *file,char __user *buf,size_t cnt, loff_t *loff)
{
	int ret;
	unsigned short raw_data[3];
	
	struct ap3216c_device *data = i2c_get_clientdata(ap3216c_client);

	ap3216c_readdata(data);

	raw_data[0] = data->ir;
	raw_data[1] = data->als;
	raw_data[2] = data->ps;

	debug("ir = %d, als = %d, ps = %d\r\n", raw_data[0], raw_data[1], raw_data[2]);
	
	ret = copy_to_user(buf, raw_data,sizeof(raw_data));
	
	return 0;
}
									
static ssize_t ir_value_show(struct device *dev,struct device_attribute *attr,char *buf)
{
	int ret;
	unsigned char ir_low  = 0x00;
	unsigned char ir_high = 0x00;
	unsigned short ir = 0x00;
	

	struct ap3216c_device *data = i2c_get_clientdata(ap3216c_client);
	if(data == NULL)
	{
		return -EINVAL;
	}

	ret = ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,IR_DATA_LOW ,&ir_low);
	ret |= ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,IR_DATA_HIGH ,&ir_high);

	if(ret != 0 )
	{
		return sprintf(buf, "%d\n", ret);;
	}

	if(ir_low & 0x80)
	{
		debug("ir ap3216c_data invailed\n");
		ir = 0;
	}else
	{
		ir  = ((ir_low & 0x03)| ((unsigned short)ir_high << 2));	
	}

	return sprintf(buf,"%d\n",ir);
	
}

static DEVICE_ATTR(ir_value, S_IRUGO, ir_value_show, NULL);

static ssize_t als_value_show(struct device *dev,struct device_attribute *attr,char *buf)
{
	int ret;
	unsigned char ir_low  = 0x00;
	unsigned char als_low  = 0x00;
	unsigned char als_high = 0x00;
	unsigned short als = 0x00;
	

	//struct ap3216c_device *data = dev_get_drvdata(dev);
	struct ap3216c_device *data = i2c_get_clientdata(ap3216c_client);
	if(data == NULL)
	{
		return -EINVAL;
	}
	
	ret  = ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,IR_DATA_LOW ,&ir_low);
	ret |= ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,ALS_DATA_LOW ,&als_low);
	ret |= ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,ALS_DATA_HIGH ,&als_high);

	if(ret != 0 )
	{
		return sprintf(buf, "%d\n", ret);;
	}

	if(ir_low & 0x80)
	{
		debug("ir ap3216c_data invailed\n");
		return sprintf(buf,"%d\n",ir_low);
	}else
	{
		als = (((unsigned short)als_high<<8 )|als_low); 
	}
	
	return sprintf(buf,"%d\n",als);
}

static DEVICE_ATTR(als_value, S_IRUGO, als_value_show, NULL);


static ssize_t ps_value_show(struct device *dev,struct device_attribute *attr,char *buf)
{
	int ret;
	unsigned char ps_low  = 0x00;
	unsigned char ps_high = 0x00;
	unsigned short ps = 0x00;
	
	struct ap3216c_device *data = i2c_get_clientdata(ap3216c_client);

	if(data == NULL)
	{
		debug("ps data is null\n");
		return -EINVAL;
	}


	ret = ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,PS_DATA_LOW ,&ps_low);
	ret |= ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,PS_DATA_HIGH ,&ps_high);

	if(ret != 0 )
	{
		return sprintf(buf, "%d\n", ret);;
	}
	
	if(ps_low & 0x40 || ps_high & 0x40)
	{
		debug("ps data invailed\n");
		return -EINVAL;
	}else
	{
		ps = ((unsigned short)(ps_high & 0x3F)<<4)|
							  (ps_low & 0x0F);	
	}
		
	return sprintf(buf,"%d\n",ps);
}

static DEVICE_ATTR(ps_value, S_IRUGO, ps_value_show, NULL);

static const struct attribute *ap3216c_attributes[] = {
	&dev_attr_ir_value.attr,
	&dev_attr_als_value.attr,
	&dev_attr_ps_value.attr,
	NULL
};
							
static int ap3216c_open(struct inode *inode, struct file *file)
{
   
	return 0;
}


static int ap3216c_release(struct inode *inode, struct file *file)
{

	return 0;
}


static struct file_operations ap3216c_fops = {
	.owner   = THIS_MODULE,
	.read    = ap3216c_read,
	.open    = ap3216c_open,
	.release = ap3216c_release,
};

static int register_ap3216c_cdev(struct ap3216c_device * data)
{
	int ret;

	if(data->major)
	{
		
		data->devid = MKDEV(data->major,data->minor);
		ret = register_chrdev_region(data->devid,AP3216C_CNT,AP3216C_NAME);
		if(ret < 0)
		{
			debug("++klz register ap3216c region failed\n");
		}
	}else
	{
		alloc_chrdev_region(&data->devid,0,AP3216C_CNT,AP3216C_NAME);
		data->major = MAJOR(data->devid);
		data->minor = MINOR(data->devid);
	}

	data->cdev.owner = THIS_MODULE;
	cdev_init(&data->cdev,&ap3216c_fops);
	ret = cdev_add(&data->cdev,data->devid,AP3216C_CNT);
	if(ret < 0)
	{
		debug("ap3216c cdev add failed\n");
		goto fail_cdev_add;
	}

	data->class = class_create(THIS_MODULE,AP3216C_NAME);
	if(IS_ERR(data->class))
	{
		debug("class create failed\n");
		goto fail_class_create;
	}

	data->device = device_create(data->class,NULL,data->devid,NULL,AP3216C_NAME);
	if(IS_ERR(data->device))
	{
		debug(
"device create failed\n");
		goto fail_device_create;
	}

	return 0;

fail_device_create:
	class_destroy(data->class);
	cdev_del(&data->cdev);
	unregister_chrdev_region(data->devid,AP3216C_CNT);
	return -1;
fail_class_create:
	cdev_del(&data->cdev);
	unregister_chrdev_region(data->devid,AP3216C_CNT);
	return -1;
fail_cdev_add:
	unregister_chrdev_region(data->devid,AP3216C_CNT);
	return -1;
}

static int ap3216c_init(struct ap3216c_device *data)
{
	int ret; 

	
	ret = ap3216c_i2c_write_reg(data->client, AP3216C_ADDR,SYSTEM_CONFIG,0x04);
	if(ret < 0)
	{
		debug("sw reset ap3216c failed\n");
		return -1;
	}
	
	
	mdelay(50);
	
	ret = ap3216c_i2c_write_reg(data->client, AP3216C_ADDR,SYSTEM_CONFIG,0x03);
	if(ret < 0)
	{
		debug("setting ap3216c failed\n");
		return -1;
	}

	return 0;
}


static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	int ret;
	struct ap3216c_device *data = NULL;

	data = kmalloc(sizeof(struct ap3216c_device), GFP_KERNEL);
	//data =devm_kmalloc(&client->dev,sizeof(ap3216c_device), GFP_KERNEL);
	if(!data)
	{
		printk(KERN_ERR"++klz,kmalloc ap3216c failed,return err\n");
		return -ENOMEM;
	}

	i2c_set_clientdata(client, data);
	
	data->client     = client;
    ap3216c_client   = client;
	data->sys_create = false;

	ret = register_ap3216c_cdev(data);
	if(ret < 0)
	{
		debug("++klz register ap3216c fops failed\n");
		return ret;
	}

	ret = ap3216c_init(data);
	if(ret < 0)
	{
		debug("++klz ap3216c init failed\n");
		return ret;
	}

	ret = sysfs_create_files(&data->device->kobj,ap3216c_attributes);
	if(ret)
	{
		debug("failed to create sys files\n");
		return -EINVAL;
	}

	data->sys_create = true;
	
	debug("ap3216c init sucess\n");
	return 0;
}

static int ap3216c_remove(struct i2c_client *client)
{
	struct ap3216c_device * data = i2c_get_clientdata(client);

	cdev_del(&data->cdev);
	unregister_chrdev_region(data->devid,AP3216C_CNT);
	device_destroy(data->class,data->devid);
	class_destroy(data->class);
	
	kfree(data);
	return 0;
}


static const struct of_device_id ap3216c_of_match[] = {
	{ .compatible = "alientek,ap3216c", },
	{/*Sentinel*/}
};
MODULE_DEVICE_TABLE(of, ap3216c_of_match);


static const struct i2c_device_id ap3216c_id[] = {
	{ "alientek,ap3216c", 0 },
	{ }
};
	
MODULE_DEVICE_TABLE(i2c, ap3216c_id);

static struct i2c_driver ap3216c_driver = {
	.driver = {
		.name	= "ap3216c_driver",
		.owner	= THIS_MODULE,
		.of_match_table = of_match_ptr(ap3216c_of_match),
	},
	.probe		= ap3216c_probe,
	.remove		= ap3216c_remove,
	.id_table	= ap3216c_id,
};

module_i2c_driver(ap3216c_driver);

MODULE_AUTHOR("klz <1255713178@qq.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ap3216c driver of atk imx6ull");
I2C接口距离传感器ap3216c读写Verilog驱动源码Quartus工程文件,FPGA型号Cyclone4E系列中的EP4CE10F17C8,Quartus版本18.0。 module ap3216c_top( //global clock input sys_clk , // 系统时钟 input sys_rst_n , // 系统复位 //ap3216c interface output ap_scl , // i2c时钟线 inout ap_sda , // i2c数据线 //user interface output [3:0] led , // led灯接口 output [5:0] sel , // 数码管位选 output [7:0] seg_led // 数码管段选 ); //parameter define parameter SLAVE_ADDR = 7'h1e ; // 器件地址 parameter BIT_CTRL = 1'b0 ; // 字地址位控制参数(16b/8b) parameter CLK_FREQ = 26'd50_000_000; // i2c_dri模块的驱动时钟频率(CLK_FREQ) parameter I2C_FREQ = 18'd250_000 ; // I2C的SCL时钟频率 //wire define wire clk ; // I2C操作时钟 wire i2c_exec ; // i2c触发控制 wire [15:0] i2c_addr ; // i2c操作地址 wire [ 7:0] i2c_data_w; // i2c写入的数据 wire i2c_done ; // i2c操作结束标志 wire i2c_ack ; // I2C应答标志 0:应答 1:未应答 wire i2c_rh_wl ; // i2c读写控制 wire [ 7:0] i2c_data_r; // i2c读出的数据 wire [15:0] als_data ; // ALS的数据 wire [ 9:0] ps_data ; // PS的数据 //***************************************************** //** main code //***************************************************** //例化i2c_dri,调用IIC协议 i2c_dri #( .SLAVE_ADDR (SLAVE_ADDR), // slave address从机地址,放此处方便参数传递 .CLK_FREQ (CLK_FREQ ), // i2c_dri模块的驱动时钟频率(CLK_FREQ) .I2C_FREQ (I2C_FREQ ) // I2C的SCL时钟频率 ) u_i2c_dri( //global clock .clk (sys_clk ), // i2c_dri模块的驱动时钟(CLK_FREQ) .rst_n (sys_rst_n ), // 复位信号 //i2c interface .i2c_exec
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值