linux传感器三之轴陀螺仪(MPU3050)驱动解析


linux传感器三之轴陀螺仪(MPU3050)驱动解析



    MPU3050是invensense公司的三轴陀螺仪芯片,三轴陀螺仪最大的作用就是“测量角速度,以判别物体的运动状态,所以也称为运动传感器.

    下图是MPU3050的系统框图,芯片有1个中断引脚,可以通过i2c来控制,获取x Gyro,y Gyro,z Gyro


    设备驱动中用mpu3050_sensor结构体来描述MPU3050设备对象(对象中包含i2c客户端及输入设备来处理获取的x,y,z轴数据的传递)

    1. struct mpu3050_sensor { //mpu3050传感器  
    2.     struct i2c_client *client;  //i2c客户端  
    3.     struct device *dev; //设备文件  
    4.     struct input_dev *idev; //输入设备  
    5. };  
    用axis_data来描述获取的x Gyro,y Gyro,z Gyro的数值
    1. struct axis_data {  //轴数据  
    2.     s16 x;  //x轴  
    3.     s16 y;  //y轴  
    4.     s16 z;  //z轴  
    5. };  
    首先注册i2c设备
    1. module_i2c_driver(mpu3050_i2c_driver);  
    1. static struct i2c_driver mpu3050_i2c_driver = {  
    2.     .driver = {  
    3.         .name   = "mpu3050",  
    4.         .owner  = THIS_MODULE,  
    5.         .pm = &mpu3050_pm,  
    6.         .of_match_table = mpu3050_of_match,  
    7.     },  
    8.     .probe      = mpu3050_probe,    //i2c probe方法  
    9.     .remove     = __devexit_p(mpu3050_remove),  
    10.     .id_table   = mpu3050_ids,  
    11. };  

    i2c设备与驱动匹配需要在板级驱动中注册i2c驱动

    htc one max板的做法如下

    1. static struct i2c_board_info __initdata mpu3050_GSBI12_boardinfo[] = {  
    2.     {  
    3.         I2C_BOARD_INFO("mpu3050", 0xD0 >> 1),  
    4.         .irq = PM8921_GPIO_IRQ(PM8921_IRQ_BASE, PM_GYRO_INT),  
    5.         .platform_data = &mpu3050_data,  
    6.     },  
    7. };  
    然后调用
    1. i2c_register_board_info(MSM8064_GSBI2_QUP_I2C_BUS_ID,  
    2.                 mpu3050_GSBI12_boardinfo,  
    3.                 ARRAY_SIZE(mpu3050_GSBI12_boardinfo));  

    注册i2c板级信息


    设备匹配后调用mpu3050_probe方法
    1. static int __devinit mpu3050_probe(struct i2c_client *client,const struct i2c_device_id *id)  
    2. {  
    3.     struct mpu3050_sensor *sensor;  //mpu3050传感器  
    4.     struct input_dev *idev; //输入设备  
    5.     int ret;  
    6.     int error;  
    7.   
    8.     sensor = kzalloc(sizeof(struct mpu3050_sensor), GFP_KERNEL);    //分配mpu3050数据  
    9.     idev = input_allocate_device(); //分配输入设备  
    10.     if (!sensor || !idev) {  
    11.         dev_err(&client->dev, "failed to allocate driver data\n");  
    12.         error = -ENOMEM;  
    13.         goto err_free_mem;  
    14.     }  
    15.   
    16.     sensor->client = client; //捆绑i2c客户端  
    17.     sensor->dev = &client->dev;   //捆绑设备文件  
    18.     sensor->idev = idev; //捆绑输入设备  
    19.   
    20.     mpu3050_set_power_mode(client, 1);  //设置设备正常电压模式  
    21.     msleep(10); //睡眠  
    22.       
    23.     ret = i2c_smbus_read_byte_data(client, MPU3050_CHIP_ID_REG);//获取0x00寄存器值  
    24.     if (ret < 0) {  
    25.         dev_err(&client->dev, "failed to detect device\n");  
    26.         error = -ENXIO;  
    27.         goto err_free_mem;  
    28.     }  
    29.   
    30.     if (ret != MPU3050_CHIP_ID) {   //判断芯片ID值(0x69)是否MPU3050  
    31.         dev_err(&client->dev, "unsupported chip id\n");  
    32.         error = -ENXIO;  
    33.         goto err_free_mem;  
    34.     }  
    35.   
    36.     idev->name = "MPU3050";  //设置输入设备名  
    37.     idev->id.bustype = BUS_I2C;  //输入设备使用i2c总线  
    38.     idev->dev.parent = &client->dev;  //设置i2c设备为输入设备父设备  
    39.   
    40.     idev->open = mpu3050_input_open; //输入设备打开方法  
    41.     idev->close = mpu3050_input_close;   //输入设备关闭方法  
    42.   
    43.     __set_bit(EV_ABS, idev->evbit);  //设置绝对位移事件标志位,设置各轴数据范围  
    44.     input_set_abs_params(idev, ABS_X,MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);  
    45.     input_set_abs_params(idev, ABS_Y,MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);  
    46.     input_set_abs_params(idev, ABS_Z,MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);  
    47.   
    48.     input_set_drvdata(idev, sensor);    //&idev->dev->p->driver_data=sensor  
    49.   
    50.     pm_runtime_set_active(&client->dev);  
    51.   
    52.     error = mpu3050_hw_init(sensor);    //初始化MPU3050固件  
    53.     if (error)  
    54.         goto err_pm_set_suspended;  
    55.     //申请中断,上升沿触发  
    56.     error = request_threaded_irq(client->irq,NULL, mpu3050_interrupt_thread,IRQF_TRIGGER_RISING,"mpu3050", sensor);  
    57.     if (error) {  
    58.         dev_err(&client->dev,"can't get IRQ %d, error %d\n", client->irq, error);  
    59.         goto err_pm_set_suspended;  
    60.     }  
    61.   
    62.     error = input_register_device(idev);    //注册输入设备  
    63.     if (error) {  
    64.         dev_err(&client->dev, "failed to register input device\n");  
    65.         goto err_free_irq;  
    66.     }  
    67.   
    68.     pm_runtime_enable(&client->dev);  
    69.     pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY);  
    70.   
    71.     return 0;  
    72.   
    73. err_free_irq:  
    74.     free_irq(client->irq, sensor);  
    75. err_pm_set_suspended:  
    76.     pm_runtime_set_suspended(&client->dev);  
    77. err_free_mem:  
    78.     input_free_device(idev);  
    79.     kfree(sensor);  
    80.     return error;  
    81. }  
    申请,配置,注册相应的input设备,设置电源模式,初始化mpu3050芯片,申请中断,并指明中断返回函数

    设置电源模式:mpu3050有两种电压模式val=1为正常模式,val=0为低功耗模式

    1. static void mpu3050_set_power_mode(struct i2c_client *client, u8 val)  
    2. {  
    3.     u8 value;  
    4.     value = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);//获取0x3E寄存器数据  
    5.     //根据val值设置0x3E寄存器第6位SLEEP  
    6.     value = (value & ~MPU3050_PWR_MGM_MASK) |  
    7.         (((val << MPU3050_PWR_MGM_POS) & MPU3050_PWR_MGM_MASK) ^ MPU3050_PWR_MGM_MASK);  
    8.     i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM,value);//设置0x3E寄存器数据  
    9. }  
    初始化mpu3050:软复位,配置时钟及分频...
    1. static int __devinit mpu3050_hw_init(struct mpu3050_sensor *sensor)  
    2. {  
    3.     struct i2c_client *client = sensor->client;  //获取i2c客户端  
    4.     int ret;  
    5.     u8 reg;  
    6.   
    7.     /* Reset 设置0x3E寄存器第7位H_RESET*/  
    8.     ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM,MPU3050_PWR_MGM_RESET);  
    9.     if (ret < 0)  
    10.         return ret;  
    11.     //获取0x3E寄存器值  
    12.     ret = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);  
    13.     if (ret < 0)  
    14.         return ret;  
    15.   
    16.     ret &= ~MPU3050_PWR_MGM_CLKSEL; //清除0x3E寄存器0~2位CLK_SET值  
    17.     ret |= MPU3050_PWR_MGM_PLL_Z;   //设置0x3E寄存器CLK_SET值为0x03  
    18.     ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM,ret);//设置0x3E寄存器  
    19.     if (ret < 0)  
    20.         return ret;  
    21.   
    22.     /* Output frequency divider. The poll interval 设置0x15寄存器值为119输出分频值*/  
    23.     ret = i2c_smbus_write_byte_data(client, MPU3050_SMPLRT_DIV,MPU3050_DEFAULT_POLL_INTERVAL - 1);  
    24.     if (ret < 0)  
    25.         return ret;  
    26.   
    27.     /* Set low pass filter and full scale 设置低通滤波器和全扫描范围*/  
    28.     reg = MPU3050_DEFAULT_FS_RANGE;  
    29.     reg |= MPU3050_DLPF_CFG_42HZ << 3;  
    30.     reg |= MPU3050_EXT_SYNC_NONE << 5;  
    31.     ret = i2c_smbus_write_byte_data(client, MPU3050_DLPF_FS_SYNC, reg);//设置0x16寄存器  
    32.     if (ret < 0)  
    33.         return ret;  
    34.   
    35.     return 0;  
    36. }  
    中断返回函数:读取xyz轴数值,并上报给input子系统
    1. static irqreturn_t mpu3050_interrupt_thread(int irq, void *data)  
    2. {  
    3.     struct mpu3050_sensor *sensor = data;   //获取mpu3050传感器  
    4.     struct axis_data axis;  
    5.   
    6.     mpu3050_read_xyz(sensor->client, &axis); //获取xyz轴数值  
    7.   
    8.     input_report_abs(sensor->idev, ABS_X, axis.x);   //上报x轴事件  
    9.     input_report_abs(sensor->idev, ABS_Y, axis.y);   //上报y轴事件  
    10.     input_report_abs(sensor->idev, ABS_Z, axis.z);   //上报z轴事件  
    11.     input_sync(sensor->idev);    //同步事件  
    12.   
    13.     return IRQ_HANDLED;  
    14. }  

    获取xyz轴数值,通过i2c命令去获取便可

    1. static int mpu3050_xyz_read_reg(struct i2c_client *client,u8 *buffer, int length)  
    2. {  
    3.     /* 
    4.      * Annoying we can't make this const because the i2c layer doesn't 
    5.      * declare input buffers const. 
    6.      */  
    7.     char cmd = MPU3050_XOUT_H;  //i2c读取0x1D~0x22寄存器值  
    8.     struct i2c_msg msg[] = {  
    9.         {  
    10.             .addr = client->addr,  
    11.             .flags = 0,  
    12.             .len = 1,  
    13.             .buf = &cmd,  
    14.         },  
    15.         {  
    16.             .addr = client->addr,  
    17.             .flags = I2C_M_RD,  
    18.             .len = length,  
    19.             .buf = buffer,  
    20.         },  
    21.     };  
    22.   
    23.     return i2c_transfer(client->adapter, msg, 2);  
    24. }  
    应用层在MPU3050设备节点的时候会开启中断,MPU3050有数据更新则会触发中断,接着调用中断返回函数上报事件,应用程序便可读取设备节点获取xyz轴的数据
    1. static int mpu3050_input_open(struct input_dev *input)  
    2. {  
    3.     struct mpu3050_sensor *sensor = input_get_drvdata(input);   //获取mpu3050传感器  
    4.     int error;  
    5.   
    6.     pm_runtime_get(sensor->dev);  
    7.   
    8.     /* Enable interrupts  
    9.     使能中断,设置0x17寄存器MPU_RDY_EN,DMP_DONE_EN,RAW_RDY_EN位*/  
    10.     error = i2c_smbus_write_byte_data(sensor->client, MPU3050_INT_CFG,  
    11.             MPU3050_LATCH_INT_EN | MPU3050_RAW_RDY_EN | MPU3050_MPU_RDY_EN);  
    12.     if (error < 0) {  
    13.         pm_runtime_put(sensor->dev);  
    14.         return error;  
    15.     }  
    16.   
    17.     return 0;  
    18. }



    • 1
      点赞
    • 3
      收藏
      觉得还不错? 一键收藏
    • 0
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值