Linux的I2C总线的原理和结构详解

Linux的I2C总线的原理和结构讲解

我前面基本已经吃透了Platform总线,关于Platform总线的原理和结构,详情见下面三篇博文:
https://blog.csdn.net/wenhao_ir/article/details/145023181

https://blog.csdn.net/wenhao_ir/article/details/145018442

https://blog.csdn.net/wenhao_ir/article/details/145030037

在Platform总线中,三个重点是匹配规则(总线的match函数)、platform_device结构体、platform_driver结构体。关于 Platform总线不错的视频总结回顾讲解,请百度网盘搜索“1-3_03_SPI总线设备驱动”,然后从头开始看。

在Linux的I2C总线中,其实重点也是类似的三点,即I2C的match机制、i2c_client结构体、i2c_driver结构体,与Platform总线不同的是,在Platform总线中,总线是一个虚拟的总线概念,而在Linux的I2C总线结构中,总线是实实在在的,I2C本来就是一个物理总线嘛,它上面可以外挂很多I2C设备。我们可以利用Linux的I2C总线为这些外挂的I2C设备也提供上相应的驱动,这样上层应用就可以不用再去像博文 https://blog.csdn.net/wenhao_ir/article/details/146361457 中那样,还需要在用户空间去操作I2C控制器去直接操作硬件,而是直接用相应的驱动程序就可以调用相应的I2C设备了。
Linux的I2C总线的结构原理图如下:
在这里插入图片描述

关于Linux的I2C总线的结构原理的详细介绍,请百度网盘搜索“1-8_4-8_I2C系统驱动程序模型”,然后从05分45秒开始看。这里就不再赘述了,因为只要吃透了Platform总线,那么理解Linux的I2C总线是很简单的。

i2c_driver的Demo实现(仅框架)

i2c_driver的Demo实现源码

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/i2c.h>

static const struct of_device_id of_match_ids_example[] = {
	{ .compatible = "com_name,chip_name" },
	{ /* END OF LIST */ }
};

static const struct i2c_device_id example_ids[] = {
	{ "chip_name", 0 },
	{ /* END OF LIST */ }
};

static int i2c_driver_example_probe(struct i2c_client *client)
{
	return 0;
}

static int i2c_driver_example_remove(struct i2c_client *client)
{
	return 0;
}

static struct i2c_driver i2c_example_driver = {
	.driver = {
		.name = "example",
		.of_match_table = of_match_ids_example,
	},
	.probe_new = i2c_driver_example_probe,
	.remove = i2c_driver_example_remove,
	.id_table = example_ids,
};

static int __init i2c_driver_example_init(void)
{
	return i2c_add_driver(&i2c_example_driver);
}

static void __exit i2c_driver_example_exit(void)
{
	i2c_del_driver(&i2c_example_driver);
}

module_init(i2c_driver_example_init);

module_exit(i2c_driver_example_exit);

MODULE_AUTHOR("suwenhao");
MODULE_LICENSE("GPL");

i2c_driver的Demo实现源码分析

因为前面基本已经吃透了Platform总线,所以看这段源码是非常轻松的,不过为了将来能快速回顾,还是用chatgpt生成分析吧。

这段代码是一个 Linux I2C 设备驱动的简单示例,它实现了一个 I2C 驱动框架,并注册到了 I2C 总线。下面是对该驱动的详细分析:


代码结构分析
该驱动实现了以下主要功能:

  1. 匹配设备

    • 使用 of_device_id 结构体支持设备树匹配。
    • 使用 i2c_device_id 结构体支持传统的 I2C 设备 ID 匹配。
  2. 驱动核心函数

    • probe_new(即 probe)函数:当匹配到 I2C 设备时调用,用于设备初始化。
    • remove 函数:当设备被移除时调用,用于释放资源。
  3. 驱动注册

    • i2c_add_driver() 注册驱动。
    • i2c_del_driver() 注销驱动。

详细分析
1. 设备树匹配表

static const struct of_device_id of_match_ids_example[] = {
    { .compatible = "com_name,chip_name" },
    { /* END OF LIST */ }
};
  • 这个表用于设备树匹配,当 compatible 属性匹配时,设备就会与该驱动匹配。
  • "com_name,chip_name" 是设备树中的 compatible 属性,格式一般为 "厂商名,芯片名"

2. 设备 ID 匹配表

static const struct i2c_device_id example_ids[] = {
    { "chip_name", 0 },
    { /* END OF LIST */ }
};
  • 用于不依赖设备树的传统 I2C 设备匹配机制。
  • example_ids(即 i2c_device_id 结构体数组)用于匹配 i2c_client 结构体中的 name 字段。i2c_client 结构体的定义在 include/linux/i2c.h 中,关键部分如下:
    struct i2c_client {
        char name[I2C_NAME_SIZE];  // 设备名称
        const struct i2c_device_id *id;
        struct device dev;
        ...
    };
    
    其中,name 是 I2C 设备的名称,用于驱动匹配。
  • "chip_name"i2c_client 结构体中的 name 字段相同,即可匹配成功。

3. probe 函数

static int i2c_driver_example_probe(struct i2c_client *client)
{
    return 0;
}
  • probe 在设备匹配成功后被调用,一般在这里进行硬件初始化,比如:
    • 读取 I2C 设备寄存器
    • 申请资源(GPIO、中断等)
    • 注册设备节点(devm_* API)
  • 这里返回 0,意味着它什么也没做,仅仅是个框架。

4. remove 函数

static int i2c_driver_example_remove(struct i2c_client *client)
{
    return 0;
}
  • 当驱动被卸载或者设备被移除时,remove 被调用,用于释放 probe 里申请的资源。
  • 这里同样没有实际操作,通常需要:
    • 释放申请的资源(GPIO、中断等)
    • 注销子设备或文件系统接口

5. I2C 驱动结构体

static struct i2c_driver i2c_example_driver = {
    .driver = {
        .name = "example",
        .of_match_table = of_match_ids_example,
    },
    .probe_new = i2c_driver_example_probe,
    .remove = i2c_driver_example_remove,
    .id_table = example_ids,
};
  • driver.name:用于 sysfs 和日志中标识该驱动,这个名字也可用来用i2c_client进行匹配。
  • of_match_table:绑定设备树匹配表。
  • probe_new:新的 probe API,建议使用 probe_new 而不是 probe
  • remove:移除设备时调用。
  • id_table:传统 I2C 设备 ID 匹配表。

6. 驱动初始化和注销

static int __init i2c_driver_example_init(void)
{
    return i2c_add_driver(&i2c_example_driver);
}

static void __exit i2c_driver_example_exit(void)
{
    i2c_del_driver(&i2c_example_driver);
}
  • i2c_add_driver():将驱动注册到 I2C 子系统。
  • i2c_del_driver():从 I2C 子系统移除驱动。

7. 模块声明

module_init(i2c_driver_example_init);
module_exit(i2c_driver_example_exit);

MODULE_AUTHOR("suwenhao");
MODULE_LICENSE("GPL");
  • module_init()module_exit() 设定了驱动的加载和卸载函数。
  • MODULE_LICENSE("GPL") 指明许可证,防止内核报 taint(污染)警告。

问:i2c_driver中并没有指定是哪条I2C总线,那么它怎么知道操作哪条I2C总线?

答案很简单,因为在i2c_client中会含有I2C设备挂接于哪条I2C总线的信息啦。你往这篇博文后面看i2c_client的实现就知道了。

i2c_client的实现

i2c_client的实现有三种方式,分别为直接使用echo命令创建、利用代码实现、利用设备树生成,详情见 https://blog.csdn.net/wenhao_ir/article/details/146417363 【搜索“i2c_client的实现和生成”】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值