瑞芯微rk3506g2:sdk内核驱动学习日记——i2c驱动篇
2026/4/27 5:43:20 网站建设 项目流程

目录

引言

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>; mpu6050@68 { compatible = "invensense,mpu6050"; reg = <0x68>; status = "okay"; }; };

这里夹两点要说一下,io这一块,使用io12和io11不能跑通输出全是0,暂且不知道是什么原因,鞭策技术支持说是电源问题,还不是很懂这一块。

另外一点,就算标签这个要我和这样一致当然型号不同就需要修改,还有就是reg这一块,这个是设备地址。

另外clock-frequency这个是i2c的速率

这里配置就完成

配置文件

这里就需要去配置一下文件

在终端内核目录下运行以下指令

make ARCH=arm menuconfig

根据以下做修改

Device Drivers ---> Industrial I/O support ---> Inertial measurement units ---> <*> Invensense MPU6050 devices

修改后,就保存编译烧录

make ARCH=arm 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 Z=3244 Temp(raw): -3312

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询