实验硬件及原理图:

1. 野火 IMX6ULL-MINI开发板

2.MPU6050G51模块

3.原理图

驱动实现:

1.更改设备树:

(一)&i2c2节点添加MPU6050设备修改IO复用

&i2c2 {clock_frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c2>;status = "okay";MPU6050: mpu6050@68 {compatible = "fireMini,mpu6050";reg = <0x68>;};
};pinctrl_i2c2: i2c2grp {fsl,pins = <MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0>;};

(二)编译设备树文件,启动开发板

当看到设备添加成功后就可以开始驱动程序的编写

(三)mpu6050.h

#ifndef MPU6050REG_H
#define MPU6050REG_Htypedef unsigned char   uint8_t;/* register define */
#define MPU6050_SMPLRT_DIV                                  0x19     /*陀螺仪采样率,典型值:0x07(125Hz) */
#define MPU6050_CONFIG                                      0x1A     /*低通滤波频率,典型值:0x06(5Hz)*/
#define MPU6050_GYRO_CONFIG                                 0x1B     /*陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)*/
#define MPU6050_ACCEL_CONFIG                                0x1C     /*加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)*/
#define MPU6050_ACCEL_XOUT_H                                0x3B     /**/
#define MPU6050_ACCEL_XOUT_L                                0x3C
#define MPU6050_ACCEL_YOUT_H                                0x3D
#define MPU6050_ACCEL_YOUT_L                                0x3E
#define MPU6050_ACCEL_ZOUT_H                                0x3F
#define MPU6050_ACCEL_ZOUT_L                                0x40
#define MPU6050_TEMP_OUT_H                                  0x41
#define MPU6050_TEMP_OUT_L                                  0x42
#define MPU6050_GYRO_XOUT_H                                 0x43
#define MPU6050_GYRO_XOUT_L                                 0x44
#define MPU6050_GYRO_YOUT_H                                 0x45
#define MPU6050_GYRO_YOUT_L                                 0x46
#define MPU6050_GYRO_ZOUT_H                                 0x47
#define MPU6050_GYRO_ZOUT_L                                 0x48
#define MPU6050_PWR_MGMT_1                                  0x6B       /*电源管理,典型值:0x00(正常启用) */
#define MPU6050_WHO_AM_I                                    0x75       /* IIC地址寄存器(默认数值0x68,只读) */
#define MPU6050_SlaveAddress                                0xD0       /*IIC写入时的地址字节数据,+1为读取*/
#define MPU6050_IIC_ADDR                                    0x68       /*MPU6050 IIC 器件地址*//* 中断状态寄存器*/
#define MPU6050_INT_STATUS                                  0x3A
#define MPU6050_INT_ENABLE                                  0x38
#define MPU6050_INT_PIN_CFG                                 0x37struct mpu6050_accel {short x;short y;short z;
};struct mpu6050_gyro {short x;short y;short z;
};struct mpu6050_data {struct mpu6050_accel accel;struct mpu6050_gyro gyro;
};#endif // __MPU6050REG_H

(四)mpu6050.c

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/i2c.h>
#include "mpu6050reg.h"#define mpu6050dev_CNT 1
#define mpu6050dev_NAME "mpu6050"
#define MPU6050_USE_INT 0   /* 不使用中断  */struct mpu6050_dev{  int major;     /*  主设备号 */  int minor;    /* 次设备号 */  struct cdev cdev;      /* cdev */struct class *class;   /* 类 */struct device *device; /* 设备 */dev_t devid;   /* 设备号 */struct i2c_client *client;  void  *private_data;  /* 私有数据   */int16_t acceleration[3], gyro[3], temp;      /* mpu6050数据 acceleration:加速度数据  gyro:陀螺仪数据  temp:温度指数据   */};static struct mpu6050_dev mpu6050dev;/** @description : 从mpu6050读取多个寄存器数据* @param - dev:  mpu6050设备* @param - reg:  要读取的寄存器首地址* @param - val:  读取到的数据* @param - len:  要读取的数据长度* @return      : 操作结果*/
static int mpu6050_read_regs(struct mpu6050_dev *dev, u8 reg, void *val, int len)
{int ret;struct i2c_msg msg[2];struct i2c_client *client = (struct i2c_client *)dev->private_data;/* msg[0]为发送要读取的首地址 */msg[0].addr = client->addr;         /* mpu6050地址 */msg[0].flags = 0;                   /* 标记为发送数据 */msg[0].buf = &reg;                    /* 读取的首地址 */msg[0].len = 1;                        /* reg长度*//* msg[1]读取数据 */msg[1].addr = client->addr;           /* mpu6050地址 */msg[1].flags = I2C_M_RD;            /* 标记为读取数据*/msg[1].buf = val;                  /* 读取数据缓冲区 */msg[1].len = len;                 /* 要读取的数据长度*/ret = i2c_transfer(client->adapter, msg, 2);if(ret == 2) {ret = 0;} else {printk("i2c read failed=%d reg=%06x len=%d\n",ret, reg, len);ret = -EREMOTEIO;}return ret;
}/** @description  : 向mpu6050多个寄存器写入数据* @param - dev:  mpu6050设备* @param - reg:  要写入的寄存器首地址* @param - val:  要写入的数据缓冲区* @param - len:  要写入的数据长度* @return     :   操作结果*/
static s32 mpu6050_write_regs(struct mpu6050_dev *dev, u8 reg, u8 *buf, u8 len)
{u8 b[256];struct i2c_msg msg;struct i2c_client *client = (struct i2c_client *)dev->private_data;b[0] = reg;                   /* 寄存器首地址 */memcpy(&b[1],buf,len);      /* 将要写入的数据拷贝到数组b里面 */msg.addr = client->addr;   /* mpu6050地址 */msg.flags = 0;              /* 标记为写数据 */msg.buf = b;               /* 要写入的数据缓冲区 */msg.len = len + 1;         /* 要写入的数据长度 */return i2c_transfer(client->adapter, &msg, 1);
}/** @description  : 读取mpu6050指定寄存器值,读取一个寄存器* @param - dev:  mpu6050设备* @param - reg:  要读取的寄存器* @return    :   读取到的寄存器值*/
static unsigned char mpu6050_read_reg(struct mpu6050_dev *dev, u8 reg)
{u8 data = 0;mpu6050_read_regs(dev, reg, &data, 1);return data;
}/** @description  : 向mpu6050指定寄存器写入指定的值,写一个寄存器* @param - dev:  mpu6050设备* @param - reg:  要写的寄存器* @param - data: 要写入的值* @return   :    无*/
static void mpu6050_write_reg(struct mpu6050_dev *dev, u8 reg, u8 data)
{u8 buf = 0;buf = data;mpu6050_write_regs(dev, reg, &buf, 1);
}/*MPU6050 设备初始化 */
static void mpu6050_reset(void) {mpu6050_write_reg(&mpu6050dev,MPU6050_PWR_MGMT_1, 0x00);   //解除休眠状态mpu6050_write_reg(&mpu6050dev,MPU6050_SMPLRT_DIV, 0x07);mpu6050_write_reg(&mpu6050dev,MPU6050_CONFIG, 0x06);mpu6050_write_reg(&mpu6050dev,MPU6050_GYRO_CONFIG, 0x18);mpu6050_write_reg(&mpu6050dev,MPU6050_ACCEL_CONFIG, 0x01);
}
/** @description   : 读取mpu6050的数据,读取原始数据* @param - dev:  mpu6050设备* @return       : 无。*/
void mpu6050_readdata(struct mpu6050_dev *dev)
{unsigned char i =0;unsigned char buf[6];unsigned char val = 0x3B;/*从加速度寄存器(0x3B)开始读取6字节数据Start reading acceleration registers from register 0x3B for 6 bytes   */mpu6050_read_regs(dev, val, buf, 6);for(i = 0; i < 3; i++)    {dev->acceleration[i] = (buf[i * 2] << 8 | buf[(i * 2) + 1]);}/*从陀螺仪器数据寄存器(0x43)读取6字节数据The register is auto incrementing on each readNow gyro data from reg 0x43 for 6 bytesThe register is auto incrementing on each read*/val = 0x43;mpu6050_read_regs(dev, val, buf, 6);for(i = 0; i < 3; i++)   {dev->gyro[i] = (buf[i * 2] << 8 | buf[(i * 2) + 1]);}/*从温度寄存器(0x41)读取2字节数据寄存器在每次读取时自动递增Now temperature from reg 0x41 for 2 bytesThe register is auto incrementing on each read*/val = 0x41;mpu6050_read_regs(dev, val, buf, 2);for(i = 0; i < 3; i++)  {dev->temp = buf[0] << 8 | buf[1];}
}/** @description      : 打开设备* @param - inode     : 传递给驱动的inode* @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量*                       一般在open的时候将private_data指向设备结构体。* @return             : 0 成功;其他 失败*/
static int mpu6050_open(struct inode *inode,struct file *filp)
{filp->private_data = &mpu6050dev;printk("mpu6050-Drive_open\r\n");return 0;
}/** @description      : 关闭/释放设备* @param - filp   : 要关闭的设备文件(文件描述符)* @return             : 0 成功;其他 失败*/
static int mpu6050_release(struct inode *inode,struct file *filp)
{return 0;
}/** @description      : 从设备读取数据 * @param - filp  : 要打开的设备文件(文件描述符)* @param - buf    : 返回给用户空间的数据缓冲区* @param - cnt  : 要读取的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int16_t MPU6050_data[7];long err = 0;struct mpu6050_dev *dev = (struct mpu6050_dev *)filp->private_data;mpu6050_readdata(dev);MPU6050_data[0] = dev->acceleration[0];MPU6050_data[1] = dev->acceleration[1];MPU6050_data[2] = dev->acceleration[2];MPU6050_data[3] = dev->gyro[0];MPU6050_data[4] = dev->gyro[1];MPU6050_data[5] = dev->gyro[2];MPU6050_data[6] = dev->temp;err = copy_to_user(buf, MPU6050_data, sizeof(MPU6050_data));return 0;}/*字符操作集*/
static const struct file_operations mpu6050_fops = {.open      =  mpu6050_open,.release   =  mpu6050_release,.read       =  mpu6050_read,.owner     =  THIS_MODULE,
};/** @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行* @param - client  : i2c设备* @return          : 0,成功;其他负值,失败*/
static int mpu6050_remove(struct i2c_client *client)
{/* 卸载字符设备驱动 */cdev_del(&mpu6050dev.cdev);unregister_chrdev_region(mpu6050dev.devid,mpu6050dev_CNT);device_destroy(mpu6050dev.class, mpu6050dev.devid);class_destroy(mpu6050dev.class);printk("mpu6050-Drive_EXIT!\r\n");return 0;
}/*当 I2C 设备和 I2C 驱动匹配成功以后 probe 函数就会执行*//** @description     : i2c驱动的probe函数,当驱动与*                    设备匹配以后此函数就会执行* @param - client  : i2c设备* @param - id      : i2c设备ID* @return          : 0,成功;其他负值,失败*/
static int mpu6050_probe(struct i2c_client *client,const struct i2c_device_id *id)
{printk("mpu6050_probe\r\n");/*搭建字符设备框架*//* 1.注册字符设备驱动 */mpu6050dev.major = 0;if(mpu6050dev.major){mpu6050dev.devid = MKDEV(mpu6050dev.major,0);register_chrdev_region(mpu6050dev.devid,mpu6050dev_CNT,mpu6050dev_NAME);}else{      alloc_chrdev_region(&mpu6050dev.devid,0,mpu6050dev_CNT,mpu6050dev_NAME);mpu6050dev.major = MAJOR(mpu6050dev.devid);mpu6050dev.minor = MINOR(mpu6050dev.devid);}/* 2.初始化cdev */mpu6050dev.cdev.owner = THIS_MODULE;cdev_init(&mpu6050dev.cdev,&mpu6050_fops);/* 3.添加cdev */cdev_add(&mpu6050dev.cdev,mpu6050dev.devid,mpu6050dev_CNT);/* 4.创建类 */mpu6050dev.class = class_create(THIS_MODULE,mpu6050dev_NAME);if (IS_ERR(mpu6050dev.class)) {return PTR_ERR(mpu6050dev.class);}/* 5.创建设备     */mpu6050dev.device = device_create(mpu6050dev.class, NULL, mpu6050dev.devid, NULL,mpu6050dev_NAME);if (IS_ERR(mpu6050dev.device)) {return PTR_ERR(mpu6050dev.device);}mpu6050dev.private_data = client;mpu6050_reset();printk("mpu6050_reset\n");return 0;
}
/* 传统的匹配表     无设备树的时候匹配 ID 表 */
static struct i2c_device_id mpu6050_id[] = {{"fireMini,mpu6050",0},{}
};/*设备匹配表*/
static struct of_device_id mpu6050_of_match[] ={{ .compatible = "fireMini,mpu6050" },{}
};/*i2c_driver*/
static struct i2c_driver mpu6050_driver = {.driver = {.name = "mpu6050",.owner = THIS_MODULE,.of_match_table = of_match_ptr(mpu6050_of_match),},.probe       = mpu6050_probe,.remove        = mpu6050_remove,.id_table   = mpu6050_id,
};static int __init mpu6050dev_init(void){int ret = 0;ret = i2c_add_driver(&mpu6050_driver);return 0;}static void __exit mpu6050dev_exit(void){i2c_del_driver(&mpu6050_driver);
}module_init(mpu6050dev_init);
module_exit(mpu6050dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("XXXX");

(五)测试程序  mpu6050APP.c

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>/** @description       : main主程序* @param - argc   : argv数组元素个数* @param - argv    : 具体参数* @return            : 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd;char *filename;int16_t databuf[7];int16_t gyro_x_adc, gyro_y_adc, gyro_z_adc;int16_t accel_x_adc, accel_y_adc, accel_z_adc;int16_t temp_adc;int ret = 0;if (argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if(fd < 0) {printf("can't open file %s\r\n", filename);return -1;}while (1) {ret = read(fd, databuf, sizeof(databuf));if(ret == 0) {            /* 数据读取成功 */accel_x_adc = databuf[0];accel_y_adc = databuf[1];accel_z_adc = databuf[2];gyro_x_adc = databuf[3];gyro_y_adc = databuf[4];gyro_z_adc = databuf[5];temp_adc = databuf[6];printf("Acc. X = %d, Y = %d, Z = %d\n", accel_x_adc, accel_y_adc, accel_z_adc);printf("Gyro. X = %d, Y = %d, Z = %d\n", gyro_x_adc, gyro_y_adc, gyro_z_adc);// Temperature is simple so use the datasheet calculation to get deg C.// Note this is chip temperature.printf("Temp. = %f\n", (temp_adc / 340.0) + 36.53);}usleep(1000000); /*1000ms */}close(fd);   /* 关闭文件 */  return 0;
}

(六)运行效果

Linux 驱动 IIC_MPU6050相关推荐

  1. linux驱动:音频驱动(六)ASoc之codec设备

    linux驱动:音频驱动(六)ASoc之codec设备

  2. linux驱动:音频驱动(五)ASoc之codec驱动

    linux驱动:音频驱动(五)ASoc之codec驱动

  3. linux驱动:音频驱动(四)ASoc之machine设备

    linux驱动:音频驱动(四)ASoc之machine设备

  4. Linux驱动之LCD驱动编写

    在Linux驱动之内核自带的S3C2440的LCD驱动分析这篇博客中已经分析了编写LCD驱动的步骤,接下来就按照这个步骤来字尝试字节编写LCD驱动.用的LCD屏幕为tft屏,每个像素点为16bit.对 ...

  5. 【Linux 驱动】第九章 与硬件通信

    在学习有关I/O总线的内容时,最好先看看相关的知识:从PC总线到ARM的内部总线 一,I/O 端口和 I/O 内存 每种外设都是通过读写寄存器来进行控制. 大部分外设都有几个寄存器,不管是在内存地址空 ...

  6. 8188无线网卡驱动linux,rtl8188eu linux驱动

    rtl8188eu linux驱动是一款适用于rtl8188eu系列的无线网卡驱动程序,瓜分没有提供的Linux驱动程序,可以下载这个安装,就可以在Linux下上网了. 安装方法 首先请确认你的无线网 ...

  7. linux更新驱动脚本,编写Linux驱动常见错误(不断更新)!

    工作中遇到的编写Linux驱动的常见错误和注意事项整理,将不断更新. 问题1.驱动的init函数声明错误 出错: [root@localhost]# insmod phyinfo.ko insmod: ...

  8. Linux驱动修炼之道-SPI驱动框架源码分析(上)

    Linux驱动修炼之道-SPI驱动框架源码分析(上)   SPI协议是一种同步的串行数据连接标准,由摩托罗拉公司命名,可工作于全双工模式.相关通讯设备可工作于m/s模式.主设备发起数据帧,允许多个从设 ...

  9. 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)

    这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...

  10. 利用Eclipse开发Linux驱动

    之前写Linux驱动都是在纯文本下完成的,最近发现原来可以使用Eclipse来开发,于是捣鼓了半天终于编译成功,感觉还不错.下面以Hello World驱动为例说一下使用Eclipse开发ARM-Li ...

最新文章

  1. Spring Cloud Stream的使用(上)
  2. 计算机组成原理6-20,计算机组成原理课后题6.20教案.ppt
  3. Linux 文件系统编程之系统调用和标准I/O库
  4. 字符编码乱码问题(servlet底层 编码大揭秘)
  5. MySQL实时获取有性能问题的SQL
  6. linux服务器知识学习:linux系统的目录结构
  7. mysql基础之mariadb的安装,连接,用户,密码,权限设置语句详解
  8. python中pass的使用_Python pass详细介绍及实例代码
  9. mac上解决Resource temporarily unavailable
  10. MapReduce再学习:资源管理框架YARN
  11. Pytorch构建Transformer实现英文翻译
  12. Office Tool Plus软件运行错误,缺少.net
  13. oCam 中文绿色版 - 免费实用的屏幕录像与截图软件 (制作视频教程/录制直播视频)
  14. 多臂老虎机(Multi-armed bandit problem)
  15. Python Numpy random.chisquare() 卡方分布
  16. 计算机流行音乐与创作软件,哪种计算机歌曲软件更好?歌曲软件推荐
  17. js中字符串数字转换为数值类型
  18. 2023最新苹果CMS 10仿韩剧TV主题模板源码+UI简约大气
  19. File-Upload
  20. 抖音商务团队三面java_腾讯抖音iOS岗位三面面经

热门文章

  1. 计算机pdf转换word,PDF怎么转换成Word?解决PDF转Word的小妙招
  2. 品牌如何正确联动B站UP主“恰饭视频”,最近一支不像恰饭视频的作品在B站火了
  3. 【VBA研究】调用API实现汉字简繁互换
  4. 【JAVA SE基础篇】27.面向对象三大特征之封装
  5. 实变函数与泛函分析课本pdf_免费推荐几本实变函数和泛函分析的书
  6. 向下舍入是什么意思_舍入是什么意思
  7. 基于JS实现简单甘特图
  8. R语言中交集,并集,补集,差集的方法:向量和数据框
  9. python俄罗斯方块思路_python实现俄罗斯方块小游戏
  10. 百度HI QQ和MSN 阿里旺旺贸易通MSN在线客服聊天代码