拆解LinuxI2C驱动之mpu6050

发布于:2025-09-13 ⋅ 阅读:(19) ⋅ 点赞:(0)

做项目的时候遇到了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 中。


网站公告

今日签到

点亮在社区的每一天
去签到