你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

从高层级来分析一下I2C系统的设计思路

[复制链接]
gaosmile 发布时间:2020-6-1 18:07
[导读] 本文通过阅读内核代码,来梳理一下I2C子系统的整体视图。在开发I2C设备驱动程序时,往往缺乏对于系统整体的认识,没有一个清晰的思路。所以从高层级来分析一下I2C系统的设计思路,将有助于设计调试具体的驱动程序。
I2C/SMBUS基础

I2C是一种芯片间通讯总线技术,最早由Philips设计制定。下面内容参考I2C 2.1 规格书

  • 半双工通信方式,通信采用主/从结构
  • 支持多主模式,下图来源于I2C 2.1规格书
微信图片_20200601180124.jpg
  • 其内部电气实现采用集电极开路(Open-collector)/漏极开路(open-drain)结构以实现线与功能,这是总线的实现基础,多芯片通过查询总线状态实现介质仲裁以实现总线控制。
  • 微信图片_20200601180127.png
  • 总线信号由两线实现,串行时钟线SCL(Serial Clock Line)/串行数据线SDA(serial Data Line)。
  • 具有三种通讯速率模式:
    • standard mode:0-100 kbps  (bps: bit/s)
    • Fast mode:0-400 kbps
    • High-speed mode : 0-3.4Mbps
  • 可支持混速模式
微信图片_20200601180130.png
  • 不同的速率在硬件设计时需要注意信号的完整性,I2C总线等效电容Cx,主要需要考虑PCB布线,以及上拉电阻选取。
  • 支持7bit/10bit 两种芯片地址模式
  • I2C总线电气特性,这个非常重要,须严格遵守标准的电气特性
微信图片_20200601180133.png
微信图片_20200601180136.png
  • SMBUS(system management bus) 。大多数SMBus系统也符合I2C,电气约束对于SMBus更为严格,并且它标准化了特定的协议消息和习惯用语。支持I2C的控制器也可以支持大多数SMBus操作,但是SMBus控制器并不支持I2C控制器将支持的所有协议选项。通过使用I2C原语或通过向不支持这些I2C操作的i2c_adapter设备发出SMBus命令,可以执行各种SMBus协议操作。

    http://smbus.org/

  • I2C bus(Inter-Integrated Circuit bus) http://www.i2c-bus.org/


I2C 在Linux设备中的拓扑结构
  • 在PC体系中,大体如下拓扑:
微信图片_20200601180139.png
PC体系中通过桥接芯片,扩展出PCI,在由PCI扩展出I2C适配器,进而得到I2C总线,或者桥接芯片直接扩展出SMBUS/I2C总线。
  • 在嵌入式应用中,则可能为:
微信图片_20200601180142.png

嵌入式应用中,则可能更多的情况是处理器内置了I2C/SMBUS总线控制器,直接可得到I2C/SMBUS总线。嵌入式系统中常常会设计很多传感器挂载在I2C总线上,比如温度检测,压力检测等等,又或者诸如电容触摸屏、电源管理IC等等。

代码实现
  • I2C 的core实现位于./drivers/i2c/下,实现了I2C总线设备以及驱动(适配器)和设备驱动的注册、注销方法,I2C通信方法algorithm抽象,以及与具体硬件无关的代码
  • I2C主控制器驱动位于 ./drivers/i2c/busses/,这里主要实现总线控制器,具体体现为i2c_adapter的实现。负责I2C适配器与从设备通信。I2C总线驱动由i2c_adapter和i2c_algorithm来抽象描述。
  • 设备驱动则分散在./driver/下,这取决于具体的实现,种类繁多。
    • i2c-dev,大多位于drivers/i2c/i2c-dev.c,这种方法只是封装了主机(I2Cmaster,一般是SoC中内置的I2C控制器)的I2C基本操作,并且向应用层提供相应的操作接口,应用层代码需要自己去实现对slave的控制和操作,所以这种I2C驱动相当于提供给应用层可以访问slave硬件设备的接口,本身并未对硬件做任何操作,应用需要实现对硬件的操作。这种模式也称为应用驱动程序。
    • 另一种I2C驱动是将所有的代码都放在驱动层实现,直接向应用层提供最终结果。应用层甚至不需要知道这里面有I2C存在,譬如电容式触摸屏驱动,直接向应用层提供/dev/input/event1的操作接口,应用层编程的人根本不知道event1中涉及到了I2C。
微信图片_20200601180145.png

I2C子系统的主要目的是,对I2C总线以及设备利用面向对象编程思想实现统一建模,以高内聚-低耦合的软件工程思想,实现一个分层体系结构,以便于内核统一管理I2C设备,从而可以更容易的在linux下实现I2C设备以及高可移植。

主要数据结构

其内部有几个关键数据结构,来梳理一下:

  • i2c_client, 用于抽象挂载在I2C总线上的从设备
  • i2c_driver,用于驱动挂载在I2C总线的从设备,也即从设备的设备驱动程序
  • i2c_adapter,用于抽象I2C的主设备
  • i2c_algorithm,抽象I2C总线操作接口
微信图片_20200601180148.png
i2c_devinfo

该结构体主要用于板级I2C信息管理

微信图片_20200601180152.png
i2c_msg

该结构体主要用于抽象I2C报文,其内容如下: 微信图片_20200601180155.png

i2c_timings

主要用于抽象I2C电气特性,对于支持设备树的系统构建而言,主要通过以下内核接口函数,从设备树解析电气特性参数。

void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults)
{
int ret;

memset(t, 0, sizeof(*t));

ret = device_property_read_u32(dev, "clock-frequency", &t->bus_freq_hz);
if (ret && use_defaults)
  t->bus_freq_hz = 100000;

ret = device_property_read_u32(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns);
if (ret && use_defaults) {
  if (t->bus_freq_hz <= 100000)
   t->scl_rise_ns = 1000;
  else if (t->bus_freq_hz <= 400000)
   t->scl_rise_ns = 300;
  else
   t->scl_rise_ns = 120;
}

ret = device_property_read_u32(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns);
if (ret && use_defaults) {
  if (t->bus_freq_hz <= 400000)
   t->scl_fall_ns = 300;
  else
   t->scl_fall_ns = 120;
}

device_property_read_u32(dev, "i2c-scl-internal-delay-ns", &t->scl_int_delay_ns);

ret = device_property_read_u32(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns);
if (ret && use_defaults)
  t->sda_fall_ns = t->scl_fall_ns;

device_property_read_u32(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns);
}
EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);

微信图片_20200601180158.jpg
i2c_device_identity

该结构体主要用于抽象I2C 设备的ID属性,通过内核接口函数i2c_get_device_id以获取设备ID属性。

微信图片_20200601180202.png
总体框架概述

Linux I2C编程接口支持总线交互的主端和从端。从高层级看由两种驱动程序和两种设备构成:

  • 适配器设备与适配器设备驱动对:I2C 适配器驱动程序用于抽象控制器硬件;它绑定到一个物理设备(可能是一个PCI设备(PC体系多一些)或platform_device(嵌入式应用居多)),并构建i2c_adapter实体以呈现所管理的1个I2C总线段。


    /* ALI1535 device address register bits */

    #define ALI1535_RD_ADDR  0x01 /* Read/Write Bit in Device */

         
    /*  Address field  */

         
    /*  -> Write = 0  */

         
    /*  -> Read  = 1  */

    #define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable  */

    /*PCI 设备驱动*/

    static
    struct pci_driver ali1535_driver;

    static
    unsigned
    long
    ali1535_smba;

    static
    unsigned
    short
    ali1535_offset;

    • pci-I2C 适配器设备。如在i2c-ali1535.c中:
    • platform_device。比如:i2c-s3c2410,如下:
        static const struct platform_device_id s3c24xx_driver_ids[] = {
{

  .name  =
"s3c2410-i2c"
,

  .driver_data =
0
,

}, {

  .name  =
"s3c2440-i2c"
,

  .driver_data = QUIRK_S3C2440,

}, {

  .name  =
"s3c2440-hdmiphy-i2c"
,

  .driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,

}, { },

};

MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
  • I2C从设备及设备驱动:每个I2C总线段上将有一个由结构i2c_client表示的I2C设备。这些设备将被绑定到一个struct i2c_driver,遵循标准的Linux驱动程序模型。
架构

图片来源:http://www.kernel.org/doc/html/latest/i2c/slave-interface.html

微信图片_20200601180205.png

主端总线驱动职责:

  • 适配器和算法驱动程序,见下面i2c_algorithm结构体详细描述
  • 管理I2C总线交互

从端设备驱动职责:

  • i2c_client结构体具有设备的I2C总线地址以及适配器的驱动程序指针

当用户程序发出文件操作申请I2C事务时

  • i2C_transfer (i2C-core.c) 调用 adap_algo_master_xfer,数据或消息以i2c_msg结构体传入。
  • 适配器对硬件I / O地址进行读/写操作,实现底层的I2C读写设备操作。

从应用程序直到底层的大致交互流程如下:

微信图片_20200601180208.png
总结一下

I2C总线子系统在Linux内核中总线模型分为主/从两端,主端主要有适配器以及适配器驱动负责管理总线,从端主要有从设备抽象以及设备驱动,实现具体的从设备应用。主端适配器以两种形式存在于内核代码中,分为PCI桥接适配器或者platform_device形式。从总体理解I2C子系统的驱动模型,以及相应主要数据结构之间的关系,将有助于开发调试驱动程序,快速定位问题。


收藏 评论1 发布时间:2020-6-1 18:07

举报

1个回答
likang1202 回答时间:2020-6-2 09:04:21
谢谢分享

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版