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_msg
和i2c_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 = ® /* 读取的首地址 */ 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