首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

编写一个通用的i2c控制器驱动框架

  • 25-03-04 14:01
  • 3083
  • 12496
blog.csdn.net

往期内容

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设备驱动框架

总线和设备树专栏:

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

前言

本章需要用到的内核源码:

Linux内核真正的I2C控制器驱动程序

  • IMX6ULL: Linux-4.9.88\drivers\i2c\busses\i2c-imx.c?i2c-imx.c

1.分辨

1.1 I2C总线和平台总线区别

i2c总线-设备-驱动平台的注册,i2c总线是真实存在的,而bus总线-设备-驱动平台中的bus则是虚拟的

i2c总线-设备-驱动平台管理着多条总线上的i2c_Controller And Devices

i2c控制器也有自己的驱动(I2c_Adapter_drv)

  • 可以自己通过注册万能的bus总线平台,让驱动程序platform_driver来和platform_device设备节点(有控制器的name、属性设置等)匹配,来代表着一个总线,创建i2c_adapter。创建平台只是为了让驱动和设备节点相匹配,使该总线上i2c控制器功能的实现
  • 一个i2c_adapter就代表着一个总线,代表着一个i2c controller
  • 核心函数是master_xfer函数,这个函数是将数据传给总线上的i2c设备

i2c_client上则记录着i2c设备自己被哪一个i2c Controller(i2c_adapter)管理着,也就是在哪一条总线上,以及设备地址,一般是有设备树节点转化而来

i2c_driver上则是为i2c设备注册驱动程序、生成设备节点、设备类等,为app提供打开设备的设备节点、提供对设备的操作函数,比如读取或写入。注意的是要先用i2c_add_driver函数将驱动添加进i2c总线平台中,并且读取和写入是要先经过i2c_controller的

  • 如i2c_transfer函数,会将device_driver的数据(来自app)转发i2c_client中记录的i2c_adapter,也就是i2c控制器,之后i2c控制器对应的驱动程序又会将数据转发给在总线上的I2C设备(i2c_client源)中的存储芯片等
  • i2c_driver和i2c_client之间的匹配则是有i2c总线平台来进行匹配的,匹配成功后才能实现数据传给i2c_controller,进而传给i2c device
  • 当然,也可以自己注册bus总线平台,使用GPIO模拟I2C来让platform_device和platform_driver进行匹配,也就是i2c设备信息client和驱动程序进行匹配,只需要将GPIO引脚模拟I2C引脚输出I2C协议就行。这一种在后面也会出文章讲解。

img

1.2 I2C驱动程序的层次

img

img

2. I2C_Adapter驱动框架

2.1 核心的结构体

i2c_adapter

img

I2C相关结构体讲解:i2c_adapter、i2c_algorithm、i2c_msg-CSDN博客该结构体在之前的文章也有介绍过其相关成员。

i2c_algorithm

\Linux-4.9.88\include\uapi\linux\i2c.h
struct i2c_algorithm {
	/* If an adapter algorithm can't do I2C-level access, set master_xfer
	   to NULL. If an adapter algorithm can do SMBus access, set
	   smbus_xfer. If set to NULL, the SMBus protocol is simulated
	   using common I2C messages */
	/* master_xfer should return the number of messages successfully
	   processed, or a negative value on error */
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
			   int num);  
        
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
			   unsigned short flags, char read_write,
			   u8 command, int size, union i2c_smbus_data *data);
	/* To determine what the adapter supports */
	u32 (*functionality) (struct i2c_adapter *);
    
#if IS_ENABLED(CONFIG_I2C_SLAVE)
	int (*reg_slave)(struct i2c_client *client);
	int (*unreg_slave)(struct i2c_client *client);
#endif
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

这个结构体在之前的文章也有讲过,这里简单提一下:

  • master_xfer:这是最重要的函数,它实现了一般的I2C传输,用来传输一个或多个i2c_msg

  • smbus_xfer:实现SMBus传输,如果不提供这个函数,SMBus传输会使用master_xfer来模拟

  • functionality: 数通常会返回一个由功能标志位组成的位掩码,表示适配器支持哪些 I2C 或 SMBus 操作(如 I2C_FUNC_I2C, I2C_FUNC_SMBUS_READ_BLOCK_DATA 等等)。

  • reg_slave:

    • 注册一个 I2C 从设备,使得该设备可以响应 I2C 主设备发起的请求。有些I2C Adapter也可工作与Slave模式,用来实现或模拟一个I2C设备
  • unreg_slave:

    • 注销一个 I2C 从设备,使其不再响应 I2C 主设备的请求。

2.2 I2C adapter的相关的API

I2C adapter定义好之后,要把它注册到kernel中去,相关的API如下:

\Linux-4.9.88\Linux-4.9.88\include\linux\i2c.h:
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
extern int i2c_add_adapter(struct i2c_adapter *);
extern void i2c_del_adapter(struct i2c_adapter *);
extern int i2c_add_numbered_adapter(struct i2c_adapter *);
extern struct i2c_adapter *i2c_get_adapter(int nr);
extern void i2c_put_adapter(struct i2c_adapter *adap);
extern unsigned int i2c_adapter_depth(struct i2c_adapter *adapter);
/* must call put_device() when done with returned i2c_adapter device */
extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node);

/* Return the functionality mask */
static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
{
	return adap->algo->functionality(adap);
}
/* Return 1 if adapter supports everything we need, 0 if not. */
static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
{
	return (func & i2c_get_functionality(adap)) == func;
}
/* Return the adapter number for a specific adapter */
static inline int i2c_adapter_id(struct i2c_adapter *adap)
{
	return adap->nr;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

(1)i2c_add_adapter和i2c_add_numbered_adapter是I2C adapter的注册接口,它们的区别是:

  • 前者将一个 i2c_adapter 注册到 I2C 核心框架中,并自动为它分配一个 nr(adapter ID)。内核会为你选择一个合适的 ID,而不需要你手动指定。适用于不需要指定特定 ID 的情况,适合大多数情况下的控制器注册。 ID 自动分配。
  • 后者允许你指定 i2c_adapter 的 nr 字段,也就是适配器的 ID。你可以手动设置 adap->nr,然后注册时内核将使用你指定的这个 ID。如果 nr 不可用或者冲突,则返回错误。适用于需要在系统中保留固定编号的 I2C 适配器,比如为了与用户空间或其他设备进行明确绑定。 ID 可以手动指定。

(2)i2c_del_adapter(struct i2c_adapter *adap):

  • 用于从 I2C 核心中删除适配器。它会取消所有注册到该适配器的从设备,释放与之相关的资源。在驱动卸载或适配器不再需要时使用,确保适配器从系统中安全移除。

(3)i2c_get_functionality(struct i2c_adapter *adap) 和 i2c_check_functionality:

  • 前者 获取指定适配器所支持的功能。返回值是一个 32 位的位掩码,用于表示该适配器支持的特性。这些特性可以包括:标准的 I2C 功能(如 7 位地址、10 位地址等)、SMBus 功能、高级协议特性(如 I2C_FUNC_I2C、I2C_FUNC_SMBUS_*)。 用于查询控制器支持的功能。
  • 后者 检查指定控制器是否支持某个特定的功能。传入的 func 是功能的位掩码。该函数会将传入的 func 与适配器实际支持的功能进行比较,如果适配器支持所有的请求功能,则返回 1,否则返回 0。 用于检查控制器是否支持某一特定功能。

(4)i2c_adapter_id(struct i2c_adapter *adap):

  • 返回给定的 i2c_adapter 的 nr 字段,也就是适配器的 ID。这个函数很简单,通常在需要识别适配器或者调试时使用

(5)i2c_get_adapter(int nr) 和 i2c_put_adapter(struct i2c_adapter *adap):

  • 前者: 根据指定的 nr(适配器的 ID)查找并返回对应的 i2c_adapter 结构指针。这个函数会增加适配器所依赖模块的引用计数(内部通过调用 try_module_get)。因此在调用这个函数后,需要确保适配器不会被卸载。 增加引用计数,获取适配器。
  • 后者:当使用完 i2c_adapter 后,必须调用这个函数以减少模块的引用计数。这样可以防止模块被错误卸载,同时释放相关资源。 增加引用计数,获取适配器。

(6)of_find_i2c_adapter_by_node(struct device_node *node):

  • 通过设备树节点(device_node)查找与之关联的 I2C 适配器。该函数通常用于通过设备树动态探测并找到某个 I2C 适配器。在调用该函数后,你还需要通过 put_device 来正确处理设备的引用计数,以确保资源释放。

2.3 驱动程序框架

分配、设置、注册一个i2c_adpater结构体:

  • i2c_adpater的核心是i2c_algorithm
  • i2c_algorithm的核心是master_xfer函数

所涉及的函数

  • 分配struct i2c_adpater *adap = kzalloc(sizeof(struct i2c_adpater), GFP_KERNEL);
  • 设置
adap->owner = THIS_MODULE;
adap->algo = &stm32f7_i2c_algo;
  • 1
  • 2
  • 注册:i2c_add_adapter/i2c_add_numbered_adapter
ret = i2c_add_adapter(adap);          // 不管adap->nr原来是什么,都动态设置adap->nr
ret = i2c_add_numbered_adapter(adap); // 如果adap->nr == -1 则动态分配nr; 否则使用该nr
  • 1
  • 2
  • 反注册
i2c_del_adapter(adap);
  • 1

i2c_algorithm示例

img

大致步骤

了解了I2C adapter有关的数据结构和API之后,编写I2C控制器驱动就简单多了,主要步骤如下:

1)定义一个struct i2c_algorithm变量,并根据I2C controller的特性,实现其中的回调函数。

2)在DTS文件(一般都放到DTSI)中,定义I2C controller相关的DTS node

3)在drives/i2c/busses目录下,以i2c-xxx.c的命名方式,编写I2C controller的platform driver,并提供match id、probe、remove等接口。

4)在platform driver的probe接口中,分配一个adapter结构,并进行必要的初始化操作后,调用i2c_add_adapter或者i2c_add_numbered_adapter接口,将其注册到kernel中即可。

2.4 编写框架

这里使用的万能的总线平台:bus+dts 实现,platfrom_driver and platform_device

?05_i2c_adapter_framework.zip – 里面有设备树

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/* Global pointer to the virtual I2C adapter */
static struct i2c_adapter *g_adapter;

/* 
 * i2c_bus_virtual_master_xfer - I2C message transfer function
 * @i2c_adap: Pointer to I2C adapter
 * @msgs: Array of I2C messages
 * @num: Number of messages
 * 
 * This function simulates the transfer of multiple I2C messages. 
 * Here you would normally interact with the hardware.
 */
static int i2c_bus_virtual_master_xfer(struct i2c_adapter *i2c_adap,
		    struct i2c_msg msgs[], int num)
{
	int i;

	/* Loop through each message and simulate transfer */
	for (i = 0; i < num; i++) {
		/* Normally, the actual hardware transfer would occur here */
		// simulate transfer of msgs[i];
	}
	
	/* Return the number of messages processed */
	return num;
}

/* 
 * i2c_bus_virtual_func - Supported functionality of the virtual I2C bus
 * @adap: Pointer to I2C adapter
 * 
 * This function reports the capabilities of the virtual I2C bus. 
 * It supports standard I2C transactions and SMBus emulation.
 */
static u32 i2c_bus_virtual_func(struct i2c_adapter *adap)
{
	/* Return a combination of supported functionalities */
	return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
	       I2C_FUNC_SMBUS_READ_BLOCK_DATA |
	       I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
	       I2C_FUNC_PROTOCOL_MANGLING;
}

/* 
 * i2c_bus_virtual_algo - Virtual I2C bus algorithm structure
 * 
 * This structure defines the transfer and functionality operations for
 * the virtual I2C bus. It is used to handle I2C operations for the
 * associated adapter.
 */
const struct i2c_algorithm i2c_bus_virtual_algo = {
	.master_xfer   = i2c_bus_virtual_master_xfer,
	.functionality = i2c_bus_virtual_func,
};

/* 
 * i2c_bus_virtual_probe - Platform driver probe function
 * @pdev: Platform device structure
 * 
 * This function is called when the driver is matched with a device.
 * It sets up and registers the I2C adapter.
 */
static int i2c_bus_virtual_probe(struct platform_device *pdev)
{
	/* Allocate memory for the I2C adapter structure */
	g_adapter = kzalloc(sizeof(*g_adapter), GFP_KERNEL);
	if (!g_adapter)
		return -ENOMEM;

	/* Initialize the I2C adapter structure */
	g_adapter->owner = THIS_MODULE;
	g_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;  // Adapter supports hardware monitor and SPD classes
	g_adapter->nr = -1;  // Dynamically assign adapter number
	snprintf(g_adapter->name, sizeof(g_adapter->name), "i2c-bus-virtual");  // Set adapter name

	/* Assign the algorithm to the adapter */
	g_adapter->algo = &i2c_bus_virtual_algo;

	/* Register the I2C adapter with the I2C core */
	i2c_add_adapter(g_adapter);  // Can also use i2c_add_numbered_adapter() for fixed numbers
	
	return 0;
}

/* 
 * i2c_bus_virtual_remove - Platform driver remove function
 * @pdev: Platform device structure
 * 
 * This function is called when the driver is removed.
 * It unregisters the I2C adapter and cleans up.
 */
static int i2c_bus_virtual_remove(struct platform_device *pdev)
{
	/* Unregister the I2C adapter */
	i2c_del_adapter(g_adapter);
	
	/* Free the allocated memory */
	kfree(g_adapter);

	return 0;
}

/* 
 * i2c_bus_virtual_dt_ids - Device tree compatible entries
 * 
 * This array defines the compatible strings that match the virtual I2C bus
 * driver to the corresponding device in the device tree.
 */
static const struct of_device_id i2c_bus_virtual_dt_ids[] = {
	{ .compatible = "yyy,i2c-bus-virtual", },
	{ /* sentinel */ }
};

/* 
 * i2c_bus_virtual_driver - Platform driver structure
 * 
 * This structure defines the probe, remove, and other operations for the
 * virtual I2C bus driver.
 */
static struct platform_driver i2c_bus_virtual_driver = {
	.driver		= {
		.name	= "i2c-gpio",  // Name of the driver
		.of_match_table	= of_match_ptr(i2c_bus_virtual_dt_ids),  // Match device tree compatible entries
	},
	.probe		= i2c_bus_virtual_probe,  // Probe function
	.remove		= i2c_bus_virtual_remove,  // Remove function
};

/* 
 * i2c_bus_virtual_init - Driver initialization function
 * 
 * This function is called when the module is loaded. It registers
 * the platform driver with the Linux kernel.
 */
static int __init i2c_bus_virtual_init(void)
{
	int ret;

	/* Register the platform driver */
	ret = platform_driver_register(&i2c_bus_virtual_driver);
	if (ret)
		printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);

	return ret;
}
module_init(i2c_bus_virtual_init);

/* 
 * i2c_bus_virtual_exit - Driver exit function
 * 
 * This function is called when the module is unloaded. It unregisters
 * the platform driver from the kernel.
 */
static void __exit i2c_bus_virtual_exit(void)
{
	/* Unregister the platform driver */
	platform_driver_unregister(&i2c_bus_virtual_driver);
}
module_exit(i2c_bus_virtual_exit);

MODULE_AUTHOR("www.100ask.net");
MODULE_LICENSE("GPL");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176

设备树

在设备树里构造控制器的设备节点:

i2c-bus-virtual {
        compatible = "yyy,i2c-bus-virtual";

        //剩下的可以是挂载在该控制器上的i2c设备的设备树节点
  };
  • 1
  • 2
  • 3
  • 4
  • 5

platform_driver

分配、设置、注册platform_driver结构体。

核心是probe函数,

它要做这几件事:

  • 根据设备树信息设置硬件(引脚、时钟等)
  • 分配、设置、注册i2c_apdater

这里注册platform只是为了将i2c_adapter_driver和控制器的设备节点能够匹配起来,实现创建i2c_adapter而已

master_xfer

i2c_apdater核心是master_xfer函数,它的实现取决于硬件,在内核提供控制器驱动文件i2c-m中:

static int i2c_imx_xfer(struct i2c_adapter *adapter,
                        struct i2c_msg *msgs, int num)
{
    unsigned int i, temp;
    int result;
    bool is_lastmsg = false;          // 标志变量,表示当前消息是否是最后一个消息
    bool enable_runtime_pm = false;   // 用于标记是否启用了运行时电源管理
    struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);  // 获取适配器的私有数据结构

    // 输出调试信息,显示函数名
    dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);

    // 检查运行时电源管理是否已启用,如果没有则启用它
    if (!pm_runtime_enabled(i2c_imx->adapter.dev.parent)) {
        pm_runtime_enable(i2c_imx->adapter.dev.parent);
        enable_runtime_pm = true;  // 标记为启用了电源管理
    }

    // 增加运行时电源的引用计数,确保设备处于活动状态
    result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
    if (result < 0)
        goto out;  // 如果获取电源失败,则跳转到 out 标签处理

    // 开始I2C传输,调用底层驱动启动传输过程
    /* Start I2C transfer */
    result = i2c_imx_start(i2c_imx);
    if (result) {
        // 如果启动失败且存在总线恢复功能,则尝试恢复总线
        if (i2c_imx->adapter.bus_recovery_info) {
            i2c_recover_bus(&i2c_imx->adapter);
            result = i2c_imx_start(i2c_imx);  // 尝试重新启动传输
        }
    }

    if (result)  // 如果传输启动仍然失败,跳转到失败处理
        goto fail0;

    // 开始处理消息队列
    /* read/write data */
    for (i = 0; i < num; i++) {
        if (i == num - 1)
            is_lastmsg = true;  // 如果是最后一条消息,标记为true

        if (i) {
            // 如果不是第一条消息,则发送重复开始信号 (Repeated Start)
            dev_dbg(&i2c_imx->adapter.dev, "<%s> repeated start\n", __func__);
            temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);  // 读取I2C控制寄存器
            temp |= I2CR_RSTA;  // 设置重复开始位
            imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);  // 写回控制寄存器
            result = i2c_imx_bus_busy(i2c_imx, 1);  // 等待总线忙完成
            if (result)
                goto fail0;  // 如果总线忙碌超时,跳转到失败处理
        }
        
        // 输出调试信息,显示当前正在传输的消息编号
        dev_dbg(&i2c_imx->adapter.dev, "<%s> transfer message: %d\n", __func__, i);

#ifdef CONFIG_I2C_DEBUG_BUS
        // 如果启用了I2C调试,输出控制寄存器和状态寄存器的调试信息
        temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
        dev_dbg(&i2c_imx->adapter.dev,
            "<%s> CONTROL: IEN=%d, IIEN=%d, MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n",
            __func__,
            (temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0),
            (temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),
            (temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));
        temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
        dev_dbg(&i2c_imx->adapter.dev,
            "<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n",
            __func__,
            (temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),
            (temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0),
            (temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),
            (temp & I2SR_RXAK ? 1 : 0));
#endif

        // 根据消息类型执行读或写操作
        if (msgs[i].flags & I2C_M_RD)  // 如果是读操作
            result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);  // 执行读操作
        else {  // 如果是写操作
            if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)  // 如果支持DMA且消息长度大于阈值
                result = i2c_imx_dma_write(i2c_imx, &msgs[i]);  // 使用DMA执行写操作
            else
                result = i2c_imx_write(i2c_imx, &msgs[i]);  // 否则使用常规方式写
        }

        if (result)  // 如果读写操作失败,跳转到失败处理
            goto fail0;
    }

fail0:
    // 停止I2C传输
    /* Stop I2C transfer */
    i2c_imx_stop(i2c_imx);

    // 更新运行时电源状态并减少引用计数
    pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);
    pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);

out:
    // 如果启用了运行时电源管理,则在结束时禁用它
    if (enable_runtime_pm)
        pm_runtime_disable(i2c_imx->adapter.dev.parent);

    // 输出调试信息,显示函数退出时的状态
    dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
            (result < 0) ? "error" : "success msg",
            (result < 0) ? result : num);
    
    // 返回最终的结果,成功则返回消息数量,失败则返回错误码
    return (result < 0) ? result : num;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112

CPU 调用 i2c_transfe(i2c device driver) → 内部调用了通过 i2c_imx_xfer(adap->algo->master_xfer) 与 I2C 控制器通信 → 如果是读操作,则调用i2c_adapter中的 i2c_imx_read → i2c_imx_read 从 I2C 控制器接收数据并填充到 msgs 的 buf 中 → CPU 读取 buf 中的数据。

在上面讲的i2c_imx_xfer,是不是有开始信号、停止信号、data。这些都是iic传输协议的格式中有提到的,至于addr、w/r,这些就封装在了i2c_msg中,格式:I2C(IIC)协议讲解-CSDN博客


框架例子:

static int xxx_master_xfer(struct i2c_adapter *adapter,
                           struct i2c_msg *msgs, int num)
{
    for (i = 0; i < num; i++) {
        struct i2c_msg *msg = msgs[i];
        {
            // 1. 发出S信号: 设置寄存器发出S信号
            CTLREG = S;

            // 2. 根据Flag发出设备地址和R/W位: 把这8位数据写入某个DATAREG即可发出信号
            //    判断是否有ACK

            if (!ACK)
                return ERROR;
            else {
                // 3. read / write
                if (read) {
                    STATUS = XXX; // 这决定读到一个数据后是否发出ACK给对方
                    val = DATAREG; // 这会发起I2C读操作
                } else if(write) {
                    DATAREG = val; // 这会发起I2C写操作
                    val = STATUS;  // 判断是否收到ACK
                    if (!ACK)
                        return ERROR;
                }                
            }
            // 4. 发出P信号
            CTLREG = P;
        }
    }

    return i;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
注:本文转载自blog.csdn.net的憧憬一下的文章"https://blog.csdn.net/caiji0169/article/details/143030671"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top