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資料的採集是指從硬體裝置讀出相關的資料。
資料的採集主要有兩種方式:
通過註冊中斷,當有中斷髮生時,驅動去採集資料;
通過輪詢主動去採集資料。主要是通過定時器定期採集資料。
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方式來實現的。
對於驅動的開發,其實都是大同小異的,只要實現相應的回呼函數就行了,另外注意以上兩點就行了。
二、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<<MAX_NUM_SENSORS)-1)
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
//定義支援的sensors類型
#define ID_A (0)
#define ID_M (1)
#define ID_O (2)
#define ID_T (3)
#define ID_P (4)
#define ID_L (5)
static int id_to_sensor[MAX_NUM_SENSORS] = {
[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,
};
//定義各個sensors,由於一個硬體上可以整合多個 sensors,所以引進了*****_GROUP,用來標識一個硬體上的多
個sensors
#define SENSORS_AKM_ACCELERATION (1<<ID_A)
#define SENSORS_AKM_MAGNETIC_FIELD (1<<ID_M)
#define SENSORS_AKM_ORIENTATION (1<<ID_O)
#define SENSORS_AKM_TEMPERATURE (1<<ID_T)
#define SENSORS_AKM_GROUP ((1<<ID_A)|(1<<ID_M)|(1<<ID_O)|(1<<ID_T))
#define SENSORS_CM_PROXIMITY (1<<ID_P)
#define SENSORS_CM_GROUP (1<<ID_P)
#define SENSORS_LIGHT (1<<ID_L)
#define SENSORS_LIGHT_GROUP (1<<ID_L)
/*****************************************************************************/
這些定義,需要根據實際情況進行重新定義,大概的思路是不變的。
3、定義sensors用到的資料結構
//由於htc的sensor整合了三個硬體,所以以下定義了akmd_fd,cmd_fd,lsd_fd三個檔案描述符。
struct sensors_control_context_t {
struct sensors_control_device_t device; // must be first
int akmd_fd;
int cmd_fd;
int lsd_fd;
uint32_t active_sensors;
};
//由於htc的sensor整合了三個硬體,所以以下定義了events_fd[3]來獲得events。
struct sensors_data_context_t {
struct sensors_data_device_t device; // must be first
int events_fd[3];
sensors_data_t sensors[MAX_NUM_SENSORS];
uint32_t pendingSensors;
};
這些定義,需要根據實際的硬體個數進行重新定義,大概的思路是不變的。
4、定義sensors的列表
static const struct sensor_t sSensorList[] = {
{ "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的詳細資料
5、定義硬體在/dev目錄下的檔案名稱
#define AKM_DEVICE_NAME "/dev/akm8973_aot"
#define CM_DEVICE_NAME "/dev/cm3602"
#define LS_DEVICE_NAME "/dev/lightsensor"
定義這些檔案節點的作用是:由於用sensors獲得資料是sensor的主要功能,但是還需要對sensor進行控制,比如open,
close, setDelay等,這些相關的控制是必不可少的。而對sensor的控制是通過對/dev/目錄下的相應的檔案節點進行控制的。
所以有了以上的相應的檔案節點。至於這些檔案節點的名字需要和在驅動中定義的相應的名字相匹配才行。
6、open_inputs(...)函數
open_inputs函數作用是:在kernel的輸入子系統中找到各個sensors的相應檔案節點並返回給sensor系統。
if (fd>=0) {
char name[80];
if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
name[0] = '/0';
}
//以下定義的compass,promixity,lightsensor-level是三個硬體裝置在kernel的input子系統中的名字,這個要
和驅動裡面的名字想匹配
if (!strcmp(name, "compass")) {
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);
}
if(dir == NULL)
return -1;
memset(devname,0,sizeof(devname)); //這行是後加的,否則libsensor.so運行之後會crash
strcpy(devname, dirname);
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) {
// TODO Auto-generated method stub
}
};
3、向系統註冊SensorEventListener。
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = (mSensorManager.getSensorList(sensor)).get(0);
mSensorManager.registerListener(mListener, mSensor,SensorManager.SENSOR_DELAY_NORMAL);
用完之後,記得登出
mSensorManager.unregisterListener(mListener);
這樣就可以用Sensor了,當sensor的資料發生變化時,就會傳過來。