consumer 角度讲一下i2c外设

往期内容

I2C子系统专栏:

  1. I2C(IIC)协议讲解-CSDN博客
  2. SMBus 协议详解-CSDN博客
  3. I2C相关结构体讲解:i2c_adapter、i2c_algorithm、i2c_msg-CSDN博客
  4. 内核提供的通用I2C设备驱动I2c-dev.c分析:注册篇
  5. 内核提供的通用I2C设备驱动I2C-dev.c分析:file_ops篇
  6. 设备驱动与设备树匹配机制详解
  7. 编写一个通用的i2c设备驱动框架
  8. 编写一个通用的i2c控制器驱动框架

总线和设备树专栏:

  1. 总线和设备树_憧憬一下的博客-CSDN博客
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客

前言

在上一章节(编写一个通用的i2c控制器驱动框架)讲了i2c_adapter。这里再继之前的文章( 编写一个通用的i2c设备驱动框架)进行补充,讲一下l2c_cleint的设备形态等

1. 两种设备形态

img

可以看出是有两种你描述的这两种设备形态反映了设备通过不同的总线进行交互的差异,并且在设备树(DTS)和驱动程序的编写上有不同的表现。根据设备的主要功能和数据传输方式,它们在 Linux 设备模型中的位置不同,从而影响了 DTS 文件中的结构和驱动程序的设计。我们来分析这两种设备形态。

形态 1: I2C-only 设备(图左侧):

  • 特点:这类设备和 CPU 之间的所有数据交互都是通过 I2C 总线完成的,没有其他传输方式。
  • DTS 描述:设备直接作为 I2C 控制器的子节点存在。在 DTS 中,设备的定义紧随其所在的 I2C 总线节点之后。例如 PMIC,它是 I2C 总线上的一个设备,因此直接在 I2C 节点的子节点中定义。
&i2c1 {
    clock-frequency = <100000>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c1>;
    status = "okay";

    pmic: pf0100@08 {
        compatible = "fsl,pfuze100";
        reg = <0x08>;
        ...
    };
};
  • 驱动模型:I2C 核心负责处理设备的创建和注册。驱动程序是以 I2C slave 设备的方式来实现,I2C 驱动框架通过 i2c_driver 结构和 i2c_device_id 来与设备匹配。设备的 proberemove 等函数由 I2C 核心进行管理。
static const struct i2c_device_id pmic_i2c_ids[] = {
    { "pfuze100", 0 },
    { /* sentinel */ }
};

static struct i2c_driver pmic_driver = {
    .driver = {
        .name = "pfuze100",
        .of_match_table = pmic_of_match,
    },
    .probe = pmic_probe,
    .remove = pmic_remove,
    .id_table = pmic_i2c_ids,
};

I2C 是主数据交互总线,设备完全依赖 I2C,所有操作通过 I2C 完成。

驱动注册流程通过 I2C 子系统的 i2c_driver 完成,设备由 I2C 核心管理。


形态 2: 复合设备(I2C 作为辅助接口,图右侧):

  • 特点:这类设备通过多种方式与 CPU 交互,例如,主要的数据传输(如音视频数据)通过 TDMS 接口,而配置信息或控制信息通过 I2C(DDC)接口。这类设备的核心功能决定了它们的主要位置在其他总线(如平台总线 platform bus)上,而 I2C 只是作为一个辅助接口。
  • DTS 描述:HDMI 设备作为 platform device 定义,并且通过 ddc-i2c-bus 属性引用 I2C 总线,用于 DDC(Display Data Channel)接口的 I2C 通信。
&hdmi {
    ddc-i2c-bus = <&i2c2>;
    status = "okay";
};
  • 驱动模型:在这种情况下,HDMI 控制器是主要设备,注册为 platform_device。I2C 通信是 HDMI 控制器的一部分,用来处理 EDID 等信息读取。在驱动程序中,I2C 总线是通过 DTS 中的 ddc-i2c-bus 属性传递给 HDMI 驱动的,驱动程序会通过 of_find_i2c_adapter_by_node() 获取并绑定 I2C 控制器。
struct i2c_adapter *ddc;
struct device_node *ddc_node;

ddc_node = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
if (!ddc_node) {
    dev_err(dev, "failed to find ddc-i2c-bus node\n");
    return -ENODEV;
}

ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
if (!ddc)
    return -EPROBE_DEFER;

/* Use ddc for I2C transactions */

I2C 是辅助总线,用于少量数据交互(如 EDID、配置信息传输),主要功能通过其他总线(如 TDMS)完成。

驱动以 platform_device 形式注册,I2C 仅作为其中一个通信接口,I2C 控制器通过设备树中的引用绑定。

2. 驱动编写

根据上面所说的形态去编写设备不同的i2c_driver

形态1: 完全依赖I2C的设备(如PMIC)

  1. 确定I2C adapter
    根据设备硬件连接的实际情况,确定该设备所从属的I2C controller,即I2C adapter(在I2C framework中,I2C controller通常被称为I2C adapter)。例如,PMIC设备可能连接在 i2c1 总线上。
  2. 在DTS中添加设备描述
    在设备树的I2C adapter节点中,添加I2C从设备的描述。这个从设备的定义格式与普通的platform device一致,例如:
&i2c1 {
    pmic: pfuze100@08 {
        compatible = "fsl,pfuze100";
        reg = <0x08>;
        // 其他字段...
    };
};
  1. compatible 字段的使用
    DTS 中的 compatible 字段用于设备驱动的匹配和 probe。I2C驱动会根据 compatible 字段匹配到合适的驱动程序,例如 "compatible = "fsl,pfuze100";"
  2. 编写I2C设备的驱动程序

示例:

static const struct of_device_id pmic_of_match[] = {
    { .compatible = "fsl,pfuze100", },
    { /* sentinel */ }
};

static struct i2c_driver pmic_driver = {
    .driver = {
        .name = "pfuze100",
        .of_match_table = pmic_of_match,
    },
    .probe = pmic_probe,
    .remove = pmic_remove,
};
static int __init i2c_driver_XXX_init(void)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return i2c_add_driver(&i2c_XXX_driver);
}
module_init(i2c_driver_XXX_init);
    • 定义并注册 i2c_driver:编写一个 struct i2c_driver 结构体,并使用 i2c_add_driver将其注册到I2C core中。
    • 匹配设备:在 i2c_driver 结构体中,定义 of_match_table,使其可以通过 compatible 字段进行匹配。
    • 实现 probe 回调:编写 probe 函数,用于设备初始化。
  1. I2C core处理设备注册
    当I2C adapter注册时,I2C framework的核心代码会自动为其下的所有I2C从设备创建 struct i2c_client 结构体,并根据设备树中的 compatible 字段,匹配合适的 i2c_driver,然后调用驱动程序的 probe 函数。
  2. probe
    当设备被I2C framework匹配到时,调用此函数进行设备初始化。主要负责获取设备资源(如设备地址、I2C Adapter),设置硬件寄存器,或者初始化其他系统资源(如注册字符设备)。
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    // 打印日志确认 probe 触发
    printk(KERN_INFO "ap3216c i2c device probe.\n");

    // 设置I2C client指针
    ap3216c_client = client;

    // 执行设备初始化:如设置初始寄存器值
    i2c_smbus_write_byte_data(client, 0x00, 0x03);  // 设置某些寄存器

    // 注册字符设备或其他资源
    major = register_chrdev(0, "ap3216c", &ap3216c_ops);
    ap3216c_class = class_create(THIS_MODULE, "ap3216c_class");
    device_create(ap3216c_class, NULL, MKDEV(major, 0), NULL, "ap3216c");

    return 0;
}

在注册的ap3216c_ops中,有关read和write的函数,使用到I2C数据有关的接口就有两类:

  • 一类是以i2c client为参数,进行简单的数据收发。该方法只可以通过标准方式,发送或者接收一定数量的数据。
  • 另一类是以i2c adapter和i2c msg为参数,可以更为灵活的read或者write数据,包括i2c_transfer。使用该方法可以以struct i2c_msg为参数,一次读取、或者写入、或者读取加写入,一定数量的数据。
  • 具体的函数去看\Linux-4.9.88\include\linux\i2c.h

形态2: 复合型设备(如HDMI)

  1. 确定I2C adapter
    根据设备硬件的连接方式,找到设备使用的I2C adapter。例如,HDMI 设备可以使用 i2c2 作为其 DDC 通道。
  2. 将设备作为平台设备描述
    将设备作为平台设备(platform device)进行描述。其 DTS 节点并不是直接位于I2C adapter下,而是位于根目录。例如:
&hdmi {
    ddc-i2c-bus = <&i2c2>;
    status = "okay";
};
  1. 获取I2C adapter的引用
    在DTS描述中使用 ddc-i2c-bus 属性引用I2C adapter节点。该属性通过 of_parse_phandle 来解析,并在驱动中获取相应的I2C adapter的指针。
  2. probe 函数中获取I2C adapter
    在平台设备的 probe 函数中,通过 of_parse_phandle 来获取I2C adapter节点,并通过 of_find_i2c_adapter_by_node() 获取对应的 struct i2c_adapter 指针。如下代码示例:
struct i2c_adapter *ddc;
struct device_node *ddc_node;

ddc_node = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
if (!ddc_node) {
    dev_err(dev, "failed to find ddc-i2c-bus node\n");
    return -ENODEV;
}

ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
if (!ddc)
    return -EPROBE_DEFER;
  1. 使用 i2c_transfer 进行读写操作
    获取I2C adapter之后,便可以使用 i2c_transfer() 接口来进行I2C读写操作。例如,通过DDC接口读取EDID信息时:
struct i2c_msg msgs[] = {
    {
        .addr = ddc_addr,
        .flags = I2C_M_RD,
        .len = 128,
        .buf = edid_buf,
    },
};

ret = i2c_transfer(ddc, msgs, 1);  // 读取EDID数据

3. i2c_client

i2c设备中用i2c_client结构体来指代:

/**
 * struct i2c_client - represent an I2C slave device
 * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
 *	I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
 * @addr: Address used on the I2C bus connected to the parent adapter.
 * @name: Indicates the type of the device, usually a chip name that's
 *	generic enough to hide second-sourcing and compatible revisions.
 * @adapter: manages the bus segment hosting this I2C device
 * @dev: Driver model device node for the slave.
 * @irq: indicates the IRQ generated by this device (if any)
 * @detected: member of an i2c_driver.clients list or i2c-core's
 *	userspace_devices list
 * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
 *	calls it to pass on slave events to the slave driver.
 *
 * An i2c_client identifies a single device (i.e. chip) connected to an
 * i2c bus. The behaviour exposed to Linux is defined by the driver
 * managing the device.
 */
struct i2c_client {
	unsigned short flags;		/* div., see below		*/
	unsigned short addr;		/* chip address - NOTE: 7bit	*/
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	char name[I2C_NAME_SIZE];
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
	struct device dev;		/* the device structure		*/
	int irq;			/* irq issued by device		*/
	struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
	i2c_slave_cb_t slave_cb;	/* callback for slave mode	*/
#endif
};

flags:

  • 表示I2C设备的标志位。可以包含以下选项:

    • I2C_CLIENT_TEN:表示设备使用的是十位地址(而不是常见的七位地址)。
    • I2C_CLIENT_PEC:表示设备使用SMBus包错误校验(Packet Error Checking,PEC),这是一种错误检测机制。

addr:

  • I2C设备的地址。地址位于I2C总线上的低7位或低10位(如果 flags 指示设备使用十位地址的话)。

name:

  • 一个字符数组,表示设备的名称,通常是设备的芯片名称或类型。名称尽可能保持通用,以适应同一设备的不同版本或兼容的设备。

adapter:

  • 指向设备所在的I2C总线适配器(i2c_adapter)的指针。每个I2C设备都挂载在一个I2C适配器上,适配器管理与设备的通信。

dev:

  • Linux驱动模型中的设备节点(struct device),用于表示I2C从设备。这是Linux设备模型的标准数据结构,提供了与设备类、驱动程序以及电源管理等相关的接口。

irq:

  • 表示设备所使用的中断号(如果该设备可以生成中断)。不使用中断的设备此字段可能为0或无效值。

detected:

  • 用于链表结构中的节点。在内核中,这个字段用于将该设备连接到 i2c_driver.clients 列表或 i2c-coreuserspace_devices 列表中。

slave_cb:

  • 仅在启用了 I2C Slave 模式(即 CONFIG_I2C_SLAVE 选项启用时)时有效。
  • 这是用于适配器从模式的回调函数指针。当I2C适配器处于从模式时,适配器调用这个回调函数,将从模式的事件传递给从设备的驱动程序。

i2c_client一般是在register adapter的时候,解析adapter的child node自行创建的,这个在对上一章节中对i2c_add_adapte函数进行分析的时候有提到过

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/896486.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

02篇 机械考研复试简历保姆级教程,考研简历联系导师邮件复试调剂超全攻略 导师喜欢看到的简历(附模板)

考研复试简历怎么写&#xff1f;导师喜欢看到的简历&#xff08;附模板&#xff09; 复试简历&#xff0c;重要程度max&#xff01;绝非小事一桩&#xff01;它就像是你硬核经历的闪亮外衣&#xff0c;条理清晰、逻辑严谨且设计感十足&#xff0c;一定能在导师心中留下深刻印象…

【新专栏】Excel数据分析与模拟决策-送完整电子版内容

专栏入口&#xff1a;Excel数据分析与模拟决策 购买专栏&#xff0c;即送对应完整版电子书及配套的Excel文件。

【学习笔记】网络设备(华为交换机)基础知识 9 —— 堆叠配置

提示&#xff1a;学习华为交换机堆叠配置&#xff0c;含堆叠的概念、功能、角色、ID和优先级&#xff1b;堆叠的建立过程以及注意事项&#xff1b;包含堆叠的配置命令&#xff0c;以及堆叠的配置案例 一、前期准备 1.已经可以正常访问交换机的命令行接口 Console口本地访问教…

逻辑移位的学习

逻辑移位&#xff08;Logical Shift&#xff09;是计算机科学中的一种位移操作&#xff0c;它用于将二进制数的位向左或向右移动。逻辑移位的特点是&#xff0c;无论是左移还是右移&#xff0c;移出边界的位都被丢弃&#xff0c;并用零填充空缺的位。逻辑移位适用于无符号数的处…

【C语言】文件操作(2)(文件缓冲区和随机读取函数)

文章目录 一、文件的随机读取函数1.fseek函数2.ftell函数3.rewind函数 二、文件读取结束的判断1.被错误使用的feof2.判断文件读取结束的方法3.判断文件结束的原因feofferror判断文件读取结束原因示例 三、文件缓冲区 一、文件的随机读取函数 在上一篇的文章中&#xff0c;我们讲…

算法笔记day07

1.最长回文子串 最长回文子串_牛客题霸_牛客网 算法思路&#xff1a; 使用中心扩散算法&#xff0c;枚举所有的中点&#xff0c;向两边扩散&#xff0c;一个中点需要枚举两次&#xff0c;一次当回文串是奇数另一次回文串是偶数的情况。 class Solution { public:int getLong…

JRT怎么从IRIS切换到PostGreSql库

1.执行M导出得到建库脚本文件 2.下载生成的脚本到本地D盘 3.修改驱动为PostGreSql 4.修改连接串 5.到PostGreSql里面创建一个jrtlis的数据库&#xff0c;模式为jrt 6.启动网站点击导入脚本按钮 导入完成了就可以正常使用PostGreSql库了

Linux 进程终止和进程等待

目录 0.前言 1. 进程终止 1.1 进程退出的场景 1.2 进程常见退出方法 1.2.1 正常退出 1.2.2 异常退出 2. 进程等待 2.1 进程等待的重要性 2.2 进程等待的方法 2.2.1 wait() 方法 2.2.2 waitpid() 方法 2.3 获取子进程 status 2.4 阻塞等待和非阻塞等待 2.4.1 阻塞等待 2.4.2 非阻…

萤石联名朱炳仁・铜推出“萤石・国礼大师”AI智能锁 共襄美好家生活

引言&#xff1a;当前&#xff0c;文化与科技正以前所未有的紧密程度相互融合&#xff0c;以人工智能为代表的智能科技的强势介入正推动非遗文化实现从创意策划、生产制造、传播方式乃至保存模式的全面革新&#xff0c;孕育着无限可能。 另一方面&#xff0c;当下智能锁行业竞…

传知代码-字里行间的背叛:博文出卖了你

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 你的博文透露了你内心的秘密 随着社交媒体和短视频行业的快速发展&#xff0c;来自文本、视频和音频的多模态数据爆发式增长。 同时&#xff0c;捕捉设备的广泛使用&#xff0c;加上其使用的简便性、移动能力和低…

界面控件DevExtreme中文教程 - 如何与Amazon S3和Azure Blob存储集成?

DevExtreme拥有高性能的HTML5 / JavaScript小部件集合&#xff0c;使您可以利用现代Web开发堆栈&#xff08;包括React&#xff0c;Angular&#xff0c;ASP.NET Core&#xff0c;jQuery&#xff0c;Knockout等&#xff09;构建交互式的Web应用程序。从Angular和Reac&#xff0c…

1. 安装框架

一、安装 Laravel 11 框架 按照官方文档直接下一步安装即可 1. 安装步骤 2. 执行数据库迁移 在.env文件中提前配置好数据库连接信息 php artisan migrate二、安装 Filament3.2 参考 中文文档 进行安装 1. 安装 拓展包 composer require filament/filament:"^3.2" -W…

【功能安全】相关项定义item definition

目录 01 item definition定义 02 相关项组成 03 相关项最佳实践 📖 推荐阅读 01 item definition定义 概念阶段的开发是以相关项定义(Item Definition)开始的,相关项定义是对系统的描述,此系统也是标准中安全要求应用的对象。 相关项定义目的: a) 在整车层面对相关…

C++ string(2)

文章目录 1.初识迭代器和范围for1.1迭代器1.2范围for1.3 aout关键字 2.字符串长度相关计算1.size 和 length2. capacity 和 reserve 3.例题演示1. [917. 仅仅反转字母 - 力扣&#xff08;LeetCode&#xff09;](https://leetcode.cn/problems/reverse-only-letters/description…

spring揭秘31-spring任务调度01-spring集成Quartz及JDKTimer定时器

文章目录 【README】【1】Quartz任务调度框架【1.1】Job调度任务【1.2】任务调度触发器Trigger【1.3】\*Quartz框架执行调度任务代码实践【1.3.1】硬编码执行Quartz调度任务【1.3.2】基于生产者模式执行quartz调度任务&#xff08;推荐&#xff09; 【2】spring集成Quartz【2.1…

查找与排序-选择排序

选择排序也是基于“比较”和“交换”两种操作来实现的排序方法 。 每一趟排序在待排序序列中选择关键字最小&#xff08;或最大&#xff09;的数据元素加入到排好序的序列前&#xff08;或后&#xff09;&#xff0c;直至所有元素排完为止。 一、简单选择排序 1.简单…

2024产品管理新风向:项目管理软件不懂敏捷开发?

一、产品管理与敏捷开发的紧密关联 产品管理和敏捷开发之间存在着紧密的关联&#xff0c;二者相互促进&#xff0c;共同为企业创造价值。 &#xff08;一&#xff09;敏捷开发为产品管理带来的优势 敏捷开发能够极大地加快产品上市速度。在传统的开发模式下&#xff0c;产品…

SAP 关于在交货单进行定价条件的确定简介

SAP 关于在交货单进行定价条件的确定简介 业务场景前台操作1、创建交货单2、创建交货单3、创建发票系统配置1、定义条件类型2、定义并分配定价过程3、定义交货的定价过程确定4、维护开票凭证的复制控制SAP交货单定价是针对销售交货单的价格计算过程,通常包括基本价格、折扣、附…

Java读取PDF后做知识库问答_SpringAI实现

​​​​​​​​​​​​​​ 核心思路&#xff1a; 简单来说&#xff0c;就是把PDF文件读取并向量化&#xff0c;然后放到向量存储里面&#xff0c;再通过大模型&#xff0c;来实现问答。 RAG&#xff08;检索增强生成&#xff09;介绍&#xff1a; 检索增强生成&#x…

数据结构——树、二叉树和森林间的转换

前言 介绍 &#x1f343;数据结构专区&#xff1a;数据结构 参考 该部分知识参考于《数据结构&#xff08;C语言版 第2版&#xff09;》129~130页 &#x1f308;每一个清晨&#xff0c;都是世界对你说的最温柔的早安&#xff1a;ૢ(≧▽≦)و✨ 目录 前言 1、基础知识 2…