三軸陀螺儀MPU3050驅動解析

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;	//輸入設備
};
用axis_data來描述獲取的x Gyro,y Gyro,z Gyro的數值

struct axis_data {	//軸數據
	s16 x;	//x軸
	s16 y;	//y軸
	s16 z;	//z軸
};
首先註冊i2c設備

module_i2c_driver(mpu3050_i2c_driver);
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板級信息


設備匹配後調用mpu3050_probe方法
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;
}
申請,配置,註冊相應的input設備,設置電源模式,初始化mpu3050芯片,申請中斷,並指明中斷返回函數

設置電源模式: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寄存器數據
}
初始化mpu3050:軟復位,配置時鐘及分頻...

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;
}
中斷返回函數:讀取xyz軸數值,並上報給input子系統

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);
}
應用層在MPU3050設備節點的時候會開啓中斷,MPU3050有數據更新則會觸發中斷,接着調用中斷返回函數上報事件,應用程序便可讀取設備節點獲取xyz軸的數據

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;
}












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章