资讯详情

Linux驱动开发—内核I2C驱动详解

Linux驱动开发-内核I2C驱动

    • I2C驱动文件结构
    • I2C数据传输过程
      • i2c_transfer
      • i2c_msg
    • I2C通信常用的接口函数(旧版)
      • 快速读写界面函数:(连续读写)
      • 常用的读操作接口函数:
      • 常用的写作操作接口函数:
      • i2c_smbus_read_byte_data
      • i2c_smbus_write_byte_data
    • I2C驱动框架
    • ap3216c光电传感器驱动
    • bit_xfer()函数
    • i2c-algo-bit.c文件
    • 结语

I2C驱动文件结构

  • i2c-core.c I2C 核心功能及/proc/bus/i2c*接口;

  • i2c-dev.c 它不是为特定的设备设计的,而是为一般设备提供的read()、write()和ioctl()等接口,但是不负责具体时序或者业务,具体的时序和业务由上层业务去实现;

  • i2c.h 对i2c_adapter、i2c_algorithm、i2c_driver和i2c_client定义了这四个数据结构。

  • busses文件夹 一些主要制造商I2C由制造商编写的适配器驱动;例如S3C2410的驱动i2c-s3c2410.c;

  • algos文件夹 实现了一些I2C总线适配器的通信方式;

  • I2C内核驱动适配器:负责初始化和使能I2C,然后生成I2C并加载适配器结构体I2C核心。

  • I2C适配器结构体(struct i2c_adapter):提供统一的I2C描述实际数据接口的数据接口 IIC 物理硬件。

  • I2C算法结构体(struct i2c_algorithm):提供I2C底层对接实际控制器的读写操作接口产生特定的 SOC 硬件(在i2c_adapter在结构体中使用) IIC 模块产生通信波形的方法。

  • I2C传参结构体(struct i2c_msg):提供I2C传参接口收发通信。

  • I2C结构体的初始化(struct i2c_driver):描述一个 I2C 我们需要自己编写设备驱动。

  • i2c_client:对应一个实际硬件上的 I2C device,不需要我们编写,数据结构由核心根据设备注册信息自动生成,设备驱动根据硬件的实际情况填写。 在这里插入图片描述

【driver 由普通驱动工程师负责,【i2c 核心层】由 Linux 提供,【i2c 芯片原厂负责以下内容。

I2C数据传输过程

在I2C通信过程中有两个重要的结构(函数),一个是i2c_msgi2c_transfer函数。 使用统一的接口i2c_transfer,不管用哪个芯片,他最终都会调用i2c_transfer,选择一个I2C从I2c设备读取数据,每次传输每次传输的数据i2c_msg表示结构体。

i2c_transfer

在I2C使用驱动i2c_transfer来传输I2C这个函数通过数据I2C适配器算法master_xfer进行操作:

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { 
             adap->algo->master_xfer(adap, msgs, num); ////调用适配器算法 } 

i2c_msg

i2c_msg结构体示:

struct i2c_msg { 
             __u16 addr;                 /* IIC设备的基地址,7位 */     __u16 flags;               /*操作标志位*/     __u16 len;                /* 读写数据的长度 */     __u8 *buf;               /* 有数据的缓冲区 */     #define I2C_M_RD 0x0001 /* 设置此标志位表示此次信i2c控制器是处于接收方,否则就是发送方 */
    #define I2C_M_TEN 0x0010 /* 设置了这个标志位表示从设备的地址是10bit */
    #define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */
    /* makes only sense in kernelspace */
    /* userspace buffers are copied anyway */
    #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
    #define I2C_M_NO_RD_ACK 0x0800 /* 设置这个标志位表示在读操作中主机不用ACK */
    #define I2C_M_IGNORE_NAK 0x1000 /* 设置这个标志意味当前i2c_msg忽略I2C器件的ack和nack信号 */
    #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
    #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
    #define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
};

一个i2c_msg结构的变量,代表着一次单方向的传输,故我们如果需要进行读写操作就需要两个i2c_msg结构的变量,分别通过对结构体成员变量flag的赋值来确定传输方向,主要赋值函数如下:

static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,unsigned short flags,char read_write, u8 command, int size,union i2c_smbus_data *data)
{ 
        

	unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
	unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
	int num = read_write == I2C_SMBUS_READ ? 2 : 1;
	int i;
	u8 partial_pec = 0;
	int status;
	struct i2c_msg msg[2] = { 
        
		{ 
        
			/* msg[0]为发送要读取的首地址 */
			.addr = addr,
			.flags = flags,
			.len = 1,	//表示寄存器地址字节长度,是以byte为单位
			.buf = msgbuf0,
		}, { 
        
			/* msg[1]读取数据 */
			.addr = addr,
			.flags = flags | I2C_M_RD,
			.len = 0,	//表示期望读到数据的字节长度(寄存器长度),是以byte为单位
			.buf = msgbuf1,	//将读取到的数据保存在buffer中
		},
	};

	/*····省略一些switch循环,*/
}

由于读操作分为了写和读两个方向,因此可以采用两个i2c_msg结构体变量,并使用结构体数组进行保存。

I2C通讯常用的接口函数(老版本)

快速读写接口函数:(连续读写)

i2c_smbus_read_byte();
i2c_smbus_write_byte();

常用的读操作接口函数:

i2c_smbus_read_byte_data();

i2c_smbus_read_word_data();

i2c_smbus_read_block_data();

常用的写操作接口函数:

i2c_smbus_write_byte_data();

i2c_smbus_write_word_data();

i2c_smbus_write_block_data();

i2c_smbus_read_byte_data

s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command);

从指定的i2c从设备的空间中读取的数据

client: i2c客户端 -- 指定的要读取数据的i2c从设备

command:i2c从设备内部的地址空间 -- 指定要从设备的哪个地址空间中读取数据

成功:读取到的数据(一个字节数据)

失败:负数

i2c_smbus_write_byte_data

s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value);

向指定的i2c从设备中的空间写入的数据

client:i2c客户端 -- 指定要写入数据的i2c从设备

command:i2c从设备内部的地址空间 -- 把数据写入到哪个地址空间中

value:要写入的数据(一个字节数据)

成功:0

失败:负数

I2C驱动框架

/* i2c 驱动的 probe 函数 */
static int xxx_probe(struct i2c_client *client,const struct i2c_device_id *id) { 
        
	/* 函数具体程序 */
	return 0;
}
/* i2c 驱动的 remove 函数 */
static int xxx_remove(struct i2c_client *client) { 
        
	/* 函数具体程序 */
	return 0;
}
/* 传统匹配方式 ID 列表 */
static const struct i2c_device_id xxx_id[] = { 
        
	{ 
        "xxx", 0},
	{ 
        }
};
/* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = { 
        
	{ 
         .compatible = "xxx" },
	{ 
         /* Sentinel */ }
};
/* i2c 驱动结构体 */
static struct i2c_driver xxx_driver = { 
        
	.probe = xxx_probe,
	.remove = xxx_remove,
	.driver = { 
        
		.owner = THIS_MODULE,
		.name = "xxx",
		.of_match_table = xxx_of_match,
	},
	.id_table = xxx_id,
};
/* 驱动入口函数 */
static int __init xxx_init(void) { 
        
	int ret = 0;
	ret = i2c_add_driver(&xxx_driver);
	return ret;
}
/* 驱动出口函数 */
static void __exit xxx_exit(void) { 
        
	i2c_del_driver(&xxx_driver);
}

module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");

ap3216c光电传感器驱动

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/workqueue.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/input/aw9523_cfg.h>

#define AP3216C_CNT 1
#define AP3216C_NAME "ap3216c"

struct ap3216c_dev { 
        
	dev_t devid;				/* 设备号 */
	struct cdev cdev;			/* cdev */
	struct class *class;		/* 类 */
	struct device *device;		/* 设备 */
	struct device_node	*nd; 	/* 设备节点 */
	int major;					/* 主设备号 */
	void *private_data;			/* 私有数据 */
	unsigned short ir, als, ps;		/* 三个光传感器数据 */
};
static struct ap3216c_dev ap3216cdev;
/* 从ap3216c读取多个寄存器数据 */
static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len){ 
        
	int ret;
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->private_data;

	/* msg[0]为发送要读取的首地址 */
	msg[0].addr = client->addr;			/* ap3216c地址 */
	msg[0].flags = 0;					/* 标记为发送数据 */
	msg[0].buf = &reg;					/* 读取的首地址 */
	msg[0].len = 1;						/* reg长度*/

	/* msg[1]读取数据 */
	msg[1].addr = client->addr;			/* ap3216c地址 */
	msg[1].flags = I2C_M_RD;			/* 标记为读取数据*/
	msg[1].buf = val;					/* 读取数据缓冲区 */
	msg[1].len = len;					/* 要读取的数据长度*/

	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) { 
        
		ret = 0;
	} else { 
        
		printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
		ret = -EREMOTEIO;
	}
	return ret;
}
/* 向ap3216c多个寄存器写入数据 */
static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len){ 
        
	u8 b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	
	b[0] = reg;					/* 寄存器首地址 */
	memcpy(&b[1],buf,len);		/* 将要写入的数据拷贝到数组b里面 */
		
	msg.addr = client->addr;	/* ap3216c地址 */
	msg.flags = 0;				/* 标记为写数据 */

	msg.buf = b;				/* 要写入的数据缓冲区 */
	msg.len = len + 1;			/* 要写入的数据长度 */

	return i2c_transfer(client->adapter, &msg, 1);
}
/* 读取ap3216c指定寄存器值,读取一个寄存器 */
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg){ 
        
	u8 data = 0;
	ap3216c_read_regs(dev, reg, &data, 1);
	return data;

#if 0
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	return i2c_smbus_read_byte_data(client, reg);
#endif
}
/* 向ap3216c指定寄存器写入指定的值,写一个寄存器 */
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data){ 
        
	u8 buf = 0;
	buf = data;
	ap3216c_write_regs(dev, reg, &buf, 1);
}
/* 读取AP3216C的数据,读取原始数据 */
void ap3216c_readdata(struct ap3216c_dev *dev){ 
        
	unsigned char i =0;
    unsigned char buf[6];
	
	/* 循环读取所有传感器数据 */
    for(i = 0; i < 6; i++){ 
        
        buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);	
    }

    if(buf[0] & 0X80) 	/* IR_OF位为1,则数据无效 */
		dev->ir = 0;					
	else 				/* 读取IR传感器的数据 */
		dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); 			
	
	dev->als = ((unsigned short)buf[3] << 8) | buf[2];	/* 读取ALS传感器的数据 */  
	
    if(buf[4] & 0x40)	/* IR_OF位为1,则数据无效 */
		dev->ps = 0;    													
	else 				/* 读取PS传感器的数据 */
		dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F); 
}
/* 打开设备 */
static int ap3216c_open(struct inode *inode, struct file *filp){ 
        
	filp->private_data = &ap3216cdev;
	/* 初始化AP3216C */
	ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04);	/* 复位AP3216C */
	mdelay(50);													
	ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X03);	/* 开启ALS、PS+IR */
	return 0;
}
/* 从设备读取数据 */
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off){ 
        
	short data[3];
	long err = 0;

	struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;	
	ap3216c_readdata(dev);

	data[0] = dev->ir;
	data[1] = dev->als;
	data[2] = dev->ps;
	err = copy_to_user(buf, data, sizeof(data));
	return 0;
}
/* 关闭/释放设备 */
static int ap3216c_release(struct inode *inode, struct file *filp){ 
        
	return 0;
}
/* AP3216C操作函数*/
static const struct file_operations ap3216c_ops = { 
        
	.owner = THIS_MODULE,
	.open = ap3216c_open,
	.read = ap3216c_read,
	.release = ap3216c_release,
};
/* i2c驱动的probe函数,当驱动与设备匹配以后此函数就会执行 */
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id){ 
        
	/* 1、构建设备号 */
	if (ap3216cdev.major) { 
        
		ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);
		register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);
	} else { 
        
		alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);
		ap3216cdev.major = MAJOR(ap3216cdev.devid);
	}

	/* 2、注册设备 */
	cdev_init(&ap3216cdev.cdev, &ap3216c_ops);
	cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);

	/* 3、创建类 */
	ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);
	if (IS_ERR(ap3216cdev.class)) { 
        
		return PTR_ERR(ap3216cdev.class);
	}

	/* 4、创建设备 */
	ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME);
	if (IS_ERR(ap3216cdev.device)) { 
        
		return PTR_ERR(ap3216cdev.device);
	}

	ap3216cdev.private_data = client;

	return 0;
}
/* i2c驱动的remove函数,移除i2c驱动的时候此函数会执行 */
static int ap3216c_remove(struct i2c_client *client){ 
        
	/* 删除设备 */
	cdev_del(&ap3216cdev.cdev);
	unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT)
        标签: 10nd光电传感器传感器pec09

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台