1、i2c基础
I2C(Inter-IntegratedCircuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。
简单的I2C协议理解
一. 技术性能:
工作速率有100Kbit/s、400Kbit/s和3.4Mbit/s三种;(一般使用小于100Kbit/s)
支持多主控模块,但同一时刻只允许有一个主控;(一般只有一个主控)
由数据线SDA和时钟SCL构成的串行总线;
每个电路和模块都有唯一的地址;(7位地址)
连接到总线的接口数量由总线电容400pF的限制决定;
二. 基本工作原理:
SDA和SCL都是双向线路,都通过一个电流源或上拉电阻连接到正的电源电压。当总线空闲时,这两条线路都是高电平。以启动信号START来掌管总线,以停止信号STOP来释放总线;
启动信号START后紧接着发送一个地址字节,其中7位为被控器件的地址码,一位为读/写控制位R/W,每个数据字节在传送时都是高位在前;
三. 寻址约定
高7位为地址码,其分为两部分:
1. 固定部分,不可改变,由厂家固化的统一地址;
2. 可编程部分,此部分由器件可使用的管脚决定。例如,如果器件有4个固定的和3个可编程的地址位,那么相同的总线上共可以连接8个相同的器件(并非所有器件都可以设定)。
2、I2C驱动分析(只分析架构)
内核i2c驱动的代码逻辑图,然后分析源码如何实现这个图
需要的源码kernel/drivers/i2c
i2c-core.c这个文件实现了I2C核心层的功能
i2c-dev实现了I2C适配器设备文件的功能
busses 包含了一些I2C总线的驱动(每个总线驱动代表一个adapter)
algos 一些I2C总线适配器的algorithm.
muxes 实现多路复用的代码(就是一个多路复用器,意义不大)
(红色部分与具体的i2c硬件相关,不是固定的)
此外,还需要一个i2c_driver注册的实例,例如TP驱动(驱动注册i2c的过程都一样,这里只关注几个函数)
结构含义:
i2c_adapter——一个i2c总线
i2c_client——一个挂在i2c的设备(外设)
i2c_driver——控制i2c设备(外设)的驱动
因此,一个i2c_ adapter可以对应多个i2c_client,而一个i2c_client与一个i2c_driver对应
先看一下一个实际设备如何注册,再看linux内核本身做了哪些工作
1、 注册一个i2c板文件(注册外设信息)
i2c_register_board_info(int busnum,struct i2c_board_info const*info, unsigned len)
busnum——i2c总线号,代表挂在i2c-0,还是i2c-1,还是i2c-2…上面
i2c_board_info——一般只定义设备名,设备地址(外设从设备信息)
unsignedlen ——i2c_board_info的个数(第二项可以定义i2c_board_info数组,即一次性的写入多个设备信息)
在dts被引入后,可能没有这一步操作,但是效果相同,在dtb被解压的时候也会进行这一步操作
2、 i2c_add_driver(注册一个i2c设备)
i2c_add_driver(driver) ——注册i2c驱动
#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE,driver)---->
i2c_register_driver---->
driver->driver.owner =owner;
driver->driver.bus =&i2c_bus_type;
driver_register(&driver->driver); //注册驱动,与设备匹配(从设备)
i2c_for_each_dev(driver, __process_new_driver);---->
//对每一个i2c_device使用__process_new_driver的操作
__process_new_driver ---->
i2c_do_add_adapter(data,to_i2c_adapter(dev));---->
这一步执行的前提是i2c_adapter和i2c_client已经被注册了,这里只是得到他们,然后与他们联系起来。
driver_register不多解释,关键是深入会执行drv->bus->match操作,就是i2c_bus_type的match操作。(他的目的是与i2c_client匹配,而且和设备匹配)
看源码:
static int i2c_device_match(structdevice *dev, struct device_driver *drv){
structi2c_client *client =i2c_verify_client(dev);
if(of_driver_match_device(dev, drv))
…… …
}
因为i2c_client->dev就是i2c总线下的设备,所以driver与device匹配通过dev就可以与i2c_client关联到一起。
同理i2c_adapter->dev也是如此把driver与adapter联系到一起,匹配过程就是match操作,match操作来源于板文件注册信息。
3、 看一下i2c_adapter是如何注册的(这里随便在i2c_buses目录下面找一份i2c代码来举例)
static int xxxxx_i2c_probe(struct platform_device *pdev){ //通过字符设备注册执行的
i2c->adap.algo = &s3c24xx_i2c_algorithm;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
ret =i2c_add_numbered_adapter(&i2c->adap);
… …
of_i2c_register_devices(&i2c->adap);
}
看一下i2c_add_numbered_adapter代码:
__i2c_add_numbered_adapter(structi2c_adapter *adap)---- >
i2c_register_adapter(struct i2c_adapter*adap) ---- >
dev_set_name(&adap->dev,"i2c-%d", adap->nr); //设备名
adap->dev.bus= &i2c_bus_type; //挂在i2c_bus下面
adap->dev.type= &i2c_adapter_type;
res= device_register(&adap->dev); //注册设备
看一下of_i2c_register_devices(structi2c_adapter *adap)代码:
------> i2c_new_device(struct i2c_adapter*adap, struct i2c_board_info const *info) ---- >
client->adapter = adap;
client->addr = info->addr; //板文件地址
strlcpy(client->name,info->type, sizeof(client->name)); //把板文件name给client->name
client->dev.parent= &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
status =device_register(&client->dev);
i2c-core.c里面全部都是上面操作的辅助接口函数
到此,一个整体的框架便完全分析完了(至于adapter如何通过algorithm来控制底层硬件,这里就不讨论了)