作者:Sam(甄峰) sam_code@hotmail.com
0. 背景
最近在Nvdia Tegra K1平台上使用Invensense MPU9250 Sensor。MPU9250使用I2C连接到K1平台。在Driver支持后,/dev/input/中新建3个node, /dev/input/eventX. 通过ioctl(EVIOCGNAME)得到其名,分别为:MPU6500, INV_DMP, akm89xx. 这与I2C设备驱动中创建的device对应起来。且其中有数据。
这说明I2C设备驱动这块任务完成,已经把Raw Data传送到/dev/input/eventX中去了。
然后在Android APP层通过SensorManager获取Sensor实体,并尝试得到数据:
可以发现,它拥有以下Sensor:
1. MPL Rotation Vector(旋转矢量传感器)
2. MPL Linear Accel. (加速度,不包括重力)
3. MPL Gravity. (仅包括重力)
4. MPL Gyro. (角速度)
5. MPL Accel. (加速度)
6. MPL magnetic field
7. MPL Orientation.(方向)
MPU9250=MPU6500+AK8963,所以加速度,角速度信息是直接从MPU6500得到的,方向信息则从AK8963得到,其它几个SensorManager层面的Sensor,则都是通过软件计算模拟出来的。具体实现则在sensors.xxxx.so部分实现. 通常是sensors.default.so, 在一些平台,则由自己定制的库实现,如K1平台使用sensors.tegra.so.
1. mlSDK的发现(抽丝拨茧):
A:在MPU9250通过I2C连接K1,Driver insmod成功后,尝试使用Android SensorManager得到Sensor,获取Sensor数据,Gyro,Acc , Magnetic, Linear_Acc, Orientation, Gravity均一切正常,说明Framework中已经针对MPU9250数据做过处理。
B:之后想使用NativeC程序直接从/dev/input/eventX中读取数据(input_event),于是打开设备名为MPU9250的node(/dev/input/event4). 读取input event. 问题立刻来了。
首先看数据格式:
#define EV_SYN 0x00
#define EV_REL 0x02
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
获取的数据很有规律(driver中填充的):
Data[0]: type=2,code=3
Data[1]: type=2,code=4
Data[2]: type=2,code=5
Data[3]: type=2,code=0
Data[4]: type=2,code=1
Data[5]: type=2,code=2
Data[6]: type=2,code=9
Data[7]: type=2,code=8
Data[0]: type=0,code=0
这就是一组数据。从Driver中可以看到:
Data[0]: type=2,code=3
Data[1]: type=2,code=4
Data[2]: type=2,code=5
他们分别是ACC的X,Y,Z的Raw Data。
Data[3]: type=2,code=0
Data[4]: type=2,code=1
Data[5]: type=2,code=2
他们分别是GYRO的X,Y,Z的Raw Data。
Data[6]: type=2,code=9
Data[7]: type=2,code=8
timestamp的高32和低32bit.
Data[0]: type=0,code=0
SYNC,作为一组数据的结束标记。
直到这里,还一切正常。但ACC, GYRO的值,却异常奇怪,很难想到公式把他们转换的正常的加速度和角速度单位。Sam自然想到,sensors.tegra.so也是读取以上node数据,但SensorManager却拿到正确数据,那肯定在这一层做了转换。
C:查数据转换踪迹:
在device/nvidia/ardbeg/sensors/目录下,可以看到sensors.cpp 和Android.mk
从Android.mk内容判断,
LOCAL_SRC_FILES := sensors.cpp
LOCAL_MODULE := sensors.tegra
sensors.cpp最终将生成sensors.tegra.so.
但请注意,其实它还包含了一系列库。
libsensors.base
libinvensense_hal
libsensor.mpl
libsensors.nvs_input
libsensors.iio.lights
libsensors.max44005
libsensors.bmpx80
那这些库的源码在哪呢?
大都在device/nvidia/drivers/sensors目录下。
这几个库中,最关键的就是libsensors.mpl,它由MPLSensor.cpp, MPLSupport.cpp CompassSensor.cpp生成,但同时,它又依赖于两个库:
libmllite.so, libmplmpu.so
这两个库,就是数据转换的关键。
D: mlsdk踪迹:
先分析libmllite.so, 它的源码在device/nvidia/drivers/sensors/mlsdk/目录下,相信此目录下源码和头文件均为Invesense提供。主要源码在device/nvidia/drivers/sensors/mlsdk/mllite目录。
将由它提供接口,把Raw Data转换为标准单位的数据。
其次是libmplmpus.so,
这个库并没有提供源码,只提供了库文件。
vendor/nvidia/tegra/prebuilt/ardbeg/target/product/ardbeg/lib/
(这意味着移植是个大问题)
2. NativeC 程序使用mlSDK:
既然清楚了sensors.tegra.so如何使用mlsdk把Raw data转化成标准单位数据。那理论上我们NativeC程序也可以做到这一点。
2.1: 生成自己的libmllite.so库:
想直接链接libmllite.so时,总会出问题,怀疑是编译选项的差异造成的,干脆自己编译一份库好了。
于是copy mlsdk整个目录,并按自己的习惯写了Android.mk+Application.mk. 编译,正常通过。
2.2: 修改代码,使用mlsdk,
于是可以得到正常单位数据了。(只尝试了ACC, GYRO)
3. sensors.xxxx.so的功能和结构:
在查看mlSDK使用情况时,也也通过sensors.tegra.so复习了一下sensors.xxxx.so的作用和写法。(之前在模拟ACC设备时,TT曾告诉我这个模板和方法)
总的来说:这个模快提供一个方法,让Framework能够得到底层Sensor(包括硬件Sensor,软件Sensor)的信息,并得到Sensor数据。
所以它需要做三件事:
A. 维护一个Sensor list. 把所有Sensor信息列入其中
B. 提供一个接口,供上层能够拿到Sensor信息,并控制Sensor(打开,关闭,得到数据)
C. 提供Sensor数据。
Sensor List如下:
static struct sensor_t sSensorList[10] = {
MPLROTATIONVECTOR_DEF,
MPLLINEARACCEL_DEF,
MPLGRAVITY_DEF,
MPLGYRO_DEF,
MPLACCEL_DEF,
MPLMAGNETICFIELD_DEF,
MPLORIENTATION_DEF,
};
具体定义如下:
#define MPLROTATIONVECTOR_DEF
{
\
"MPL
rotation
vector",
\
"Invensense",
\
1,
ID_RV,
\
SENSOR_TYPE_ROTATION_VECTOR, 10240.0f,
1.0f,
\
0.5f, 20000,
0, 0, { } }
含义如下:
struct sensor_t {
// Name
of this sensor.
// All
sensors of the same "type" must have a different "name".
const char* name;
//vendor
of the hardware part //
const
char*
vendor;
//
version of the hardware part + driver. The value of this
field
// must
increase when the driver is updated in a way that changes the
// output of
this sensor. This is important for fused sensors when the
// fusion
algorithm is updated.
int version;
// handle
that identifies this sensors. This handle is used to
reference
//this
sensor throughout the HAL API.
/
int
handle;
//this
sensor's type. //
int
type;
//
maximum range of this sensor's value in SI units /
float
maxRange;
/
smallest difference between two values reported by this sensor
/
float
resolution;
// rough
estimate of this sensor's power consumption in mA /
float
power;
// this
value depends on the trigger mode:
// continuous: minimum sample
period allowed in microseconds
// on-change : 0
// one-shot
:-1
//
special : 0, unless otherwise
noted
int32_t
minDelay;
// number
of events reserved for this sensor in the batch mode FIFO.
//If there is a dedicated FIFO for this sensor, then this is
the
// size of
this FIFO. If the FIFO is shared with other sensors,
// this is
the size reserved for that sensor and it can be zero.
uint32_t
fifoReservedEventCount;
//
maximum number of events of this sensor that could be
batched.
// This is especially relevant when the FIFO is shared
between
//several sensors; this value is then set to the size of that
FIFO.
uint32_t
fifoMaxEventCount;
//
reserved fields, must be zero
void*
reserved[6];
};
接口如下:
struct sensors_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: SENSORS_HARDWARE_MODULE_ID,
name: "Ardbeg sensors module",
author: "nvidia",
methods: &sensors_module_methods,
dso: NULL,
reserved: {0}
},
get_sensors_list: sensors__get_sensors_list,
};
static int sensors__get_sensors_list(struct sensors_module_t* module, struct sensor_t const** list)
把维护的Sensor通过参数二提供给调用方。
提供操作Sensor的接口;
static struct hw_module_methods_t
sensors_module_methods = {
open: open_sensors
};
最重要的当然是如何提供数据:
dev->device.poll = poll__poll;
pollEvents(sensors_event_t* data, int count)
在这个函数内提供数据:
其中参数1,即为存放数据的结构体。(sensors_event_t结构,在附2)
参数2应该是Sensor个数。
sensors.xxxx.so的开发者应该依次填充sensors_event_t结构体数组中的内容。以供上层使用。
注1:MPU9150,MPU9250介绍:
MPU9150和MPU9250均为9轴芯片。
MPU9150着眼于精度和性能,内部采用MPU6050(3轴加速度,3轴陀螺仪)+AK8975(磁)。 只支持I2C
MPU9250着眼于低功耗,内部采用MPU6500(3轴加速度,3轴陀螺仪)+AK8963。支持I2C和SPI。
注2:sensors_event_t
typedef struct sensors_event_t {
int32_t
version;
int32_t
sensor;
int32_t
type;
int32_t
reserved0;
int64_t
timestamp;
union
{
union {
float
data[16];
sensors_vec_t acceleration;
sensors_vec_t magnetic;
sensors_vec_t orientation;
sensors_vec_t gyro;
float
temperature;
float
distance;
float
light;
float
pressure;
float
relative_humidity;
uncalibrated_event_t uncalibrated_gyro;
uncalibrated_event_t uncalibrated_magnetic;
meta_data_event_t meta_data;
};
union {
uint64_t
data[8];
uint64_t
step_counter;
} u64;
};
uint32_t
reserved1[4];
} sensors_event_t;