Android驱动开发之陀螺仪(一)

Android驱动开发之陀螺仪

开发平台:君正M200S

安卓系统:Android5.1

一、前言

        很不容易,经过几天加班加点的调试,终于成功将MPU9250移植到这块板子上,在此记录下这个兴奋的时刻,每次调完一个驱动,都感觉我能统治世界,但同样每次因为种种原因中止调试,就感觉自己跟咸鱼没啥两样,因此不断克服困难才能提升自己,不然与其半途而废,不如一开始知难而退,毕竟每次的失败都会在心中留下一个不断告诉你自己你不行的心魔。

        MPU9250是一种9轴陀螺仪,由陀螺仪MPU6500和磁力计AK8963组成,能输出加速度、角加速度、磁场以及温度。内部还集成DMP,可以输出四元数,但由于没有拿到这部分资料,而且项目中也不是很需要,所以没有深入研究。

    下面进入正题,若有错误,敬请各路大神赐教。

二、修改板级描述

文件在kernel-3.10.14/arch/mips/xburst/soc-m200/chip-m200/newton/common/i2c_bus.c

        MPU9250为MPU6500和AK8963的集合,其中MPU9250的芯片ID为0x68,我用直连的方式去驱动AK8963,其芯片ID为0x0C。

struct i2c_board_info jz_i2c2_devs[] __initdata = {
      I2C_BOARD_INFO("mpu9250", 0x68),                            //MPU9250的I2C设备地址
      .irq = (IRQ_GPIO_BASE + GPIO_GSENSOR_INT),                  //中断引脚
      .platform_data = &mpu9250_platform_data,
   }
};
struct mpu_platform_data mpu9250_platform_data = {
        .int_config  = 0x10,
      .level_shifter = 0,
      .orientation = {
         -1,  0,  0,
         0,  1,  0,
         0,  0, -1,
      },
   .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,                
   .sec_slave_id   = COMPASS_ID_AK8963,                           
 .secondary_i2c_addr = 0x0c,                                      //AK8963的I2C设备地址
   .secondary_orientation = {
      -1, 0,  0,
      0, -1,  0,
      0,  0, 1,
   },
   .board_init = inv_mpu_early_init,
   .board_exit = inv_mpu_exit,
   .power_on = inv_mpu_power_on,
   .power_off = inv_mpu_power_off
};//MPU9250的I2C设备地址
      .irq = (IRQ_GPIO_BASE + GPIO_GSENSOR_INT),                  //中断引脚
      .platform_data = &mpu9250_platform_data,
   }
};
struct mpu_platform_data mpu9250_platform_data = {
        .int_config  = 0x10,
      .level_shifter = 0,
      .orientation = {
         -1,  0,  0,
         0,  1,  0,
         0,  0, -1,
      },
   .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,                
   .sec_slave_id   = COMPASS_ID_AK8963,                           
 .secondary_i2c_addr = 0x0c,                                      //AK8963的I2C设备地址
   .secondary_orientation = {
      -1, 0,  0,
      0, -1,  0,
      0,  0, 1,
   },
   .board_init = inv_mpu_early_init,
   .board_exit = inv_mpu_exit,
   .power_on = inv_mpu_power_on,
   .power_off = inv_mpu_power_off
};

 

三、kernel驱动注册

 

文件在kernel-3.10.14/drivers/iio/imu/inv_mpu/inv_mpu_core.c

驱动在这里注册,包括引脚初始化,iio设备注册,MPU配置、SYS文件创建

static int inv_mpu_probe(struct i2c_client *client,
	const struct i2c_device_id *id)
{
	struct inv_mpu_state *st;
	struct iio_dev *indio_dev;
	int result,i;
	u8 test,data;
	PRINT_DBG("============%s start=============\n", __FUNCTION__);

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 
		result = -ENOSYS;
		PRINT_ERR("I2c function error\n");
		goto out_no_free;
	}
	indio_dev = iio_device_alloc(sizeof(*st));    //动态申请iio设备
	if (indio_dev == NULL) {
		PRINT_ERR("memory allocation failed\n");
		result =  -ENOMEM;
		goto out_no_free;
	}
	st = iio_priv(indio_dev);
	st->client = client;
	st->sl_handle = client->adapter;
	st->i2c_addr = client->addr;

	/* power is turned on inside check chip type */
	st->plat_data =
	*(struct mpu_platform_data *)dev_get_platdata(&client->dev);
	if (st->plat_data.board_init) {
		result = st->plat_data.board_init(&client->dev);
		if (result < 0) {
			PRINT_ERR("board_init failed ! errno = %d\n",result);
			goto out_free;
		}
	}
	if (st->plat_data.power_on) {
		result = st->plat_data.power_on();
		if (result < 0) {
			PRINT_ERR("board_init failed ! errno = %d\n",result);
			goto out_free;
		}
	}

	result = inv_gpio_init();                        //GPIO初始化
	
	result = inv_check_chip_type(st, id);
	if (result){
		PRINT_ERR("inv_check_chip_type failed ! errno = %d\n",result);
		goto out_free;
	}

	result = st->init_config(indio_dev);            //mpu9250寄存器配置
	if (result) {
		//printk(&client->adapter->dev,
		PRINT_ERR("Could not initialize device.\n");
		goto out_free;
	}
	/* Make state variables available to all _show and _store functions. */
	i2c_set_clientdata(client, indio_dev);
	indio_dev->dev.parent = &client->dev;
	if (!strcmp(id->name, "mpu6xxx"))
		indio_dev->name = st->name;
	else
		indio_dev->name = id->name;
	indio_dev->channels = inv_mpu_channels;
	indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);

	indio_dev->info = &mpu_info;                       //SYS文件结构体注册
	indio_dev->modes = INDIO_DIRECT_MODE;
	indio_dev->currentmode = INDIO_DIRECT_MODE;
	
	result = inv_mpu_configure_ring(indio_dev);       
	if (result) {
		PRINT_ERR("configure ring buffer fail\n");
		goto out_free;
	}
	result = iio_buffer_register(indio_dev, indio_dev->channels,  
					indio_dev->num_channels);
	if (result) {
		PRINT_ERR("ring buffer register fail\n");
		goto out_unreg_ring;
	}
	st->irq = client->irq;
	result = inv_mpu_probe_trigger(indio_dev);
	if (result) {
		PRINT_ERR("trigger probe fail\n");
		goto out_remove_ring;
	}

	/* Tell the i2c counter, we have an IRQ */
	INV_I2C_SETIRQ(IRQ_MPU, client->irq);

	result = iio_device_register(indio_dev);
	if (result) {
		PRINT_ERR("IIO device register fail\n");
		goto out_remove_trigger;
	}

	if (INV_MPU6050 == st->chip_type ||
		INV_MPU6500 == st->chip_type) {
		result = inv_create_dmp_sysfs(indio_dev);      
		if (result) {
			PRINT_ERR("create dmp sysfs failed\n");
			goto out_unreg_iio;
		}
	}
	INIT_KFIFO(st->timestamps);
	spin_lock_init(&st->time_stamp_lock);
	mutex_init(&st->suspend_resume_lock);
	result = st->set_power_state(st, false);
	if (result) {
		//dev_err(&client->adapter->dev,
		//	"%s could not be turned off.\n", st->hw->name);
		PRINT_ERR("%s could not be truned off.\n", st->hw->name);
		goto out_unreg_iio;
	}
	inv_init_sensor_struct(st);
	dev_info(&client->dev, "%s is ready to go!\n",
					indio_dev->name);
	result = inv_i2c_single_write(st, REG_PWR_MGMT_1 ,KR_SENSOR_OFF);
	if (result){
		PRINT_ERR("pwr_mgmt_1 wirte failed :%d",result);
		return result;
	}

	PRINT_DBG("============%s end=============\n", __FUNCTION__);
	return 0;
out_unreg_iio:
	iio_device_unregister(indio_dev);
out_remove_trigger:
	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
		inv_mpu_remove_trigger(indio_dev);
out_remove_ring:
	iio_buffer_unregister(indio_dev);
out_unreg_ring:
	inv_mpu_unconfigure_ring(indio_dev);
out_free:
	iio_device_free(indio_dev);
out_no_free:
	//dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
	PRINT_ERR("%s failed %d\n", __func__, result);
	return -EIO;
}//动态申请iio设备
	if (indio_dev == NULL) {
		PRINT_ERR("memory allocation failed\n");
		result =  -ENOMEM;
		goto out_no_free;
	}
	st = iio_priv(indio_dev);
	st->client = client;
	st->sl_handle = client->adapter;
	st->i2c_addr = client->addr;

	/* power is turned on inside check chip type */
	st->plat_data =
	*(struct mpu_platform_data *)dev_get_platdata(&client->dev);
	if (st->plat_data.board_init) {
		result = st->plat_data.board_init(&client->dev);
		if (result < 0) {
			PRINT_ERR("board_init failed ! errno = %d\n",result);
			goto out_free;
		}
	}
	if (st->plat_data.power_on) {
		result = st->plat_data.power_on();
		if (result < 0) {
			PRINT_ERR("board_init failed ! errno = %d\n",result);
			goto out_free;
		}
	}

	result = inv_gpio_init();                        //GPIO初始化
	
	result = inv_check_chip_type(st, id);
	if (result){
		PRINT_ERR("inv_check_chip_type failed ! errno = %d\n",result);
		goto out_free;
	}

	result = st->init_config(indio_dev);            //mpu9250寄存器配置
	if (result) {
		//printk(&client->adapter->dev,
		PRINT_ERR("Could not initialize device.\n");
		goto out_free;
	}
	/* Make state variables available to all _show and _store functions. */
	i2c_set_clientdata(client, indio_dev);
	indio_dev->dev.parent = &client->dev;
	if (!strcmp(id->name, "mpu6xxx"))
		indio_dev->name = st->name;
	else
		indio_dev->name = id->name;
	indio_dev->channels = inv_mpu_channels;
	indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);

	indio_dev->info = &mpu_info;                       //SYS文件结构体注册
	indio_dev->modes = INDIO_DIRECT_MODE;
	indio_dev->currentmode = INDIO_DIRECT_MODE;
	
	result = inv_mpu_configure_ring(indio_dev);       
	if (result) {
		PRINT_ERR("configure ring buffer fail\n");
		goto out_free;
	}
	result = iio_buffer_register(indio_dev, indio_dev->channels,  
					indio_dev->num_channels);
	if (result) {
		PRINT_ERR("ring buffer register fail\n");
		goto out_unreg_ring;
	}
	st->irq = client->irq;
	result = inv_mpu_probe_trigger(indio_dev);
	if (result) {
		PRINT_ERR("trigger probe fail\n");
		goto out_remove_ring;
	}

	/* Tell the i2c counter, we have an IRQ */
	INV_I2C_SETIRQ(IRQ_MPU, client->irq);

	result = iio_device_register(indio_dev);
	if (result) {
		PRINT_ERR("IIO device register fail\n");
		goto out_remove_trigger;
	}

	if (INV_MPU6050 == st->chip_type ||
		INV_MPU6500 == st->chip_type) {
		result = inv_create_dmp_sysfs(indio_dev);      
		if (result) {
			PRINT_ERR("create dmp sysfs failed\n");
			goto out_unreg_iio;
		}
	}
	INIT_KFIFO(st->timestamps);
	spin_lock_init(&st->time_stamp_lock);
	mutex_init(&st->suspend_resume_lock);
	result = st->set_power_state(st, false);
	if (result) {
		//dev_err(&client->adapter->dev,
		//	"%s could not be turned off.\n", st->hw->name);
		PRINT_ERR("%s could not be truned off.\n", st->hw->name);
		goto out_unreg_iio;
	}
	inv_init_sensor_struct(st);
	dev_info(&client->dev, "%s is ready to go!\n",
					indio_dev->name);
	result = inv_i2c_single_write(st, REG_PWR_MGMT_1 ,KR_SENSOR_OFF);
	if (result){
		PRINT_ERR("pwr_mgmt_1 wirte failed :%d",result);
		return result;
	}

	PRINT_DBG("============%s end=============\n", __FUNCTION__);
	return 0;
out_unreg_iio:
	iio_device_unregister(indio_dev);
out_remove_trigger:
	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
		inv_mpu_remove_trigger(indio_dev);
out_remove_ring:
	iio_buffer_unregister(indio_dev);
out_unreg_ring:
	inv_mpu_unconfigure_ring(indio_dev);
out_free:
	iio_device_free(indio_dev);
out_no_free:
	//dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
	PRINT_ERR("%s failed %d\n", __func__, result);
	return -EIO;
}

其中着重关注这几个函数

1、MPU9250的配置

st->init_config(indio_dev);
static void inv_setup_func_ptr(struct inv_mpu_state *st)
{
		st->set_power_state    = set_power_itg;
		st->switch_gyro_engine = inv_switch_gyro_engine;
		st->switch_accel_engine = inv_switch_accel_engine;
		st->init_config        = inv_init_config;
		st->setup_reg          = inv_setup_reg;
}
static int inv_init_config(struct iio_dev *indio_dev)
{
	struct inv_reg_map_s *reg;
	int result, i;
	struct inv_mpu_state *st = iio_priv(indio_dev);
	const u8 *ch;
	u8 d[2];

	reg = &st->reg;
	PRINT_DBG("============enter  inv_init_config-=======\n ");
	result = inv_i2c_single_write(st, reg->gyro_config,
		INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT);
	if (result)
		return result;

	st->chip_config.fsr = INV_FSR_2000DPS;

	result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_42HZ);
	if (result)
		return result;
	st->chip_config.lpf = INV_FILTER_42HZ;

	result = inv_i2c_single_write(st, reg->sample_rate_div,
					ONE_K_HZ / INIT_FIFO_RATE - 1);
	if (result)
		return result;
	st->chip_config.fifo_rate = INIT_FIFO_RATE;
	st->irq_dur_ns            = INIT_DUR_TIME;
	st->chip_config.prog_start_addr = DMP_START_ADDR;
	if (INV_MPU6050 == st->chip_type)
		st->self_test.samples = INIT_ST_MPU6050_SAMPLES;
	else
		st->self_test.samples = INIT_ST_SAMPLES;
	st->self_test.threshold = INIT_ST_THRESHOLD;
	st->batch.wake_fifo_on = true;
	if (INV_ITG3500 != st->chip_type) {
		st->chip_config.accel_fs = INV_FS_02G;
		result = inv_i2c_single_write(st, reg->accel_config,
			(INV_FS_02G << ACCEL_CONFIG_FSR_SHIFT));
		if (result)
			return result;

		st->tap.time = INIT_TAP_TIME;
		st->tap.thresh = INIT_TAP_THRESHOLD;
		st->tap.min_count = INIT_TAP_MIN_COUNT;
		st->sample_divider = INIT_SAMPLE_DIVIDER;
		st->smd.threshold = MPU_INIT_SMD_THLD;
		st->smd.delay     = MPU_INIT_SMD_DELAY_THLD;
		st->smd.delay2    = MPU_INIT_SMD_DELAY2_THLD;
		st->ped.int_thresh = INIT_PED_INT_THRESH;
		st->ped.step_thresh = INIT_PED_THRESH;
		st->sensor[SENSOR_STEP].rate = MAX_DMP_OUTPUT_RATE;

		result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR,
						INIT_MOT_THR);
		if (result)
			return result;
		st->mot_int.mot_thr = INIT_MOT_THR;

		for (i = 0; i < 3; i++) {
			result = inv_i2c_read(st, reg_gyro_offset[i], 2, d);
			if (result)
				return result;
			st->rom_gyro_offset[i] =
					(short)be16_to_cpup((__be16 *)(d));
			st->input_gyro_offset[i] = 0;
			st->input_gyro_dmp_bias[i] = 0;
		}
		if (INV_MPU6050 == st->chip_type)
			ch = reg_6050_accel_offset;
		else
			ch = reg_6500_accel_offset;
		for (i = 0; i < 3; i++) {
			result = inv_i2c_read(st, ch[i], 2, d);
			if (result)
				return result;
			st->rom_accel_offset[i] =
					(short)be16_to_cpup((__be16 *)(d));
			st->input_accel_offset[i] = 0;
			st->input_accel_dmp_bias[i] = 0;
		}
		st->ped.step = 0;
		st->ped.time = 0;


	}
	result = inv_i2c_single_write(st, REG_PWR_MGMT_1,0x00);        //使能陀螺仪
	if (result){
		PRINT_ERR("======cpu on err======\n");
	}

	result = inv_i2c_single_write(st, REG_PWR_MGMT_2,0x00);        //开启6轴数据
	if (result){
		PRINT_ERR("======REG_INT_ENABLE err======\n");
	}

	result = inv_i2c_single_write(st, REG_ACCEL_CONFIG2,0x00);    //DLPF配置
	if (result){
		PRINT_ERR("======REG_INT_ENABLE err======\n");
	}

	result = inv_i2c_single_write(st, REG_LP_ACCEL_ODR,0x09);
	if (result){
		PRINT_ERR("======REG_INT_ENABLE err======\n");
	}
	
	result = inv_i2c_single_write(st, REG_FIFO_EN,0x00);        
	if (result){
		PRINT_ERR("======REG_FIFO_EN err======\n");
	}

	result = inv_i2c_single_write(st, REG_INT_PIN_CFG,0x22);           //配置直连模式
	if (result){
	    PRINT_ERR("======REG_INT_PIN_CFG err======\n");
	}
	PRINT_DBG("============end  inv_init_config-=======\n ");
	return 0;
}

 

2、SYS文件创建,info这个成员会在iio设备注册的时候创建设备节点文件,安卓hal层通过这些文件节点实现基本的读写功能。

indio_dev->info = &mpu_info; 
static const struct iio_info mpu_info = {
	.driver_module = THIS_MODULE,
	.attrs = &inv_attribute_group,
};
static const struct attribute_group inv_attribute_group = {
	.name = "mpu",
	.attrs = inv_attributes
};
static struct attribute *inv_attributes[
	ARRAY_SIZE(inv_gyro_attributes) +
	ARRAY_SIZE(inv_mpu6xxx_attributes) +
	ARRAY_SIZE(inv_mpu6500_attributes) +
	ARRAY_SIZE(inv_compass_attributes) +
	ARRAY_SIZE(inv_akxxxx_attributes) +
	ARRAY_SIZE(inv_pressure_attributes) +
	ARRAY_SIZE(inv_tap_attributes) +
	ARRAY_SIZE(inv_display_orient_attributes) +
	1
];

这里就贴一部分吧 ,接口太多了,很多一部分都是关于dmp的。

static const struct attribute *inv_mpu6500_attributes[] = {
	&iio_dev_attr_motion_lpa_on.dev_attr.attr,
	&iio_dev_attr_motion_lpa_freq.dev_attr.attr,
	&iio_dev_attr_motion_lpa_threshold.dev_attr.attr,
};
static IIO_DEVICE_ATTR(motion_lpa_on, S_IRUGO | S_IWUSR, inv_attr_show,
	inv_attr_store, ATTR_MOTION_LPA_ON);

inv_attr_show和inv_attr_store就是具体的方法了, ATTR_MOTION_LPA_ON这个为参数。
 

3、INT中断处理函数,MPU9250有这么几种中断方式

    a、运动检测中断:根据加速度计的数值变化阈值,产生中断,但同时陀螺仪不能正常工作。

    b、FIFO溢出中断:MPU9250有512K的FIFO接受9轴数据,FIFO满了之后就会产生中断。

    c、原始数据准备中断:当数据准备好后产生中断。

    d、第三方数据准备中断:外接sensor数据准备好产生中断。

我采取的是第三种中断方式。

int inv_mpu_configure_ring(struct iio_dev *indio_dev)
{
	int ret,result;
	struct inv_mpu_state *st = iio_priv(indio_dev);
	struct iio_buffer *ring;

	ring = iio_kfifo_allocate(indio_dev);
	if (!ring)
		return -ENOMEM;
	indio_dev->buffer = ring;
	/* setup ring buffer */
	ring->scan_timestamp = true;
	indio_dev->setup_ops = &inv_mpu_ring_setup_ops;
	/*scan count double count timestamp. should subtract 1. but
	number of channels still includes timestamp*/


    if (INV_MPU3050 == st->chip_type)
        ret = request_threaded_irq(st->client->irq, inv_irq_handler,
                inv_read_fifo_mpu3050,
                IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st);
    else
        ret = request_threaded_irq(st->client->irq, inv_irq_handler,
			    inv_read_fifo,
			    IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st);
    if (ret){
        PRINT_ERR("======request_threaded_irq err======");
        goto error_iio_sw_rb_free;
    }


	indio_dev->modes |= INDIO_BUFFER_TRIGGERED;

	return 0;
error_iio_sw_rb_free:
	iio_kfifo_free(indio_dev->buffer);

	return ret;
}

这里的inv_read_fifo是中断handle实现
 

irqreturn_t inv_read_fifo(int irq, void *dev_id)
{

	struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id;
	struct iio_dev *indio_dev = iio_priv_to_dev(st);
	int result, bpm,i;
	u8 Adata[BYTES_PER_SENSOR],Gdata[BYTES_PER_SENSOR],Mdata[BYTES_PER_SENSOR],data[1];
	u16 fifo_count;
	struct inv_reg_map_s *reg;
	u64 pts1;

	if (!(iio_buffer_enabled(indio_dev)) || (!st->chip_config.enable)){
	if (!(iio_buffer_enabled(indio_dev))){
	    PRINT_ERR("++++++iio_buffer_enabled end_session : %d+++++\n",st->chip_config.enable);
		goto end_session;
	}

	reg = &st->reg;

	if (st->sensor[SENSOR_ACCEL].on) {
		result = inv_i2c_read(st, reg->raw_accel,BYTES_PER_SENSOR, Adata);
		if (result)
			goto end_session;
	}
	if (st->sensor[SENSOR_GYRO].on) {
        	result = inv_i2c_read(st, reg->raw_gyro,BYTES_PER_SENSOR, Gdata);
		if (result)
			goto end_session;
	}


	if (st->sensor[SENSOR_COMPASS].on) {
        	result = inv_secondary_read(0x02, 1 , data);
        	if (result){
            		goto end_session;
	}

	if(!(data && 0x01))
            	goto end_session;

	result = inv_secondary_read(0x03,BYTES_PER_SENSOR, Mdata);
	if (result)
            	goto end_session;

	result = inv_secondary_read(0x09, 1 , data);
	if (result)
            	goto end_session;

	inv_report_gyro_accel(indio_dev, Adata, Gdata, Mdata, get_time_ns());

	goto end_session;

end_session:
    	inv_process_motion(st);
	return IRQ_HANDLED;
}

inv_report_gyro_accel函数负责将获取到的数据report到安卓hal层。

 

Android驱动开发之陀螺仪(二)

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值