MPU3050是invensense公司的三轴陀螺仪芯片,三轴陀螺仪最大的作用就是“测量角速度,以判别物体的运动状态,所以也称为运动传感器.
下图是MPU3050的系统框图,芯片有1个中断引脚,可以通过i2c来控制,获取x Gyro,y Gyro,z Gyro
设备驱动中用mpu3050_sensor结构体来描述MPU3050设备对象(对象中包含i2c客户端及输入设备来处理获取的x,y,z轴数据的传递)
- struct mpu3050_sensor { //mpu3050传感器
- struct i2c_client *client; //i2c客户端
- struct device *dev; //设备文件
- struct input_dev *idev; //输入设备
- };
- static struct i2c_driver mpu3050_i2c_driver = {
- .driver = {
- .name = "mpu3050",
- .owner = THIS_MODULE,
- .pm = &mpu3050_pm,
- .of_match_table = mpu3050_of_match,
- },
- .probe = mpu3050_probe, //i2c probe方法
- .remove = __devexit_p(mpu3050_remove),
- .id_table = mpu3050_ids,
- };
i2c设备与驱动匹配需要在板级驱动中注册i2c驱动
htc one max板的做法如下
- static struct i2c_board_info __initdata mpu3050_GSBI12_boardinfo[] = {
- {
- I2C_BOARD_INFO("mpu3050", 0xD0 >> 1),
- .irq = PM8921_GPIO_IRQ(PM8921_IRQ_BASE, PM_GYRO_INT),
- .platform_data = &mpu3050_data,
- },
- };
- i2c_register_board_info(MSM8064_GSBI2_QUP_I2C_BUS_ID,
- mpu3050_GSBI12_boardinfo,
- ARRAY_SIZE(mpu3050_GSBI12_boardinfo));
注册i2c板级信息
- static int __devinit mpu3050_probe(struct i2c_client *client,const struct i2c_device_id *id)
- {
- struct mpu3050_sensor *sensor; //mpu3050传感器
- struct input_dev *idev; //输入设备
- int ret;
- int error;
- sensor = kzalloc(sizeof(struct mpu3050_sensor), GFP_KERNEL); //分配mpu3050数据
- idev = input_allocate_device(); //分配输入设备
- if (!sensor || !idev) {
- dev_err(&client->dev, "failed to allocate driver data\n");
- error = -ENOMEM;
- goto err_free_mem;
- }
- sensor->client = client; //捆绑i2c客户端
- sensor->dev = &client->dev; //捆绑设备文件
- sensor->idev = idev; //捆绑输入设备
- mpu3050_set_power_mode(client, 1); //设置设备正常电压模式
- msleep(10); //睡眠
- ret = i2c_smbus_read_byte_data(client, MPU3050_CHIP_ID_REG);//获取0x00寄存器值
- if (ret < 0) {
- dev_err(&client->dev, "failed to detect device\n");
- error = -ENXIO;
- goto err_free_mem;
- }
- if (ret != MPU3050_CHIP_ID) { //判断芯片ID值(0x69)是否MPU3050
- dev_err(&client->dev, "unsupported chip id\n");
- error = -ENXIO;
- goto err_free_mem;
- }
- idev->name = "MPU3050"; //设置输入设备名
- idev->id.bustype = BUS_I2C; //输入设备使用i2c总线
- idev->dev.parent = &client->dev; //设置i2c设备为输入设备父设备
- idev->open = mpu3050_input_open; //输入设备打开方法
- idev->close = mpu3050_input_close; //输入设备关闭方法
- __set_bit(EV_ABS, idev->evbit); //设置绝对位移事件标志位,设置各轴数据范围
- input_set_abs_params(idev, ABS_X,MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
- input_set_abs_params(idev, ABS_Y,MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
- input_set_abs_params(idev, ABS_Z,MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
- input_set_drvdata(idev, sensor); //&idev->dev->p->driver_data=sensor
- pm_runtime_set_active(&client->dev);
- error = mpu3050_hw_init(sensor); //初始化MPU3050固件
- if (error)
- goto err_pm_set_suspended;
- //申请中断,上升沿触发
- error = request_threaded_irq(client->irq,NULL, mpu3050_interrupt_thread,IRQF_TRIGGER_RISING,"mpu3050", sensor);
- if (error) {
- dev_err(&client->dev,"can't get IRQ %d, error %d\n", client->irq, error);
- goto err_pm_set_suspended;
- }
- error = input_register_device(idev); //注册输入设备
- if (error) {
- dev_err(&client->dev, "failed to register input device\n");
- goto err_free_irq;
- }
- pm_runtime_enable(&client->dev);
- pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY);
- return 0;
- err_free_irq:
- free_irq(client->irq, sensor);
- err_pm_set_suspended:
- pm_runtime_set_suspended(&client->dev);
- err_free_mem:
- input_free_device(idev);
- kfree(sensor);
- return error;
- }
设置电源模式:mpu3050有两种电压模式val=1为正常模式,val=0为低功耗模式
- static void mpu3050_set_power_mode(struct i2c_client *client, u8 val)
- {
- u8 value;
- value = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);//获取0x3E寄存器数据
- //根据val值设置0x3E寄存器第6位SLEEP
- value = (value & ~MPU3050_PWR_MGM_MASK) |
- (((val << MPU3050_PWR_MGM_POS) & MPU3050_PWR_MGM_MASK) ^ MPU3050_PWR_MGM_MASK);
- i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM,value);//设置0x3E寄存器数据
- }
- static int __devinit mpu3050_hw_init(struct mpu3050_sensor *sensor)
- {
- struct i2c_client *client = sensor->client; //获取i2c客户端
- int ret;
- u8 reg;
- /* Reset 设置0x3E寄存器第7位H_RESET*/
- ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM,MPU3050_PWR_MGM_RESET);
- if (ret < 0)
- return ret;
- //获取0x3E寄存器值
- ret = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);
- if (ret < 0)
- return ret;
- ret &= ~MPU3050_PWR_MGM_CLKSEL; //清除0x3E寄存器0~2位CLK_SET值
- ret |= MPU3050_PWR_MGM_PLL_Z; //设置0x3E寄存器CLK_SET值为0x03
- ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM,ret);//设置0x3E寄存器
- if (ret < 0)
- return ret;
- /* Output frequency divider. The poll interval 设置0x15寄存器值为119输出分频值*/
- ret = i2c_smbus_write_byte_data(client, MPU3050_SMPLRT_DIV,MPU3050_DEFAULT_POLL_INTERVAL - 1);
- if (ret < 0)
- return ret;
- /* Set low pass filter and full scale 设置低通滤波器和全扫描范围*/
- reg = MPU3050_DEFAULT_FS_RANGE;
- reg |= MPU3050_DLPF_CFG_42HZ << 3;
- reg |= MPU3050_EXT_SYNC_NONE << 5;
- ret = i2c_smbus_write_byte_data(client, MPU3050_DLPF_FS_SYNC, reg);//设置0x16寄存器
- if (ret < 0)
- return ret;
- return 0;
- }
- static irqreturn_t mpu3050_interrupt_thread(int irq, void *data)
- {
- struct mpu3050_sensor *sensor = data; //获取mpu3050传感器
- struct axis_data axis;
- mpu3050_read_xyz(sensor->client, &axis); //获取xyz轴数值
- input_report_abs(sensor->idev, ABS_X, axis.x); //上报x轴事件
- input_report_abs(sensor->idev, ABS_Y, axis.y); //上报y轴事件
- input_report_abs(sensor->idev, ABS_Z, axis.z); //上报z轴事件
- input_sync(sensor->idev); //同步事件
- return IRQ_HANDLED;
- }
获取xyz轴数值,通过i2c命令去获取便可
- static int mpu3050_xyz_read_reg(struct i2c_client *client,u8 *buffer, int length)
- {
- /*
- * Annoying we can't make this const because the i2c layer doesn't
- * declare input buffers const.
- */
- char cmd = MPU3050_XOUT_H; //i2c读取0x1D~0x22寄存器值
- struct i2c_msg msg[] = {
- {
- .addr = client->addr,
- .flags = 0,
- .len = 1,
- .buf = &cmd,
- },
- {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = length,
- .buf = buffer,
- },
- };
- return i2c_transfer(client->adapter, msg, 2);
- }
- static int mpu3050_input_open(struct input_dev *input)
- {
- struct mpu3050_sensor *sensor = input_get_drvdata(input); //获取mpu3050传感器
- int error;
- pm_runtime_get(sensor->dev);
- /* Enable interrupts
- 使能中断,设置0x17寄存器MPU_RDY_EN,DMP_DONE_EN,RAW_RDY_EN位*/
- error = i2c_smbus_write_byte_data(sensor->client, MPU3050_INT_CFG,
- MPU3050_LATCH_INT_EN | MPU3050_RAW_RDY_EN | MPU3050_MPU_RDY_EN);
- if (error < 0) {
- pm_runtime_put(sensor->dev);
- return error;
- }
- return 0;
- }