Linux I2C 驱动实验

I2C 是很常用的一个串行通信接口,用于连接各种外设、传感器等器件,在裸机篇已经对I.MX6U 的 I2C 接口做了详细的讲解。本章我们来学习一下如何在 Linux 下开发 I2C 接口器件驱动,重点是学习 Linux 下的 I2C 驱动框架,按照指定的框架去编写 I2C 设备驱动。本章同样以 I.MX6U-ALPHA 开发板上的 AP3216C 这个三合一环境光传感器为例,通过 AP3216C 讲解一下如何编写 Linux 下的 I2C 设备驱动程序。

回想一下我们在裸机篇中是怎么编写 AP3216C 驱动的,我们编写了四个文件:bsp_i2c.c、bsp_i2c.h、bsp_ap3216c.c 和 bsp_ap3216c.h。其中前两个是 I.MX6U 的 IIC 接口驱动,后两个文件是 AP3216C 这个 I2C 设备驱动文件。相当于有两部分驱动:

①、I2C 主机驱动。

②、I2C 设备驱动。

对于 I2C 主机驱动,一旦编写完成就不需要再做修改,其他的 I2C 设备直接调用主机驱动提供的 API 函数完成读写操作即可。这个正好符合 Linux 的驱动分离与分层的思想,因此 Linux内核也将 I2C 驱动分为两部分:

①、I2C 总线驱动,I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。

②、I2C 设备驱动,I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。

I2C 总线驱动

首先来看一下 I2C 总线,在讲 platform 的时候就说过,platform 是虚拟出来的一条总线,目的是为了实现总线、设备、驱动框架。对于 I2C 而言,不需要虚拟出一条总线,直接使用 I2C总线即可。I2C 总线驱动重点是 I2C 适配器(也就是 SOC 的 I2C 接口控制器)驱动,这里要用到两个重要的数据结构:i2c_adapter 和 i2c_algorithm,Linux 内核将 SOC 的 I2C 适配器(控制器)抽象成 i2c_adapter,i2c_adapter 结构体定义在 include/linux/i2c.h 文件中,结构体内容如下:

第 501 行,i2c_algorithm 类型的指针变量 algo,对于一个 I2C 适配器,肯定要对外提供读写 API 函数,设备驱动程序可以使用这些 API 函数来完成读写操作。i2c_algorithm 就是 I2C 适配器与 IIC 设备进行通信的方法。

i2c_algorithm 结构体定义在 include/linux/i2c.h 文件中,内容如下(删除条件编译):

第 398 行,master_xfer 就是 I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备之间的通信。

第 400 行,smbus_xfer 就是 SMBUS 总线的传输函数。

综上所述,I2C 总线驱动,或者说 I2C 适配器驱动的主要工作就是初始化 i2c_adapter 结构体变量,然后设置 i2c_algorithm 中的 master_xfer 函数。完成以后通过 i2c_add_numbered_adapter或 i2c_add_adapter 这两个函数向系统注册设置好的 i2c_adapter,这两个函数的原型如下:

int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)

这两个函数的区别在于 i2c_add_adapter 使用动态的总线号,而 i2c_add_numbered_adapter使用静态总线号。函数参数和返回值含义如下:

adapter 或 adap:要添加到 Linux 内核中的 i2c_adapter,也就是 I2C 适配器。

返回值:0,成功;负值,失败。

如果要删除 I2C 适配器的话使用 i2c_del_adapter 函数即可,函数原型如下:

void i2c_del_adapter(struct i2c_adapter * adap)

函数参数和返回值含义如下:

adap:要删除的 I2C 适配器。

返回值:无。

关于 I2C 的总线(控制器或适配器)驱动就讲解到这里,一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的,比如 I.MX6U 的 I2C 适配器驱动 NXP 已经编写好了,这个不需要用户去编写。因此 I2C 总线驱动对我们这些 SOC 使用者来说是被屏蔽掉的,我们只要专注于 I2C 设备驱动即可。除非你是在半导体公司上班,工作内容就是写 I2C 适配器驱动。

开发 Linux 内核中的 i2c_adapter 驱动涉及实现一个 I2C 总线控制器的驱动程序。i2c_adapter 是 I2C 总线控制器的抽象,负责管理 I2C 总线上的通信。以下是 i2c_adapter 驱动的开发过程详解。

1. 开发环境准备

  • 硬件:I2C 总线控制器(如 SoC 的 I2C 接口)。

  • 内核版本:确保内核支持 I2C 子系统(通常默认支持)。

2. i2c_adapter 驱动开发流程

步骤 1:定义 i2c_adapter 结构体

i2c_adapter 是 I2C 总线控制器的抽象,需要实现其操作函数(如 i2c_algorithm)。

#include <linux/i2c.h>

static struct i2c_adapter my_i2c_adapter;

步骤 2:实现 i2c_algorithm

i2c_algorithm 是 I2C 总线控制器的操作函数集合,包括 master_xfer(传输函数)和 functionality(功能支持)。

#include <linux/i2c.h>

static int my_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) {
    int i;
    for (i = 0; i < num; i++) {
        struct i2c_msg *msg = &msgs[i];
        printk(KERN_INFO "I2C %s: addr=0x%02x, len=%d\n",
               msg->flags & I2C_M_RD ? "read" : "write",
               msg->addr, msg->len);
        // 实现具体的 I2C 传输逻辑
    }
    return num;
}

static u32 my_i2c_func(struct i2c_adapter *adapter) {
    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}

static struct i2c_algorithm my_i2c_algo = {
    .master_xfer = my_i2c_xfer,
    .functionality = my_i2c_func,
};

步骤 3:初始化 i2c_adapter

在驱动初始化时,设置 i2c_adapter 的属性和操作函数。

#include <linux/i2c.h>

static int __init my_i2c_adapter_init(void) {
    strscpy(my_i2c_adapter.name, "my_i2c_adapter", sizeof(my_i2c_adapter.name));
    my_i2c_adapter.algo = &my_i2c_algo;
    my_i2c_adapter.owner = THIS_MODULE;

    // 注册 I2C 适配器
    int ret = i2c_add_adapter(&my_i2c_adapter);
    if (ret < 0) {
        printk(KERN_ERR "Failed to add I2C adapter\n");
        return ret;
    }

    printk(KERN_INFO "I2C adapter registered\n");
    return 0;
}

步骤 4:注销 i2c_adapter

在驱动退出时,注销 i2c_adapter

static void __exit my_i2c_adapter_exit(void) {
    i2c_del_adapter(&my_i2c_adapter);
    printk(KERN_INFO "I2C adapter unregistered\n");
}

步骤 5:注册驱动模块

将初始化和退出函数注册为模块的入口和出口。

module_init(my_i2c_adapter_init);
module_exit(my_i2c_adapter_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("My I2C Adapter Driver");

3. 完整示例代码

以下是一个完整的 i2c_adapter 驱动示例:

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

static struct i2c_adapter my_i2c_adapter;

static int my_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) {
    int i;
    for (i = 0; i < num; i++) {
        struct i2c_msg *msg = &msgs[i];
        printk(KERN_INFO "I2C %s: addr=0x%02x, len=%d\n",
               msg->flags & I2C_M_RD ? "read" : "write",
               msg->addr, msg->len);
        // 实现具体的 I2C 传输逻辑
    }
    return num;
}

static u32 my_i2c_func(struct i2c_adapter *adapter) {
    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}

static struct i2c_algorithm my_i2c_algo = {
    .master_xfer = my_i2c_xfer,
    .functionality = my_i2c_func,
};

static int __init my_i2c_adapter_init(void) {
    strscpy(my_i2c_adapter.name, "my_i2c_adapter", sizeof(my_i2c_adapter.name));
    my_i2c_adapter.algo = &my_i2c_algo;
    my_i2c_adapter.owner = THIS_MODULE;

    int ret = i2c_add_adapter(&my_i2c_adapter);
    if (ret < 0) {
        printk(KERN_ERR "Failed to add I2C adapter\n");
        return ret;
    }

    printk(KERN_INFO "I2C adapter registered\n");
    return 0;
}

static void __exit my_i2c_adapter_exit(void) {
    i2c_del_adapter(&my_i2c_adapter);
    printk(KERN_INFO "I2C adapter unregistered\n");
}

module_init(my_i2c_adapter_init);
module_exit(my_i2c_adapter_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("My I2C Adapter Driver");

master_xfer传输是发送函数还是接收函数<

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值