开发平台:君正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驱动开发之陀螺仪(二)