驱动程序实例(六):mpu6050(IIC + cdev)

在我们实际开发中,I2C 总线驱动一般芯片原厂会提供,我们开发一般是设计设备驱动。

本文结合之前对Linux内核的IIC子系统的分析 ,以及对字符设备的cdev接口的分析,本文将编写基于IIC总线与cdev接口的MPU6050设备的实例代码并对其进行分析。

IIC子系统分析:详见Linux IIC总线驱动框架

字符设备的cdev接口分析:详见Linux字符设备驱动(一):cdev接口

硬件接口:

  CPU:s5pv210;

  挂载IIC总线编号:0。

IIC从设备驱动挂载在IIC总线下,IIC总线管理着IIC从设备的设备信息(i2c_client)与设备驱动(i2c_driver)。因此,IIC从设备驱动的编写分为两个部分:注册IIC从设备信息、编写IIC从设备驱动程序。

1. 注册IIC从设备信息

在/kernel/arch/arm/mach-s5pv210/mach-x210.c文件中添加如下信息,向Linux内核注册IIC从设备信息。

static struct i2c_board_info mpu6050_i2c_devs0[] __initdata =
{
    {
        I2C_BOARD_INFO("mpu6050", 0x2b),//构建i2c_board_info结构体,0x2b为从设备的地址
    },
};

static void __init smdkc110_machine_init(void)
{
    ... ...
    i2c_register_board_info(0, mpu6050_i2c_devs0, ARRAY_SIZE(mpu6050_i2c_devs0));//向内核注册IIC从设备信息,0表示该从设备挂载在IIC总线适配器0
    ... ...
}    

2. 编写IIC从设备驱动程序

(1)mpu6050_common.h

将mpu6050_common.h文件添加至/kernel/drivers/i2c/busses目录下。

#ifndef _MPU6050_COMMON_H_
#define _MPU6050_COMMON_H_

#define MPU6050_MAGIC ‘K‘

//mpu6050数据结构
union mpu6050_data
{
    struct {
        short x;
        short y;
        short z;
    }accel;
    struct {
        short x;
        short y;
        short z;
    }gyro;
    unsigned short temp;
};

//mpu6050的ioctl的命令定义
#define GET_ACCEL _IOR(MPU6050_MAGIC, 0, union mpu6050_data)//读取加速度计的数据
#define GET_GYRO  _IOR(MPU6050_MAGIC, 1, union mpu6050_data)//读取陀螺仪的数据
#define GET_TEMP  _IOR(MPU6050_MAGIC, 2, union mpu6050_data)//读取温度的数据

#endif

mpu6050_common.h

(2)mpu6050_dev.h

mpu6050_dev.h文件是mpu6050的寄存器地址的定义文件,将其添加至添加至/kernel/drivers/i2c/busses目录下。

#ifndef _MPU6050_DEV_H_
#define _MPU6050_DEV_H_

#define SMPLRT_DIV      0x19    //陀螺仪采样率,典型值:0x07(125Hz)
#define CONFIG          0x1A    //低通滤波频率,典型值:0x06(5Hz)
#define GYRO_CONFIG     0x1B    //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define ACCEL_CONFIG    0x1C    //加速计自检、测量范围及高通滤波,典型值:0x18(不自检,2G,5Hz)
#define ACCEL_XOUT_H    0x3B
#define ACCEL_XOUT_L    0x3C
#define ACCEL_YOUT_H    0x3D
#define ACCEL_YOUT_L    0x3E
#define ACCEL_ZOUT_H    0x3F
#define ACCEL_ZOUT_L    0x40
#define TEMP_OUT_H      0x41
#define TEMP_OUT_L      0x42
#define GYRO_XOUT_H     0x43
#define GYRO_XOUT_L     0x44
#define GYRO_YOUT_H     0x45
#define GYRO_YOUT_L     0x46
#define GYRO_ZOUT_H     0x47    //陀螺仪z轴角速度数据寄存器(高位)
#define GYRO_ZOUT_L     0x48    //陀螺仪z轴角速度数据寄存器(低位)
#define PWR_MGMT_1      0x6B    //电源管理,典型值:0x00(正常启用)
#define WHO_AM_I        0x75    //IIC地址寄存器(默认数值0x68,只读)
#define SlaveAddress    0x68    //MPU6050-I2C地址寄存器

#endif

mpu6050_dev.h

(3)mpu6050.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/cdev.h>

#include "mpu6050_dev.h"
#include "mpu6050_common.h"

#define DEV_MINOR 100       //IIC从设备的起始次设备号
#define DEV_CNT 1           //IIC从设备的个数
#define DEV_NAME "mpu6050"  //IIC从设备名称

static struct i2c_client *mpu6050_client;

static struct cdev mpu6050_dev;
static dev_t mpu6050_devnum;       //设备号
static struct class *mpu6050_class;//设备类

/*
*    功能:向mpu6050从设备写入数据
*
*    参数:struct i2c_client *client:指向mpu6050从设备
*          const unsigned char reg:需写入的mpu6050的寄存器
*          const unsigned char val:写入的数值
*/
static void mpu6050_write_byte(struct i2c_client *client, const unsigned char reg, const unsigned char val)
{
    char txbuf[2] = {reg, val};//数据缓存buffer

    //封装msg
    struct i2c_msg msg[2] = {

        [0] =
        {
            .addr = client->addr,
            .flags= 0,
            .len = sizeof(txbuf),
            .buf = txbuf,
        },
    };

    i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));//与从设备进行数据通信
}

/*
*    功能:向mpu6050从设备读取数据
*
*    参数:struct i2c_client *client:指向mpu6050从设备
*          const unsigned char reg:需读取的mpu6050的寄存器
*
*    返回值:char:读取的数据
*/
static char mpu6050_read_byte(struct i2c_client *client,const unsigned char reg)
{
    char txbuf[1] = {reg};//数据缓冲buffer
    char rxbuf[1] = {0};

    //封装msg
    struct i2c_msg msg[2] =
    {
        [0] =
        {
            .addr = client->addr,
            .flags = 0,
            .len = sizeof(txbuf),
            .buf = txbuf,
        },

        [1] =
        {
            .addr = client->addr,
            .flags = I2C_M_RD,
            .len = sizeof(rxbuf),
            .buf = rxbuf,
        },
    };

    i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); //与从设备进行数据通信

    return rxbuf[0];
}

//mpu6050硬件初始化
static void mpu6050_init(struct i2c_client *client)
{
    mpu6050_write_byte(client, PWR_MGMT_1, 0x00);
    mpu6050_write_byte(client, SMPLRT_DIV, 0x07);
    mpu6050_write_byte(client, CONFIG, 0x06);
    mpu6050_write_byte(client, GYRO_CONFIG, 0x18);
    mpu6050_write_byte(client, ACCEL_CONFIG, 0x0);
}

static int mpu6050_open(struct inode *ip, struct file *fp)
{
    return 0;
}
static int mpu6050_release(struct inode *ip, struct file *fp)
{
    return 0;
}

static long mpu6050_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
    int res = 0;
    union mpu6050_data data = {{0}};

    switch(cmd)
    {
        //读取加速度计的数据
        case GET_ACCEL:
            data.accel.x = mpu6050_read_byte(mpu6050_client,ACCEL_XOUT_L);
            data.accel.x|= mpu6050_read_byte(mpu6050_client,ACCEL_XOUT_H)<<8;
            data.accel.y = mpu6050_read_byte(mpu6050_client,ACCEL_YOUT_L);
            data.accel.y|= mpu6050_read_byte(mpu6050_client,ACCEL_YOUT_H)<<8;
            data.accel.z = mpu6050_read_byte(mpu6050_client,ACCEL_ZOUT_L);
            data.accel.z|= mpu6050_read_byte(mpu6050_client,ACCEL_ZOUT_H)<<8;
            break;

        //读取陀螺仪的数据
        case GET_GYRO:
            data.gyro.x = mpu6050_read_byte(mpu6050_client,GYRO_XOUT_L);
            data.gyro.x|= mpu6050_read_byte(mpu6050_client,GYRO_XOUT_H)<<8;
            data.gyro.y = mpu6050_read_byte(mpu6050_client,GYRO_YOUT_L);
            data.gyro.y|= mpu6050_read_byte(mpu6050_client,GYRO_YOUT_H)<<8;
            data.gyro.z = mpu6050_read_byte(mpu6050_client,GYRO_ZOUT_L);
            data.gyro.z|= mpu6050_read_byte(mpu6050_client,GYRO_ZOUT_H)<<8;
            printk("gyro:x %d, y:%d, z:%d\n",data.gyro.x,data.gyro.y,data.gyro.z);
            break;

        //读取温度的数据
        case GET_TEMP:
            data.temp = mpu6050_read_byte(mpu6050_client,TEMP_OUT_L);
            data.temp|= mpu6050_read_byte(mpu6050_client,TEMP_OUT_H)<<8;
            printk("temp: %d\n",data.temp);
            break;

        default:
            printk(KERN_INFO "invalid cmd");
            break;
    }
    printk("acc:x %d, y:%d, z:%d\n",data.accel.x,data.accel.y,data.accel.z);
    res = copy_to_user((void *)arg,&data,sizeof(data));
    return sizeof(data);
}

//mpu6050操作集
static const struct file_operations mpu6050_fops =
{
    .owner = THIS_MODULE,
    .open  = mpu6050_open,
    .release = mpu6050_release,
    .unlocked_ioctl = mpu6050_ioctl,
};

static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct device *dev;

    mpu6050_client = client;

    /*****************************初始化硬件设备******************************/
    //初始化mpu6050
    mpu6050_init(client);

    dbg("probe:name = %s,flag =%d,addr = %d,adapter = %d,driver = %s\n", client->name,
         client->flags,client->addr,client->adapter->nr,client->driver->driver.name );

    /*********************************创建接口********************************/
    cdev_init(&mpu6050_dev, &mpu6050_fops);                            //关联dev与fops
    alloc_chrdev_region(&mpu6050_devnum, DEV_MINOR, DEV_CNT, DEV_NAME);//自动分配设备号
    cdev_add(&mpu6050_dev, mpu6050_devnum, DEV_CNT);                   //添加设备至设备链表

    mpu6050_class = class_create(THIS_MODULE,DEV_NAME);                         //创建设备类
    dev = device_create(mpu6050_class, NULL , mpu6050_devnum, "%s%d", DEV_NAME);//创建mpu6050设备
    if (IS_ERR(dev))
    {
        dbg("device create error\n");
        goto out;
    }

    return 0;
out:
    return -1;
}

static int  mpu6050_remove(struct i2c_client *client)
{
    dbg("remove\n");

    device_destroy(mpu6050_class, mpu6050_devnum);
    class_destroy(mpu6050_class);
    unregister_chrdev_region(mpu6050_devnum,DEV_CNT);

    return 0;
}

//与mpu6050的设备信息匹配
static struct i2c_device_id mpu6050_ids[] =
{
    {"mpu6050",0x2b},
    {}
};

//声明mpu6050_ids是i2c类型的一个设备表
MODULE_DEVICE_TABLE(i2c,mpu6050_ids);

//定义并初始化从设备驱动信息
static struct i2c_driver mpu6050_driver =
{
    .probe    = mpu6050_probe,
    .remove   = mpu6050_remove,
    .id_table = mpu6050_ids,
    .driver =
    {
        .name = "mpu6050",
        .owner = THIS_MODULE,
    },
};

static int __init mpu6050_i2c_init(void)
{
    return i2c_add_driver(&mpu6050_driver);//注册设备驱动
}

static void __exit mpu6050_i2c_exit(void)
{
    i2c_del_driver(&mpu6050_driver);       //注销设备驱动
}

MODULE_AUTHOR("Lin");
MODULE_DESCRIPTION("mpu6050 driver");
MODULE_LICENSE("GPL");
module_init(mpu6050_i2c_init);
module_exit(mpu6050_i2c_exit);

原文地址:https://www.cnblogs.com/linfeng-learning/p/9532338.html

时间: 2024-08-29 23:05:51

驱动程序实例(六):mpu6050(IIC + cdev)的相关文章

C语言库函数大全及应用实例六

原文:C语言库函数大全及应用实例六                                              [编程资料]C语言库函数大全及应用实例六 函数名: getlinesettings 功 能: 取当前线型.模式和宽度 用 法: void far getlinesettings(struct linesettingstype far *lininfo): 程序例: #i nclude #i nclude #i nclude #i nclude /* the names o

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十六:IIC储存模块

IIC储存器是笔者用来练习精密控时的经典例子.<整合篇>之际,IIC储存器的解释,笔者也自认变态.如今笔者回头望去,笔者也不知道自己当初到底发什么神经,既然将IIC的时序都解释一番.由于开发上板也嵌着IIC储存器(24LC04),笔者还得循例地介绍一下. IIC储存器是应用IIC总线的储存器,时序本身并不是很复杂不过缺有一大堆时序参数,而且官方提供的时序也不利于描述,所以许多时序都必须自行绘制,真是麻烦死人.麻烦归麻烦,笔者终究还要吃饭,为了肚子,再麻烦的事情也要硬着头皮捱过去 ... 这也是

ASP.NET MVC3 实例(六) 增加、修改和删除操作(二)

http://www.jquery001.com/asp.net-mvc3-instance-add-update-delete2.html 上篇我们在 ASP.NET MVC3 中实现了添加操作,由于时间关系没有完成修改.删除操作,我们新建了一个名为"Contact"的 Controller,并实现了添加方法,下边就让我们在此基础上来完成 ASP.NET MVC3 中的修改和删除操作. 首先,我们在 Contact 控制器类中添加一个名为 View()的方法,用来从 Contact

android4.0 USB Camera实例(六)ffmpeg mpeg编码

前面本来说是做h264编码的 研究了两天发现ffmpeg里的h264编码似乎是要信赖第三方库x264 还是怎么简单怎么来吧所以就整了个mpeg编码 ffmpeg移植前面我有一篇ffmpeg解码里已经给了 具体链接在这http://blog.csdn.net/hclydao/article/details/18546757 怎么使用那里面也已经说了 这里主要是通过ffmpeg将yuv422格式转换成rgb 然后就是yuv422转成mpeg格式 接前面几篇 获取到yuv422数据后 为了能显示出来

python 实例六

题目:斐波那契数列. 程序分析:这个数列从第3项开始,每一项都等于前两项之和.故 n=1,2,f=1 n>2,f=f(n-1)+f(n-2) 例如:1,1,2,3,5,8..... >>> def f6(n): if n==1 or n==2: return 1 elif n>2: return f6(n-1)+f6(n-2) else: print 'please input an incorrect number' >>> for i in range(

jQuery插件实例六:jQuery 前端分页

先来看看效果: 对于前端分页,关键是思路,和分页算法.本想多说两句,可又觉得没什么可说的,看代码吧: 如何使用? $("#pging").zPagination({ 'navEvent':function(){ console.log('取数据Ajax'); } }); JS代码 //分页Pagination ; (function ($, window) { var defaults = { rowCount: 400, //总数据行数 navPage: 10, //每次显示多少页导

实例六分页处理

分页的外部封装类 <?php /** file: page.class.php 完美分页类 Page */ class Page { private $total; //数据表中总记录数 private $listRows; //每页显示行数 private $limit; //SQL语句使用limit从句,限制获取记录个数 private $uri; //自动获取url的请求地址 private $pageNum; //总页数 private $page; //当前页 private $con

6轴速度计/陀螺仪MPU6050模块 IIC程序C代码

资料下载 http://pan.baidu.com/s/15QGGG 产品参数 名称:MPU-6050模块(三轴陀螺仪+三轴加速度) 使用芯片:MPU-6050 供电电源:3-5V(内部低压差稳压) 通信方式:标准IIC通信协议 芯片内置16bit AD转换器,16位数据输出 陀螺仪范围::±250 500 1000 2000  °/s 加速度范围:±2±4±8±16g 采用沉金PCB,机器焊接工艺保证质量 引脚间距2.54mm MPU-6000为全球首例整合性6轴运动处理组件,相较于多组件方案

IIC设备驱动程序

IIC设备是一种通过IIC总线连接的设备,由于其简单性,被广泛引用于电子系统中.在现代电子系统中,有很多的IIC设备需要进行相互之间通信 IIC总线是由PHILIPS公司开发的两线式串行总线,用于连接微处理器和外部IIC设备.IIC设备产生于20世纪80年代,最初专用与音频和视频设备,现在在各种电子设备中都广泛应用 IIC总线有两条总线线路,一条是串行数据线(SDA),一条是串行时钟线(SCL).SDA负责数据传输,SCL负责数据传输的时钟同步.IIC设备通过这两条总线连接到处理器的IIC总线控