目录引言sdk自带的mpu6050驱动设备树配置文件验证自己写驱动程序驱动代码配置文件验证引言在嵌入式当中i2c算是一个简单外设具体的i2c知识这里不过多说明主要说在内核中配置出来。这里是用iic驱动一个mpu6050,有两种方式这里要说一点在内核驱动开发中没有提供数学库不能做姿态解算哈sdk自带的mpu6050驱动需要配置一下设备树和改一下配置文件即可。设备树i2c1 { status okay; pinctrl-names default; pinctrl-0 rm_io29_i2c1_scl rm_io30_i2c1_sda;//不可使用io11和io12不知道为什么使用io29和io30可以成功扫描到mpu6050设备 clock-frequency 400000; mpu605068 { compatible invensense,mpu6050; reg 0x68; status okay; }; };这里夹两点要说一下io这一块使用io12和io11不能跑通输出全是0,暂且不知道是什么原因鞭策技术支持说是电源问题还不是很懂这一块。另外一点就算标签这个要我和这样一致当然型号不同就需要修改还有就是reg这一块这个是设备地址。另外clock-frequency这个是i2c的速率。这里配置就完成配置文件这里就需要去配置一下文件在终端内核目录下运行以下指令make ARCHarm menuconfig根据以下做修改Device Drivers --- Industrial I/O support --- Inertial measurement units --- * Invensense MPU6050 devices修改后就保存编译烧录make ARCHarm savedefconfig cp arch/arm/configs/vanxoak_hd_rk3506g_evm_nand_defconfig arch/arm/configs/vanxoak_hd_rk3506g_evm_nand_defconfig_bak cp defconfig arch/arm/configs/vanxoak_hd_rk3506g_evm_nand_defconfig cd .. ./build.sh kernel验证在开发板运行以下指令ls /sys/bus/iio/devices/会出现一个以及以上类似这样iio:deviceX是哪一个呢运行以下指令ls -l /sys/bus/iio/devices/会出现类型以下的信息lrwxrwxrwx 1 root root 0 Jan 1 00:00 iio:device1 - ../../../devices/platform/ff050000.i2c/i2c-1/1-0068/iio:device1注意看后面的0068,这一个就是mpu6050的地址我们进入cd /sys/bus/iio/devices/iio:device1 ls会出现类似以下列表current_timestamp_clock in_anglvel_y_calibbias in_accel_matrix in_anglvel_y_raw in_accel_mount_matrix in_anglvel_z_calibbias in_accel_scale in_anglvel_z_raw in_accel_scale_available in_gyro_matrix in_accel_x_calibbias in_temp_offset in_accel_x_raw in_temp_raw in_accel_y_calibbias in_temp_scale in_accel_y_raw name in_accel_z_calibbias of_node in_accel_z_raw power in_anglvel_mount_matrix sampling_frequency in_anglvel_scale sampling_frequency_available in_anglvel_scale_available subsystem in_anglvel_x_calibbias uevent in_anglvel_x_raw waiting_for_supplier其中cat in_accel_x_raw加速度计X轴原始数据cat in_accel_y_raw加速度计Y轴原始数据cat in_accel_z_raw加速度计Z轴原始数据cat in_anglvel_x_raw陀螺仪X轴原始数据cat in_anglvel_y_raw陀螺仪Y轴原始数据cat in_anglvel_z_raw陀螺仪Z轴原始数据cat in_temp_raw温度传感器原始数据其他的自己研究均使用cat查看。自己写驱动程序驱动代码设备树不用修改需要把之前的menuconfig的配置文件那个取消保存在内核源码目录下的drivers/iio/imu下创建mpu6050_driver.c并添加以下代码// mpu6050_cdev.c #include linux/types.h #include linux/module.h #include linux/i2c.h #include linux/cdev.h #include linux/fs.h #include linux/uaccess.h #include linux/device.h #include linux/delay.h #define DEVICE_NAME mpu6050 #define CLASS_NAME mpu6050 struct mpu6050_dev { struct i2c_client *client; struct cdev cdev; dev_t dev_num; struct class *class; struct device *device; }; static struct mpu6050_dev *mpu6050_dev; static int mpu6050_open(struct inode *inode, struct file *file) { return 0; } static ssize_t mpu6050_read(struct file *file, char __user *buf, size_t len, loff_t *off) { struct mpu6050_dev *dev mpu6050_dev; u8 data[14]; s16 accel_x, accel_y, accel_z, temp, gyro_x, gyro_y, gyro_z; char kbuf[256]; int ret; if (!dev || !dev-client) return -ENODEV; // 连续读取14字节从0x3B开始 ret i2c_smbus_read_i2c_block_data(dev-client, 0x3B, 14, data); if (ret ! 14) { dev_err(dev-device, Failed to read MPU6050 data (ret%d)\n, ret); return -EIO; } // 大端序组装高字节在前 accel_x (s16)((data[0] 8) | data[1]); accel_y (s16)((data[2] 8) | data[3]); accel_z (s16)((data[4] 8) | data[5]); temp (s16)((data[6] 8) | data[7]); gyro_x (s16)((data[8] 8) | data[9]); gyro_y (s16)((data[10] 8) | data[11]); gyro_z (s16)((data[12] 8) | data[13]); ret snprintf(kbuf, sizeof(kbuf), Accel: X%d Y%d Z%d\nTemp(raw): %d\nGyro: X%d Y%d Z%d\n, accel_x, accel_y, accel_z, temp, gyro_x, gyro_y, gyro_z); if (len ret) return -EINVAL; if (copy_to_user(buf, kbuf, ret)) return -EFAULT; return ret; } static int mpu6050_release(struct inode *inode, struct file *file) { return 0; } static const 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) { int ret; struct device *dev client-dev; mpu6050_dev devm_kzalloc(dev, sizeof(*mpu6050_dev), GFP_KERNEL); if (!mpu6050_dev) return -ENOMEM; mpu6050_dev-client client; // 分配设备号 ret alloc_chrdev_region(mpu6050_dev-dev_num, 0, 1, DEVICE_NAME); if (ret) { dev_err(dev, Failed to allocate chrdev region\n); return ret; } // 创建类 mpu6050_dev-class class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(mpu6050_dev-class)) { ret PTR_ERR(mpu6050_dev-class); goto err_unregister; } // 初始化 cdev cdev_init(mpu6050_dev-cdev, mpu6050_fops); ret cdev_add(mpu6050_dev-cdev, mpu6050_dev-dev_num, 1); if (ret) { dev_err(dev, Failed to add cdev\n); goto err_destroy_class; } // 创建设备节点 mpu6050_dev-device device_create(mpu6050_dev-class, NULL, mpu6050_dev-dev_num, NULL, DEVICE_NAME); if (IS_ERR(mpu6050_dev-device)) { ret PTR_ERR(mpu6050_dev-device); dev_err(dev, Failed to create device\n); goto err_cdev_del; } // --- 正确的MPU6050初始化序列 --- // 1. 检查WHO_AM_I ret i2c_smbus_read_byte_data(client, 0x75); if (ret 0) { dev_err(dev, Failed to read WHO_AM_I\n); goto err_device_destroy; } dev_info(dev, WHO_AM_I 0x%02x\n, ret); // 2. 复位设备 i2c_smbus_write_byte_data(client, 0x6B, 0x80); msleep(100); // 3. 唤醒并配置时钟源写0x01是关键 ret i2c_smbus_write_byte_data(client, 0x6B, 0x01); if (ret 0) { dev_err(dev, Failed to wake up MPU6050\n); goto err_device_destroy; } msleep(50); // 4. 可选量程配置 i2c_smbus_write_byte_data(client, 0x1B, 0x18); // 陀螺仪 ±2000°/s i2c_smbus_write_byte_data(client, 0x1C, 0x00); // 加速度 ±2g dev_info(dev, MPU6050 cdev driver loaded, /dev/%s created\n, DEVICE_NAME); return 0; err_device_destroy: device_destroy(mpu6050_dev-class, mpu6050_dev-dev_num); err_cdev_del: cdev_del(mpu6050_dev-cdev); err_destroy_class: class_destroy(mpu6050_dev-class); err_unregister: unregister_chrdev_region(mpu6050_dev-dev_num, 1); return ret; } static void mpu6050_remove(struct i2c_client *client) { device_destroy(mpu6050_dev-class, mpu6050_dev-dev_num); cdev_del(mpu6050_dev-cdev); class_destroy(mpu6050_dev-class); unregister_chrdev_region(mpu6050_dev-dev_num, 1); dev_info(client-dev, MPU6050 driver removed\n); } static const struct of_device_id mpu6050_of_match[] { { .compatible invensense,mpu6050 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mpu6050_of_match); static const struct i2c_device_id mpu6050_id[] { { mpu6050, 0 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, mpu6050_id); static struct i2c_driver mpu6050_driver { .driver { .name mpu6050_cdev, .of_match_table mpu6050_of_match, }, .probe mpu6050_probe, .remove mpu6050_remove, .id_table mpu6050_id, }; module_i2c_driver(mpu6050_driver); MODULE_LICENSE(GPL); MODULE_AUTHOR(Your Name); MODULE_DESCRIPTION(MPU6050 character device driver with correct init);配置文件在对应文件夹下的Makefile末尾添加以下代码obj-y mpu6050_driver.o验证编译烧录后在开发板上运行cat /dev/mpu6050就会出现mpu6050的数据。类似这样Gyro: X-21 Y-10 Z-1 Accel: X-15304 Y-2220 Z3244 Temp(raw): -3312