赞
踩
i2c-core.c
I2C 核心的功能以及/proc/bus/i2c*接口;
i2c-dev.c
并没有针对特定的设备而设计,只是提供了通用的read()、write()和ioctl()等接口,但是不负责具体时序或者业务,具体的时序和业务由上层业务去实现;
i2c.h
对i2c_adapter、i2c_algorithm、i2c_driver和i2c_client这4个数据结构进行了定义。
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_msg
和i2c_transfer
函数。
使用统一的接口i2c_transfer
,不关使用哪个芯片,他最终都会调用i2c_transfer
,来选择某一款I2C控制器,把数据发送出去,或者从I2c设备读到数据,对于每一次传输的数据都可以用一个i2c_msg
结构体来表示。
在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
结构体的成员变量如下所示:
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_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();
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command);
函数功能: 从指定的i2c从设备的指定地址空间中读取一个字节的数据
形参列表:
client: i2c客户端 -- 指定的要读取数据的i2c从设备
command:i2c从设备内部的地址空间 -- 指定要从设备的哪个地址空间中读取数据
返回值:
成功:读取到的数据(一个字节数据)
失败:负数
s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value);
函数功能: 向指定的i2c从设备中的指定地址空间写入一个字节的数据
形参列表:
client:i2c客户端 -- 指定要写入数据的i2c从设备
command:i2c从设备内部的地址空间 -- 把数据写入到哪个地址空间中
value:要写入的数据(一个字节数据)
返回值:
成功:0
失败:负数
/* 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");
#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 = ® /* 读取的首地址 */
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);
/* 注销掉类和设备 */
device_destroy(ap3216cdev.class, ap3216cdev.devid);
class_destroy(ap3216cdev.class);
return 0;
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {
{"alientek,ap3216c", 0},
{}
};
/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {
{ .compatible = "alientek,ap3216c" },
{ /* Sentinel */ }
};
/* i2c驱动结构体 */
static struct i2c_driver ap3216c_driver = {
.probe = ap3216c_probe, /*通过match方法匹配后会调用probe函数*/
.remove = ap3216c_remove,/*移除i2c驱动的时候此函数会执行*/
.driver = {
.owner = THIS_MODULE,
.name = "ap3216c",
.of_match_table = ap3216c_of_match,
},
.id_table = ap3216c_id,
};
/* 驱动入口函数 */
static int __init ap3216c_init(void){
int ret = 0;
ret = i2c_add_driver(&ap3216c_driver);//通过i2c_add_driver调用register向i2c-core注册ap3216c_driver的驱动;
return ret;
}
/* 驱动出口函数 */
static void __exit ap3216c_exit(void){
i2c_del_driver(&ap3216c_driver);
}
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
我们使用的接口i2c_transfer
会调用bit_xfer
函数,bit_xfer
函数主要作用是按照I2C协议,通过调用i2c_start
、i2c_stop
等函数产生满足I2C协议的高低电平。
/*该函数已删除判断函数,仅保留了操作语句*/
static int bit_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msgs[], int num)
{
/*产生开始位*/
i2c_start(adap);
/*发送从机地址*/
ret = bit_doAddress(i2c_adap, pmsg);
/*判断是读操作还是写操作*/
if (pmsg->flags & I2C_M_RD)
{
/*读操作*/
ret = readbytes(i2c_adap, pmsg);
}
else
{
/*写操作*/
ret = sendbytes(i2c_adap, pmsg);
}
i2c_stop(adap);/*产生停止位*/
}
i2c-algo-bit.c
文件为发出相应波形的函数,该文件位于 I2C -> algos文件夹
下,有最开始的文件结构可知algos文件夹
主要是用来实现了一些I2C总线适配器的通信方法;
//sda拉低电平
static inline void sdalo(struct i2c_algo_bit_data *adap)
{
setsda(adap, 0);
udelay((adap->udelay + 1) / 2);
}
//sda拉高电平
static inline void sdahi(struct i2c_algo_bit_data *adap)
{
setsda(adap, 1);
udelay((adap->udelay + 1) / 2);
}
//scl拉低电平
static inline void scllo(struct i2c_algo_bit_data *adap)
{
setscl(adap, 0);
udelay(adap->udelay / 2);
}
static void i2c_start(struct i2c_algo_bit_data *adap)
{
/* assert: scl, sda are high */
setsda(adap, 0);
udelay(adap->udelay);
scllo(adap);
}
static void i2c_repstart(struct i2c_algo_bit_data *adap)
{
/* assert: scl is low */
sdahi(adap);
sclhi(adap);
setsda(adap, 0);
udelay(adap->udelay);
scllo(adap);
}
static void i2c_stop(struct i2c_algo_bit_data *adap)
{
/* assert: scl is low */
sdalo(adap);
sclhi(adap);
setsda(adap, 1);
udelay(adap->udelay);
}
I2C驱动具有中间大、两头小
的特点,即向上接口很小,被浓缩成一个接口即i2c_transfer()
,向下也很小,最终都是到达i2c-algo-bit.c文件
产生波形。中间你无论是哪个厂家,无论你使用什么算法,最终提供给驱动开发人员的接口一定是i2c_transfer()
接口,向下使用的文件一定是i2c-algo-bit.c文件
中的函数产生波形。
故对于开发者来说,不用关心硬件使用的是什么(理论上,实际还是需要考虑的),都可以很就容易的使用I2C驱动,并且一个好的I2C程序扩展性强,可移植性强!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。