[I2C总线的信号状态]
1、 空闲状态:SDA和SCL都是高电平;
2、 开始条件(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传输数据;
3、 结束条件(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传输数据;
4、 数据有效:在SCL的高电平期间,SDA保持稳定,数据有效。SDA的改变只能发生在SCL的低电平期间;
5、 ACK信号:数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。
先看下源码目录:drivers/i2c/
• i2c-core.c
这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口。
• i2c-dev.c
实 现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适配器访问设备时的主设备号都为89,次设备号为0~255。应用程序通过 “i2c-%d” (i2c-0, i2c-1, ..., i2c-10, ...)文件名并使用文件操作接口open()、write()、read()、ioctl()和close()等来访问这个设备。
i2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read()、write()和ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器并控制I2C设备的工作方式。
• chips文件夹
这个目录中包含了一些特定的I2C设备驱动,如Dallas公司的DS1337实时钟芯片、EPSON公司的RTC8564实时钟芯片和I2C接口的EEPROM驱动等。
• busses文件夹
这个文件中包含了一些I2C总线的驱动,如S3C2410的I2C控制器驱动为i2c-s3c2410.c。
• algos文件夹
实现了一些I2C总线适配器的algorithm算法;
代码中涉及到4个结构体:2c_driver、i2c_client、i2c_adapter和i2c_algorithm
• i2c_adapter与i2c_algorithm
i2c_adapter 对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配 器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用的 i2c_algorithm的指针。
i2c_algorithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg(即I2C消息)为单位。i2c_msg结构体也非常关键。
代码清单 i2c_msg结构体
1 struct i2c_msg {
2 __u16 addr; /* 设备地址*/
3 __u16 flags; /* 标志 */
4 __u16 len; /* 消息长度*/
5 __u8 *buf; /* 消息数据*/
6 };
• i2c_driver与i2c_client
i2c_driver对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。i2c_client一般被包含在i2c字符设备的私有信息结构体中。
i2c_driver 与i2c_client发生关联的时刻在i2c_driver的attach_adapter()函数被运行时。attach_adapter()会探测 物理设备,当确定一个client存在时,把该client使用的i2c_client数据结构的adapter指针指向对应的i2c_adapter, driver指针指向该i2c_driver,并会调用i2c_adapter的client_register()函数。相反的过程发生在 i2c_driver 的detach_client()函数被调用的时候。
• i2c_adpater与i2c_client
i2c_adpater 与i2c_client的关系与I2C硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adpater。由于一个适配器上可以连 接多个I2C设备,所以一个i2c_adpater也可以被多个i2c_client依附,i2c_adpater中包括依附于它的i2c_client 的链表;
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */ 7 bit模式,去读写时地址要右移一位;
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct i2c_driver *driver; /* and our access routines */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
};
driver/i2c/i2c-core.c:
static int __init i2c_init(void)
{
....
retval = bus_register(&i2c_bus_type);//bus_type
....
retval = i2c_add_driver(&dummy_driver);
}
static struct i2c_driver dummy_driver = {
.driver.name = "dummy",
.probe = dummy_probe,
.remove = dummy_remove,
.id_table = dummy_id,
};
i2c_add_driver() --> i2c_register_driver --> driver_register -> bus_add_driver() --> driver_attach --> bus_for_each_dev --> while遍历device列表 执行__driver_attach()
__driver_attach执行;
调用总线上match函数判断设备和驱动是否匹配,若匹配则返真,找到对应的设备,继续执行后面的程序,若没有找到,则返回假,函数执行结束。
driver端:
设备驱动init 函数里面i2c_add_driver > i2c_register_driver注册驱动并去匹配相应的设备并调用驱动注册的probe函数;
i2c_check_functionality:查看adapte的2c_algorithm支持类型,对应定义在/i2c/busses/i2c-xx.c里面;对应的传输函数也在i2c_algorithm结构体里面;
static const struct i2c_algorithm xxx_i2c_algo = {
.master_xfer = xxx_i2c_master_xfer,
.functionality = xxx_i2c_func,
};
i2c/busses/i2c-xx.c:
注册在platform上的一个虚拟设备,并通过probe函数初始化一个i2c_adapter,通过函数i2c_add_numbered_adapter注册这个adapeter,这样适配器的algorithm也就对应好了;
i2c_add_numbered_adapter -> i2c_register_adapter
Master_xfer函数实现模板:
static int i2c_adapter_xxx_xfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)
{
......
for (i = 0; i < num; i++) {
i2c_adapter_xxx_start(); /*产生起始位*/
if (msgs[i]->flags & I2C_M_RD) { /*读取*/
i2c_adapter_xxx_setaddr((msg->addr << 1) | 1); /*发送从设备地址*/
i2c_adapter_xxx_wait_ack(); /*获得从设备的ACK*/
i2c_adapter_xxx_readbytes(msgs[i]->buf,msgs[i]->len); /*读取len长度的数据到buf中*/
} else {
i2c_adapter_xxx_setaddr(msg->addr << 1);
i2c_adapter_xxx_wait_ack();
i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len);
}
}
i2c_adapter_xxx_stop(); /*产生停止位*/
}
框架图:
总结:
执行顺序:
(1) i2c-core.c 完成i2c bus的注册;
(2) bus/i2c-xxx.c 通过probe函数注册adapter,并添加相应的algorithm;
(3) register adapter device的同时会添加boardinfo i2c设备device type为i2c_client_type;
(4) i2c-dev.c:添加adapter 字符设备,创建设备文件提供上层相应的操作接口;
(5) 相应i2c驱动,通过add_i2c_driver,匹配bus上相应设备,走入相应的probe;