偷得浮生半桶水(半日闲), 好记性不如抄下来(烂笔头). 信息爆炸的时代, 学习是一项持续的工作.
全部博文(1748)
分类: Android平台
2016-02-15 17:38:49
Sensor作为Android系统的一个输入设备,对Android设备来说是必不可少的。Sensor主要报告G-Sensor、LightsSensor、ProximitySensor、TemperatureSensor等。由于各个Sensor的移植大同小异。本文就主要对G-Sensor和LightSensor进行说明。
Sensor的移植主要包括三部分的工作:
Linux Kernel中相关设备的驱动开发、Android中HAL中相关库的开发以及Android中应用层中测试程序的开发。
一、Linux Kernel中相关设备的驱动的开发。
对于Sensor驱动的开发,主要分为两部分:Sensor数据的采集以及上报和Sensor的控制。
1、Sensor数据的采集和上报。
a、Sensor数据的采集是指从硬件设备读出相关的数据。
数据的采集主要有两种方式:
通过注册中断,当有中断发生时,驱动去采集数据;
通过轮询主动去采集数据。主要是通过定时器定期采集数据。
:中断的方式可以参考 将Gsensor lis301 driver 升级到 lis331 driver 过程总结,以及android中Gsensor相关流程
我移植的是G/M-sensor都没有中断,要启动一个daemon来启动定时器,读取数据.
b、Sensor数据的上报
由于Sensor是属于输入设备,所以Sensor的数据上报要上报到Linux Kernel输入子系统里面。
上层只要到相应的子系统里面读数据就可以了。需要把数据上报的Linux Kernel
输入子系统的设备还有TouchScreen,Keyboard,Mouse,Sensor等。
上报数据的code大致是
static void report_abs(void)
{
short x,y,z,tilt;
if(read_data(&x,&y,&z,&tilt) != 0) {
/* report the absulate sensor data to input device */
input_report_abs(idev, ABS_X, y);
input_report_abs(idev, ABS_Y, x);
input_report_abs(idev, ABS_Z, z);
input_sync(idev);
}
2、Sensor的控制
Sensor的控制包括设备的打开、关闭、设置才数据采集的频率、enable/disable,等等。
Sensor的控制主要通过IOCTL方式来实现的。
对于驱动的开发,其实都是大同小异的,只要实现相应的回调函数就行了,另外注意以上两点就行了。
与sensor的通信无非就是data(通过input系统)和command(通过ioctrl),HAL与driver的交互就这两条路线
二、Android中HAL层相关库的开发。
Android中Framework对Sensor的相关支持,Android中已经弄好了。但是由于HAL层是很硬件密切相连的,与每个硬件相对应的HAL中的东西都有所不同,所以Anroid中HAL层对Sensor的支持是没有的。所以需要开发。只要编译出的库的名字“libsensor.so”,然后放到固定的目录下就可以了。
本文一下的关于libsensor.so的开发是基于Android2.2的,在Android2.3上代码的整体结构有所不同,但是也是大同小异的东西。只要知道核心内容就可以了。
对于libsensor.so库的开发,只要写个C程序,然后编译成库就行了。对于Android2.2来说,我们不需要自己一点点的开发这个库,因为Android2.2里面提供了htc相关的源文件,我们只要拿过来把里面关键的几点改一下就行了。
以下对关键的几点加以说明。参考android目录下 device/htc/passion-common/libsensors/Sensors.c
1、open_sensors(...)
这个函数比较重要。在Sensors.c文件里面实现的函数大多是系统的回调函数。在这个函数里面主要的作用就是把自己写的函数,注
册成系统用的回调函数。
2、定义支持的sensors
/*****************************************************************************/
#define MAX_NUM_SENSORS 6 //定义主持的sensors的个数
#define SUPPORTED_SENSORS ((1<
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
//定义支持的sensors类型
#define ID_A (0)
static int id_to_sensor[MAX_NUM_SENSORS] = {
//定义各个sensors,由于一个硬件上可以集成多个 sensors,所以引进了*****_GROUP,用来标识一个硬件上的多
个sensors
#define SENSORS_AKM_ACCELERATION (1<
#define SENSORS_CM_PROXIMITY (1<
#define SENSORS_LIGHT (1<
/*****************************************************************************/
这些定义,需要根据实际情况进行重新定义,大概的思路是不变的。
3、定义sensors用到的数据结构
//由于htc的sensor集成了三个硬件,所以以下定义了akmd_fd,cmd_fd,lsd_fd三个文件描述符。
struct sensors_control_context_t {
//由于htc的sensor集成了三个硬件,所以以下定义了events_fd[3]来获得events。
struct sensors_data_context_t {
这些定义,需要根据实际的硬件个数进行重新定义,大概的思路是不变的。
4、定义sensors的列表
static const struct sensor_t sSensorList[] = {
5、定义硬件在/dev目录下的文件名====》实现控制
#define AKM_DEVICE_NAME "/dev/akm8973_aot"
上面这些在driver中注册为miscdevice,在这里取得dev_fd,以调用ioctrl实现控制。
6、open_inputs(...)函数======》读取input报上来的数据
open_inputs函数作用是:
if (fd>=0) {
//以下定义的compass,promixity,lightsensor-level是三个硬件设备在kernel的input子系统中的名字,
if (!strcmp(name, "compass")) {
if(dir == NULL)
memset(devname,0,sizeof(devname)); //这行是后加的,否则libsensor.so运行之后会crash
7、对Android.mk进行修改。
对编译出来的libsensor.so库需要编译到指定的位置/system/lib/hw下系统才能加载,但是遗憾的是用device/htc/passion-
common/libsensors/Sensors.c进行编译,编译之后的库不能被编译到指定的位置,对Android.mk进行修改,强制将
libsensor.so拷到指定的目录。
PRODUCT_COPY_FILES := /
out/target/product/ARMv7/obj/SHARED_LIBRARIES/sensors.default_intermediates/LINKED/sensors.default.so:system/lib/hw/sensors.default.so
这样就可以强制的将制定目录下的文件拷到指定的目录。
对以上的关键点进行修改,就可以实现自己的libsensor.so了
三、Android中应用层中相关测试程序的开发。
对Android中各个sensors的测试有很多方法。可以下载各种应用程序对sensors进行测试。本人比较喜欢自己动手,所以就自己做了
一个应用程序对sensor进行测试。
在Android应用层要实现Sensor的相关功能功能。
在Android中,和Sensor相关的几个类是:
SensorManager : 通过它实现系统对Sensor的相关调用。
SensorEvent :对各个Sensor数据的封装,具体可以参考Android的开发文档。
SensorEventListener :对Sensor数据的监视,一旦有数据,就会调相应的函数。
Sensor : 对Sensor的封装。
为了实现Sensor的相关功能。
1、定义要监控的Sensor。
sensor = Sensor.TYPE_ACCELEROMETER;
2、实现SensorEvenetListener。
private SensorEventListener mListener = new SensorEventListener() {
public void onSensorChanged(SensorEvent event) {
handle sensor data code
... ...
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
3、向系统注册SensorEventListener。
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = (mSensorManager.getSensorList(sensor)).get(0);
用完之后,记得注销
mSensorManager.unregisterListener(mListener);
这样就可以用Sensor了,当sensor的数据发生变化时,就会传过来。
#define ID_M (1)
#define ID_O (2)
#define ID_T (3)
#define ID_P (4)
#define ID_L (5)
[ID_A] = SENSOR_TYPE_ACCELEROMETER,
[ID_M] = SENSOR_TYPE_MAGNETIC_FIELD,
[ID_O] = SENSOR_TYPE_ORIENTATION,
[ID_T] = SENSOR_TYPE_TEMPERATURE,
[ID_P] = SENSOR_TYPE_PROXIMITY,
[ID_L] = SENSOR_TYPE_LIGHT,
};
struct sensors_control_device_t device; // must be first
int akmd_fd;
int cmd_fd;
int lsd_fd;
uint32_t active_sensors;
};
struct sensors_data_device_t device; // must be first
int events_fd[3];
sensors_data_t sensors[MAX_NUM_SENSORS];
uint32_t pendingSensors;
};
{ "BMA150 3-axis Accelerometer",
"Bosh",
1, SENSORS_HANDLE_BASE+ID_A,
SENSOR_TYPE_ACCELEROMETER, 4.0f*9.81f, (4.0f*9.81f)/256.0f, 0.2f, { } },
{ "AK8973 3-axis Magnetic field sensor",
"Asahi Kasei",
1, SENSORS_HANDLE_BASE+ID_M,
SENSOR_TYPE_MAGNETIC_FIELD, 2000.0f, 1.0f/16.0f, 6.8f, { } },
{ "AK8973 Orientation sensor",
"Asahi Kasei",
1, SENSORS_HANDLE_BASE+ID_O,
SENSOR_TYPE_ORIENTATION, 360.0f, 1.0f, 7.0f, { } },
{ "CM3602 Proximity sensor",
"Capella Microsystems",
1, SENSORS_HANDLE_BASE+ID_P,
SENSOR_TYPE_PROXIMITY,
PROXIMITY_THRESHOLD_CM, PROXIMITY_THRESHOLD_CM,
0.5f, { } },
{ "CM3602 Light sensor",
"Capella Microsystems",
1, SENSORS_HANDLE_BASE+ID_L,
SENSOR_TYPE_LIGHT, 10240.0f, 1.0f, 0.5f, { } },
};
以上定义了系统的sensors列表,以便系统加载时获得sensors的详细信息
#define CM_DEVICE_NAME "/dev/cm3602"
#define LS_DEVICE_NAME "/dev/lightsensor"
定义这些文件节点的作用是:由于用sensors获得数据是sensor的主要功能,但是还需要对
sensor进行控制,比如open, close, setDelay等,这些相关的控制是必不可少的。
而对sensor的控制是通过对/dev/目录下的相应的文件节点进行控制的。
所以有了以上的相应的文件节点。
至于这些文件节点的名字需要和在驱动中定义的相应的名字相匹配才行。
在driver中注册为input device,在这里取得data_fd,来read data。
input有系统提供的ioictrl和read,wirte,详细参考 input subsystem
在kernel的输入子系统中(/dev/input目录下)找到各个sensors的相应文件节点并返回给sensor系统。
char name[80];
if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
name[0] = '/0';
}
// 这个要和驱动里面的名字匹配
LOGV("using %s (name=%s)", devname, name);
*akm_fd = fd;
}
else if (!strcmp(name, "proximity")) {
LOGV("using %s (name=%s)", devname, name);
*p_fd = fd;
}
else if (!strcmp(name, "lightsensor-level")) {
LOGV("using %s (name=%s)", devname, name);
*l_fd = fd;
}
else
close(fd);
}
return -1;
strcpy(devname, dirname);
... ...
}
}
// TODO Auto-generated method stub
};
mSensorManager.registerListener(mListener, mSensor,SensorManager.SENSOR_DELAY_NORMAL);