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

本文详细介绍了如何利用invensense公司的MPU3050三轴陀螺仪芯片进行设备驱动开发,包括i2c设备注册、驱动匹配、传感器初始化、中断回调函数实现等关键步骤,以及如何通过i2c命令获取x、y、z轴的数据,并将其上报给input子系统,最后讨论了应用层如何在设备节点开启中断,实现MPU3050数据实时更新的流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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;  

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值