赞
踩
I2C总线因为它极简单的硬件连接和通讯方式,在现在的很多设备上它是一种不可或缺的通讯总线。当用单片机直接操作I2C时,其实很简单,只要正确把握IIC的操作时序就可以了,但是在linux系统中,I2C子系统结构是稍微有一点复杂度的,因为它涉及到很多linux内核相关的知识。
这里的I2C Bus并不是通讯上的总线,而是linux系统为了管理设备和驱动而虚拟出来的(注意这里的虚拟和平台总线的虚拟,意思上有区别),I2C Bus被用来挂载I2C适配器(adapter)和I2C设备(client)。
平台总线和IIC总线在Linux下都是被虚拟成总线模块,但是两者有很大区别,平台总线是纯虚拟的总线,不是通讯上的物理总线;而IIC总线是通讯上的物理总线,是有具体的控制器的(即dapter)。
I2C适配器是SoC中内置i2c控制器的软件抽象,可以理解为他所代表的是一个I2C主机。
I2C子系统提供的两种驱动实现方法(源码中I2C相关的驱动均位于:drivers/i2c目录下)
第一种叫i2c-dev,对应drivers/i2c/i2c-dev.c,这种方法只是封装了主机(I2C master,一般是SoC中内置的I2C 控制器)的I2C 基本操作,并且向应用层提供相应的操作接口,应用层代码需要自己去实现对slave 的控制和操作,所以这种I2C 驱动相当于只是提供给应用层可以访问slave 硬件设备的接口,本身并未对硬件做任何操作,应用需要实现对硬件的操作,因此写应用的人必须对硬件非常了解,所以这种I2C 驱动又叫做“应用层驱动”,这种方式并不主流,它的优势是把差异化都放在应用中,这样在设备比较难缠(尤其是slave是非标准I2C时)时不用动驱动,而只需要修改应用就可以实现对各种设备的驱动。
第二种I2C 驱动是所有的代码都放在驱动层实现,直接向应用层提供最终结果。应用层甚至不需要知道这里面有I2C存在,譬如电容式触摸屏驱动,直接向应用层提供/dev/input/event1 的操作接口,应用层编程的人根本不知道event1 中涉及到了I2C。
(1)struct i2c_adapter(I2C适配器)
struct i2c_adapter是用来描述一个I2C适配器,在SoC中的指的就是内部外设I2C控制器,当向I2C核心层注册一个I2C适配器时就需要提供这样的一个结构体变量。
- struct i2c_adapter {
- struct module *owner; //所有者
- unsigned int id;
- unsigned int class; //该适配器支持的从机设备的类型
- const struct i2c_algorithm *algo; //该适配器与从机设备的通信算法
- int timeout; //超时时间
- struct device dev; //该适配器设备对应的device
-
- int nr; //适配器的编号
- char name[48]; //适配器的名字
- struct list_head userspace_clients; //用来挂接与适配器匹配成功的从机设备i2c_client的一个链表头
- };
(2)struct i2c_algorithm(I2C算法)
struct i2c_algorithm结构体代表的是适配器的通信算法,在构建i2c_adapter结构体变量的时候会去填充这个元素。
- struct i2c_algorithm {
- int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
- int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);
- u32 (*functionality) (struct i2c_adapter *);
- };
注意:smbus协议是从I2C协议的基础上发展而来的,他们之间有很大的相似度,SMBus与I2C总线之间在时序特性上存在一些差别。
(3)struct i2c_client(I2C设备)
- struct i2c_client { //用来描述一个i2c 从机设备
- unsigned short flags; //描述i2c 从机设备特性的标志位
- unsigned short addr; //i2c 从机设备的地址
- ······
- char name[I2C_NAME_SIZE]; //i2c 从机设备的名字
- struct i2c_adapter *adapter; //指向与从机设备匹配成功的适配器
- struct i2c_driver *driver; //指向与从机设备匹配成功的设备驱动
- struct device dev; //该从机设备对应的device
- int irq; //从机设备的中断引脚
- struct list_head detected; //作为一个链表节点挂接到与他匹配成功的i2c_driver相应的链表头上
- };
(4)struct device_driver(I2C设备驱动)
- struct i2c_driver { //代表一个i2c设备驱动
- unsigned int class; //i2c设备驱动所支持的i2c设备的类型
-
- int (*attach_adapter)(struct i2c_adapter *); //用来匹配适配器的函数 adapter
- int (*detach_adapter)(struct i2c_adapter *);
- ······
- int (*probe)(struct i2c_client *, const struct i2c_device_id *); //设备驱动层的probe函数
- int (*remove)(struct i2c_client *); //设备驱动层卸载函数
- struct device_driver driver; //该i2c设备驱动所对应的device_driver
- const struct i2c_device_id *id_table; //设备驱动层用来匹配设备的id_table
- const unsigned short *address_list; //该设备驱动支持的所有次设备的地址数组
- struct list_head clients; //用来挂接与该i2c_driver匹配成功的i2c_client (从机设备)的一个链表头
- };
- struct i2c_board_info { //这个结构体是用来描述板子上的一个i2c从机设备的信息
- char type[I2C_NAME_SIZE]; //i2c从机设备的名字,用来初始化i2c_client.name
- unsigned short flags; //用来初始化i2c_client.flags
- unsigned short addr; //用来初始化 i2c_client.addr
- void *platform_data; //用来初始化 i2c_client.dev.platform_data
- struct dev_archdata *archdata; //用来初始化i2c_client.dev.archdata
- #ifdef CONFIG_OF
- struct device_node *of_node;
- #endif
- int irq; //用来初始化i2c_client.irq
- };
-
- struct i2c_devinfo {
- struct list_head list; //作为一个链表节点挂接到__i2c_board_list 链表上去
- int busnum; //适配器的编号
- struct i2c_board_info board_info; //内置的i2c_board_info 结构体
- };
(1)i2c-core.c: i2c核心层
(2)busses目录:这个文件中是已经编写好的各种向i2c核心层注册的适配器
(3)algos目录:这个目录里面是一些I2C通信算法
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。