本篇文章为大家展示了怎么进行Linux的I2C驱动框架分析,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
总线
总线代表着同类设备需要共同遵守的工作时序,不同的总线对于物理电平的要求是不一样的,对于每个比特的电平维持宽度也是不一样,而总线上传递的命令也会有自己的格式约束。如I2C总线、USB总线、PCI总线等等。以I2C总线为例,在同一组I2C总线上连接着不同的I2C设备。
设备
设备代表真实的、具体的物理器件,在软件上用器件的独特的参数属性来代表该器件。如I2C总线上连接的I2C从设备都有一个标识自己的设备地址,由这个设备地址来确定主设备发过来的命令是否该由它来响应。
驱动
简单的说驱动代表着操作设备的方式和流程。
Linux总线设备框架的工作原理
如果想要弄清楚I2C驱动框架,必须深刻的理解Linux的总线设备框架。之所以会形成这样的框架,很重要的原因是为了代码的复用性。因为驱动和设备的关系是一对多的,对于相同类型的不同的设备,可共用同一套驱动程序接口。为了提高驱动的可移植性,Linux抽象出一套管理资源的函数。设备是存在的硬件,在设备里包含自己的属性,也包含需要用到的资源。
总线的作用就是在软件层面上对设备和驱动进行管理,设备要让系统感知到自己的存在,所以需要向总线去注册设备,驱动同样也要向总线去注册。对于总线,有I2C总线,Platform总线等等。但是Platform是虚拟总线。
对于总线上设备与驱动的匹配,由总线负责,设备在注册的时候,总线会遍历注册在总线上的驱动,如果名字相同,则匹配上了,此时调用驱动程序的probe函数。同样的驱动在注册的时候,也会遍历总线上的设备,如果匹配上(名字一样),则也会调用驱动程序的probe函数。
对于I2C来说,有如下的特点:
1.一条串行数据线(SDA),一条串行时钟线(SCL)
2.每个接到总线上的器件都可以使用软件根据它的唯一地址来识别。
3.串行的8位双向数据传输,位速率在标志模式下可达100kbit/s,在快速模式下可达400kbit/s。在高速模式下可达3.4Mbit/s。
下面来看一下具体的硬件连接
以上是TFS上的摄像头I2C的连接方式,只有两根线即可实现数据的传输。在传输过程中,需要注意以下三种类型的信号:
(1)开始信号(S):SCL为高电平时,SDA由高向低电平跳变,开始传输数据
(2)结束信号(P):SCL为高电平时,SDA由低向高电平跳变,结束传输数据
(3)响应信号(ACK):接收器在接收到8位数据后,在第9个时钟周期,拉低SDA的电平
以上就是I2C的硬件层与协议层的基本概述,这部分可以作为基本认知。
对于Linux下的I2C驱动,其体系结构的组成主要分为三个部分
(1)I2C核心:I2C核心提供了I2C总线驱动和设备驱动的注册,注销方法,I2C通信方法(”algorithm”)上层的,与具体适配器无关的代码以及探测设备,检测设备地址的上层代码等。
(2)I2C总线驱动:I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。
(3)I2C设备驱动:I2C设备驱动(也称为客户驱动)是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。
比较重要的文件
\kernel\drivers\i2c\i2c-core.c
这个文件实现了 I2C 核心的功能以及/proc/bus/i2c*接口。同时对I2C底层的收发函数进行封装。会调用i2c_transfer ,里面实现了adap->algo->master_xfer(adap, msgs, num)
kernel\drivers\i2c\i2c-dev.c
该函数注册了一个设备文件的功能,也就是注册了一个字符设备驱动程序,可以通过/dev/i2c-0(i2c-0, i2c-1,…, i2c-10,…)找到具体的I2C适配器,这个I2C设备的主设备号为89,次设备号0~255。通过访问这个接口,可以通过open()、 write()、 read()、 ioctl()和 close()等来访问这个设备。
kernel\drivers\i2c\busses\i2c-v12-jz.c
该函数对君正的x1000底层的I2C操作控制函数,通过设置寄存器来进行I2C的控制。其最底层的收发函数都在该文件里定义。重要的是i2c_jz_algorithm,其中algorithm实现了对底层寄存器的操作。
比较重要的结构体
i2c_driver、 i2c_client、 i2c_adapter 和 i2c_algorithm这四个结构体十分的关键
i2c_driver
对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。
i2c_client
对应于真实的物理设备,每个 I2C 设备都需要一个 i2c_client 来描述。i2c_client 一般被包含在 I2C 字符设备的私有信息结构体中。
i2c_adpater
用来匹配i2c_driver与i2c_client。即 i2c_client 依附于 i2c_adpater。由于一个适配器上可以连接多个 I2C 设备, 所以一个 i2c_adpater 也可以被多个 i2c_client 依附, i2c_adpater 中包括依附于它的 i2c_client 的链表 。
i2c_algorithm
struct i2c_algorithm{
//i2c模式下,收发函数接口
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
//用于SMBUS模式下,收发函数接口
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);
//用于检查I2C主控制器所支持访问接口,如I2C_FUNC_SMBUS_BYTE,查看是否支持smbus单字节读取和写操作
u32 (*functionality) (struct i2c_adapter *);
}
该函数主要实现其I2C底层的操作
对于摄像头驱动程序,首先要知道如何让摄像头能够正常工作。
第一步:摄像头上电
在这一步的工作中,可以控制相关的GPIO进行摄像头使能,控制RESET及POWERON来让摄像头正常工作。
GC0328的上电时序如下图所示:
第二步:给摄像头提供时钟
这一步也比较的关键,对于摄像头来说,其时钟就是心跳,如果要让摄像头正常的工作,则需要x1000的CIM提供24MHz的时钟给摄像头。
第三步:配置摄像头的寄存器
对于一个摄像头sensor,需要其输出指定大小及指定格式的图片,则需要配置摄像头的寄存器。而配置摄像头寄存器就是需要通过I2C来进行配置。
第四步:配置CIM
x1000内部的摄像头接口控制模块,可以将摄像头数据进行处理,可以进行帧错误检查以及数据的传输。这部分的控制需要那些CIM相关的寄存器来完成。
第五步:启动CIM
配置及初始化完成后就可以启动摄像头了,CIM负责数据传输及产生相应的中断。
以上是摄像头初始化的一个完整的过程,对于摄像头初始化部分,I2C又是如何进行初始化及设置的呢?这也是本文的重点。
根据前面的总线设备驱动的框架,有driver那么肯定会有device。这两者的匹配靠的是.id_table
对于gc0308,具体可以通过kernel/arch/mips/xburst/soc-x1000/chip-x1000/halley2/common/i2c_bus.c
可以看到向I2C总线注册的device的是gc0308
如果匹配上了,则调用driver的.probe函数。下面我们来看一下该函数具体做了什么事情。
在probe函数中,主要向v412_i2c_subdev提供了一个可操作的client,也就是相当于I2C的操作函数的接口交给V4L2视频驱动框架来进行管理。向V4L2视频驱动框架提供的函数如下:
第一个结构体是有关视频操作的接口,比如设备gc0328的输出格式,得到当前的视频输出格式等等
第二个结构体是控制camera上电与断电,以及控制白平衡,对焦的其他的参数
通过V4L2的I2C子设备控制来进行设置。下面来基本分析一下其调用过程:
当应用程序通过ioctl传递VIDIO_S_FMT,是可以设置摄像头输出的格式
然后看一下写寄存器的过程
调用了i2c_smbus_write_byte_data该函数在kernel\drivers\i2c\i2c-core.c,这样就进入了i2c总线操作函数中。
该函数会调用i2c_smbus_xfer
为什么不满足条件,可以看注册的i2c的平台设备,在kernel\drivers\i2c\busses\i2c-v12-jz.c路径下
有个i2c_algorithm的结构体
struct i2c_algorithm{
//i2c模式下,收发函数接口
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
//用于SMBUS模式下,收发函数接口
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);
//用于检查I2C主控制器所支持访问接口,如I2C_FUNC_SMBUS_BYTE,查看是否支持smbus单字节读取和写操作
u32 (*functionality) (struct i2c_adapter *);
}
所以只会向下执行,当执行到i2c_smbus_xfer_emulated ,会调用
该函数会调用
最后调用到kernel\drivers\i2c\busses\i2c-v12-jz.c的最底层的实现
在kernel\drivers\i2c\busses\i2c-v12-jz.c函数中
这个函数指向i2c_jz_xfer
在这个函数中,实现了I2C的读写,可以根据传递的flag进行判断是读操作函数写操作
最底层操作寄存器来实现其读写函数
到这里,一个I2C完整的传输流程就完成了。
对于I2C完整的传输协议,最重要的是弄清楚总线驱动程序的框架,因为I2C也是属于总线框架。对于I2C总线设备框架的模型,可以用下图来说明:
也就是device与driver同时向i2c总线上注册。当注册在总线上时,可以通过id_table进行匹配,匹配上之后会调用driver的.probe函数。对于一般的I2C设备,可以在probe函数中注册一个字符设备驱动,从而应用层可以通过open函数打开/dev/i2c-0等设备节点。从而对I2C设备进行读写操作。而摄像头部分,直接将控制接口传递给V4L2进行管理,这样通过视频设备驱动框架进行摄像头调节,从而达到控制的目的。
上述内容就是怎么进行Linux的I2C驱动框架分析,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。