驅動程序實例(六):mpu6050(IIC + cdev)

在咱們實際開發中,I2C 總線驅動通常芯片原廠會提供,咱們開發通常是設計設備驅動。html

本文結合以前對Linux內核的IIC子系統的分析 ,以及對字符設備的cdev接口的分析,本文將編寫基於IIC總線與cdev接口的MPU6050設備的實例代碼並對其進行分析。node

IIC子系統分析:詳見Linux IIC總線驅動框架linux

字符設備的cdev接口分析:詳見Linux字符設備驅動(一):cdev接口緩存

 

硬件接口:數據結構

  CPU:s5pv210;框架

  掛載IIC總線編號:0。ide

 

IIC從設備驅動掛載在IIC總線下,IIC總線管理着IIC從設備的設備信息(i2c_client)與設備驅動(i2c_driver)。所以,IIC從設備驅動的編寫分爲兩個部分:註冊IIC從設備信息、編寫IIC從設備驅動程序。spa

在設備驅動程序中使用IIC總線,需確保Linux內核支持IIC。在menuconfig下作以下設置接口。設計

  Device Drivers  --->code

    <*> I2C support  --->  

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);
相關文章
相關標籤/搜索