Ⅰ 如何在android的驱动程序中对加速度传感器的数据进行方向和坐标的转
一部智能手机或便携设备应具有Wi-Fi 和互联网功能,能够运行应用软件等诸多特征,而且一定会具有内置传感器。高端智能手机可能集成接近传感器,环境光传感器,3
轴加速度计,以及磁力计等多种传感器。 Android 2.3
添加了一些支持多种新型传感器的API,包括陀螺仪、旋转向量、线性加速度、重力和气压传感器等。应用软件可以使用这些新型传感器,将它们组合起来,就可以实现高精确度的高级运动检测功能。
3 轴加速度计或低g 值传感器是Android API
支持的传感器之一,具有特定的坐标系统,可以给应用程序提供标准的接口数据。坐标空间的定义与手机屏幕的默认方向有关,如图1所示。
图
1. 3 轴加速度计的Android 坐标系统
在Android 坐标系统中,坐标原点位于屏幕的左下角,X 轴水平指向右侧,Y 轴垂直指向顶部,Z
轴指向屏幕前方。在该系统中,屏幕后方的坐标具有负的Z 轴值。Android 加速度计数据定义为:
Sensor.TYPE_ACCELEROMETER
所有数值都采用SI
标准单位(m/s2),测量手机的加速度值,并减去重力加速度分量。
values[0]:x 轴上的加速度值减去Gx
values[1]:y
轴上的加速度值减去Gy
values[2]:z 轴上的加速度值减去Gz
例如,当设备平放在桌上并推着其左侧向右移动时,x
轴加速度值为正。当设备平放在桌上时,加速度值为+9.81,这是用设备的加速度值 (0 m/s2) 减去重力加速度值 (-9.81 m/s2)得到的。
当设备平放在桌上放,并以加速度A m/s2 朝天空的方向推动时,加速度值等于A+9.81,这是用设备加速度值(+A
m/s2)减去重力加速度值(-9.81 m/s2)得到的。
表 1
列出了与设备的各个位置相对应的传感器的加速度值读数。用户可以用下表检查加速度计的方向与系统坐标是否一致。
在 Android HAL 文件中改变 X、Y 和Z 轴的方向
在 HAL
文件中,会有一组宏定义,用于把从传感器中读取的加速度数据转换为标准单位(m/s2)。如以下代码:
// conversion of
acceleration data to SI units (m/s^2)
#define CONVERT_A (GRAVITY_EARTH /
LSG)
#define CONVERT_A_X (-CONVERT_A)
#define CONVERT_A_Y (CONVERT_A)
#define CONVERT_A_Z (CONVERT_A)
在这个宏定义中,常量GRAVITY_EARTH
是一个标准重力加速度值,即9.81m/s2,LSG为一个重力加速度值的最小有效计数值,例如,MMA8452
在正常模式下的读数为1024。因此,CONVERT_A 用于把从加速度传感器中读取的数据,从数字读数转换为标准重力加速度单位。
通过分别修改CONVERT_A_X、CONVERT_A_Y 和CONVERT_A_Z,我们可以轻松地改变X、Y 和Z
轴的方向。如果该轴的方向与系统定义相反,可以使用(-CONVERT_A)来改变其方向。如果方向一致,就使用(CONVERT_A),则保持方向不变。
这个宏定义位于FSL Android 9 (Android 2.2)驱动程序的HAL文件sensor.c 中。对于FSLAndroid 10
(Android 2.3),您可以在’libsensors’文件夹的HAL 文件Sensor.h 中找到它。
在 Android 2.2 HAL
文件中交换X 轴和Y 轴
在某些情况下,X 和Y 轴必须进行交换,以便使传感器数据的坐标与系统坐标保持一致。
对于 FSL
Android 9 (Android 2.2)驱动程序来说,X 轴和Y 轴的交换非常简单。首先,在HAL 文件sensor.c
中,在函数sensor_poll() 中找到以下代码:
switch (event.code) {
case ABS_X:
sSensors.acceleration.x = event.value * CONVERT_A_X;
break;
case
ABS_Y:
sSensors.acceleration.y = event.value * CONVERT_A_Y;
break;
case ABS_Z:
sSensors.acceleration.z = event.value * CONVERT_A_Z;
break;
}
然后,根据如下所示修改代码:
switch (event.code) {
case
ABS_X:
sSensors.acceleration.y = event.value * CONVERT_A_Y;
break;
case ABS_Y:
sSensors.acceleration.x = event.value * CONVERT_A_X;
break;
case ABS_Z:
sSensors.acceleration.z = event.value *
CONVERT_A_Z;
break;
}
在 Android 2.3 的HAL 文件中交换X 轴和Y 轴
在
Android 2.3 的HAL 文件中交换X 轴和Y 轴会更加复杂些,因为它具有更复杂的HAL文件结构。所有HAL
文件都位于文件夹‘libsensors’中。文件AccelSensor.cpp 中的两个函数需要修改。
首先,修改函数AccelSensor()的代码,如下所示:
if
(accel_is_sensor_enabled(SENSOR_TYPE_ACCELEROMETER)) {
mEnabled |=
1<<accelerometer; if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_ACCEL_X),
&absinfo)) {
mPendingEvents[Accelerometer].acceleration.y =
absinfo.value * CONVERT_A_Y;
}
if (!ioctl(data_fd,
EVIOCGABS(EVENT_TYPE_ACCEL_Y), &absinfo)) {
mPendingEvents[Accelerometer].acceleration.x = absinfo.value * CONVERT_A_X;
}
if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_ACCEL_Z), &absinfo)) {
mPendingEvents[Accelerometer].acceleration.z = absinfo.value * CONVERT_A_Z;
}
}
然后,修改函数processEvent()的代码,如下所示:
void
AccelSensor::processEvent(int code, int value)
{
switch (code) {
case EVENT_TYPE_ACCEL_X:
mPendingMask |= 1<<accelerometer; mPendingEvents[Accelerometer].acceleration.y = value * CONVERT_A_Y;
break;
case EVENT_TYPE_ACCEL_Y:
mPendingMask |= 1<<accelerometer; mPendingEvents[Accelerometer].acceleration.x = value * CONVERT_A_X;
break;
case EVENT_TYPE_ACCEL_Z:
mPendingMask |= 1<<accelerometer; mPendingEvents[Accelerometer].acceleration.z = value * CONVERT_A_Z;
break;
}
}
完成后,X 轴和Y 轴的数据就互相交换了。
在 Kernel 驱动文件中交换X 轴和Y 轴
X 轴和Y 轴的数据交换可以在底层的linux 驱动中,在刚开始读取传感器数据时实施。通过这种方法,无论传感器芯片以何种方式安装在PCB
中,或者使用各种不同类型的传感器,HAL 文件都可以保持一致。
对于 Android 2.2 和2.3
来说,执行该操作的最便捷的方式是修改函数report_abs()中的代码。在该函数中,传感器数据通过调用函数mma8452_read_data()读取,如下所示(当使用的传感器为MMA8452Q
时):
if (mma8452_read_data(&x,&y,&z) != 0) {
//DBG("mma8452 data read failed
");
return; }
X 轴和Y
轴可以通过以下方式轻松交换:
if (mma8452_read_data(&y,&x,&z) != 0) {
//DBG("mma8452 data read failed
");
return; }
对于 Android
2.2,MMA8452 的Kernel 驱动文件为mma8452.c;对于Android 2.3,驱动文件是‘hwmon’文件夹中的mxc_mma8452.c。
在 Kernel 驱动文件中改变 X、Y 和Z 轴的方向
传感器数据的方向也可以在Kernel
驱动文件中更改。以下带有注释的语句可以添加到函数report_abs()中,从而改变数据方向:
if
(mma8452_read_data(&y,&x,&z) != 0) {
//DBG("mma8452 data read
failed
");
return;
}
x *= -1; //Reverse X direction
y *= -1;
//Reverse Y direction
z *= -1; //Reverse Z direction
input_report_abs(mma8452_idev->input, ABS_X, x);
input_report_abs(mma8452_idev->input, ABS_Y, y);
input_report_abs(mma8452_idev->input, ABS_Z, z);
input_sync(mma8452_idev->input);
总结
Android
系统已经为加速度计定义了坐标系统,因此用户必须转换从实际传感器中读取的数据,从而与其保持一致。无论是否需要转换,都应检查X、Y 和Z
轴的方向以及X-Y轴坐标。我们可以更改HAL 文件或Kernel 驱动文件来改变轴的方向,或交换X 和Y 轴,但是不要同时修改HAL 文件和Kernel 驱动。
找找
Ⅱ 谁有没有Android串口的使用例子
1 首先做的是创建新的工程然后添加一下文件
3代码
好了 然后就是关于这个页面的Code了,
这是我的:
package android.serialport;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.BreakIterator;
import java.util.ServiceConfigurationError;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MyserialActivity extendsActivity
{
EditText sendedit;
EditText receiveedit;
FileInputStream mInStream;
FileOutputStream mOutStream;
SerialPort classserialport;
ReadThread mReadThread;
private class ReadThread extends Thread
{
public void run()
{
super.run();
while(!isInterrupted())
{
int size;
}
}
}
void onDataReceive(final byte[] buffer,finalint size)
{
runOnUiThread(new Runnable()
{
@Override
publicvoid run()
{
// TODO Auto-generated method stub
if(mReadThread != null)
{
receiveedit.append(newString(buffer,0,size));
}
}
});
}
@Override
protectedvoid onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_myserial);
sendedit= (EditText)findViewById(R.id.editText1);
receiveedit=(EditText)findViewById(R.id.editText2);
receiveedit.setFocusable(false);//进制输入
/*
* 打开串口
* */
finalButton openserial =(Button)findViewById(R.id.button1);
openserial.setOnClickListener(newView.OnClickListener()
{
@Override
publicvoid onClick(View arg0)
{
//TODO Auto-generated method stub
try
{
classserialport=new SerialPort(new File("/dev/ttyS2"),9600);
}catch(SecurityExceptione)
{
e.printStackTrace();
}
catch(IOExceptione)
{
e.printStackTrace();
}
mInStream=(FileInputStream) classserialport.getInputStream();
Toast.makeText(MyserialActivity.this,"串口打开成功",Toast.LENGTH_SHORT).show();
}
});
/*
* 发送数据
* */
finalButton sendButton =(Button)findViewById(R.id.button2);
sendButton.setOnClickListener(newView.OnClickListener()
{
@Override
publicvoid onClick(View arg0)
{
Stringindata;
indata=sendedit.getText().toString();
//TODO Auto-generated method stub
try
{
mOutStream=(FileOutputStream) classserialport.getOutputStream();
mOutStream.write(indata.getBytes());
mOutStream.write('
');
}
catch(IOExceptione)
{
e.printStackTrace();
}
Toast.makeText(MyserialActivity.this,"数据发送成功",Toast.LENGTH_SHORT).show();
sendedit.setText("");
}
});
/*
* 接收数据
* */
finalButton receButton= (Button)findViewById(R.id.button3);
receButton.setOnClickListener(newView.OnClickListener()
{//inttag =0;
@Override
publicvoid onClick(View arg0)
{
// TODO Auto-generated method stub
intsize;
try
{
byte[]buffer = new byte[64];
if(mInStream== null) return;
size= mInStream.read(buffer);
if(size>0)
{
receiveedit.setText("");
}
if(size>0)
{
onDataReceive(buffer,size);
}
inttag =1;
receiveedit.setText(newString(buffer, 0, size));
}catch(IOExceptione)
{
e.printStackTrace();
return;
}
}
privateboolean isInterrupted()
{
// TODO Auto-generated methodstub
returnfalse;
}
});
/*
* 清楚接收区
* */
finalButton ClearButton = (Button)findViewById(R.id.clear);
ClearButton.setOnClickListener(newView.OnClickListener()
{
@Override
publicvoid onClick(View arg0)
{
//TODO Auto-generated method stub
receiveedit.setText("");
}
});}
@Override
publicboolean onCreateOptionsMenu(Menu menu)
{
//Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.myserial,menu);
returntrue;
}
}
好吧 你做好了。
3需要加载的文件
下面我把所需要添加的代码贴一贴
第一个是Serialport.java
/*
* Copyright 2009 Cedric Priscal
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.serialport;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.util.Log;
public class SerialPort {
private static final String TAG = "SerialPort";
/*
* Do not remove or rename the field mFd: it is used by native method close();
*/
private FileDescriptor mFd; //创建一个文件描述符对象 mFd
private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;
public SerialPort(File device, int baudrate) throws SecurityException, IOException {
/*
* 检查访问权限
* */
/* Check access permission */
if (!device.canRead() || !device.canWrite()) {//如果设备不可读或者设备不可写
try {
/* Missing read/write permission, trying to chmod the file *///没有读写权限,就尝试去挂载权限
Process su; //流程进程 su
su = Runtime.getRuntime().exec("/system/bin/su");//通过执行挂载到/system/bin/su 获得执行
String cmd = "chmod 777 " + device.getAbsolutePath() + "
"
+ "exit
";
/*String cmd = "chmod 777 /dev/s3c_serial0" + "
"
+ "exit
";*/
su.getOutputStream().write(cmd.getBytes());//进程。获得输出流。写(命令。获得二进制)
if ((su.waitFor() != 0) || !device.canRead()
|| !device.canWrite()) {//如果 进程等待不是0 或者 设备不能读写就
throw new SecurityException();//抛出一个权限异常
}
} catch (Exception e) {
e.printStackTrace();
throw new SecurityException();
}
}
/*
*
* */
mFd = open(device.getAbsolutePath(), baudrate);
//device.getAbsolutePath()这是要挂载的路径new File("/dev/ttyS2")
if (mFd == null) {
Log.e(TAG, "native open returns null");
throw new IOException();//输入输出异常
}
//将文件描述符 做输入输出流的参数 传递给创建的输入输出流
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
}
// Getters and setters
public InputStream getInputStream() {
return mFileInputStream;
}
public OutputStream getOutputStream() {
return mFileOutputStream;
}
// JNI
private native static FileDescriptor open(String path, int baudrate);
public native void close();
static {
System.loadLibrary("serial_port");
}
}
第二个是SerialPortFinder.java
/*
* Copyright 2009 Cedric Priscal
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.serialport;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.Iterator;
import java.util.Vector;
import android.util.Log;
public class SerialPortFinder {
/*
* 创建一个驱动程序类
* */
public class Driver {
public Driver(String name, String root) {
mDriverName = name;//String 类型的
mDeviceRoot = root;
}
private String mDriverName;
private String mDeviceRoot;
Vector<File> mDevices = null;
/*
* Vector 类在 java 中可以实现自动增长的对象数组
* 简单的使用方法如下:
vector<int> test;//建立一个vector
test.push_back(1);
test.push_back(2);//把1和2压入vector这样test[0]就是1,test[1]就是2
* */
public Vector<File> getDevices() {
if (mDevices == null) {
mDevices = new Vector<File>();
File dev = new File("/dev");
File[] files = dev.listFiles();
int i;
for (i=0; i<files.length; i++) {
if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) {
Log.d(TAG, "Found new device: " + files[i]);
mDevices.add(files[i]);
}
}
}
return mDevices;
}
public String getName() {
return mDriverName;
}
}
/*
*
*
* */
private static final String TAG = "SerialPort";
private Vector<Driver> mDrivers = null;
Vector<Driver> getDrivers() throws IOException {
if (mDrivers == null) {
mDrivers = new Vector<Driver>();
LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers"));
String l;
while((l = r.readLine()) != null) {
String[] w = l.split(" +");
if ((w.length == 5) && (w[4].equals("serial"))) {
Log.d(TAG, "Found new driver: " + w[1]);
mDrivers.add(new Driver(w[0], w[1]));
}
}
r.close();
}
return mDrivers;
}
public String[] getAllDevices() {
Vector<String> devices = new Vector<String>();
// Parse each driver
Iterator<Driver> itdriv;
try {
itdriv = getDrivers().iterator();
while(itdriv.hasNext()) {
Driver driver = itdriv.next();
Iterator<File> itdev = driver.getDevices().iterator();
while(itdev.hasNext()) {
String device = itdev.next().getName();
String value = String.format("%s (%s)", device, driver.getName());
devices.add(value);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return devices.toArray(new String[devices.size()]);
}
public String[] getAllDevicesPath() {
Vector<String> devices = new Vector<String>();
// Parse each driver
Iterator<Driver> itdriv;
try {
itdriv = getDrivers().iterator();
while(itdriv.hasNext()) {
Driver driver = itdriv.next();
Iterator<File> itdev = driver.getDevices().iterator();
while(itdev.hasNext()) {
String device = itdev.next().getAbsolutePath();
devices.add(device);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return devices.toArray(new String[devices.size()]);
}
}
第三个是Android.mk
#
# Copyright 2009 Cedric Priscal
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
TARGET_PLATFORM := android-3
LOCAL_MODULE := serial_port
LOCAL_SRC_FILES := SerialPort.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
第四个是SerialPort.c
/*
* Copyright 2009 Cedric Priscal
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>
#include "android/log.h"
static const char *TAG="serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
static speed_t getBaudrate(jint baudrate)
{
switch(baudrate) {
case 0: return B0;
case 50: return B50;
case 75: return B75;
case 110: return B110;
case 134: return B134;
case 150: return B150;
case 200: return B200;
case 300: return B300;
case 600: return B600;
case 1200: return B1200;
case 1800: return B1800;
case 2400: return B2400;
case 4800: return B4800;
case 9600: return B9600;
case 19200: return B19200;
case 38400: return B38400;
case 57600: return B57600;
case 115200: return B115200;
case 230400: return B230400;
case 460800: return B460800;
case 500000: return B500000;
case 576000: return B576000;
case 921600: return B921600;
case 1000000: return B1000000;
case 1152000: return B1152000;
case 1500000: return B1500000;
case 2000000: return B2000000;
case 2500000: return B2500000;
case 3000000: return B3000000;
case 3500000: return B3500000;
case 4000000: return B4000000;
default: return -1;
}
}
/*
* Class: cedric_serial_SerialPort
* Method: open
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open
(JNIEnv *env, jobject thiz, jstring path, jint baudrate)
{
int fd;
speed_t speed;
jobject mFileDescriptor;
/* Check arguments */
{
speed = getBaudrate(baudrate);
if (speed == -1) {
/* TODO: throw an exception */
LOGE("Invalid baudrate");
return NULL;
}
}
/* Opening device */
{
jboolean is;
const char *path_utf = (*env)->GetStringUTFChars(env, path, &is);
LOGD("Opening serial port %s", path_utf);
fd = open(path_utf, O_RDWR | O_DIRECT | O_SYNC);
LOGD("open() fd = %d", fd);
(*env)->ReleaseStringUTFChars(env, path, path_utf);
if (fd == -1)
{
/* Throw an exception */
LOGE("Cannot open port");
/* TODO: throw an exception */
return NULL;
}
}
/* Configure device */
{
struct termios cfg;
LOGD("Configuring serial port");
if (tcgetattr(fd, &cfg))
{
LOGE("tcgetattr() failed");
close(fd);
/* TODO: throw an exception */
return NULL;
}
cfmakeraw(&cfg);
cfsetispeed(&cfg, speed);
cfsetospeed(&cfg, speed);
if (tcsetattr(fd, TCSANOW, &cfg))
{
LOGE("tcsetattr() failed");
close(fd);
/* TODO: throw an exception */
return NULL;
}
}
/* Create a corresponding file descriptor */
{
jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
}
return mFileDescriptor;
}
/*
* Class: cedric_serial_SerialPort
* Method: close
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_android_serialport_SerialPort_close
(JNIEnv *env, jobject thiz)
{
jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");
jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");
jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);
LOGD("close(fd = %d)", descriptor);
close(descriptor);
}
Ⅲ 如何编写一个用于Android的音效驱动和控制程序
本教程将逐步讲解从入门开始如何编写一个可用于Android 4.0的音效驱动和控制程序(Android 2.3上只是部分接口不同而已)。对于Android操作系统的架构等将不再叙述。
软件需求(Windows环境):
Windows操作系统、最新版Cygwin、Android NDK r8或更高、Eclipse、最新版Android SDK
专业技术需求:
掌握基本C/C++语法、掌握基本Java语法、基本Android UI设计、定点数学原理和算法、基本音频处理技术
可选高级技术需求:
IIR/FIR滤波器、FFT、Thumb/ARM汇编、NEON指令集
1、什么是Android里的音效驱动,它是什么架构。
从Android 2.3版开始,在系统多媒体框架里增加了一个SoundFX层,这个层就是“音效处理过程”。当多媒体系统运作时,框架允许将一个“标准”的SoundFX库载入对应媒体流的Mixer处。SoundFX库需要遵循OpenSLES架构,即所谓的标准就是实现一个基于OpenSLES架构的.so库。
SoundFX可以被加载到任何一个音频流上,每个音频流使用会话ID作为标识符。注意:0表示系统总输出的音频流会话ID。一般情况下音效驱动就要加载到这个流上,才可以对系统内所有的声音做处理(包括音视频播放器、游戏、铃声等)。
同样的,每一个SoundFX在加载时/后都有很多配置参数和控制权的优先级。而完成对SoundFX的加载就需要一个控制程序。控制程序一般由Java语言在Eclipse中实现,通俗的说控制程序就是一个Android的apk程序。SoundFX可以理解为Windows系统里的底层混合器,控制程序可以理解为Windows的控制面板,在控制面板上控制SoundFX的加载和启动,各个参数的设置等。当一个控制程序启动后,它首先要做的事情就是按照OpenSLES框架来通知系统加载一个SoundFX到一个媒体流,然后通过UI交互来启用/禁用该SoundFX,同时根据UI来控制SoundFX的参数,当退出时也需要通知系统卸载该SoundFX。
2、从哪开始?
因为Android规定SoundFX必须基于OpenSLES,所以最先要做的事情就是选择一个效果器的类型。这是为什么呢?到底是什么意思呢?OpenSLES规定一个效果器要有两个必须的条件,一个是该效果器的类型,一个是该效果器的唯一识别码。这两个东西在C/C++语言中是按照GUID结构体来存储的(GUID是什么?找度娘)。
其中类型的GUID是OpenSLES定死的,音量(SL_IID_VOLUME)、采样率控制(SL_IID_PLAYBACKRATE)、均衡器(SL_IID_EQUALIZER)、预设混响(SL_IID_PRESETREVERB)、环境混响(SL_IID_ENVIRONMENTALREVERB)、3D定位(SL_IID_3DLOCATION)、多普勒效应(SL_IID_3DDOPPLER)、低音增强(SL_IID_BASSBOOST)、升降调(SL_IID_PITCH)、虚拟化(SL_IID_VIRTUALIZER)。这里没有你想要的?你想自定义?什么,你要做一个高音增强?无论做什么,都得在这里面选一个。为了简单一点,那就选虚拟化吧,虚拟化只有一个固定参数。(这里没看明白?那就把整个教程都看完,相信看到最后你会明白的)
下一步是生成一个自己独一无二的GUID来给自己的SoundFX命名。生成的办法有很多,有现成软件也有网页。这里我生成的是{42C6510E-1811-4857-8CA5-C204A8A3B0D4}。
以上提及的详细内容和编程指导请阅读Android NDK\platforms\android-14\arch-arm\usr\include\SLES\OpenSLES.h。(Android 4.0对应android
Ⅳ Android 重学系列 ion驱动源码浅析
上一篇文章,在解析初始化GraphicBuffer中,遇到一个ion驱动,对图元进行管理。首先看看ion是怎么使用的:
我们按照这个流程分析ion的源码。
如果对ion使用感兴趣,可以去这篇文章下面看 https://blog.csdn.net/hexiaolong2009/article/details/102596744
本文基于Android的Linux内核版本3.1.8
遇到什么问题欢迎来本文讨论 https://www.jianshu.com/p/5fe57566691f
什么是ion?如果是音视频,Camera的工程师会对这个驱动比较熟悉。最早的GPU和其他驱动协作申请一块内存进行绘制是使用比较粗暴的共享内存。在Android系统中使用的是匿名内存。最早由三星实现了一个Display和Camera共享内存的问题,曾经在Linux社区掀起过一段时间。之后各路大牛不断的改进之下,就成为了dma_buf驱动。并在 Linux-3.3 主线版本合入主线。现在已经广泛的运用到各大多媒体开发中。
首先介绍dma_buf的2个角色,importer和exporter。importer是dma_buf驱动中的图元消费者,exporter是dma_buf驱动中的图元生产者。
这里借用大佬的图片:
ion是基于dma_buf设计完成的。经过阅读源码,其实不少思路和Android的匿名内存有点相似。阅读本文之前就算不知道dma_buf的设计思想也没关系,我不会仔细到每一行,我会注重其在gralloc服务中的申请流程,看看ion是如何管理共享内存,为什么要抛弃ashmem。
我们先来看看ion的file_operation:
只有一个open和ioctl函数。但是没有mmap映射。因此mmap映射的时候一定其他对象在工作。
我们关注显卡英伟达的初始化模块。
文件:/ drivers / staging / android / ion / tegra / tegra_ion.c
mole_platform_driver实际上就是我之前经常提到过的mole_init的一个宏,多了一个register注册到对应名字的平台中的步骤。在这里面注册了一个probe方法指针,probe指向的tegra_ion_probe是加载内核模块注册的时候调用。
先来看看对应的结构体:
再来看看对应ion内的堆结构体:
完成的事情如下几个步骤:
我们不关注debug模式。其实整个就是我们分析了很多次的方法。把这个对象注册miscdevice中。等到insmod就会把整个整个内核模块从dev_t的map中关联出来。
我们来看看这个驱动结构体:
文件:/ drivers / staging / android / ion / ion_heap.c
这里有四个不同堆会申请出来,我们主要来看看默认的ION_HEAP_TYPE_SYSTEM对应的heap流程。
其实真正象征ion的内存堆是下面这个结构体
不管原来的那个heap,会新建3个ion_system_heap,分别order为8,4,0,大于4为大内存。意思就是这个heap中持有一个ion_page_pool 页资源池子,里面只有对应order的2的次幂,内存块。其实就和伙伴系统有点相似。
还会设置flag为ION_HEAP_FLAG_DEFER_FREE,这个标志位后面会用到。
文件:/ drivers / staging / android / ion / ion_page_pool.c
在pool中分为2个链表一个是high_items,另一个是low_items。他们之间的区分在此时就是以2为底4的次幂为分界线。
文件:/ drivers / staging / android / ion / ion.c
因为打开了标志位ION_HEAP_FLAG_DEFER_FREE和heap存在shrink方法。因此会初始化两个回收函数。
文件:/ drivers / staging / android / ion / ion_heap.c
此时会创建一个内核线程,调用ion_heap_deferred_free内核不断的循环处理。不过由于这个线程设置的是SCHED_IDLE,这是最低等级的时间片轮转抢占。和Handler那个adle一样的处理规则,就是闲时处理。
在这个循环中,不断的循环销毁处理heap的free_list里面已经没有用的ion_buffer缓冲对象。
文件:/ drivers / staging / android / ion / ion_system_heap.c
注册了heap的销毁内存的方法。当系统需要销毁页的时候,就会调用通过register_shrinker注册进来的函数。
文件:/ drivers / staging / android / ion / ion_page_pool.c
整个流程很简单,其实就是遍历循环需要销毁的页面数量,接着如果是8的次幂就是移除high_items中的page缓存。4和0则销毁low_items中的page缓存。至于为什么是2的次幂其实很简单,为了销毁和申请简单。__free_pages能够整页的销毁。
文件:/ drivers / staging / android / ion / ion.c
主要就是初始化ion_client各个参数,最后把ion_client插入到ion_device的clients。来看看ion_client结构体:
核心还是调用ion_alloc申请一个ion缓冲区的句柄。最后把数据拷贝会用户空间。
这个实际上就是找到最小能承载的大小,去申请内存。如果8kb申请内存,就会拆分积分在0-4kb,4kb-16kb,16kb-128kb区间找。刚好dma也是在128kb之内才能申请。超过这个数字就禁止申请。8kb就会拆成2个4kb保存在第一个pool中。
最后所有的申请的page都添加到pages集合中。
文件:/ drivers / staging / android / ion / ion_page_pool.c
能看到此时会从 ion_page_pool冲取出对应大小区域的空闲页返回上层,如果最早的时候没有则会调用ion_page_pool_alloc_pages申请一个新的page。由于引用最终来自ion_page_pool中,因此之后申请之后还是在ion_page_pool中。
这里的处理就是为了避免DMA直接内存造成的缓存差异(一般的申请,默认会带一个DMA标志位)。换句话说,是否打开cache其实就是,关闭了则使用pool的cache,打开了则不使用pool缓存,只依赖DMA的缓存。
我们可以看另一个dma的heap,它是怎么做到dma内存的一致性.
文件: drivers / staging / android / ion / ion_cma_heap.c
能看到它为了能办到dma缓存的一致性,使用了dma_alloc_coherent创建了一个所有强制同步的地址,也就是没有DMA缓存的地址。
这里出现了几个新的结构体,sg_table和scatterlist
文件:/ lib / scatterlist.c
这里面实际上做的事情就是一件:初始化sg_table.
sg_table中有一个核心的对象scatterlist链表。如果pages申请的对象数量<PAGE_SIZE/sizeof(scatterlist),每一项sg_table只有一个scatterlist。但是超出这个数字就会增加一个scatterlist。
用公式来说:
换句话说,每一次生成scatterlist的链表就会直接尽可能占满一页,让内存更好管理。
返回了sg_table。
初始化ion_handle,并且记录对应的ion_client是当前打开文件的进程,并且设置ion_buffer到handle中。使得句柄能够和buffer关联起来。
每当ion_buffer需要销毁,
Ⅳ 如何写一个Android USB接口驱动
说到 android 驱动是离不开 Linux 驱动的。Android 内核采用的是 Linux2.6 内核 (最近Linux 3.3 已经包含了一些 Android 代码)。但 Android 并没有完全照搬 Linux 系统内核,除了对Linux 进行部分修正,还增加了不少内容。android 驱动 主要分两种类型:Android 专用驱动 和 Android 使用的设备驱动(linux)。
Android 专有驱动程序:
1)Android Ashmem 匿名共享内存; 为用户空间程序提供分配内存的机制,为进程间提供大块共享内存,同时为内核提供回收和管理这个内存。
2)Android Logger 轻量级的LOG(日志) 驱动;
3)Android Binder 基于 OpenBinder 框架的一个驱动;
4)Android Power Management 电源管理模块;
5)Low Memory Killer 低内存管理器;
6)Android PMEM 物理内存驱动;
7)USB Gadget USB 驱动(基于 gaeget 框架);
8)Ram Console 用于调试写入日志信息的设备;
9)Time Device 定时控制设备;
10)Android Alarm 硬件时钟;
Android 上的设备驱动:
1)Framebuff 显示驱动;
2)Event 输入设备驱动;
3)ALSA 音频驱动;
4)OSS 音频驱动;
5)v412摄像头:视频驱动;
6)MTD 驱动;
7)蓝牙驱动;
8)WLAN 设备驱动;
Android 专有驱动程序
1.Android Ashmem
为用户空间程序提供分配内存的机制,为进程间提供大块共享内存,同时为内核提供回收和管理这个内存。
设备节点:/dev/ashmen .主设备号 10.
源码位置: include/linux/ashmen.h Kernel /mm/ashmen.c
相比于 malloc 和 anonymous/named mmap 等传统的内存分配机制,其优势是通过内核驱动提供了辅助内核的内存回收算法机制(pin/unoin)
2.Android Logger
无论是底层的源代码还上层的应用,我们都可以使用 logger 这个日志设备看、来进行调试。
设备节点: /dev/log/main /dev/log/event /dev/log/radio
源码位置:include/linux/logger.h include/linux/logger.c
3.Android Binder
IPC Binder 一种进程间通信机制。他的进程能够为其它进程提供服务 ----- 通过标准的 Linux 系统调用 API。
设备节点 :/dev/binder
源码位置:Kernel/include/linux/binder.h Kernel/drivers/misc/binder.c
4.Android Power Management
一个基于标准 linux 电源管理的轻量级 Android 电源管理系统,在 drivers/android/power.c kernel/power/
5.Low Memory Killer
它在用户空间中指定了一组内存临界值,当其中某个值与进程描述中的 oom_adj 值在同一范围时,该进程将被Kill掉(在parameters/adj中指定oome_adj 的最小值)。它与标准的Linux OOM机制类似,只是实现方法不同
源码位置:drivers/misc/lowmemorykiller.c
6.Android PMEM
PMEM 主要作用就是向用户空间提供连续的物理内存区域。
1.让 GPU 或 VPU 缓冲区共享 CPU 核心。
2.用于 Android service 堆。
源码位置:include/linux/android_pmem.h drivers/android/pmem.c
7.USB Gadget
基于标准 Linux USB gaeget 驱动框架的设备驱动。
源码位置:drivers/usb/gadet/
8.Ram Console
为了提供调试功能,android 允许将调试日志信息写入这个设备,它是基于 RAM 的 buffer.
源码位置: drivers/staging/android/ram_console.c
9.Time Device
定时控制,提供了对设备进行定时控制的功能。
源码位置:drivers/staging/android/timed_output.c(timed_gpio.c)
10.Android Alarm
提供一个定时器,用于把设备从睡眠状态唤醒,同时它还提供了一个即使在设备睡眠时也会运行的时钟基准。
设备节点:/dev/alarm
源码位置:drivers/trc/alarm.c
Android 设备驱动
1. Framebuffer 帧缓存设备
Framebuffer 驱动在 Linux 中是标准的显示设备的驱动。对于 PC 系统,它是显卡的驱动 ; 对于嵌入式 SOC 处理器系统,它是 LCD 控制器或者其他显示控制器的驱动。它是一个字符设备,在文件系统中设备节点通常是 /dev/fbx 。 每个系统可以有多个显示设备 , 依次用 /dev/fbO 、 /dev/fb l
等来表示。在 Android 系统中主设备号为 29 ,次设备号递增生成。
Android 对 Framebuffer 驱动的使用方式是标准的 , 在 / dev / graphie / 中的 Framebuffer 设备节点由 init 进程自动创建 , 被 libui 库调用 。 Android 的 GUI 系统中 , 通过调用 Framebuffer 驱动的标准接口,实现显示设备的抽象。
Framebuff的结构框架和实现 :
linux LCD驱动(二)--FrameBuffer
Linux LCD驱动(四)--驱动的实现
2.Event输入设备驱动
Input 驱动程序是 Linux 输入设备的驱动程序 , 分为游戏杆 (joystick) 、 鼠标 (mouse 和 mice)和事件设备 (Event queue)3 种驱动程序。其中事件驱动程序是目前通用的程序,可支持键盘 、 鼠标、触摸屏等多种输入设备。 Input 驱动程序的主设备号是 l3 ,每一种 Input 设备从设备号占 用5 位 , 3 种从设备号分配是 : 游戏杆 0 ~ 61 ; Mouse 鼠标 33 ~ 62 ; Mice 鼠标 63 ; 事件设备 64 ~ 95 ,各个具体的设备在 misc 、 touchscreen 、 keyboard 等目录中。
Event 设备在用户空问使用 read 、 ioctl 、 poll 等文件系统的接口操作, read 用于读取输入信息, ioctl 用于获取和设置信息, poll 用于用户空间的阻塞,当内核有按键等中断时,通过在中断中唤醒内核的 poll 实现。
Event 输入驱动的架构和实现:
Linux设备驱动之——input子系统
3.ALSA音频驱动
高级 Linux 声音体系 ALSA(Advanced Linux Sound Architecture ) 是为音频系统提供驱动 的Linux 内核组件,以替代原先的开发声音系统 OSS 。它是一个完全开放源代码的音频驱动程序集 ,除了像 OSS 那样提供一组内核驱动程序模块之外 , ALSA 还专门为简化应用程序的编写提供相应的函数库,与 OSS 提供的基于 ioctl 等原始编程接口相比, ALSA 函数库使用起来要更加方便一些
利用该函数库,开发人员可以方便、快捷地开发出自己的应用程序,细节则留给函数库进行内部处理 。 所以虽然 ALSA 也提供了类似于 OSS 的系统接口 , 但建议应用程序开发者使用音频函数库,而不是直接调用驱动函数。
ALSA 驱动的主设备号为 116 ,次设备号由各个设备单独定义,主要的设备节点如下:
/ dev / snd / contmlCX —— 主控制 ;
/ dev / snd / pcmXXXc —— PCM 数据通道 ;
/ dev / snd / seq —— 顺序器;
/ dev / snd / timer —— 定义器。
在用户空问中 , ALSA 驱动通常配合 ALsA 库使用 , 库通过 ioctl 等接口调用 ALSA 驱动程序的设备节点。对于 AIJSA 驱动的调用,调用的是用户空间的 ALsA 库的接口,而不是直接调用 ALSA 驱动程序。
ALSA 驱动程序的主要头文件是 include / sound ./ sound . h ,驱动核心数据结构和具体驱动的注册函数是 include / sound / core . h ,驱动程序 的核心实现是 Sound / core / sound . c 文件。
ALSA 驱动程序使用下面的函数注册控制和设备:
int snd _ pcm _ new (struct snd _ card * card , char * id , int device , int playback _ count , int capture _ count , struct snd _ pcm ** rpcm) ;
int snd ctl _ add(struct snd _ card * card , struct snd _ kcontrol * kcontro1) ;
ALSA 音频驱动在内核进行 menuconfig 配置时 , 配置选项为 “ Device Drivers ” > “ Sound c ard support ” 一 > “ Advanced Linux Sound Architecture ” 。子选项包含了 Generic sound devices( 通用声音设备 ) 、 ARM 体系结构支持,以及兼容 OSS 的几个选项。 ALsA 音频驱动配置对应的文件是sound / core / Kconfig 。
Android 没有直接使用 ALSA 驱动,可以基于 A-LSA 驱动和 ALSA 库实现 Android Audio 的硬件抽象层; ALSA 库调用内核的 ALSA 驱动, Audio 的硬件抽象层调用 ALSA 库。
4.OSS音频驱动
OSS(Open Sound System开放声音系统)是 linux 上最早出现的声卡驱动。OSS 由一套完整的内核驱动程序模块组成,可以为绝大多数声卡提供统一的编程接口。
OSS 是字符设备,主设备号14,主要包括下面几种设备文件:
1) /dev/sndstat
它是声卡驱动程序提供的简单接口,它通常是一个只读文件,作用也只限于汇报声卡的当前状态。(用于检测声卡)
2)/dev/dsp
用于数字采样和数字录音的设备文件。对于音频编程很重要。实现模拟信号和数字信号的转换。
3)/dev/audio
类似于/dev/dsp,使用的是 mu-law 编码方式。
4)/dev/mixer
用于多个信号组合或者叠加在一起,对于不同的声卡来说,其混音器的作用可能各不相同。
5)/dev/sequencer
这个设备用来对声卡内建的波表合成器进行操作,或者对 MIDI 总线上的乐器进行控制。
OSS 驱动所涉及的文件主要包括:
kernel/include/linux/soundcard.h
kernel/include/linux/sound.h 定义 OSS 驱动的次设备号和注册函数
kernel/sound_core.c OSS核心实现部分
5.V4l2视频驱动
V4L2是V4L的升级版本,为linux下视频设备程序提供了一套接口规范。包括一套数据结构和底层V4L2驱动接口。V4L2提供了很多访问接口,你可以根据具体需要选择操作方法。需要注意的是,很少有驱动完全实现了所有的接口功能。所以在使用时需要参考驱动源码,或仔细阅读驱动提供者的使用说明。
V4L2的主设备号是81,次设备号:0~255,这些次设备号里也有好几种设备(视频设备、Radio设备、Teletext、VBI)。
V4L2的设备节点: /dev/videoX, /dev/vbiX and /dev/radioX
Android 设备驱动(下)
MTD 驱动
Flash 驱动通常使用 MTD (memory technology device ),内存技术设备。
MTD 的字符设备:
/dev/mtdX
主设备号 90.
MTD 的块设备:
/dev/block/mtdblockX
主设备号 13.
MTD 驱动源码
drivers/mtd/mtdcore.c:MTD核心,定义MTD原始设备
drivers/mtd/mtdchar.c:MTD字符设备
drivers/mtd/mtdblock.c:MTD块设备
MTD 驱动程序是 Linux 下专门为嵌入式环境开发的新一类驱动程序。Linux 下的 MTD 驱动程序接口被划分为用户模块和硬件模块:
用户模块 提供从用户空间直接使用的接口:原始字符访问、原始块访问、FTL (Flash Transition Layer)和JFS(Journaled File System)。
硬件模块 提供内存设备的物理访问,但不直接使用它们,二十通过上述的用户模块来访问。这些模块提供了闪存上读、写和擦除等操作的实现。
蓝牙驱动
在 Linux 中,蓝牙设备驱动是网络设备,使用网络接口。
Android 的蓝牙协议栈使用BlueZ实现来对GAP, SDP以及RFCOMM等应用规范的支持,并获得了SIG认证。由于Bluez使用GPL授权, 所以Android 框架通过D-BUS IPC来与bluez的用户空间代码交互以避免使用未经授权的代码。
蓝牙协议部分头文件:
include/net/bluetooth/hci_core.h
include/net/bluetooth/bluetooth.h
蓝牙协议源代码文件:
net/bluetooth/*
蓝牙驱动程序部分的文件:
drivers/bluetooth/*
蓝牙的驱动程序一般都通过标准的HCI控制实现。但根据硬件接口和初始化流程的不同,又存在一些差别。这类初始化动作一般是一些晶振频率,波特率等基础设置。比如CSR的芯片一般通过BCSP协议完成最初的初始化配置,再激活标准HCI控制流程。对Linux来说,一旦bluez可以使用HCI与芯片建立起通信(一般是hciattach + hciconfig),便可以利用其上的标准协议(SCO, L2CAP等),与蓝牙通信,使其正常工作了。
WLAN 设备驱动(Wi-Fi)(比较复杂我面会专门写个wifi分析)
在linux中,Wlan设备属于网络设备,采用网络接口。
Wlan在用户空间采用标准的socket接口进行控制。
WiFi协议部分头文件:
include/net/wireless.h
WiFi协议部分源文件:
net/wireless/*
WiFi驱动程序部分:
drivers/net/wireless/*
Ⅵ android 下如何动态加载触摸屏驱动
TP驱动实现
1 修改ProjectConfig.mk
修改mediatek\config\prj\ProjectConfig.mk下的CUSTOM_KERNEL_TOUCHPANEL
其值由GT818B改为msg2133
2 增加ms2133驱动文件夹
根据TP厂家提供的驱动,我们在\mediatek\custom\common\kernel\touchpanel增加msg2133触摸屏驱动文件夹msg2133,并做下面一些简单修改正常使用。
(1) Msg2133接口的初始化
1) CHIP_EN片选使能引脚
mt_set_gpio_mode(GPIO_CTP_MSG2133_EN_PIN,GPIO_CTP_MSG2133_EN_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_MSG2133_EN_PIN,GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_MSG2133_EN_PIN,GPIO_OUT_ONE);</span>
msg2133芯片使能引脚配置为GPIO模式、输出高电平使能。
2) INT中断引脚
mt_set_gpio_mode(GPIO_CTP_MSG2133_EINT_PIN,GPIO_CTP_MSG2133_EINT_PIN_M_EINT);
mt_set_gpio_dir(GPIO_CTP_MSG2133_EINT_PIN,GPIO_DIR_IN);
mt_set_gpio_pull_enable(GPIO_CTP_MSG2133_EINT_PIN,GPIO_PULL_ENABLE);
mt_set_gpio_pull_select(GPIO_CTP_MSG2133_EINT_PIN,GPIO_PULL_UP);</span>
配置为中断模式、输入、使能上下拉功能和设置为上拉。
(2) IIC地址
Msg2133的iic读写地址,我从数据手册上没有找到是如何确定这两个地址的,驱动厂家在驱动代码中提供,如果想要具体是怎么确定的,可咨询厂家。
#defineFW_ADDR_MSG21XX (0xC4>>1)
#defineFW_ADDR_MSG21XX_TP (0x4C>>1)//write,0x26
#defineFW_UPDATE_ADDR_MSG21XX (0x92>>1)//read,0x49</span>
(3) 增加TP的虚拟按键(virtual key)
要在TP上增加虚拟按键,需要在tp对应的头文件添加下面的设置:
1) 定义TPD_HAVE_BUTTON
2) 定义TPD_BUTTON_HEIGHT、TPD_KEY_COUNT、TPD_KEYS和TPD_KEYS_DIM,分别用于定义button被识别的纵向坐标、虚拟按键个数、对应的功能键和每个功能键的坐标
#defineTPD_HAVE_BUTTON
#defineTPD_BUTTON_HEIGHT 800
#defineTPD_KEY_COUNT 4
#defineTPD_KEYS { KEY_BACK, KEY_SEARCH,KEY_MENU, KEY_HOMEPAGE }
#define TPD_KEYS_DIM {{200,900,10,10},{260,900,10,10},{40,900,10,10},{120,900,10,10}}</span>
其中,{200,900,10,10}对应了KEY_BACK的坐标, (200,900)是该key center的坐标,10是该键的宽度,10是该键的高度。
3) 根据显示屏分辨率修改相关的宏定义
#defineTPD_RES_X 480 // (320)
#defineTPD_RES_Y 800 //(480)</span>
把常用的实体按键(导航按键)映射到触屏区域的快捷方式,不强制要求一定要有物理按键来支持用户操作,这对开发全触摸屏的产品非常有利。
Ⅶ android驱动怎么用
把手机和电脑连接,并打开设备铅败神管理器枯锋,你可以找到一个带有黄色问号的未知设备
双击它,看看是不是你买得手机 的设备名称 和制造商,如果是,那就是这个设备了
确定是这个驱动后,切换到驱动程序 选项卡中,有一个更新驱动按钮,单击后,选择手动槐亏查找并安装驱动,然后将路径指向你下载的驱动文件夹中,OK。希望可以帮到你