i2c设备:ts、camera、audio、gsensor、e2prom
I2C基本协议:
写:开始 -> 设备地址 -> 写标志 -> 应答 -> 内部地址 -> 应答 -> 数据 -> 应答 -> 停止
读:开始 -> 设备地址 -> 写标志 -> 应答 -> 内部地址 -> 应答 -> 停止 -> 开始 -> 设备地址 -> 读标志 -> 应答 -> 数据 -> 应答 -> 停止
开始信号:在SCL为高电平期间,SDA从高电平变为低电平
结束信号:在SCL为高电平期间,SDA从低电平变为高电平
应答信号:主设备发送完一个字节,释放SDA,SDA被从设备拉低为ACK,没有被拉低为NACK。
I2C传输数据时,SCL为高电平时采集SDA信号(有效),SCL为低电平时SDA可变化(无效)。
i2c驱动框架:
应用层
----------------------------------------------------------------------------
i2c driver 层:需要程序员实现
1、与用户进行交互--->fops
2、知道数据是什么,但是不知道如何传输个硬件
----------------------------------------------------------------------------
i2c core 层:
维护i2c总线,负责进行匹配
代码位置:drivers/i2c/i2c-core.c
----------------------------------------------------------------------------
i2c adapter 层:
1、与硬件进行交互
2、直到如何将数据传输给硬件,但不知道数据是什么
代码位置:drivers/i2c/i2c-xxx.c
----------------------------------------------------------------------------
i2c_adapter 结构体
struct i2c_adapter {
struct module *owner; // 所属的模块
const struct i2c_algorithm *algo; // 总线通信方法结构体指针
void *algo_data; // algorithm数据
int retries; // 重试次数
struct device dev; // 适配器设备
char name[48]; // 适配器名称
... ...
};
i2c_algorithm 结构体
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); // i2c传输函数指针
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, // smbus 传输函数指针
u8 command, int size, union i2c_smbus_data *data);
u32 (*functionality) (struct i2c_adapter *); // 返回适配器支持的功能
};
i2c_driver 结构体
struct i2c_driver {
unsigned int class;
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
struct device_driver driver; // 设备驱动结构体
const struct i2c_device_id *id_table;
... ...
};
i2c_client 结构体
struct i2c_client {
unsigned short flags; // 标志
unsigned short addr; // 低7位芯片地址
char name[I2C_NAME_SIZE]; // 设备名称
struct i2c_adapter *adapter; // 依附的 i2c_adapter
struct i2c_driver *driver; // 依附的 i2c_driver
struct device dev; // 设备结构体
int irq; // 设备中断
struct list_head detected; // 链表头
};
i2c_msg 结构体
struct i2c_msg {
__u16 addr; // 从设备地址
__u16 flags; // 读写标志
__u16 len; // 消息包长度
__u8 *buf; // 消息包数据
};
i2c adapter 层中使用的是平台设备总线,每个i2c控制器有一个 platform_device,但只有一个 platform_driver,匹配成功后会执行 xxx_probe,每个platform_device 都会生成一个i2c适配器(struct i2c_adapter),i2c adapter 层会生成 i2c_client,并将 i2c_adapter 包含在 i2c_client;
在 i2c driver 层,i2c_client 和 i2c_driver 在 i2c 总线上匹配成功后,会执行 i2c driver 层的 probe 函数,此时 i2c driver 层会获取到 i2c_client 信息,读写通过 i2c_client ---> adapter ---> algo ---> master_xfer 就可以完成与从设备通信。
以下代码为S5PV210读写AT24C02:
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/miscdevice.h> #include <linux/i2c.h> #include <linux/slab.h> #include <asm/ioctl.h> #include <asm/uaccess.h> #include <asm/io.h> struct e2prom_private { int size; int version; }; struct global_e2prom { struct i2c_client *client; struct e2prom_private *private; }; struct global_e2prom *e2prom_dev; //i2c_master_send(const struct i2c_client * client,const char * buf,int count) //i2c_master_recv(const struct i2c_client * client,char * buf,int count) int at24_i2c_send(struct i2c_client *client, char *buf, int count) { int ret; struct i2c_adapter *adapter = client->adapter; struct i2c_msg msg; msg.addr = client->addr; msg.flags = 0; msg.len = count; msg.buf = buf; ret = i2c_transfer(adapter, &msg, 1); return (ret == 1) ? count : ret; } int at24_i2c_recv(struct i2c_client *client, char *buf, int count) { int ret; struct i2c_adapter *adapter = client->adapter; struct i2c_msg msg; msg.addr = client->addr; msg.flags = I2C_M_RD; msg.len = count; msg.buf = buf; ret = i2c_transfer(adapter, &msg, 1); return (ret == 1) ? count : ret; } int at24_drv_open(struct inode *inode, struct file *filp) { printk("-----%s----- ", __func__); return 0; } int at24_drv_close(struct inode *inode, struct file *filp) { printk("-----%s----- ", __func__); return 0; } ssize_t at24_drv_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) { int ret; printk("-----%s----- ", __func__); if ((size < 0) || (size > e2prom_dev->private->size)) { printk("read size error "); return -EINVAL; } char *tmp = kzalloc(size, GFP_KERNEL); if (tmp == NULL) { printk("kzalloc fail "); return -ENOMEM; } ret = at24_i2c_recv(e2prom_dev->client, tmp, size); if (ret < 0) { printk("at24_i2c_recv error "); ret = -EIO; goto err_free; } ret = copy_to_user(buf, tmp, size); if (ret > 0) { printk("copy_to_user error "); ret = -EFAULT; goto err_free; } kfree(tmp); return size; err_free: kfree(tmp); return ret; } ssize_t at24_drv_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) { int ret; printk("-----%s----- ", __func__); if ((size < 0) || (size > e2prom_dev->private->size)) { printk("write size error "); return -EINVAL; } char *tmp = kzalloc(size, GFP_KERNEL); if (tmp == NULL) { printk("kzalloc fail "); return -ENOMEM; } ret = copy_from_user(tmp, buf, size); if (ret > 0) { printk("copy_from_user error "); ret = -EINVAL; goto err_free; } ret = at24_i2c_send(e2prom_dev->client, tmp, size); if (ret < 0) { printk("at24_i2c_send error "); ret = -EIO; goto err_free; } kfree(tmp); return size; err_free: kfree(tmp); return ret; } const struct file_operations at24_fops = { .open = at24_drv_open, .release = at24_drv_close, .read = at24_drv_read, .write = at24_drv_write, }; struct miscdevice at24_miscdev = { .minor = MISC_DYNAMIC_MINOR, //自动分配次设备号 .name = "e2prom_dev", //用于生成设备节点 .fops = &at24_fops, }; int i2c_at24_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret; e2prom_dev = kzalloc(sizeof(struct global_e2prom), GFP_KERNEL); if (e2prom_dev == NULL) { printk("kzalloc fail "); return -ENOMEM; } e2prom_dev->client = client; e2prom_dev->private = (struct e2prom_private *)id->driver_data; misc_register(&at24_miscdev); return 0; } int i2c_at24_remove(struct i2c_client *client) { misc_deregister(&at24_miscdev); kfree(e2prom_dev); return 0; } struct e2prom_private at24c02_private = { .size = 256, .version = 0x01, }; const struct i2c_device_id at24_id_table[] = { {"at24c02", (kernel_ulong_t)&at24c02_private}, }; struct i2c_driver at24_drv = { .probe = i2c_at24_probe, .remove = i2c_at24_remove, .driver = { .name = "at24_drv", }, .id_table = &at24_id_table, //name用于匹配 }; static int __init i2c_at24_drv_init(void) { return i2c_add_driver(&at24_drv); } static void __exit i2c_at24_drv_exit(void) { i2c_del_driver(&at24_drv); } module_init(i2c_at24_drv_init); module_exit(i2c_at24_drv_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Aaron Lee");