拆解LinuxI2C驱动之mpu6050
做项目的时候遇到了mpu6050这个加速度传感器,今天来复现重新看一下i2c总线下如何添加一个驱动代码。
一、注册
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/i2c.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>#include <linux/platform_device.h>#include "i2c_mpu6050.h"/*------------------字符设备内容----------------------*/
#define DEV_NAME "I2C4_mpu6050"
#define DEV_CNT (1)/*定义 led 资源结构体,保存获取得到的节点信息以及转换后的虚拟寄存器地址*/
static dev_t mpu6050_devno; //定义字符设备的设备号
static struct cdev mpu6050_chr_dev; //定义字符设备结构体chr_dev
struct class *class_mpu6050; //保存创建的类
struct device *device_mpu6050; // 保存创建的设备
struct device_node *mpu6050_device_node; //rgb_led的设备树节点结构体/*------------------IIC设备内容----------------------*/
struct i2c_client *mpu6050_client = NULL; //保存mpu6050设备对应的i2c_client结构体,匹配成功后由.prob函数带回。
static int __init mpu6050_driver_init(void){int ret;printk("mpu6050 init\n");ret = i2c_add_driver(&mpu6050_driver);return 0;
}static void __exic mpu6050_driver_exit(void){printk("mpu6050 exit\n");i2c_del_driver(&mpu6050_driver);return;
}
二、设置i2c总线下的结构体,类似platform_dirver
/*定义ID 匹配表*/
static const struct i2c_device_id gtp_device_id[] = {{"fire,i2c_mpu6050", 0},{}};/*定义设备树匹配表*/
static const struct of_device_id mpu6050_of_match_table[] = {{.compatible = "fire,i2c_mpu6050"},{/* sentinel */}};struct i2c_driver mpu6050_driver = {.probe = mpu6050_probe,.remove = mpu6050_remove,.id_table = gtp_device_id,.driver = {.name = "fire,i2c_mpu6050",.owner = THIS_MODULE,.of_match_table = mpu6050_of_match_table,},
};
三、提供probe 函数,在probe函数去注册字符设备
static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{int ret;/* int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name) */ret = alloc_chrdev_region(&mpu6050_devne, 0, DEV_CNT, DEV_NAME);/* 这里使用的是cdev_init */mpu6050_chr_dev.owner = THIS_MODULE;/* 将fops挂到这个设备 */cdev_init(&mpu6050_chr_dev, &mpu6050_chr_dev_fops);ret = cdev_add(&mpu6050_chr_dev, mpu6050_devno, DEV_CNT);class_mpu6050 = class_create(THIS_MODULE, DEV_NAME);device_mpu6050 = device_create(class_mpu6050, NULL, mpu6050_devno, NULL, DEV_NAME);return 0;
}static int mpu6050_remove(struct i2c_client *client)
{/*删除设备*/device_destroy(class_mpu6050, mpu6050_devno); //清除设备class_destroy(class_mpu6050); //清除类cdev_del(&mpu6050_chr_dev); //清除设备号unregister_chrdev_region(mpu6050_devno, DEV_CNT); //取消注册字符设备return 0;
}
四、提供file_operations结构体
static struct file_operations mpu6050_chr_dev_fops =
{.owner = THIS_MODULE,.open = mpu6050_open,.read = mpu6050_read,.release = mpu6050_release,
};
4.1 open函数,初始化i2c设备
static int mpu6050_open(struct inode *inode, struct file *filp)
{// printk("\n mpu6050_open \n");/*向 mpu6050 发送配置数据,让mpu6050处于正常工作状态*/mpu6050_init();return 0;
}static int mpu6050_init(void)
{int error = 0;/*配置mpu6050*/error += i2c_write_mpu6050(mpu6050_client, PWR_MGMT_1, 0X00);error += i2c_write_mpu6050(mpu6050_client, SMPLRT_DIV, 0X07);error += i2c_write_mpu6050(mpu6050_client, CONFIG, 0X06);error += i2c_write_mpu6050(mpu6050_client, ACCEL_CONFIG, 0X01);if (error < 0){/*初始化错误*/printk(KERN_DEBUG "\n mpu6050_init error \n");return -1;}return 0;
}
4.2 如何对i2c设备进行初始化?
主要是对mpu6050寄存器的一些配置,写寄存器操作。
这里我们看一下对应的write函数
static int i2c_write_mpu6050(struct i2c_client *mpu6050_client, u8 address, u8 data)
{int error;u8 write_data[2];struct i2c_msg send_msg;write_data[0] = address;write_data[1] = data;send_msg.addr = mpu6050_client->addr;send_msg.flag = 0; // 写操作send_msg.data = &write_data;send_msg.len = 2;error = i2c_transfer(mpu6050_client->adapter, &send_msg, 1);return 0;
}
五、初始化结束之后就可以对该i2c设备进行读操作了
static int i2c_read_mpu6050(struct i2c_client *mpu6050_client, u8 address, void *data, u32 length)
{int error;u8 address_data = address;struct i2c_msg mpu6050_msg[2];mpu6050_msg[0].addr = mpu6050_client->addr;mpu6050_msg[0].flag = 0;mpu6050_msg[0].data = &address_data;mpu6050_msg[0].len = 1;mpu6050_msg[1].addr = mpu6050_client->addr;mpu6050_msg[1].flag = I2C_M_RD; // 读数据mpu6050_msg[1].buf = data; //读到的数据保存的位置mpu6050_msg[1].len = 1;error = i2c_transfer(mpu6050_client->adapter, mpu6050_msg, 2);return 0;
}
static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{char data_H;char data_L;int error;short mpu6050_result[6]; //保存mpu6050转换得到的原始数据// printk("\n mpu6050_read \n");i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_H, &data_H, 1);i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_L, &data_L, 1);mpu6050_result[0] = data_H << 8;mpu6050_result[0] += data_L;i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_H, &data_H, 1);i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_L, &data_L, 1);mpu6050_result[1] = data_H << 8;mpu6050_result[1] += data_L;i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_H, &data_H, 1);i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_L, &data_L, 1);mpu6050_result[2] = data_H << 8;mpu6050_result[2] += data_L;i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_H, &data_H, 1);i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_L, &data_L, 1);mpu6050_result[3] = data_H << 8;mpu6050_result[3] += data_L;i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_H, &data_H, 1);i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_L, &data_L, 1);mpu6050_result[4] = data_H << 8;mpu6050_result[4] += data_L;i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_H, &data_H, 1);i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_L, &data_L, 1);mpu6050_result[5] = data_H << 8;mpu6050_result[5] += data_L;// printk("AX=%d, AY=%d, AZ=%d \n",(int)mpu6050_result[0],(int)mpu6050_result[1],(int)mpu6050_result[2]);// printk("GX=%d, GY=%d, GZ=%d \n \n",(int)mpu6050_result[3],(int)mpu6050_result[4],(int)mpu6050_result[5]);/*将读取得到的数据拷贝到用户空间*/error = copy_to_user(buf, mpu6050_result, cnt);if(error != 0){printk("copy_to_user error!");return -1;}return 0;
}
到指定寄存器去读数据,读到的数据存入需要的result 中。