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 };
三、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; }
其中着重关注这几个函数
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层。