**文章目录** [TOC] # 简介   Linux中的I2C驱动框架和platform平台设备驱动类似,都是采用的设备驱动分离分层的概念,不过platform平台总线是内核中虚拟出来的,而I2C总线则是SoC里真实存在的。既然都是采用总线的方式,那么在Linux内核中I2C驱动也是被分为两部分: - **I2C总线驱动**:也称**I2C适配器驱动**或**I2C控制器驱动**,比如IMX6ULL的I2C控制器驱动,就是控制I2C的SCL、SDA引脚电平传输数据的函数封装,这部分已经由SoC原厂做好了,我们直接可以使用; - **I2C设备驱动**:这部分就是针对具体的外接设备的驱动,比如MPU6050的驱动。同时,这部分还被分成**I2C设备**和**I2C驱动**,前者偏向于描述硬件的信息,后者偏向于通用的软件逻辑,在编写某个I2C设备的驱动时这部分是重点关注。   接下来就来以Freescale i.MX6ULL(内核版本:4.1.15)为例先探究I2C总线驱动与I2C设备驱动的原理,然后再自己写一个简陋的MPU6050驱动。注意,如果只是想快速了解I2C设备的驱动,则可以直接跳到**第4小节**开始。 # I2C总线、设备和驱动的结构体定义   这一小节主要是先了解一下总线、设备和驱动被内核抽象成的结构体定义及注册函数,下一小节“I2C总线、设备和驱动三者是如何联系起来的”再深入理解三者的关系。 ## 结构体定义–I2C总线   正如前面所说,I2C总线驱动一般都是由SoC厂商在Linux原有框架上编写其特有的部分注册到内核中去,这部分我们不用我们去编写,但是我们还是可以了解它有利于理解驱动的框架等。它是被抽象成一个`i2c_adapter`结构体,在`include/linux/i2c.h`中定义: ```c struct i2c_adapter { struct module *owner; unsigned int class; const struct i2c_algorithm *algo; /* 硬件操作相关的“算法” */ void *algo_data; struct rt_mutex bus_lock; int timeout; int retries; struct device dev; int nr; char name[48]; struct completion dev_released; struct mutex userspace_clients_lock; struct list_head userspace_clients; struct i2c_bus_recovery_info *bus_recovery_info; const struct i2c_adapter_quirks *quirks; }; ```   其中,内部还包含了`i2c_algorithm`“算法”结构体,它里面定义了I2C总线控制传输的函数指针,也是在同文件中定义: ```c struct i2c_algorithm { int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); /* 用来和I2C设备通信的函数 */ int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); 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 }; ```   其中,I2C总线驱动的主要工作就是初始化`i2c_adapter`结构体变量和`i2c_algorithm`的`master_xfer`传输函数,它就是用于与I2C设备通信的读写函数。   辛辛苦苦构造好的结构体,那么接下来就要把它告诉内核。其中注册进内核和从内核中注销的函数如下: ```c int i2c_add_adapter(struct i2c_adapter *adapter); // 动态总线号 int i2c_add_numbered_adapter(struct i2c_adapter *adap); // 静态总线号 void i2c_del_adapter(struct i2c_adapter * adap); // 删除I2C适配器 ``` ## 结构体定义–I2C设备   关于I2C设备比较重要的就是`i2c_client`结构体,它也是在`include/linux/i2c.h`中定义,它会在内核解析设备树或创建I2C设备时进行填充: ```c struct i2c_client { unsigned short flags; /* 标志:I2C_CLIENT_TEN表示使用10位芯片地址, I2C_CLIENT_PEC表示使用SMBus错包检测 */ unsigned short addr; /* 连接到父适配器的I2C总线上使用的地址 */ char name[I2C_NAME_SIZE]; /* 芯片名字 */ struct i2c_adapter *adapter;/* “绑定”该I2C设备所在的总线适配器 */ struct device dev; /* 设备节点 */ int irq; /* 设备终端号 */ struct list_head detected; #if IS_ENABLED(CONFIG_I2C_SLAVE) i2c_slave_cb_t slave_cb; #endif }; ``` ## 结构体定义–I2C驱动   与I2C总线、设备一样,I2C驱动也同样在`include/linux/i2c.h`被抽象成了`i2c_driver`结构体类型: ```c struct i2c_driver { unsigned int class; int (*attach_adapter)(struct i2c_adapter *) __deprecated; int (*probe)(struct i2c_client *, const struct i2c_device_id *); /* 设备与驱动匹配之后被调用 */ int (*remove)(struct i2c_client *); /* 与probe函数刚好相反 */ void (*shutdown)(struct i2c_client *); void (*alert)(struct i2c_client *, unsigned int data); int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); struct device_driver driver; /* 如果使用设备树则设置它的of_match_table成员 */ const struct i2c_device_id *id_table; /* 用于与传统不使用设备树的匹配列表 */ int (*detect)(struct i2c_client *, struct i2c_board_info *); const unsigned short *address_list; struct list_head clients; }; ```   配置好`i2c_driver`结构体之后,就可以通过`i2c_register_driver`或`i2c_add_driver`将它注册到内核中了,相反地,不再使用的时候可以通过`i2c_del_driver`将它注销,相关函数定义如下: ```c int i2c_register_driver(struct module *owner, struct i2c_driver *driver); #define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver) void i2c_del_driver(struct i2c_driver *driver); ``` # I2C总线、设备、驱动、硬件操作的联系 ## I2C总线驱动加载到内核的过程   以Freescale i.MX6ULL,Linux 4.1.15版本内核为例,在设备树文件`imx6ull.dtsi`中可以看到i2c节点的定义: ```c i2c1: i2c@021a0000 { #address-cells = <1>; #size-cells = <0>; compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c"; reg = <0x021a0000 0x4000>; interrupts = ; clocks = <&clks IMX6UL_CLK_I2C1>; status = "disabled"; }; ```   直接grep命令搜索一下“`fsl,imx6ul-i2c`”,不过好像没看到和驱动文件相关的,那就搜索下一个`compatible`属性“`fsl,imx21-i2c`”,这时可以找到在`drivers/i2c/busses/i2c-imx.c`中有定义,查看代码就会发现I2C总线驱动还是通过platform总线驱动的方法来注册进内核: ```c // ... 省略 static const struct of_device_id i2c_imx_dt_ids[] = { { .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, }, { .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, }, { .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids); // ... 省略 static struct platform_driver i2c_imx_driver = { .probe = i2c_imx_probe, .remove = i2c_imx_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = i2c_imx_dt_ids, .pm = IMX_I2C_PM, }, .id_table = imx_i2c_devtype, }; static int __init i2c_adap_imx_init(void) { return platform_driver_register(&i2c_imx_driver); } subsys_initcall(i2c_adap_imx_init); static void __exit i2c_adap_imx_exit(void) { platform_driver_unregister(&i2c_imx_driver); } module_exit(i2c_adap_imx_exit); ``` ## I2C总线驱动如何操作硬件   按照platform总线驱动的惯例,I2C总线驱动`probe`函数里面就是做一系列的初始化,我们进去函数看看: ```c /* probe函数里面用到的结构体定义,其中就包含了I2C控制器的结构体i2c_adapter */ struct imx_i2c_struct { struct i2c_adapter adapter; // ... 省略 }; static int i2c_imx_probe(struct platform_device *pdev) { // ... 省略 struct imx_i2c_struct *i2c_imx; // ... 省略 /* 设置i2c_adapter结构体 */ strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name)); i2c_imx->adapter.owner = THIS_MODULE; i2c_imx->adapter.algo = &i2c_imx_algo; // 硬件操作相关的“算法” ---------------> i2c_imx->adapter.dev.parent = &pdev->dev; i2c_imx->adapter.nr = pdev->id; i2c_imx->adapter.dev.of_node= pdev->dev.of_node; // ... 省略 /* 这个函数后面小节还会回来看这个它的调用过程 */ ret = i2c_add_numbered_adapter(&i2c_imx->adapter); // ... 省略 } ```   从上面`probe`函数里面可以知道其中一部分工作就是配置`i2c_adapter`结构体,还记得上面说过`i2c_adapter`结构体里面比较重要的一个成员是`algo`,它就是硬件操作相关的“算法”,继续看`i2c_imx_algo`,它就在同个文件中定义: ```c static struct i2c_algorithm i2c_imx_algo = { .master_xfer = i2c_imx_xfer, // ---------------> .functionality = i2c_imx_func, }; 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; struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter); /* Start I2C transfer */ result = i2c_imx_start(i2c_imx); // ... 省略 /* read/write data */ for (i = 0; i < num; i++) { if (i == num - 1) is_lastmsg = true; if (i) { temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); temp |= I2CR_RSTA; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); result = i2c_imx_bus_busy(i2c_imx, 1); // ... 省略 } // ... 省略 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) result = i2c_imx_dma_write(i2c_imx, &msgs[i]); else result = i2c_imx_write(i2c_imx, &msgs[i]); } // ... 省略 } /* Stop I2C transfer */ i2c_imx_stop(i2c_imx); // ... 省略 } static u32 i2c_imx_func(struct i2c_adapter *adapter) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA; } ```   可以看到,上面的`i2c_imx_xfer`就是用于SoC操作I2C的SCL、SDA的时序来实现I2C的start、stop信号等通信功能。那么它又是什么时候被调用的呢?直接从传输函数入手,其中就可以查看`i2c_transfer`的调用关系: ```c int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); adap->algo->master_xfer(adap, msgs, num); ```   很明显,`i2c_transfer`函数最终就是调用了`adap`适配器的`algo`“算法”成员的硬件传输函数`master_xfer`。所以,在I2C驱动部分调用该函数就可以通过总线驱动来间接地操作到硬件I2C了。 ## I2C设备 <-----> I2C驱动   不管是I2C设备、驱动还是I2C总线驱动,都会配置某个成员来进行I2C设备与I2C驱动的配对。以I2C总线驱动为例,上面`probe`函数最后部分说了会查看函数`i2c_add_numbered_adapter`的调用过程就是该函数,往里面探究一下函数的调用关系: ```c /* 函数调用关系 */ i2c_add_numbered_adapter/i2c_add_adapter __i2c_add_numbered_adapter i2c_register_adapter adap->dev.bus = &i2c_bus_type; ```   看了函数调用关系,最终还是配置在配置`adapter`适配器中发现了`i2c_bus_type`,它在内核`drivers/i2c/i2c-core.c`中定义: ```c /* i2c_bus_type的定义 */ 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_device_match的实现 */ static int i2c_device_match(struct device *dev, struct device_driver *drv) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; if (!client) return 0; /* Attempt an OF style match */ if (of_driver_match_device(dev, drv)) return 1; /* Then ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; driver = to_i2c_driver(drv); /* match on an id table if there is one */ if (driver->id_table) return i2c_match_id(driver->id_table, client) != NULL; return 0; } ```   最终还是和platform平台总线类似,都是使用`match`函数来匹配dev和drv,不过这里的`match`函数是I2C而不是platform。 ## I2C设备 <-----> I2C总线(控制器) **设备树方式**:设备放在哪个节点下就是使用哪个总线。 ```c &i2c1 { clock-frequency = <100000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c1>; status = "okay"; mag3110@0e { compatible = "fsl,mag3110"; reg = <0x0e>; position = <2>; }; }; ``` **传统方式**:以`i2c_new_device`函数为例去添加设备,通过函数创建时就已经指定I2C总线`client->adapter`: ```c struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { ... client->adapter = adap; ... } ``` ## I2C驱动 <-----> I2C总线(控制器)   事实上,I2C驱动并没有直接与I2C总线建立联系(要不然将设备和驱动分离也没什么意义了是吧),它是在I2C设备与I2C驱动成功配对之后调用的`probe`函数,在`probe`函数的参数里获得I2C设备的结构体`i2c_client`,里面就包含了`adapter`成员(I2C总线): ```c int (*probe)(struct i2c_client *, const struct i2c_device_id *); // i2c_client --------> // I2C设备的定义 struct i2c_client { struct i2c_adapter *adapter; ... }; ``` # I2C驱动传输数据API函数   内核里提供两套传输函数,一套是`i2c_transfer`,另一套是`SMBus(System Management Bus)`。其中,SMBus是I2C协议的一个子集,而且有些适配器仅支持SMBus,所以推荐使用SMBus,相关文档在内核`Documentation/i2c/smbus-protocol`。相关函数定义主要在`drivers/i2c/i2c-core.c`中定义,仅列出常用的几个: ```c /* 传输数据:adap是收发消息所用的适配器、msgs是消息的结构体、num是消息的数量 */ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); /* 内部调用i2c_transfer,client是I2C设备、buf是要发送/接收的数据、count是发送/接收的字节数 */ int i2c_master_send(const struct i2c_client *client, const char *buf, int count); int i2c_master_recv(const struct i2c_client *client, char *buf, int count); /* SMBus相关传输函数 */ s32 i2c_smbus_read_byte(const struct i2c_client *client); s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value); s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command); s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value); s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command); s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, u16 value); s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, u8 *values); s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values); ``` # I2C设备驱动编写框架 ## I2C设备框架   I2C设备编写框架参考内核文档`Documentation/i2c/instantiating-devices`,里面覆盖设备树、命令行等多种写法,这里仅列出常见的三种: ### 不使用设备树的写法 ```c static struct i2c_board_info xxx_info = { I2C_BOARD_INFO("xxx", 0x12), /* <用于与drv匹配的name> <设备地址> */ }; static struct i2c_client *xxx_client; static int xxx_dev_init(void) { struct i2c_adapter *i2c_adap; i2c_adap = i2c_get_adapter(0); /* 参数是I2C控制器的编号 */ xxx_client= i2c_new_device(i2c_adap, &xxx_info); i2c_put_adapter(i2c_adap); /* 配置完之后要put回去 */ return 0; } static void xxx_dev_exit(void) { i2c_unregister_device(xxx_client); } module_init(xxx_dev_init); module_exit(xxx_dev_exit); MODULE_LICENSE("GPL"); 12345678910111213141516171819202122232425 ``` ### 设备树写法–添加节点 ```c &i2c1 { clock-frequency = <100000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c1>; status = "okay"; // ... 省略 /* 添加的节点 */ xxx@12 { compatible = "xxx"; reg = <0x12>; }; }; 1234567891011121314 ``` ### 用户空间echo命令添加设备 ```c /* 添加设备需要:name addr adpter */ echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device /* 删除设备只需要:addr adpter */ echo 0x50 > /sys/bus/i2c/devices/i2c-3/delete_device 12345 ``` ## I2C驱动框架   不管“I2C设备”采用哪一种写法,“I2C驱动”的代码框架都是一样的。 ```c static int xxx_open(struct inode *inode, struct file *filp) { // ... return 0; } static int xxx_close(struct inode *inode, struct file *filp) { // ... return 0; } static struct file_operations xxx_fops = { .owner = THIS_MODULE, .open = xxx_open, .release = xxx_close, }; static int xxx_probe(struct i2c_client *client, const struct i2c_device_id *id) { // ... cdev_init(&cdev, &xxx_fops); return 0; } static int xxx_remove(struct i2c_client *client) { // ... cdev_del(&cdev); return 0; } /* 传统不使用设备树的匹配列表 */ static const struct i2c_device_id xxx_id[] = { {"xxx", 0}, /* <用于匹配dev的名字> <私有数据> */ {} }; /* 使用设备树的匹配列表 */ static const struct of_device_id xxx_of_match[] = { {.compatible = "xxx"}, /* 与设备树节点匹配的名字 */ {} }; static struct i2c_driver xxx_driver = { .probe = xxx_probe, .remove = xxx_remove, .driver = { .owner = THIS_MODULE, .name = "xxx", .of_match_table = xxx_of_match, }, .id_table = xxx_id, }; static int __init xxx_init(void) { i2c_add_driver(&xxx_driver); return 0; } static void __exit xxx_exit(void) { i2c_del_driver(&xxx_driver); } module_init(xxx_init); module_exit(xxx_exit); MODULE_LICENSE("GPL"); ``` # MPU6050驱动示例 ## I2C设备程序 ### 不使用设备树的写法 ```c #include #include #include #include #include #include #include static struct i2c_client *mpu6050_client; static struct i2c_board_info mpu6050_info = { /* STM32的地址写成0xD0 -> 1101 000_ * Linux的地址写成0x64 -> 110 1000 _ * 否则出现错误 i2c i2c-0: Invalid 7-bit I2C address 0xd0 */ I2C_BOARD_INFO("test,mpu6050", 0x68), /* <用于与驱动匹配的名字>, <设备地址> */ }; static int mpu6050_dev_init(void) { struct i2c_adapter *i2c_adap; i2c_adap = i2c_get_adapter(0); /* 放在第0个I2C适配器:I2C1 */ mpu6050_client = i2c_new_device(i2c_adap, &mpu6050_info); i2c_put_adapter(i2c_adap); return 0; } static void mpu6050_dev_exit(void) { i2c_unregister_device(mpu6050_client); } module_init(mpu6050_dev_init); module_exit(mpu6050_dev_exit); MODULE_LICENSE("GPL"); ``` ### 设备树写法–添加节点 ```c &i2c1 { clock-frequency = <100000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c1>; status = "okay"; // ... 省略 /* 添加mpu6050节点 */ mpu6050@68 { compatible = "book,mpu6050"; reg = <0x68>; }; }; ``` ## I2C驱动程序   正如前面所说,不管“I2C设备”采用哪一种写法,“I2C驱动”的代码框架都是一样的,下面分别使用`i2c_transfer`和`SMBus`两种方式来编写驱动程序。 ### I2C驱动写法1:i2c_transfer函数 ```c #include #include #include #include #include #include #include #include #include #define SMPLRT_DIV 0x19 #define CONFIG 0x1A #define GYRO_CONFIG 0x1B #define ACCEL_CONFIG 0x1C #define ACCEL_XOUT_H 0x3B #define ACCEL_XOUT_L 0x3C #define ACCEL_YOUT_H 0x3D #define ACCEL_YOUT_L 0x3E #define ACCEL_ZOUT_H 0x3F #define ACCEL_ZOUT_L 0x40 #define TEMP_OUT_H 0x41 #define TEMP_OUT_L 0x42 #define GYRO_XOUT_H 0x43 #define GYRO_XOUT_L 0x44 #define GYRO_YOUT_H 0x45 #define GYRO_YOUT_L 0x46 #define GYRO_ZOUT_H 0x47 #define GYRO_ZOUT_L 0x48 #define PWR_MGMT_1 0x6B #define DEV_CNT 1 struct mpu6050_drv_info{ int major; struct class *cls; struct cdev cdev; struct i2c_client *client; }; static struct mpu6050_drv_info mpu6050_drv; static int mpu6050_read_reg(struct i2c_client *client, u8 addr, u8 *buf, u16 size) { struct i2c_msg msg[2]; msg[0].addr = client->addr; /* 设备地址 */ msg[0].flags = 0; /* 标记为发送数据 */ msg[0].buf = &addr; /* 要读的设备的内部地址 */ msg[0].len = 1; /* 长度 */ msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; /* 标记为读取数据*/ msg[1].buf = buf; /* 把读到的数据传出去 */ msg[1].len = size; if(2 != i2c_transfer(client->adapter, msg, 2)){ printk("mpu6050_read_reg error!\n"); return -1; } return 0; } static int mpu6050_write_reg(struct i2c_client *client, u8 addr, u8 data) { u8 write_data[2]; struct i2c_msg msg; write_data[0] = addr; write_data[1] = data; msg.addr = client->addr; /* 设备地址 */ msg.flags = 0; /* 标记为发送数据 */ msg.buf = write_data; /* 要写的设备的内部地址及数据 */ msg.len = 2; if(1 != i2c_transfer(client->adapter, &msg, 1)){ printk("mpu6050_write_reg error!\n"); return -1; } return 0; } static int mpu6050_open(struct inode *inode, struct file *filp) { int ret = 0; printk("mpu6050_open!\n"); filp->private_data = &mpu6050_drv; /* 初始化MPU6050 */ ret |= mpu6050_write_reg(mpu6050_drv.client, PWR_MGMT_1, 0x00); ret |= mpu6050_write_reg(mpu6050_drv.client, SMPLRT_DIV, 0X07); ret |= mpu6050_write_reg(mpu6050_drv.client, CONFIG, 0x06); ret |= mpu6050_write_reg(mpu6050_drv.client, ACCEL_CONFIG, 0x01); return ret; } static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t sz, loff_t *loff) { int ret; u8 raw_data[12]; struct mpu6050_drv_info *dev = filp->private_data; printk("mpu6050_read!\n"); mpu6050_read_reg(dev->client, ACCEL_XOUT_H, &raw_data[0], 1); mpu6050_read_reg(dev->client, ACCEL_XOUT_L, &raw_data[1], 1); mpu6050_read_reg(dev->client, ACCEL_YOUT_H, &raw_data[2], 1); mpu6050_read_reg(dev->client, ACCEL_YOUT_L, &raw_data[3], 1); mpu6050_read_reg(dev->client, ACCEL_ZOUT_H, &raw_data[4], 1); mpu6050_read_reg(dev->client, ACCEL_ZOUT_L, &raw_data[5], 1); mpu6050_read_reg(dev->client, GYRO_XOUT_H, &raw_data[6], 1); mpu6050_read_reg(dev->client, GYRO_XOUT_L, &raw_data[7], 1); mpu6050_read_reg(dev->client, GYRO_YOUT_H, &raw_data[8], 1); mpu6050_read_reg(dev->client, GYRO_YOUT_L, &raw_data[9], 1); mpu6050_read_reg(dev->client, GYRO_ZOUT_H, &raw_data[10], 1); mpu6050_read_reg(dev->client, GYRO_ZOUT_L, &raw_data[11], 1); ret = copy_to_user(buf, raw_data, sz); if(ret < 0){ printk("copy_to_user error!"); return -1; } return 0; } static int mpu6050_release(struct inode *inode, struct file *filp) { printk("mpu6050_release!\n"); return 0; } static struct file_operations mpu6050_fops = { .owner = THIS_MODULE, .open = mpu6050_open, .read = mpu6050_read, .release = mpu6050_release, }; static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id) { dev_t devid; printk("mpu6050_probe!\n"); /* probe函数记得将I2C设备保存起来 */ mpu6050_drv.client = client; if(mpu6050_drv.major){ devid = MKDEV(mpu6050_drv.major , 0); register_chrdev_region(devid, DEV_CNT, "mpu6050"); } else{ alloc_chrdev_region(&devid, 0, DEV_CNT, "mpu6050"); mpu6050_drv.major = MAJOR(devid); } cdev_init(&mpu6050_drv.cdev, &mpu6050_fops); cdev_add(&mpu6050_drv.cdev, devid, DEV_CNT); mpu6050_drv.cls = class_create(THIS_MODULE, "mpu6050"); device_create(mpu6050_drv.cls, NULL, MKDEV(mpu6050_drv.major, 0), NULL, "mpu6050"); return 0; } static int mpu6050_remove(struct i2c_client *client) { printk("mpu6050_remove!\n"); device_destroy(mpu6050_drv.cls, MKDEV(mpu6050_drv.major, 0)); class_destroy(mpu6050_drv.cls); cdev_del(&mpu6050_drv.cdev); unregister_chrdev_region(MKDEV(mpu6050_drv.major, 0), DEV_CNT); return 0; } /* 传统不使用设备树的匹配列表 */ static const struct i2c_device_id mpu6050_id[] = { {"test,mpu6050", 0}, {} }; /* 使用设备树的匹配列表 */ static const struct of_device_id mpu6050_of_match[] = { {.compatible = "test,mpu6050"}, {} }; static struct i2c_driver mpu6050_driver = { .probe = mpu6050_probe, .remove = mpu6050_remove, .driver = { .owner = THIS_MODULE, .name = "mpu6050", .of_match_table = mpu6050_of_match, }, .id_table = mpu6050_id, }; static int __init mpu6050_drv_init(void) { printk("mpu6050_init!\n"); i2c_add_driver(&mpu6050_driver); return 0; } static void __exit mpu6050_drv_exit(void) { printk("mpu6050_exit!\n"); i2c_del_driver(&mpu6050_driver); } module_init(mpu6050_drv_init); module_exit(mpu6050_drv_exit); MODULE_LICENSE("GPL"); ``` ### I2C驱动写法2:SMBus传输函数 ```c #include #include #include #include #include #include #include #include #include #define SMPLRT_DIV 0x19 #define CONFIG 0x1A #define GYRO_CONFIG 0x1B #define ACCEL_CONFIG 0x1C #define ACCEL_XOUT_H 0x3B #define ACCEL_XOUT_L 0x3C #define ACCEL_YOUT_H 0x3D #define ACCEL_YOUT_L 0x3E #define ACCEL_ZOUT_H 0x3F #define ACCEL_ZOUT_L 0x40 #define TEMP_OUT_H 0x41 #define TEMP_OUT_L 0x42 #define GYRO_XOUT_H 0x43 #define GYRO_XOUT_L 0x44 #define GYRO_YOUT_H 0x45 #define GYRO_YOUT_L 0x46 #define GYRO_ZOUT_H 0x47 #define GYRO_ZOUT_L 0x48 #define PWR_MGMT_1 0x6B #define DEV_CNT 1 struct mpu6050_drv_info{ int major; struct class *cls; struct cdev cdev; struct i2c_client *client; }; static struct mpu6050_drv_info mpu6050_drv; static int mpu6050_open(struct inode *inode, struct file *filp) { int ret = 0; printk("mpu6050_open!\n"); filp->private_data = &mpu6050_drv; /* 初始化MPU6050 */ ret |= i2c_smbus_write_byte_data(mpu6050_drv.client, PWR_MGMT_1, 0x00); ret |= i2c_smbus_write_byte_data(mpu6050_drv.client, SMPLRT_DIV, 0X07); ret |= i2c_smbus_write_byte_data(mpu6050_drv.client, CONFIG, 0x06); ret |= i2c_smbus_write_byte_data(mpu6050_drv.client, ACCEL_CONFIG, 0x01); return ret; } static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t sz, loff_t *loff) { int ret; u8 raw_data[12]; struct mpu6050_drv_info *dev = filp->private_data; printk("mpu6050_read!\n"); raw_data[0] = i2c_smbus_read_byte_data(dev->client, ACCEL_XOUT_H); raw_data[1] = i2c_smbus_read_byte_data(dev->client, ACCEL_XOUT_L); raw_data[2] = i2c_smbus_read_byte_data(dev->client, ACCEL_YOUT_H); raw_data[3] = i2c_smbus_read_byte_data(dev->client, ACCEL_YOUT_L); raw_data[4] = i2c_smbus_read_byte_data(dev->client, ACCEL_ZOUT_H); raw_data[5] = i2c_smbus_read_byte_data(dev->client, ACCEL_ZOUT_L); raw_data[6] = i2c_smbus_read_byte_data(dev->client, GYRO_XOUT_H); raw_data[7] = i2c_smbus_read_byte_data(dev->client, GYRO_XOUT_L); raw_data[8] = i2c_smbus_read_byte_data(dev->client, GYRO_YOUT_H); raw_data[9] = i2c_smbus_read_byte_data(dev->client, GYRO_YOUT_L); raw_data[10]= i2c_smbus_read_byte_data(dev->client, GYRO_ZOUT_H); raw_data[11]= i2c_smbus_read_byte_data(dev->client, GYRO_ZOUT_L); ret = copy_to_user(buf, raw_data, sz); if(ret < 0){ printk("copy_to_user error!"); return -1; } return 0; } static int mpu6050_release(struct inode *inode, struct file *filp) { printk("mpu6050_release!\n"); return 0; } static struct file_operations mpu6050_fops = { .owner = THIS_MODULE, .open = mpu6050_open, .read = mpu6050_read, .release = mpu6050_release, }; static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id) { dev_t devid; printk("mpu6050_probe!\n"); /* probe函数记得将I2C设备保存起来 */ mpu6050_drv.client = client; if(mpu6050_drv.major){ devid = MKDEV(mpu6050_drv.major , 0); register_chrdev_region(devid, DEV_CNT, "mpu6050"); } else{ alloc_chrdev_region(&devid, 0, DEV_CNT, "mpu6050"); mpu6050_drv.major = MAJOR(devid); } cdev_init(&mpu6050_drv.cdev, &mpu6050_fops); cdev_add(&mpu6050_drv.cdev, devid, DEV_CNT); mpu6050_drv.cls = class_create(THIS_MODULE, "mpu6050"); device_create(mpu6050_drv.cls, NULL, MKDEV(mpu6050_drv.major, 0), NULL, "mpu6050"); return 0; } static int mpu6050_remove(struct i2c_client *client) { printk("mpu6050_remove!\n"); device_destroy(mpu6050_drv.cls, MKDEV(mpu6050_drv.major, 0)); class_destroy(mpu6050_drv.cls); cdev_del(&mpu6050_drv.cdev); unregister_chrdev_region(MKDEV(mpu6050_drv.major, 0), DEV_CNT); return 0; } /* 传统不使用设备树的匹配列表 */ static const struct i2c_device_id mpu6050_id[] = { {"test,mpu6050", 0}, {} }; /* 使用设备树的匹配列表 */ static const struct of_device_id mpu6050_of_match[] = { {.compatible = "test,mpu6050"}, {} }; static struct i2c_driver mpu6050_driver = { .probe = mpu6050_probe, .remove = mpu6050_remove, .driver = { .owner = THIS_MODULE, .name = "mpu6050", .of_match_table = mpu6050_of_match, }, .id_table = mpu6050_id, }; static int __init mpu6050_drv_init(void) { printk("mpu6050_init!\n"); i2c_add_driver(&mpu6050_driver); return 0; } static void __exit mpu6050_drv_exit(void) { printk("mpu6050_exit!\n"); i2c_del_driver(&mpu6050_driver); } module_init(mpu6050_drv_init); module_exit(mpu6050_drv_exit); MODULE_LICENSE("GPL"); ``` # MPU6050测试程序   不管前面驱动程序采用`i2c_transfer`还是`SMBus`写法,用户空间的应用程序都是一样的: ```c #include #include #include #include int main(int argc, char **argv) { int fd; int ret; char mpu6050_rawdata[12]; if(argc != 2){ printf("Usage: ./MPU6050APP /dev/xxx\n"); return -1; } fd = open(argv[1], O_RDWR); if (fd < 0){ printf("Open %s error!\n", argv[1]); return -1; } while (1) { ret = read(fd, mpu6050_rawdata, 12); if(ret < 0){ printf("Read %s error!\n", argv[1]); return -1; } printf("Ax: %d Ay: %d Az: %d \n", (mpu6050_rawdata[0] << 8) | mpu6050_rawdata[1], (mpu6050_rawdata[2] << 8) | mpu6050_rawdata[3], (mpu6050_rawdata[4] << 8) | mpu6050_rawdata[5]); printf("Gx: %d Gy: %d Gz: %d \n", (mpu6050_rawdata[6] << 8) | mpu6050_rawdata[7], (mpu6050_rawdata[8] << 8) | mpu6050_rawdata[9], (mpu6050_rawdata[10] << 8) | mpu6050_rawdata[11]); sleep(1); } return 0; } ``` # MPU6050测试结果 ```c /drivers # cat /sys/bus/i2c/devices/0-0068/name mpu6050 /drivers # /drivers # insmod 19_i2c_mpu6050_dts.ko mpu6050_init! mpu6050_probe! /drivers # /drivers # ./MPU6050APP /dev/mpu6050 mpu6050_open! mpu6050_read! Ax: 0 Ay: 0 Az: 0 Gx: 111 Gy: 3205 Gz: 731 mpu6050_read! Ax: 49644 Ay: 62874 Az: 456 Gx: 65524 Gy: 65507 Gz: 64 mpu6050_read! Ax: 49636 Ay: 62876 Az: 438 Gx: 65530 Gy: 65503 Gz: 64 mpu6050_read! Ax: 49662 Ay: 62874 Az: 436 Gx: 65525 Gy: 65507 Gz: 65 ^Cmpu6050_release! /drivers # ```