嵌入式Linux驱动开发——IIC驱动(基于野火I.MX6ULL)

本文详细介绍了I2C驱动的核心结构,包括i2c_adapter、i2c_driver、设备树在I2C驱动中的作用,以及如何通过实例进行MPU6050数据读取。涵盖了关键函数如i2c_add_adapter、i2c_register_driver和i2c_transfer的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一.IIC驱动框架简介

核心数据结构

I2C 总线驱动分析

二.IIC驱动核心函数

i2c_add_adapter()函数

i2c_add_driver()宏

i2c_register_driver()函数

i2c_transfer()函数

三.IIC驱动实验:读取mpu6050数据

1.添加设备树节点

2.编译设备树

3.编译驱动程序和APP程序

4.加载设备树以及修改uEnv.txt

5.测试效果


一.IIC驱动框架简介

由于现在引入了设备树这种机制,所以i2c已经不需要自己手动注册了,开发板一上电,i2c总线就注册好了。而设备树又是和paltform平台总线相配合的,设备树中的节点会被解析为paltform的平台设备。所以我们需要将平台总线设备转换为i2c设备,再注册到i2c总线里。

IIC设备:i2c_client        IIC驱动:i2c_driver 

- I2C核心

  提供I2C总线驱动和设备驱动的注册方法、注销方法、I2C通信硬件无关代码

- I2C 总线驱动

  主要包含I2C硬件体系结构中适配器(iic控制器)的控制,用于I2C 读写时序

  主要数据结构:I2C_adapter、i2c_algorithm

- I2C设备驱动

  通过I2C适配器与CPU交换数据

  主要数据结构:i2c_driver和i2c_client

核心数据结构

i2c_adapter

include/linux/i2c.h

```
struct i2c_adapter {
    struct module *owner;
    unsigned int class;          /* classes to allow probing for */
    const struct i2c_algorithm *algo; /* the algorithm to access the bus */
    void *algo_data;

    ...
};
```

- 对应一个IIC控制器

- 相关API

        - int i2c_add_adapter(struct i2c_adapter *adapter)

                 注册一个i2c_adapter ,系统分配编号

         - int i2c_add_numbered_adapter(struct i2c_adapter *adapter)

                注册一个i2c_adapter ,自己指定编号

         - void i2c_del_adapter(struct i2c_adapter * adap)

                注销一个i2c_adapter 

i2c_algorithm

include/linux/i2c.h

```
struct i2c_algorithm {
    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
};
```

- 对应一套具体的通信方法

- master_xfer:产生I2C通信时序

struct i2c_client

include/linux/i2c.h

```
struct i2c_client {
    unsigned short flags;        /* div., see below        */
    unsigned short addr;        /* chip address - NOTE: 7bit    */

    char name[I2C_NAME_SIZE];
    struct i2c_adapter *adapter;    /* the adapter we sit on    */
    struct device dev;        /* the device structure        */
    int init_irq;            /* irq set at initialization    */
    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
};
```

- addr:i2c设备地址

struct i2c_driver

include/linux/i2c.h

```
struct i2c_driver {
    unsigned int class;

    /* Standard driver model interfaces */
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    int (*remove)(struct i2c_client *);
    ...
    struct device_driver driver;
    const struct i2c_device_id *id_table;
    ...
}
```

- id_table:i2c总线传统匹配方式

- probe:i2c设备和i2c驱动匹配后,回调该函数指针

- 相关API

        - int i2c_add_driver (struct i2c_driver *driver)

                注册一个i2c_driver

        - void i2c_del_driver(struct i2c_driver *driver)

                注销一个i2c_driver

I2C 总线驱动分析

i2c总线注册

drivers/i2c/i2c-core-base.c

```
static int __init i2c_init(void)
{
    int retval;
    ...
    retval = bus_register(&i2c_bus_type);
    if (retval)
        return retval;

    is_registered = true;
    ...
    retval = i2c_add_driver(&dummy_driver);
    if (retval)
        goto class_err;

    if (IS_ENABLED(CONFIG_OF_DYNAMIC))
        WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
    if (IS_ENABLED(CONFIG_ACPI))
        WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));

    return 0;
    ...
}
```

i2c总线定义

```
struct bus_type i2c_bus_type = {
    .name        = "i2c",
    .match        = i2c_device_match,
    .probe        = i2c_device_probe,
    .remove        = i2c_device_remove,
    .shutdown    = i2c_device_shutdown,
};
```

i2c设备和i2c驱动匹配规则

```
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
    struct i2c_client    *client = i2c_verify_client(dev);
    struct i2c_driver    *driver;


    /* Attempt an OF style match */
    if (i2c_of_match_device(drv->of_match_table, client))
        return 1;

    /* Then ACPI style match */
    if (acpi_driver_match_device(dev, drv))
        return 1;

    driver = to_i2c_driver(drv);

    /* Finally an I2C match */
    if (i2c_match_id(driver->id_table, client))
        return 1;

    return 0;
}
```

- of_driver_match_device:设备树匹配方式
        - 比较 I2C 设备节点的 compatible 属性和 of_device_id 中的 compatible 属性 

- acpi_driver_match_device : ACPI 匹配方式
- i2c_match_id:i2c总线传统匹配方式
        - 比较 I2C设备名字和 i2c驱动的id_table->name 字段是否相等

二.IIC驱动核心函数

i2c_add_adapter()函数

drivers/i2c/i2c-core-base.c

注册一个i2c适配器
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adapter)

adapter->nr:适配器的编号

参数:

- adapter:i2c物理控制器对应的适配器

返回值:

- 成功:0
- 失败:负数

i2c_add_driver()宏

include/linux/i2c.h

注册一个i2c驱动
#define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver)

i2c_register_driver()函数

drivers/i2c/i2c-core-base.c

注册一个i2c驱动
int i2c_register_driver(struct module *owner,
struct i2c_driver *driver)

参数:

- owner: :一般为 THIS_MODULE
- driver:要注册的 i2c_driver.

返回值:

- 成功:0
- 失败:负数

i2c_transfer()函数

drivers/i2c/i2c-core-base.c

收发iic消息
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

参数:

- adap :所使用的 I2C 适配器,i2c_client 会保存其对应的 i2c_adapter
- msgs:I2C 要发送的一个或多个消息
- num :消息数量,也就是 msgs 的数量

返回值:

- 成功:发送的msgs 的数量
- 失败:负数

三.IIC驱动实验:读取mpu6050数据

MPU6050陀螺仪

- 3轴加速度+3轴角速度

1.添加设备树节点

//iomuxc子节点

```
pinctrl_i2c1: i2c1grp {
        		fsl,pins = <
        			MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
        			MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
        		>;
        	}; 
```
//i2c1子节点

```

&i2c1{
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1>;
	status = "okay";
	
	i2c_mpu6050@68 {
	        	compatible = "fire,i2c_mpu6050";
	        	reg = <0x68>;
	        	status = "okay";
	 };
};
```

2.编译设备树

将 linux_driver/I2c_MPU6050/imx6ull-mmc-npi.dts 拷贝到 内核源码/arch/arm/boot/dts/
如下命令编译设备树:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig
make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs

我建议修改一下Makefile,添加以下部分

ARCH ?= arm

CROSS_COMPILE ?= arm-linux-gnueabihf-

这样在编译设备树时就可以直接执行make dtbs
编译成功后生成的设备树文件(.dtb)位于源码目录下的 内核源码/arch/arm/boot/dts,开发板适配的设备树文件名为 imx6ull-mmc-npi.dts。

3.编译驱动程序和APP程序

将 linux_driver/I2c_MPU6050/ 拷贝到内核源码同级目录,执行里面的 MakeFile,生成i2c_mpu6050.ko 和 6050_test_app

4.加载设备树以及修改uEnv.txt

通过 SCP 或 NFS 将编译好的设备树cp到开发板上。替换掉原来的设备树文件 /usr/lib/linux-image-4.19.35-imx6/imx6ull-mmc-npi.dts 。除此以外还需要将开发板上其他 i2c 设备屏蔽(所有驱动都要注意这个问题,避免驱动冲突),打开/boot/目录下的 uEnv.txt 文件,注释掉所有IIC设备。

然后reboot重启开发板。

5.测试效果

加载驱动 i2c_mpu6050.ko

sudo insmod i2c_mpu6050.ko

运行 6050_test_app 或test_app

sudo ./6050_test_app

至此,完成IIC设备驱动实验。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值