1 硬件连接

在这里插入图片描述

2 开启树莓派I2C功能

# 1. 启用I2C
sudo raspi-config
→ 3 Interface Options → I2C → 选择Yes → 重启树莓派

# 2. 安装I2C工具(用于检测设备)
sudo apt install -y i2c-tools

# 3. 检测MPU6050是否被识别(地址默认0x68)
sudo i2cdetect -y 1
# 输出中出现“68” → 硬件连接成功

3 修改设备树

3.1 编辑树莓派设备树源文件

cd 你的内核源码目录/arch/arm64/boot/dts/broadcom
sudo vim bcm2711-rpi-4-b.dts

3.2 在I2C节点下添加MPU6050

&i2c1 {
    status = "okay";
    clock-frequency = <100000>; // I2C速率100kHz

    // MPU6050节点(名称格式:设备名@地址)
    mpu6050@68 {
        compatible = "invensense,mpu6050"; // 驱动匹配的关键标识
        reg = <0x68>;                     // MPU6050的I2C地址
        status = "okay";                  // 启用该节点
        // 若使用中断,添加以下配置(INT接GPIO4):
        // interrupt-parent = <&gpio>;
        // interrupts = <4 IRQ_TYPE_EDGE_RISING>;
    };
};

3.3 编译设备树并替换到SD卡

# 回到内核源码根目录,编译设备树
sudo make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs

# 将编译后的DTB文件(bcm2711-rpi-4-b.dtb)拷贝到SD卡boot分区
sudo cp arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb /mnt/rpi_boot/

4 编写MPU6050字符设备驱动(适配设备树)

驱动需通过设备树的compatible属性匹配MPU6050节点,实现I2C通信+字符设备接口,用户空间可通过/dev/mpu6050读取传感器数据。

// mpu6050_drv.c
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/of.h>

// MPU6050寄存器定义
#define MPU6050_PWR_MGMT_1    0x6B  // 电源管理寄存器
#define MPU6050_ACCEL_XOUT_H  0x3B  // 加速度X轴高8位
#define MPU6050_ACCEL_YOUT_H  0x3D  // 加速度Y轴高8位
#define MPU6050_ACCEL_ZOUT_H  0x3F  // 加速度Z轴高8位
#define MPU6050_GYRO_XOUT_H   0x43  // 陀螺仪X轴高8位
#define MPU6050_GYRO_YOUT_H   0x45  // 陀螺仪Y轴高8位
#define MPU6050_GYRO_ZOUT_H   0x47  // 陀螺仪Z轴高8位

// 字符设备相关定义
#define DEV_NAME "mpu6050"
#define DEV_CNT  1

static dev_t dev_num;                // 设备号
static struct cdev mpu6050_cdev;     // 字符设备结构体
static struct class *mpu6050_class;  // 设备类
static struct i2c_client *g_client;  // I2C客户端指针

// 读取MPU6050寄存器(16位有符号数)
static int mpu6050_read_reg(struct i2c_client *client, u8 reg, short *val)
{
    u8 buf[2];
    int ret;
    ret = i2c_smbus_read_i2c_block_data(client, reg, 2, buf);
    if (ret < 0) {
        dev_err(&client->dev, "读取寄存器0x%02x失败\n", reg);
        return ret;
    }
    *val = (buf[0] << 8) | buf[1];
    return 0;
}

// 初始化MPU6050(唤醒传感器+配置量程)
static int mpu6050_init(struct i2c_client *client)
{
    // 唤醒传感器(关闭睡眠)
    if (i2c_smbus_write_byte_data(client, MPU6050_PWR_MGMT_1, 0x00) < 0) {
        dev_err(&client->dev, "唤醒MPU6050失败\n");
        return -EIO;
    }
    // 配置加速度计量程:±2g(0x00)
    i2c_smbus_write_byte_data(client, 0x1C, 0x00);
    // 配置陀螺仪量程:±2000°/s(0x18)
    i2c_smbus_write_byte_data(client, 0x1B, 0x18);
    dev_info(&client->dev, "MPU6050初始化成功\n");
    return 0;
}

// ==================== 关键修改:去掉浮点运算,改用整数模拟两位小数 ====================
// 字符设备read接口:用户空间读取传感器数据(纯整数运算)
static ssize_t mpu6050_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    short accel_x, accel_y, accel_z;
    short gyro_x, gyro_y, gyro_z;
    char data[128];
    int len;
    // 整数版:放大100倍计算两位小数(避免浮点)
    int accel_x_int, accel_y_int, accel_z_int;
    int gyro_x_int, gyro_y_int, gyro_z_int;

    // 读取传感器原始数据(16位有符号整数)
    mpu6050_read_reg(g_client, MPU6050_ACCEL_XOUT_H, &accel_x);
    mpu6050_read_reg(g_client, MPU6050_ACCEL_YOUT_H, &accel_y);
    mpu6050_read_reg(g_client, MPU6050_ACCEL_ZOUT_H, &accel_z);
    mpu6050_read_reg(g_client, MPU6050_GYRO_XOUT_H, &gyro_x);
    mpu6050_read_reg(g_client, MPU6050_GYRO_YOUT_H, &gyro_y);
    mpu6050_read_reg(g_client, MPU6050_GYRO_ZOUT_H, &gyro_z);

    // 整数运算模拟两位小数(避免浮点):
    // 加速度:±2g量程 → 16384 LSB/g → 1g = 16384 → 1/100g = 163.84 → 放大100倍:accel_x * 100 / 16384
    accel_x_int = (accel_x * 100) / 16384;
    accel_y_int = (accel_y * 100) / 16384;
    accel_z_int = (accel_z * 100) / 16384;
    // 陀螺仪:±2000°/s量程 → 131 LSB/(°/s) → 1°/s = 131 → 1/100°/s = 1.31 → 放大100倍:gyro_x * 100 / 131
    gyro_x_int = (gyro_x * 100) / 131;
    gyro_y_int = (gyro_y * 100) / 131;
    gyro_z_int = (gyro_z * 100) / 131;

    // 格式化数据:用整数拆分出“整数部分”和“小数部分”(替代%.2f)
    len = snprintf(data, sizeof(data),
        "加速度(X,Y,Z): %d.%02d, %d.%02d, %d.%02d g\n"
        "陀螺仪(X,Y,Z): %d.%02d, %d.%02d, %d.%02d °/s\n",
        // 加速度:整数部分=值/100,小数部分=值%100(补0到两位)
        accel_x_int / 100, abs(accel_x_int) % 100,
        accel_y_int / 100, abs(accel_y_int) % 100,
        accel_z_int / 100, abs(accel_z_int) % 100,
        // 陀螺仪:同上
        gyro_x_int / 100, abs(gyro_x_int) % 100,
        gyro_y_int / 100, abs(gyro_y_int) % 100,
        gyro_z_int / 100, abs(gyro_z_int) % 100);

    // 拷贝数据到用户空间
    if (copy_to_user(buf, data, len)) {
        return -EFAULT;
    }
    return len;
}

// 文件操作结构体
static const struct file_operations mpu6050_fops = {
    .owner = THIS_MODULE,
    .read = mpu6050_read,
};

// 设备树匹配表(与设备树compatible一致)
static const struct of_device_id mpu6050_of_match[] = {
    { .compatible = "invensense,mpu6050" },
    { /* 哨兵节点 */ }
};
MODULE_DEVICE_TABLE(of, mpu6050_of_match);

// 适配6.6内核的probe函数(仅i2c_client参数)
static int mpu6050_probe(struct i2c_client *client)
{
    int ret;
    g_client = client;

    // 初始化MPU6050硬件
    if (mpu6050_init(client) < 0) {
        return -EIO;
    }

    // 动态分配设备号
    ret = alloc_chrdev_region(&dev_num, 0, DEV_CNT, DEV_NAME);
    if (ret < 0) {
        dev_err(&client->dev, "分配设备号失败\n");
        return ret;
    }

    // 初始化并添加字符设备
    cdev_init(&mpu6050_cdev, &mpu6050_fops);
    ret = cdev_add(&mpu6050_cdev, dev_num, DEV_CNT);
    if (ret < 0) {
        dev_err(&client->dev, "添加字符设备失败\n");
        goto unregister_chrdev;
    }

    // 适配6.6内核的class_create(仅名称参数)
    mpu6050_class = class_create(DEV_NAME);
    if (IS_ERR(mpu6050_class)) {
        ret = PTR_ERR(mpu6050_class);
        dev_err(&client->dev, "创建设备类失败\n");
        goto cdev_del;
    }

    // 创建设备文件(/dev/mpu6050)
    device_create(mpu6050_class, NULL, dev_num, NULL, DEV_NAME);

    dev_info(&client->dev, "MPU6050驱动加载成功,设备文件:/dev/mpu6050\n");
    return 0;

// 错误处理:反向释放资源
cdev_del:
    cdev_del(&mpu6050_cdev);
unregister_chrdev:
    unregister_chrdev_region(dev_num, DEV_CNT);
    return ret;
}

// I2C驱动remove函数
static void mpu6050_remove(struct i2c_client *client)
{
    // 销毁设备文件和类
    device_destroy(mpu6050_class, dev_num);
    class_destroy(mpu6050_class);
    // 删除字符设备并释放设备号
    cdev_del(&mpu6050_cdev);
    unregister_chrdev_region(dev_num, DEV_CNT);
    dev_info(&client->dev, "MPU6050驱动卸载成功\n");
}

// 适配6.6内核的i2c_driver结构体
static struct i2c_driver mpu6050_driver = {
    .driver = {
        .name = "mpu6050",
        .of_match_table = mpu6050_of_match,
    },
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
};

// 注册I2C驱动
module_i2c_driver(mpu6050_driver);

// 模块信息(必须声明GPL许可证)
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MPU6050字符设备驱动(适配Linux 6.6内核+树莓派4,纯整数运算)");
MODULE_AUTHOR("Custom");

5 编译驱动与设备树

5.1 编译驱动模块

创建Makefile(与mpu6050_drv.c同目录):

obj-m += mpu6050_drv.o
KDIR := 你的内核源码目录
PWD := $(shell pwd)

all:
    make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C $(KDIR) M=$(PWD) modules
clean:
    make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C $(KDIR) M=$(PWD) clean
make
# 生成 mpu6050_drv.ko 模块文件

5.2 编译设备树

确保设备树已编译为DTB并替换到SD卡boot分区:

sudo cp 内核源码目录/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb /mnt/rpi_boot/

6 测试驱动

  • 将mpu6050_drv.ko拷贝到树莓派,加载驱动:
sudo insmod mpu6050_drv.ko
  • 查看驱动是否匹配成功:
dmesg | grep mpu6050
# 输出“MPU6050设备匹配成功” → 设备树+驱动匹配成功
  • 读取传感器数据
cat /dev/mpu6050
# 输出示例:
# Accel(X,Y,Z): 0.02, -0.01, 1.00 g
# Gyro(X,Y,Z): 0.12, -0.05, 0.03 °/s

关键说明:
设备树匹配:驱动通过of_device_id中的compatible与设备树节点的compatible一致,实现“自动匹配”;
I2C通信:驱动通过i2c_client与MPU6050通信,无需硬编码I2C地址(地址从设备树中读取);
字符设备接口:用户空间通过/dev/mpu6050直接读取传感器数据,符合Linux设备驱动规范。

若需卸载驱动,执行:

sudo rmmod mpu6050_drv

解决系统驱动冲突的代码:

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>

// ==================== 寄存器/设备ID定义(核心兼容MPU6050/6500) ====================
#define MPU6050_WHOAMI_REG     0x75    // WhoAmI寄存器(读取设备ID)
#define MPU6050_PWR_MGMT_1_REG 0x6B    // 电源管理寄存器
#define MPU6050_ACCEL_XOUT_H   0x3B    // 加速度X轴高8位
#define MPU6050_ACCEL_YOUT_H   0x3D    // 加速度Y轴高8位
#define MPU6050_ACCEL_ZOUT_H   0x3F    // 加速度Z轴高8位
#define MPU6050_GYRO_XOUT_H    0x43    // 陀螺仪X轴高8位
#define MPU6050_GYRO_YOUT_H    0x45    // 陀螺仪Y轴高8位
#define MPU6050_GYRO_ZOUT_H    0x47    // 陀螺仪Z轴高8位

#define MPU6050_DEVICE_ID      0x68    // MPU6050的WhoAmI值
#define MPU6500_DEVICE_ID      0x70    // MPU6500的WhoAmI值

// ==================== 字符设备基础定义 ====================
#define DEVICE_NAME    "mpu6050"
#define DEVICE_COUNT   1

// ==================== 全局变量 ====================
static dev_t mpu6050_dev_num;          // 设备号
static struct cdev mpu6050_cdev;       // 字符设备结构体
static struct class *mpu6050_class;    // 设备类
static struct i2c_client *mpu6050_client; // I2C客户端指针

// ==================== 寄存器读取函数(16位有符号数) ====================
static int mpu6050_read_reg_16(struct i2c_client *client, u8 reg, short *value)
{
    u8 buf[2];
    int ret;

    ret = i2c_smbus_read_i2c_block_data(client, reg, 2, buf);
    if (ret < 0) {
        dev_err(&client->dev, "读取寄存器0x%02x失败, 错误码: %d\n", reg, ret);
        return ret;
    }

    // 合并高低8位为16位有符号数
    *value = (short)((buf[0] << 8) | buf[1]);
    return 0;
}

// ==================== 设备ID检测函数(兼容6050/6500) ====================
static int mpu6050_check_device_id(struct i2c_client *client)
{
    u8 whoami;
    int ret;

    // 读取WhoAmI寄存器(1字节)
    ret = i2c_smbus_read_byte_data(client, MPU6050_WHOAMI_REG);
    if (ret < 0) {
        dev_err(&client->dev, "读取设备ID失败, 错误码: %d\n", ret);
        return ret;
    }
    whoami = (u8)ret;

    // 验证设备ID
    if (whoami != MPU6050_DEVICE_ID && whoami != MPU6500_DEVICE_ID) {
        dev_err(&client->dev, "设备ID不匹配! 读取到0x%02x, 期望0x%02x(MPU6050)或0x%02x(MPU6500)\n",
                whoami, MPU6050_DEVICE_ID, MPU6500_DEVICE_ID);
        return -ENODEV;
    }

    // 打印识别到的设备型号
    dev_info(&client->dev, "检测到传感器: %s (ID=0x%02x)\n",
             (whoami == MPU6050_DEVICE_ID) ? "MPU6050" : "MPU6500", whoami);
    return 0;
}

// ==================== 传感器初始化函数 ====================
static int mpu6050_hw_init(struct i2c_client *client)
{
    int ret;

    // 第一步:检测设备ID
    ret = mpu6050_check_device_id(client);
    if (ret < 0) {
        return ret;
    }

    // 第二步:唤醒传感器(关闭睡眠模式)
    ret = i2c_smbus_write_byte_data(client, MPU6050_PWR_MGMT_1_REG, 0x00);
    if (ret < 0) {
        dev_err(&client->dev, "唤醒传感器失败, 错误码: %d\n", ret);
        return ret;
    }

    // 第三步:配置加速度计量程(±2g,0x00)
    ret = i2c_smbus_write_byte_data(client, 0x1C, 0x00);
    if (ret < 0) {
        dev_err(&client->dev, "配置加速度计失败, 错误码: %d\n", ret);
        return ret;
    }

    // 第四步:配置陀螺仪量程(±2000°/s,0x18)
    ret = i2c_smbus_write_byte_data(client, 0x1B, 0x18);
    if (ret < 0) {
        dev_err(&client->dev, "配置陀螺仪失败, 错误码: %d\n", ret);
        return ret;
    }

    dev_info(&client->dev, "传感器初始化完成\n");
    return 0;
}

// ==================== 字符设备READ接口(纯整数运算,无浮点) ====================
static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    short accel_x, accel_y, accel_z;
    short gyro_x, gyro_y, gyro_z;
    char data_buf[128];
    int len;
    // 整数放大100倍模拟两位小数(规避浮点运算)
    int acc_x, acc_y, acc_z;
    int gyr_x, gyr_y, gyr_z;

    // 读取加速度原始数据
    if (mpu6050_read_reg_16(mpu6050_client, MPU6050_ACCEL_XOUT_H, &accel_x) < 0) return -EIO;
    if (mpu6050_read_reg_16(mpu6050_client, MPU6050_ACCEL_YOUT_H, &accel_y) < 0) return -EIO;
    if (mpu6050_read_reg_16(mpu6050_client, MPU6050_ACCEL_ZOUT_H, &accel_z) < 0) return -EIO;

    // 读取陀螺仪原始数据
    if (mpu6050_read_reg_16(mpu6050_client, MPU6050_GYRO_XOUT_H, &gyro_x) < 0) return -EIO;
    if (mpu6050_read_reg_16(mpu6050_client, MPU6050_GYRO_YOUT_H, &gyro_y) < 0) return -EIO;
    if (mpu6050_read_reg_16(mpu6050_client, MPU6050_GYRO_ZOUT_H, &gyro_z) < 0) return -EIO;

    // 整数运算转换为物理单位(放大100倍)
    acc_x = (accel_x * 100) / 16384;  // ±2g量程:16384 LSB/g
    acc_y = (accel_y * 100) / 16384;
    acc_z = (accel_z * 100) / 16384;
    gyr_x = (gyro_x * 100) / 131;     // ±2000°/s量程:131 LSB/(°/s)
    gyr_y = (gyro_y * 100) / 131;
    gyr_z = (gyro_z * 100) / 131;

    // 格式化输出(拆分整数/小数部分,替代%.2f)
    len = snprintf(data_buf, sizeof(data_buf),
        "加速度(X,Y,Z): %d.%02d, %d.%02d, %d.%02d g\n"
        "陀螺仪(X,Y,Z): %d.%02d, %d.%02d, %d.%02d °/s\n",
        acc_x / 100, abs(acc_x) % 100,
        acc_y / 100, abs(acc_y) % 100,
        acc_z / 100, abs(acc_z) % 100,
        gyr_x / 100, abs(gyr_x) % 100,
        gyr_y / 100, abs(gyr_y) % 100,
        gyr_z / 100, abs(gyr_z) % 100);

    // 拷贝数据到用户空间
    if (copy_to_user(buf, data_buf, len)) {
        dev_err(&mpu6050_client->dev, "数据拷贝到用户空间失败\n");
        return -EFAULT;
    }

    return len;
}

// ==================== 文件操作结构体 ====================
static const struct file_operations mpu6050_fops = {
    .owner   = THIS_MODULE,
    .read    = mpu6050_read,
    .llseek  = no_llseek,  // 禁止偏移,每次读取最新数据
};

// ==================== 设备树匹配表(与设备树compatible对应) ====================
static const struct of_device_id mpu6050_of_match[] = {
    { .compatible = "invensense,mpu6050" },
    { /* 哨兵节点 */ }
};
MODULE_DEVICE_TABLE(of, mpu6050_of_match);

// ==================== I2C驱动PROBE函数(适配6.6内核,仅i2c_client参数) ====================
static int mpu6050_probe(struct i2c_client *client)
{
    int ret;

    // 保存I2C客户端指针
    mpu6050_client = client;

    // 第一步:初始化传感器硬件
    ret = mpu6050_hw_init(client);
    if (ret < 0) {
        return ret;
    }

    // 第二步:动态分配设备号
    ret = alloc_chrdev_region(&mpu6050_dev_num, 0, DEVICE_COUNT, DEVICE_NAME);
    if (ret < 0) {
        dev_err(&client->dev, "分配设备号失败, 错误码: %d\n", ret);
        return ret;
    }

    // 第三步:初始化字符设备
    cdev_init(&mpu6050_cdev, &mpu6050_fops);
    ret = cdev_add(&mpu6050_cdev, mpu6050_dev_num, DEVICE_COUNT);
    if (ret < 0) {
        dev_err(&client->dev, "添加字符设备失败, 错误码: %d\n", ret);
        goto err_unregister;
    }

    // 第四步:创建设备类(适配6.6内核,仅名称参数)
    mpu6050_class = class_create(DEVICE_NAME);
    if (IS_ERR(mpu6050_class)) {
        ret = PTR_ERR(mpu6050_class);
        dev_err(&client->dev, "创建设备类失败, 错误码: %d\n", ret);
        goto err_cdev_del;
    }

    // 第五步:创建设备文件(/dev/mpu6050)
    if (IS_ERR(device_create(mpu6050_class, NULL, mpu6050_dev_num, NULL, DEVICE_NAME))) {
        dev_err(&client->dev, "创建设备文件失败\n");
        ret = -EIO;
        goto err_class_destroy;
    }

    dev_info(&client->dev, "%s驱动加载成功! 设备文件: /dev/%s\n", DEVICE_NAME, DEVICE_NAME);
    return 0;

    // 错误处理(反向释放资源)
err_class_destroy:
    class_destroy(mpu6050_class);
err_cdev_del:
    cdev_del(&mpu6050_cdev);
err_unregister:
    unregister_chrdev_region(mpu6050_dev_num, DEVICE_COUNT);
    return ret;
}

// ==================== I2C驱动REMOVE函数 ====================
static void mpu6050_remove(struct i2c_client *client)
{
    // 销毁设备文件
    device_destroy(mpu6050_class, mpu6050_dev_num);
    // 销毁设备类
    class_destroy(mpu6050_class);
    // 删除字符设备
    cdev_del(&mpu6050_cdev);
    // 释放设备号
    unregister_chrdev_region(mpu6050_dev_num, DEVICE_COUNT);

    dev_info(&client->dev, "%s驱动卸载成功\n", DEVICE_NAME);
}

// ==================== I2C驱动结构体(适配6.6内核) ====================
static struct i2c_driver mpu6050_driver = {
    .driver = {
        .name = DEVICE_NAME,
        .of_match_table = mpu6050_of_match,
        .owner = THIS_MODULE,
    },
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
};

// ==================== 模块注册/卸载 ====================
module_i2c_driver(mpu6050_driver);

// ==================== 模块信息 ====================
MODULE_LICENSE("GPL");                  // 必须声明GPL,否则内核拒绝加载
MODULE_AUTHOR("Custom");
MODULE_DESCRIPTION("MPU6050/6500驱动(适配Linux 6.6+树莓派4+Debian 13 Trixie)");
MODULE_VERSION("1.0");
Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐