最新文章
Cocos2d-x游戏开发实例详解7:对象释放时机
03-25 13:59
Cocos2d-x游戏开发实例详解6:自动释放池
03-25 13:55
Cocos2d-x游戏开发实例详解5:神奇的自动释放
03-25 13:49
Cocos2d-x游戏开发实例详解4:游戏主循环
03-25 13:44
Cocos2d-x游戏开发实例详解3:无限滚动地图
03-25 13:37
Cocos2d-x游戏开发实例详解2:开始菜单续
03-25 13:32
unity如何控制android陀螺仪
一、背景
1. 需求
陀螺仪硬件未直接连接到Android CPU,因此不存在陀螺仪驱动。陀螺仪数据是通过用户空间的一个C程序传输过来的。
2. 思路
修改陀螺仪HAL(Hardware Abstraction Layer,硬件抽象层)层,在HAL层构建socket客户端,同时在数据源的C程序上构建socket服务端。当有数据产生时,C程序通过socket将数据发送到陀螺仪HAL层并进行上报。
二、步骤
1. 拷贝目录
将device/samsung/crespo/libsensors目录拷贝到hardware/libhardware/modules目录下。
2. 修改传感器数组定义
打开sensors.cpp文件,修改其中的传感器数组定义。由于我们仅使用陀螺仪传感器,所以将其他传感器的定义删除,仅保留陀螺仪传感器的定义,代码如下:
static const struct sensor_t sSensorList[] = {
{ "K3G Gyroscope sensor",
"STMicroelectronics",
1, SENSORS_GYROSCOPE_HANDLE,
SENSOR_TYPE_GYROSCOPE, RANGE_GYRO, CONVERT_GYRO, 6.1f, 1190, { } },
};
3. 修改sensors_poll_context_t结构体
在sensors.cpp中修改sensors_poll_context_t结构体,删除其他传感器的定义,代码如下:
struct sensors_poll_context_t {
struct sensors_poll_device_t device; // must be first
sensors_poll_context_t();
~sensors_poll_context_t();
int activate(int handle, int enabled);
int setDelay(int handle, int64_t ns);
int pollEvents(sensors_event_t* data, int count);
private:
enum {
gyro = 0,
numSensorDrivers,
numFds,
};
static const size_t wake = numFds - 1; //wake = 1
static const char WAKE_MESSAGE = 'W';
struct pollfd mPollFds[numFds];//2
int mWritePipeFd;
SensorBase* mSensors[numSensorDrivers];// 2
int handleToDriver(int handle) const {
switch (handle) {
case ID_GY:
return gyro;
}
return -EINVAL;
}
};
同时,修改构造函数,删除与其他传感器相关的代码,只保留陀螺仪传感器的初始化代码:
sensors_poll_context_t::sensors_poll_context_t()
{
mSensors[gyro] = new GyroSensor();
mPollFds[gyro].fd = mSensors[gyro]->getFd();
mPollFds[gyro].events = POLLIN;
mPollFds[gyro].revents = 0;
int wakeFds[2];
int result = pipe(wakeFds);
LOGE_IF(result<0, "error creating wake pipe (%s)", strerror(errno));
fcntl(wakeFds[0], F_SETFL, O_NONBLOCK);
fcntl(wakeFds[1], F_SETFL, O_NONBLOCK);
mWritePipeFd = wakeFds[1];
mPollFds[wake].fd = wakeFds[0];//store the reading fd of the pipe
mPollFds[wake].events = POLLIN;
mPollFds[wake].revents = 0;
}
4. 修改GyroSensor.cpp的getFd()函数
将GyroSensor.cpp的getFd()函数中原来返回陀螺仪设备驱动的文件描述符,改为返回socket的文件描述符,代码如下:
int GyroSensor::getFd()
{
if(mSocketFd == -1){
socket_connect();
}
return mSocketFd;
}
其中,mSocketFd是在GyroSensor.h中新定义的socket文件描述符,socket_connect()函数用于实现连接socket服务端。
在sensors.cpp文件的sensors_poll_context_t构造函数中,初始化gyro类后,通过getFd()获取socket文件描述符,然后就可以使用poll函数对文件描述符进行数据读取。poll的读取操作在sensors_poll_context_t::pollEvents函数中,代码无需改变,如下所示:
int sensors_poll_context_t::pollEvents(sensors_event_t* data, int count)
{
int nbEvents = 0;
int n = 0;
do {
// see if we have some leftover from the last poll()
for (int i=0 ; count && i<numSensorDrivers ; i++) {
SensorBase* const sensor(mSensors[i]);
if ((mPollFds[i].revents & POLLIN) || (sensor->hasPendingEvents())) {
int nb = sensor->readEvents(data, count);
if (nb < count) {
// no more data for this sensor
mPollFds[i].revents = 0;
}
count -= nb;
nbEvents += nb;
data += nb;
}
}
if (count) {
// we still have some room, so try to see if we can get
// some events immediately or just wait if we don't have
// anything to return
n = poll(mPollFds, numFds, nbEvents ? 0 : -1);
if (n<0) {
LOGE("poll() failed (%s)", strerror(errno));
return -errno;
}
//read data from pipe
if (mPollFds[wake].revents & POLLIN) {
char msg;
int result = read(mPollFds[wake].fd, &msg, 1);
LOGE_IF(result<0, "error reading from wake pipe (%s)", strerror(errno));
LOGE_IF(msg != WAKE_MESSAGE, "unknown message on wake queue (0x%02x)", int(msg));
mPollFds[wake].revents = 0;
}
}
// if we have events and space, go read them
} while (n && count);
return nbEvents;
}
5. 修改gyro类的构造函数
删除gyro类构造函数中与设备驱动相关的代码,代码如下:
GyroSensor::GyroSensor()
: SensorBase(NULL, "gyro"),
mEnabled(0),
mInputReader(4),
mHasPendingEvent(false),
mEnabledTime(0),
mSocketFd(-1)
{
mPendingEvent.version = sizeof(sensors_event_t);
mPendingEvent.sensor = ID_GY;
mPendingEvent.type = SENSOR_TYPE_GYROSCOPE;
memset(mPendingEvent.data, 0, sizeof(mPendingEvent.data));
}
6. 修改gyro类的其他成员函数
删除gyro类的setInitialState、enable、setDelay等函数中的代码,使其返回0,代码如下:
int GyroSensor::setInitialState() {
return 0;
}
int GyroSensor::enable(int32_t, int en) {
return 0;
}
int GyroSensor::setDelay(int32_t handle, int64_t delay_ns) {
return 0;
}
7. 修改gyro类的readEvents函数
该函数是最重要的函数,用于从socket中读取陀螺仪数据,代码如下:
float value = 0;
char buf[2];
int nread = 0;
nread = read(mSocketFd, buf, 2);
if(nread <= 0){
LOGE("GyroSensor::readEvents read error.");
return 0;
}
value = (float)((buf[0] << 8) + buf[1]);
value *= CONVERT_GYRO_X;
mPendingEvent.data[0] = value;
mPendingEvent.data[1] = value;
mPendingEvent.data[2] = value;
*data = *mPendingEvent;
return 1;
socket服务端每次发送2个字节的数据,由于是单轴陀螺仪,只有1个数据。我们将mPendingEvent的data数组的3个元素都填充为同一数据,然后赋值给*data,最后返回1,表示读到1个数据。
8. 修改Android.mk文件
打开libsensors下的Android.mk文件,修改如下:
LOCAL_MODULE := sensors.default
9. 编译与测试
编译后,会生成sensors.default.so文件,将其放置到lib/hw目录下。删除hw目录下其他包含sensors文字的.so文件,仅保留sensors.default.so文件。然后就可以在模拟器或设备上进行测试,以下是App层的Java测试程序:
package com.hase.ng102.sensors;
import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
public class SensorDemoActivity extends Activity {
//设置LOG标签
private static final String TAG = "sensors";
private SensorManager sm;
private TextView tv1;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tv1 = (TextView)this.findViewById(R.id.tv1);
sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
Log.d(String.format("getSystemService %s", sm == null ? "sucess":"fail"), TAG);
int sensorType = Sensor.TYPE_GYROSCOPE;
sm.registerListener(myAccelerometerListener,sm.getDefaultSensor(sensorType),SensorManager.SENSOR_DELAY_NORMAL);
}
/*
* SensorEventListener接口的实现,需要实现两个方法
* 方法1 onSensorChanged 当数据变化的时候被触发调用
* 方法2 onAccuracyChanged 当获得数据的精度发生变化的时候被调用,比如突然无法获得数据时
* */
final SensorEventListener myAccelerometerListener = new SensorEventListener(){
//复写onSensorChanged方法
public void onSensorChanged(SensorEvent sensorEvent){
if(sensorEvent.sensor.getType() == Sensor.TYPE_GYROSCOPE){
Log.i(TAG,"onSensorChanged");
//图解中已经解释三个值的含义
float x = sensorEvent.values[0];
float y = sensorEvent.values[1];
float z = sensorEvent.values[2];
String msg = String.format("x=%f\n y=%f\n z=%f\n",x,y,z);
tv1.setText(msg);
Log.i(TAG, msg);
}
else {
String msg = "other sensors data changed";
tv1.setText(msg);
Log.i(TAG, msg);
}
}
//复写onAccuracyChanged方法
public void onAccuracyChanged(Sensor sensor , int accuracy){
Log.i(TAG, "onAccuracyChanged");
}
};
public void onPause(){
/*
* 很关键的部分:注意,说明文档中提到,即使activity不可见的时候,感应器依然会继续的工作,测试的时候可以发现,没有正常的刷新频率
* 也会非常高,所以一定要在onPause方法中关闭触发器,否则将耗费用户大量电量,很不负责。
* */
sm.unregisterListener(myAccelerometerListener);
super.onPause();
}
}