专栏首页嵌入式iotLinux的I2C驱动框架分析

Linux的I2C驱动框架分析

1.基本概念


总线

总线代表着同类设备需要共同遵守的工作时序,不同的总线对于物理电平的要求是不一样的,对于每个比特的电平维持宽度也是不一样,而总线上传递的命令也会有自己的格式约束。如I2C总线、USB总线、PCI总线等等。以I2C总线为例,在同一组I2C总线上连接着不同的I2C设备。

设备

设备代表真实的、具体的物理器件,在软件上用器件的独特的参数属性来代表该器件。如I2C总线上连接的I2C从设备都有一个标识自己的设备地址,由这个设备地址来确定主设备发过来的命令是否该由它来响应。

驱动

简单的说驱动代表着操作设备的方式和流程。

Linux总线设备框架的工作原理

如果想要弄清楚I2C驱动框架,必须深刻的理解Linux的总线设备框架。之所以会形成这样的框架,很重要的原因是为了代码的复用性。因为驱动和设备的关系是一对多的,对于相同类型的不同的设备,可共用同一套驱动程序接口。为了提高驱动的可移植性,Linux抽象出一套管理资源的函数。设备是存在的硬件,在设备里包含自己的属性,也包含需要用到的资源。

总线的作用就是在软件层面上对设备和驱动进行管理,设备要让系统感知到自己的存在,所以需要向总线去注册设备,驱动同样也要向总线去注册。对于总线,有I2C总线,Platform总线等等。但是Platform是虚拟总线。

对于总线上设备与驱动的匹配,由总线负责,设备在注册的时候,总线会遍历注册在总线上的驱动,如果名字相同,则匹配上了,此时调用驱动程序的probe函数。同样的驱动在注册的时候,也会遍历总线上的设备,如果匹配上(名字一样),则也会调用驱动程序的probe函数。

2.I2C传输协议


对于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的硬件层与协议层的基本概述,这部分可以作为基本认知。

3.Linux下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底层的操作

4.GC0328摄像头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完整的传输流程就完成了。

5.总结


对于I2C完整的传输协议,最重要的是弄清楚总线驱动程序的框架,因为I2C也是属于总线框架。对于I2C总线设备框架的模型,可以用下图来说明:

也就是device与driver同时向i2c总线上注册。当注册在总线上时,可以通过id_table进行匹配,匹配上之后会调用driver的.probe函数。对于一般的I2C设备,可以在probe函数中注册一个字符设备驱动,从而应用层可以通过open函数打开/dev/i2c-0等设备节点。从而对I2C设备进行读写操作。而摄像头部分,直接将控制接口传递给V4L2进行管理,这样通过视频设备驱动框架进行摄像头调节,从而达到控制的目的。

本文分享自微信公众号 - 嵌入式IoT(Embeded_IoT),作者:bigmagic

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-10-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 串口通信用户层协议编制技巧与实现

    协议就是约束双方通信的一种规范,只有严格遵守这种协议的设备才能进行相互的通信。比如串口通信协议,必须包含起始位、主体数据、校验位及停止位,双方需要约定一致的数据...

    bigmagic
  • 8266wifi模块开发详解(四)OLED

    1. 说明2.什么是OLED?3.OLED的技术特点4.实物赏析5.SSD13066.通信总线基本介绍7.操作原理8.实战操作8.1 硬件连接8.2 软件操作8...

    bigmagic
  • 肖特基二极管与瞬态抑制二极管TVS的作用

    电路中二级管的种类繁多,现在主要讲一下肖特基二极管与瞬态抑制二极管的作用与实际的电路连接。

    bigmagic
  • I2C 总线协议

    1.I2C协议 2条双向串行线,一条数据线SDA,一条时钟线SCL。 SDA传输数据是大端传输,每次传输8bit,即一字节。 支持多主控(...

    RainMark
  • 很清晰的解读i2c协议【转】

    转自:https://blog.csdn.net/weixin_41718085/article/details/79376823

    用户3033338
  • 大吉大利,今晚如何用 Python 解锁“吃鸡”的正确姿势

    大吉大利,今晚吃鸡~ 今天跟朋友玩了几把吃鸡,经历了各种死法,还被嘲笑说论女生吃鸡的100种死法,比如被拳头抡死、跳伞落到房顶边缘摔死 、把吃鸡玩成飞车被车技秀...

    IT派
  • 教你用Python解锁“吃鸡”的正确姿势!

    本文用Python分析吃鸡比赛的真实数据,解答至关重要的9个问题,助你提高吃鸡概率。

    数据派THU
  • 数据可视化教你玩吃鸡——吃鸡 9 问 !

    大吉大利,今晚吃鸡~ 今天跟朋友玩了几把吃鸡,经历了各种死法,还被嘲笑说论女生吃鸡的100种死法,比如被拳头抡死、跳伞落到房顶边缘摔死 、把吃鸡玩成飞车被车技秀...

    小小詹同学
  • 大数据助你“吃鸡”一臂之力!

    对于我这样一直喜欢苟着的良心玩家,在经历了无数次落地成河的惨痛经历后,我是坚决不会选择跳P城这样楼房密集的城市,穷归穷但保命要紧。所以我们决定统计一下到底哪些地...

    数据森麟
  • HEXO博客迁移的步骤

    最近买了新电脑,但是博客是今天才迁移过来。本以为很麻烦,实际上操作非常简单,只需要三个步骤。

    故事尾音

扫码关注云+社区

领取腾讯云代金券