Linux I2C驱动主要分为
1.总线(主机)驱动(I2C BUS Driver)
2.设备(外设)驱动(I2C Clients Driver)
Linux主机驱动和外设驱动分离思想
外设驱动→API→主机驱动→板级逻辑--具体的i2c设备(camera,ts,eeprom等等)
主机驱动:根据控制器硬件手册,操作具体的寄存器,产生波形。(单片机工程师肯定有强烈的冲动去配置i2c寄存器,产生波形!)。
linux应用工程师:屏蔽了驱动和硬件(只看到了read、write、socket等接口)。
linux驱动程师:屏蔽硬件!提供标准的主机驱动(已经通过主机驱动屏蔽掉了,原厂的工程师已经做好了),驱动工程师需要完成“外设驱动”
IIC相关的重要的数据结构
•struct i2c_msg; //I2C消息数据结构 •struct i2c_algorithm; //算法驱动数据结构 •struct i2c_adapter; //I2C适配器数据结构 •struct i2c_client; //I2C客户数据结构 •struct i2c_driver; //I2C设备驱动数据结构 •struct i2c_board_info; //描述板载I2C设备的信息
1. 当总线(plat_form)上注册了对应的IIC从设备时,如果可以匹配成功(驱动名称和plat_form的名字相同),则调用probe初始化设备,注册字符设备,提供接口给应用程序
struct i2c_driver { char name[32]; //驱动的名称 int id;//驱动的ID unsigned int flags; /* div,see below */ int (*attach_adapter(struct i2c_adapter *); int("detach_ch_client)(struct i2c_client *); int(*command)(struct i2c_client *client,unsigned int cmd,void *arg); void (*inc_use)(strtuct i2c_client *client ); void (*dec_use)(struct i2c_client *client ); }
2.IIC发送或者接收一次数据都以数据包 struct i2c_msg 封装
struct i2c_msg { __u16 addr; // slave address __u16 flags; #define I2C_M_TEN 0x0010 //10bit地址 #define I2C_M_RD 0x0001 //读取数据标志,清零表示写 #define I2C_M_NOSTART x4000 //不发送起始位 #define I2C_M_REV_DIR_ADDR 0x2000 // 反转读写标志 #define I2C_M_IGNORE_NAK x1000 //忽略I2C器件的ack和nack信号 #define I2C_M_NO_RD_ACK 0x0800 //读操作时不去ACK #define I2C_M_RECV_LEN 0x0400 //length will be first received byte __u16 len; // msg length __u8 *buf; // pointer to msg data };
3. I2c_adapter 结构标识一个物理I2C总线(适配器),总线通过算法结构访问到适配器。I2C总线上对I2C slave设备的具体操作是在适配器驱动中完成的。适配器驱动作为platform_driver而注册,在probe()到驱动设备后,向总线声明并被添加:
i2c_add_numbered_adapter(&i2c->adap);
I2c-dev驱动是系统自带的一个通用客户驱动,它不是针对某一个I2C设备(即没有自己驱动设备id-table),它可以使得用户空间的程序通过i2c-tools访问总线上的i2c/SMBUS设备。
struct i2c_adapter { struct module *owner; unsigned int id; unsigned int class; /* 适配器支持的类型 */ const struct i2c_algorithm *algo; /* 该适配器的通信函数 */ void *algo_data; /* --- administration stuff. */ int (*client_register)(struct i2c_client *) __deprecated; int (*client_unregister)(struct i2c_client *) __deprecated; /* data fields that are valid for all devices */ u8 level; /* nesting level for lockdep */ struct mutex bus_lock; struct mutex clist_lock; int timeout; /*超时时间段设定10 */ int retries; //通信重复次数限定 /*以下是内嵌的标准device 其中dev->type标识该设备是一个adapter,其值为 * i2c_adapter_type */ struct device dev; /* the adapter device */ int nr; // 适配器编号也是Bus编号,第几条IIC总线 struct list_head clients; /* DEPRECATED */ char name[48]; struct completion dev_released; };
4.一个struct i2c_client结构体代表IIC总线上的一个从设备
struct i2c_client { unsigned short flags; /* div., see below */ unsigned short addr; /* chip address - NOTE: 7bit */ /* addresses are stored in the */ /* _LOWER_ 7 bits */ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; /* the adapter we sit on client所在IIC bUS对应的adapter */ struct i2c_driver *driver; /* and our access routines client使用的驱动 */ struct device dev; /* the device structure */ int irq; /* irq issued by device */ struct list_head list; /* DEPRECATED */ struct list_head detected; struct completion released; };
一. 内核函数接口:(API)。主机驱动提供给外设驱动的函数接口。
注册i2c设备:i2c_board_info
驱动注册和卸载函数以及结构体:i2c_del_driver/i2c_add_driver,i2c_driver
添加客户驱动:static inline int i2c_add_driver(struct i2c_driver *driver) 删除客户驱动:extern void i2c_del_driver(struct i2c_driver *);
读写函数和结构体:i2c_transfer,i2c_msg
这几个函数是放之四海而皆准!
IIC相关的重要数据结构
外设驱动:针对具体的外部器件的代码。
摄像头以及声卡中i2c用来配置外部设备(声卡和摄像头)→地址和配置的内容都不一样!
板级逻辑:描述主机和外部设备是怎么连接的(IIC协议的内容)。
二.设备-i2c设备注册以及设备注册之后的查询方法
查询i2c设备地址:ls /sys/bus/i2c/devices/
怎么和原理图以及外部设备对应:3-0038→I2C_3_SCL(外部设备的地址addr:datasheet中查0x38)
查询i2c设备名称:cat /sys/bus/i2c/devices/3-0038/name
menuconfig中去掉触摸的驱动
Device Drivers --->
Input device support --->
Touchscreens --->
FT5X0X based touchscreens(去掉)
添加i2c设备:i2c_devs3[]中添加
{
I2C_BOARD_INFO("i2c_test", 0x70>>1), // 1.设备名称 (一定要和驱动名称一致才会进入probe)2.从机地址
},
cat /sys/bus/i2c/devices/3-0038/name结果是i2c_test
三.驱动-i2c驱动注册和卸载
i2c设备初始化完成-进入probe函数。
i2c_del_driver/i2c_add_driver,i2c_driver
module_init和late_initcall:module_init先运行,late_initcall后运行
4.驱动-i2c数据的传输(9.7寸或者7寸屏幕)
i2c_transfer,i2c_msg
5.Liux-i2c利用字符驱动完成应用层对i2c的读和写(9.7寸或者7寸屏幕)
#include <linux/kernel.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/gpio.h> #include <linux/platform_device.h> #ifdef CONFIG_HAS_EARLYSUSPEND #include <linux/earlysuspend.h> #endif #include <linux/regulator/consumer.h> #include <mach/gpio.h> #include <plat/gpio-cfg.h> #include <asm/uaccess.h> #include <linux/miscdevice.h> static struct i2c_client *this_client; static int i2c_tes_read_reg(struct i2c_client *client,u8 addr, u8 *pdata) { u8 buf1[4] = { 0 }; u8 buf2[4] = { 0 }; struct i2c_msg msgs[] = { { .addr = client->addr, //0x38 .flags = 0, //写 .len = 1, //要写的数据的长度 .buf = buf1, }, { .addr = client->addr, .flags = I2C_M_RD, .len = 1, .buf = buf2, }, }; int ret; buf1[0] = addr; ret = i2c_transfer(client->adapter, msgs, 2); if (ret < 0) { pr_err("read reg (0x%02x) error, %d ", addr, ret); } else { *pdata = buf2[0]; } return ret; } static int i2c_tes_read_fw_reg(struct i2c_client *client,unsigned char *val) { int ret; *val = 0xff; ret = i2c_tes_read_reg(client,0xa6, val); printk("ts reg 0xa6 val is %d ",*val); return ret; } static int i2c_open_func(struct file *filp) { return 0; } static int i2c_release_func(struct file *filp) { return 0; } static ssize_t i2c_read_func(struct file *filp, char __user *buffer, size_t count, loff_t *ppos) { int ret; u8 reg_data; ret = copy_from_user(®_data,buffer,1); //目的是从用户空间拷贝数据到内核空间,失败返回没有被拷贝的字节数,成功返回0 struct i2c_msg msgs[] = { //IIC发送或者接收一次数据都以数据包 struct i2c_msg 封装 { .addr = this_client->addr, //从机地址 0x38 .flags = 0, //写 .len = 1, //要写的数据的长度 .buf = ®_data, }, { .addr = this_client->addr, .flags = I2C_M_RD, .len = 1, .buf = ®_data, }, }; // 参数:1.适配器结构体的指针 2.消息数组的头指针 3.消息的数量。 ret = i2c_transfer(this_client->adapter, msgs, 2); if (ret < 0) { pr_err("read reg error! "); } ret = copy_to_user(buffer,®_data,1); return ret; } static ssize_t i2c_write_func(struct file *filp, char __user *buffer, size_t count, loff_t *ppos){ int ret; u8 buf[2]; struct i2c_msg msgs[1]; ret = copy_from_user(&buf,buffer,2); msgs[0].addr = this_client->addr; //0x38 msgs[0].flags = 0; //写 msgs[0].len = 2; //第一个是要写的寄存器地址,第二个是要写的内容 msgs[0].buf = buf; ret = i2c_transfer(this_client->adapter, msgs, 1); if (ret < 0) { pr_err("write reg 0x%02x error! ",buf[0]); } ret = copy_to_user(buffer,buf,1); return ret; } static struct file_operations i2c_ops = { // 设备驱动的结构体 .owner = THIS_MODULE, .open = i2c_open_func, .release= i2c_release_func, .write = i2c_write_func, .read = i2c_read_func, }; static struct miscdevice i2c_dev = { .minor = MISC_DYNAMIC_MINOR, .fops = &i2c_ops, .name = "i2c_control", }; static int i2c_test_probe(struct i2c_client *client, const struct i2c_device_id *id) { unsigned char val; printk("==%s: ", __FUNCTION__); i2c_tes_read_fw_reg(client,&val); this_client = client; misc_register(&i2c_dev); return 0; } static int __devexit i2c_test_remove(struct i2c_client *client) { i2c_set_clientdata(client, NULL); misc_deregister(&i2c_dev); printk("==%s: ", __FUNCTION__); return 0; } static const struct i2c_device_id i2c_test_id[] = { { "i2c_test", 0 }, { } }; static struct i2c_driver i2c_test_driver = { //驱动结构体 .probe = i2c_test_probe, .remove = __devexit_p(i2c_test_remove), .id_table = i2c_test_id, .driver = { .name = "i2c_test", .owner = THIS_MODULE, }, }; static void i2c_io_init(void) // 相关管脚的初始化 { int ret; ret = gpio_request(EXYNOS4_GPL0(2), "TP1_EN"); if (ret) { printk(KERN_ERR "failed to request TP1_EN for " "I2C control "); //return err; } gpio_direction_output(EXYNOS4_GPL0(2), 1); s3c_gpio_cfgpin(EXYNOS4_GPL0(2), S3C_GPIO_OUTPUT); gpio_free(EXYNOS4_GPL0(2)); mdelay(5); ret = gpio_request(EXYNOS4_GPX0(3), "GPX0_3"); if (ret) { gpio_free(EXYNOS4_GPX0(3)); ret = gpio_request(EXYNOS4_GPX0(3), "GPX0_3"); if(ret) { printk("ft5xox: Failed to request GPX0_3 "); } } gpio_direction_output(EXYNOS4_GPX0(3), 0); mdelay(200); gpio_direction_output(EXYNOS4_GPX0(3), 1); s3c_gpio_cfgpin(EXYNOS4_GPX0(3), S3C_GPIO_OUTPUT); gpio_free(EXYNOS4_GPX0(3)); msleep(300); } static int __init i2c_test_init(void) { printk("==%s: ", __FUNCTION__); i2c_io_init(); //iic对应的gpio引脚初始化 printk("==%s: ", __FUNCTION__); return i2c_add_driver(&i2c_test_driver); // 添加iic设备驱动 } static void __exit i2c_test_exit(void) { printk("==%s: ", __FUNCTION__); i2c_del_driver(&i2c_test_driver); // 删除驱动 } late_initcall(i2c_test_init); module_exit(i2c_test_exit); MODULE_AUTHOR("xunwei_rty"); MODULE_DESCRIPTION("TsI2CTest"); MODULE_LICENSE("GPL");
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> int main(int argc,char **argv) { int fd,ret; char *i2c_device = "/dev/i2c_control"; unsigned char buffer[2]; printf("open %s! ",i2c_device); if((fd = open(i2c_device,O_RDWR|O_NDELAY))<0) printf("APP open %s failed",i2c_device); else{ printf("APP open %s success! ",i2c_device); } //读一个数据0xa6 buffer[0] = 0xa6; ret = read(fd,buffer,1); if(ret<0) printf("i2c read failed! "); else{ printf("i2c read reg 0xa6 data is 0x%02x! ",buffer[0]); } //01先从0x00读出一个数据,02写一个数据到0x00,03再读出来对比 //01 buffer[0] = 0x00; read(fd,buffer,1); printf("i2c read reg 0x00 data is 0x%02x! ",buffer[0]); //02 buffer[0] = 0x00; buffer[1] = 0x40; ret = write(fd,buffer,2); if(ret<0){ printf("i2c write failed! "); goto exit; } //03 buffer[0] = 0x00; read(fd,buffer,1); printf("i2c read reg 0x00 data is 0x%02x! ",buffer[0]); close(fd); exit: close(fd); return -1; }