这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【PocketBeagle2】五,I2C子系统-MPU6050驱动

共1条 1/1 1 跳转至

【PocketBeagle2】五,I2C子系统-MPU6050驱动

菜鸟
2025-11-03 09:20:27     打赏

【PocketBeagle2】五,I2C子系统-MPU6050驱动

本次实验我们选择i2c2接口与mpu6050相连。实物连接图如下:

image-20251101160201751image-20251101160201751.png

一,MPU6050设备树插件

设备树插件(Device Tree Overlay,简称DTO)是Linux内核(通常在4.4版本及以上引入)中一种强大的动态设备树扩展机制。可以把它理解成能给正在运行的系统“打补丁”,允许在系统运行时动态地添加、修改或删除设备树中的节点和属性,而无需重新编译和烧写整个设备树。

1.设备树源码

/dts-v1/;
/plugin/;

#include <dt-bindings/leds/common.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>

/*
* Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/
*/
&{/chosen} {
overlays {
 kone-pocketbeagle2.kernel = __TIMESTAMP__;
};
};
&main_i2c2 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;

mpu6050@68{
 status = "okay";
 compatible = "kone,i2c_mpu6050";
 reg = <0x68>;
};
};

2.编译设备树插件

设置正确的环境变量


export PATH="/home/mxz/pocketbeagle_2/sdk/gcc-11.5.0-nolibc/aarch64-linux/bin:$PATH"
# 设置架构和交叉编译前缀
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-

makefile中添加新增的设备树

nano /home/mxz/pocketbeagle_2/sdk/linux/arch/arm64/boot/dts/ti/Makefile
dtb-$(CONFIG_ARCH_K3) += kone-pocketbeagle2.dtbo

image-20251101161259115image-20251101161259115.png

编译

# 进入内核源码目录
cd /home/mxz/pocketbeagle_2/sdk/linux

# 编译所有设备树
make dtbs

image-20251101161413714image-20251101161413714.png

3.加载设备树插件

  1. 将生成的 kone-pocketbeagle2.dtbo  copy到/boot/firmware/overlays/下

  2. 更新引导配置

    sudo nano /boot/firmware/extlinux/extlinux.conf
    label Linux
       kernel /Image.gz
       fdtdir /
       append console=ttyS2,115200n8 earlycon=ns16550a,mmio32,0x02860000 root=/dev/mmcblk1p2 ro rootfstype=ext4 rootwait net.ifnames=0
       fdtoverlays /overlays/kone-pocketbeagle2.dtbo
  3. 验证

    # 重启
    sudo reboot

    确认设备树覆盖层加载成功

    # 查看覆盖层时间戳(确认编译时间)
    cat /sys/firmware/devicetree/base/chosen/overlays/kone-pocketbeagle2.kernel

    # 查看所有加载的覆盖层
    ls /sys/firmware/devicetree/base/chosen/overlays/

    image-20251101165934395image.png



    查看 I2C2 总线

    ls /sys/bus/i2c2/devices/

image-20251101170020447    image.png

二,MPU6050驱动

  1. 驱动源码

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/kdev_t.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/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>
    //==================================define======================================
    /*---- MPU6050 设备地址与寄存器宏 ----*/
    #define Address 0x68                  // MPU6050默认I2C地址(AD0接地)。若AD0接VCC则地址为0x69
    #define SMPLRT_DIV 0x19               // 采样率分频器寄存器:配置陀螺仪输出率分频系数(默认0x07)
    #define CONFIG 0x1A                   // 配置寄存器:设置数字低通滤波器带宽
    #define GYRO_CONFIG 0x1B              // 陀螺仪配置寄存器:量程选择(±250/500/1000/2000°/s)与自检
    #define ACCEL_CONFIG 0x1C             // 加速度计配置寄存器:量程选择(±2/4/8/16g)与自检
    
    /* 加速度计数据输出寄存器 (16-bit, 高位在前) */
    #define ACCEL_XOUT_H 0x3B             // 加速度计X轴高8位数据
    #define ACCEL_XOUT_L 0x3C             // 加速度计X轴低8位数据
    #define ACCEL_YOUT_H 0x3D             // 加速度计Y轴高8位数据
    #define ACCEL_YOUT_L 0x3E             // 加速度计Y轴低8位数据
    #define ACCEL_ZOUT_H 0x3F             // 加速度计Z轴高8位数据
    #define ACCEL_ZOUT_L 0x40             // 加速度计Z轴低8位数据
    
    /* 温度与陀螺仪数据输出寄存器 */
    #define TEMP_OUT_H 0x41               // 温度传感器高8位数据(计算公式:TEMP/340 + 36.53)
    #define TEMP_OUT_L 0x42               // 温度传感器低8位数据
    #define GYRO_XOUT_H 0x43              // 陀螺仪X轴高8位数据
    #define GYRO_XOUT_L 0x44              // 陀螺仪X轴低8位数据
    #define GYRO_YOUT_H 0x45              // 陀螺仪Y轴高8位数据
    #define GYRO_YOUT_L 0x46              // 陀螺仪Y轴低8位数据
    #define GYRO_ZOUT_H 0x47              // 陀螺仪Z轴高8位数据
    #define GYRO_ZOUT_L 0x48              // 陀螺仪Z轴低8位数据
    
    /* 关键控制寄存器 */
    #define PWR_MGMT_1 0x6B               // 电源管理寄存器1:控制时钟源、睡眠模式、温度传感器禁用
    #define WHO_AM_I 0x75                 // 设备ID寄存器:读取值应为0x68(用于验证连接)
    
    /*---- I2C控制参数宏 (Linux ioctl专用) ----*/
    #define SlaveAddress 0xD0              // I2C写地址 = (Address << 1) | 0。读地址应为0xD1
    #define I2C_RETRIES 0x0701            // ioctl命令:设置I2C传输失败重试次数
    #define I2C_TIMEOUT 0x0702            // ioctl命令:设置I2C总线超时时间(单位:10ms步进)
    #define I2C_SLAVE 0x0703              // ioctl命令:设置I2C从机地址(需配合Address使用)
    #define I2C_BUS_MODE 0x0780           // ioctl命令:设置I2C总线模式(标准模式100kbps/快速模式400kbps)
    
    
    #define DEV_NAME                    "mpu6050"
    
    static dev_t dev_mpu6050;
    static struct cdev cdev_mpu6050;
    
    struct class *class_mpu6050;
    struct device *device_mpu6050;
    
    struct i2c_client* mpu6050_client = NULL;
    static int i2c_write_mpu6050(struct i2c_client*mpu6050_client,uint8_t address,uint8_t data)
    {
        int ret;
        uint8_t write_data[2];
        struct i2c_msg send_msg;
        write_data[0] = address;
        write_data[1] = data;
        send_msg.addr = mpu6050_client->addr;
        send_msg.flags = 0;
        send_msg.buf = write_data;
        send_msg.len = 2;
    
        ret = i2c_transfer(mpu6050_client->adapter,&send_msg,1);
        if(ret != 1)
        {
            printk("i2c_write_mpu6050_i2c_transfer is error\n");
            return -1;
        }
        return 0;
    }
    static int i2c_read_mpu6050(struct i2c_client *i2c_client,uint8_t address,uint8_t *data,uint32_t len)
    {
        int ret;
        uint8_t address_data = address;
        struct i2c_msg i2c_msg[2] ;
    
        i2c_msg[0].addr = i2c_client->addr;
        i2c_msg[0].flags = 0;      //写入
        i2c_msg[0].buf = &address_data;
        i2c_msg[0].len = 1;
    
        i2c_msg[1].addr = i2c_client->addr;
        i2c_msg[1].flags = I2C_M_RD;
        i2c_msg[1].buf = data;
        i2c_msg[1].len = len;
    
        ret = i2c_transfer(i2c_client->adapter,i2c_msg,2);
        if(ret != 2)
        {
            printk("i2c_read_mpu6050 i2c_transfer is error\n");
            return -1;
        }
        return 0;
    }
    
    static int mpu6050_init(void)
    {
        static uint8_t last_register = 0;
        /* 检查I2C客户端是否有效 */
        if(!mpu6050_client)
        {
            printk(KERN_ERR "MPU6050: Invalid I2C client\n");
            return -EINVAL;
        }
        /* 配置电源管理寄存器 */
        last_register = PWR_MGMT_1;
        if(i2c_write_mpu6050(mpu6050_client,PWR_MGMT_1,0x00) < 0)
        {
            goto init_failed;
        }
        /* 配置采样率分频器 */
        last_register = SMPLRT_DIV;
        if(i2c_write_mpu6050(mpu6050_client,SMPLRT_DIV,0x07) < 0)
        {
            goto init_failed;
        }
        /* 配置低通滤波器 */
        last_register = CONFIG;
        if(i2c_write_mpu6050(mpu6050_client,CONFIG,0X06) < 0)
        {
            goto init_failed;
        }
        /* 配置加速度计量程 */
        last_register = ACCEL_CONFIG;
        if(i2c_write_mpu6050(mpu6050_client,ACCEL_CONFIG,0x01) < 0)
        {
            goto init_failed;
        }
        /* 初始化成功 */
        printk(KERN_DEBUG "MPU6050 initialized successfully\n");
        return 0;
    init_failed:
        printk(KERN_ERR "MPU6050 init failed at register 0x%02X\n", last_register);
        return -EIO;
    }
    
    static int mpu6050_open(struct  inode*inode,struct file*file)
    {
        mpu6050_init();
        return 0;
    }
    static ssize_t mpu6050_read(struct file*file,char __user*buf,size_t size,loff_t*off)
    {
        uint8_t data_H,data_L;
        int ret;
        short mpu6050_data[7];
    
        // 加速度
        i2c_read_mpu6050(mpu6050_client,ACCEL_XOUT_H,&data_H,1);
        i2c_read_mpu6050(mpu6050_client,ACCEL_XOUT_L,&data_L,1);
        mpu6050_data[0] = (data_H << 8) | (data_L & 0xff);
    
        i2c_read_mpu6050(mpu6050_client,ACCEL_YOUT_H,&data_H,1);
        i2c_read_mpu6050(mpu6050_client,ACCEL_YOUT_L,&data_L,1);
        mpu6050_data[1] = (data_H << 8) | (data_L & 0xff);
    
        i2c_read_mpu6050(mpu6050_client,ACCEL_ZOUT_H,&data_H,1);
        i2c_read_mpu6050(mpu6050_client,ACCEL_ZOUT_L,&data_L,1);
        mpu6050_data[2] = (data_H << 8) | (data_L & 0xff);
    
        //角速度
        i2c_read_mpu6050(mpu6050_client,GYRO_XOUT_H,&data_H,1);
        i2c_read_mpu6050(mpu6050_client,GYRO_XOUT_L,&data_L,1);
        mpu6050_data[3] = (data_H << 8) | (data_L & 0xff);
    
        i2c_read_mpu6050(mpu6050_client,GYRO_YOUT_H,&data_H,1);
        i2c_read_mpu6050(mpu6050_client,GYRO_YOUT_L,&data_L,1);
        mpu6050_data[4] = (data_H << 8) | (data_L & 0xff);
    
        i2c_read_mpu6050(mpu6050_client,GYRO_ZOUT_H,&data_H,1);
        i2c_read_mpu6050(mpu6050_client,GYRO_ZOUT_L,&data_L,1);
        mpu6050_data[5] = (data_H << 8) | (data_L & 0xff);
    
        //温度
        i2c_read_mpu6050(mpu6050_client,TEMP_OUT_H,&data_H,1);
        i2c_read_mpu6050(mpu6050_client,TEMP_OUT_L,&data_L,1);
        mpu6050_data[6] = (data_H << 8) | (data_L & 0xff);
    
        ret = copy_to_user(buf,mpu6050_data,size);//目标add  源add
        if(ret != 0)
        {
            printk("copy_to_user is error\n");
        }
        return 0;
    }
    static int mpu6050_release(struct inode*inode,struct file*file)
    {
        return 0;
    }
    
    static struct file_operations  mpu6050_fops = {
        .owner = THIS_MODULE,
        .open = mpu6050_open,
        .read = mpu6050_read,
        .release = mpu6050_release,
    };
    
    //平台驱动函数集,主要用于申请设备号,注册添加设备,创建class等
    static int mpu6050_probe(struct i2c_client*client)
    {
        int ret;
        int major,minor;
        ret = alloc_chrdev_region(&dev_mpu6050,0,1,DEV_NAME);
        if(ret < 0)
        {
            printk("alloc_chrdev_region is error \n");
            return -1;
        }
        major = MAJOR(dev_mpu6050);
        minor = MINOR(dev_mpu6050);
        printk("major is %d \n minor is %d\n",major,minor);
    
        cdev_mpu6050.owner = THIS_MODULE;
        cdev_init(&cdev_mpu6050,&mpu6050_fops);
    
        ret = cdev_add(&cdev_mpu6050,dev_mpu6050,1);
        if(ret < 0)
        {
            printk("cdev_add is error\n");
            return -1;
        }
    
        class_mpu6050 = class_create(DEV_NAME);
        device_mpu6050 = device_create(class_mpu6050,NULL,dev_mpu6050,NULL,DEV_NAME);
    
        mpu6050_client = client;
        return 0;
    }
    static void mpu6050_remove(struct i2c_client*client)
    {
        device_destroy(class_mpu6050,dev_mpu6050);
        class_destroy(class_mpu6050);
        cdev_del(&cdev_mpu6050);
        unregister_chrdev_region(dev_mpu6050,1);
    }
    
    //定义ID匹配表
    static const struct i2c_device_id mpu6050_device_id[] = {
        {"kone,i2c_mpu6050"},
        {},
    };
    //定义设备树匹配表
    static const struct of_device_id mpu6050_of_match_table[] = {
        {.compatible = "kone,i2c_mpu6050"},
        {},
    };
    
    struct i2c_driver mpu6050_driver = {
        .probe = mpu6050_probe,
        .remove = mpu6050_remove,
        .id_table = mpu6050_device_id,
        .driver = {
            .name = "kone,i2c_mpu6050",
            .owner = THIS_MODULE,
            .of_match_table = mpu6050_of_match_table,
        },
    };
    
    static int __init mpu6050_driver_init(void)
    {
        printk("mpu6050_driver_init\n");
        i2c_add_driver(&mpu6050_driver);
        return 0;
    }
    static void __exit mpu6050_driver_exit(void)
    {
        i2c_del_driver(&mpu6050_driver);
        printk("mpu6050_driver_exit \n");
    }
    
    
    module_init(mpu6050_driver_init);
    module_exit(mpu6050_driver_exit);
    MODULE_LICENSE("GPL v2");
    MODULE_AUTHOR("kone");
  2. Makefile

    #export PATH="/home/mxz/pocketbeagle_2/sdk/gcc-11.5.0-nolibc/aarch64-linux/bin:$PATH"
    # 确保系统工具目录在 PATH 的最前面
    export PATH="/bin:/usr/bin:/home/mxz/pocketbeagle_2/sdk/gcc-11.5.0-nolibc/aarch64-linux/bin:$PATH"
    # 设置架构和交叉编译前缀
    export ARCH=arm64
    export CROSS_COMPILE=aarch64-linux-
    obj-m:=mpu6050.o
    
    KDIR:=/home/mxz/pocketbeagle_2/sdk/linux
    PWD:=$(shell pwd)
    
    all:
     make -C $(KDIR) M=$(PWD) modules
    clean:
     make -C $(KDIR) M=$(PWD) clean


  3. 测试app

    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    #include <stdlib.h>
    
    int main(int argc,char *argv[])
    {
        int fd,ret;
        short mpu6050_data[7] = {0};
        fd = open(argv[1],O_RDWR);
        if(fd < 0)
        {
            perror("open file is error \n");
            return -1;
        }
        while(1)
        {
            ret = read(fd,mpu6050_data,sizeof(mpu6050_data));
            if(ret < 0)
            {
                perror("read file is error\n");
                return -1;
            }
    
            printf("ax:%d,ay:%d,az:%d\n",mpu6050_data[0],mpu6050_data[1],mpu6050_data[2]);
            printf("gx:%d,gy:%d,gz:%d\n",mpu6050_data[3],mpu6050_data[4],mpu6050_data[5]);
            printf("temperature:%d \n",mpu6050_data[6]);
    
            sleep(1);
        }
        close(fd);
        return 0;
    }

三,演示

将生成的mpu6050.ko和app.c   copy到开发板上

image-20251102162023153image.png

image-20251102161544064



共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]