摘要:Android 是面向智能手機(jī)和其他便攜式設(shè)備的最受歡迎的操作系統(tǒng)(OS)之一。它為多種傳感器提供了標(biāo)準(zhǔn)的API 接口,包括加速度計。加速度計的標(biāo)準(zhǔn)API 定義了原始加速度數(shù)據(jù)的坐標(biāo)系統(tǒng)。用戶必須將從傳感器中讀取的原始數(shù)據(jù)轉(zhuǎn)換為標(biāo)準(zhǔn)單位,并使其符合系統(tǒng)定義的坐標(biāo)方向。本文介紹了Android 中的坐標(biāo)系統(tǒng)是如何定義的,以及如何在Android 系統(tǒng)的驅(qū)動代碼中對3 軸加速度計數(shù)據(jù)的方向和坐標(biāo)進(jìn)行轉(zhuǎn)換。本文討論的示例代碼基于飛思卡爾的Android 2.2 和2.3 驅(qū)動程序,加速度計則以飛思卡爾的MMA8452Q 加
速度傳感器為例。
關(guān)鍵詞:加速度計,傳感器驅(qū)動,Android
一部智能手機(jī)或便攜設(shè)備應(yīng)具有Wi-Fi 和互聯(lián)網(wǎng)功能,能夠運行應(yīng)用軟件等諸多特征,而且一定會具有內(nèi)置傳感器。高端智能手機(jī)可能集成接近傳感器,環(huán)境光傳感器,3 軸加速度計,以及磁力計等多種傳感器。 Android 2.3 添加了一些支持多種新型傳感器的API,包括陀螺儀、旋轉(zhuǎn)向量、線性加速度、重力和氣壓傳感器等。應(yīng)用軟件可以使用這些新型傳感器,將它們組合起來,就可以實現(xiàn)高精確度的高級運動檢測功能。
3 軸加速度計或低g 值傳感器是Android API 支持的傳感器之一,具有特定的坐標(biāo)系統(tǒng),可以給應(yīng)用程序提供標(biāo)準(zhǔn)的接口數(shù)據(jù)。坐標(biāo)空間的定義與手機(jī)屏幕的默認(rèn)方向有關(guān),如圖1所示。
圖 1. 3 軸加速度計的Android 坐標(biāo)系統(tǒng)
在Android 坐標(biāo)系統(tǒng)中,坐標(biāo)原點位于屏幕的左下角,X 軸水平指向右側(cè),Y 軸垂直指向頂部,Z 軸指向屏幕前方。在該系統(tǒng)中,屏幕后方的坐標(biāo)具有負(fù)的Z 軸值。Android 加速度計數(shù)據(jù)定義為:
Sensor.TYPE_ACCELEROMETER
所有數(shù)值都采用SI 標(biāo)準(zhǔn)單位(m/s2),測量手機(jī)的加速度值,并減去重力加速度分量。
values[0]:x 軸上的加速度值減去Gx
values[1]:y 軸上的加速度值減去Gy
values[2]:z 軸上的加速度值減去Gz
例如,當(dāng)設(shè)備平放在桌上并推著其左側(cè)向右移動時,x 軸加速度值為正。當(dāng)設(shè)備平放在桌上時,加速度值為+9.81,這是用設(shè)備的加速度值 (0 m/s2) 減去重力加速度值 (-9.81 m/s2)得到的。
當(dāng)設(shè)備平放在桌上放,并以加速度A m/s2 朝天空的方向推動時,加速度值等于A+9.81,這是用設(shè)備加速度值(+A m/s2)減去重力加速度值(-9.81 m/s2)得到的。
表 1 列出了與設(shè)備的各個位置相對應(yīng)的傳感器的加速度值讀數(shù)。用戶可以用下表檢查加速度計的方向與系統(tǒng)坐標(biāo)是否一致。
表 1. 不同位置上各軸的加速度值

通過
加速度傳感器讀取3 軸加速度值時,需要假設(shè)傳感器的3 軸方向與系統(tǒng)坐標(biāo)是一致的。但是在實際的產(chǎn)品中,可能會使用不同的傳感器芯片,或者采用不同的安裝方向,因此數(shù)據(jù)方向也會不同。圖2 所示的是飛思卡爾MMA8452Q 3 軸加速度傳感器的方向定義。
圖 2. MMA8452Q 的方向定義
在圖 2 中,我們可以看到當(dāng)安裝芯片時,必須讓引腳1 處于右下角的位置(PD),并安裝在PCB 的前方,這樣才能與Android 坐標(biāo)系統(tǒng)的默認(rèn)位置相符。這樣安裝后,用戶可確定數(shù)據(jù)方向與系統(tǒng)坐標(biāo)定義是一致的。在任何其他情形下,數(shù)據(jù)都無法與系統(tǒng)定義保持完全一致,所以需要更改數(shù)據(jù)方向和坐標(biāo)。在某些情況下,X 和Y 軸必須交換,或者既要改變方向,也要交換X-Y 軸。
判斷是否需要改變方向或交換X-Y 軸的方法如下所述:
1. 將設(shè)備放置在朝上(UP)的位置,如表1 中所示。
2. 從傳感器中讀取3 軸的數(shù)據(jù)。如果Y 軸上的數(shù)據(jù)為 ±1 g (±9.81m/s2),其他兩個軸上的數(shù)據(jù)大約為0,則不需要交換X-Y 軸。否則,需要交換X 和Y 軸,請轉(zhuǎn)至步驟3。
2.1. 在該位置上,如果Y 軸上讀取的數(shù)據(jù)為+1 g (+9.81m/s2),則Y 軸的方向不需要改變,如果數(shù)據(jù)為負(fù),則Y 軸的方向需要改變。
2.2. 將設(shè)備放置在朝左(LEFT)的位置,如表1 中所示。X 軸上讀取的數(shù)據(jù)應(yīng)為±1g (±9.81m/s2),其他兩個軸上的數(shù)據(jù)應(yīng)大約為0。如果X 軸上的數(shù)據(jù)為正,則其方向不需要改變;否則X 軸的方向需要改變。然后,執(zhí)行第4 步判斷Z 軸的方向。
3. 設(shè)備仍然放置在朝上(UP)的位置,并從傳感器中讀取3 個軸的數(shù)據(jù)。此時X 軸上的數(shù)據(jù)應(yīng)為 ±1 g (±9.81m/s2),其他兩個軸上的數(shù)據(jù)大約為0,需要X-Y 交換。
3.1. 在該位置上,如果X 軸的數(shù)據(jù)讀取為+1 g (+9.81m/s2),則X 軸的方向不需要改變;否則需要改變。
3.2. 將設(shè)備放置在向左(LEFT)位置上,如表1 中所示。Y 軸上讀取的數(shù)據(jù)應(yīng)為±1g (±9.81m/s2),其他兩個軸上的數(shù)據(jù)應(yīng)大約為0。如果Y 軸上的數(shù)據(jù)為正,則其方向不需要改變;否則需要改變。然后執(zhí)行第4 步判斷Z 軸的方向。
4. 將設(shè)備放置在正面朝上(FRONT-UP)的位置,并從傳感器中讀取3 軸數(shù)據(jù)。如果 Z軸上的數(shù)據(jù)為+1 g (+9.81m/s2),其他兩個軸上的數(shù)據(jù)大約為0,則Z 軸方向無需改變;如果Z 軸數(shù)據(jù)為-1 g (-9.81m/s2),則Z 軸方向需要改變。
在 Android 系統(tǒng)中,傳感器數(shù)據(jù)由內(nèi)核空間中的Linux 驅(qū)動讀取,然后由HAL 層驅(qū)動發(fā)送至API。分層結(jié)構(gòu)如圖3 所示。因此,傳感器數(shù)據(jù)可以在Linux 驅(qū)動層或在HAL 層上進(jìn)行轉(zhuǎn)換。
圖 3. Android 驅(qū)動架構(gòu)
在 Android HAL 文件中改變 X、Y 和Z 軸的方向
在 HAL 文件中,會有一組宏定義,用于把從傳感器中讀取的加速度數(shù)據(jù)轉(zhuǎn)換為標(biāo)準(zhǔn)單位(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 是一個標(biāo)準(zhǔn)重力加速度值,即9.81m/s2,LSG為一個重力加速度值的最小有效計數(shù)值,例如,MMA8452 在正常模式下的讀數(shù)為1024。因此,CONVERT_A 用于把從加速度傳感器中讀取的數(shù)據(jù),從數(shù)字讀數(shù)轉(zhuǎn)換為標(biāo)準(zhǔn)重力加速度單位。
通過分別修改CONVERT_A_X、CONVERT_A_Y 和CONVERT_A_Z,我們可以輕松地改變X、Y 和Z 軸的方向。如果該軸的方向與系統(tǒng)定義相反,可以使用(-CONVERT_A)來改變其方向。如果方向一致,就使用(CONVERT_A),則保持方向不變。
這個宏定義位于FSL Android 9 (Android 2.2)驅(qū)動程序的HAL文件sensor.c 中。對于FSLAndroid 10 (Android 2.3),您可以在’libsensors’文件夾的HAL 文件Sensor.h 中找到它。
在 Android 2.2 HAL 文件中交換X 軸和Y 軸
在某些情況下,X 和Y 軸必須進(jìn)行交換,以便使傳感器數(shù)據(jù)的坐標(biāo)與系統(tǒng)坐標(biāo)保持一致。
對于 FSL Android 9 (Android 2.2)驅(qū)動程序來說,X 軸和Y 軸的交換非常簡單。首先,在HAL 文件sensor.c 中,在函數(shù)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;
}
然后,根據(jù)如下所示修改代碼:
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 軸會更加復(fù)雜些,因為它具有更復(fù)雜的HAL文件結(jié)構(gòu)。所有HAL 文件都位于文件夾‘libsensors’中。文件AccelSensor.cpp 中的兩個函數(shù)需要修改。
首先,修改函數(shù)AccelSensor()的代碼,如下所示:
if (accel_is_sensor_enabled(SENSOR_TYPE_ACCELEROMETER)) {
mEnabled |= 1<
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;
}
}
然后,修改函數(shù)processEvent()的代碼,如下所示:
void AccelSensor::processEvent(int code, int value)
{
switch (code) {
case EVENT_TYPE_ACCEL_X:
mPendingMask |= 1<mPendingEvents[Accelerometer].acceleration.y = value * CONVERT_A_Y;
break;
case EVENT_TYPE_ACCEL_Y:
mPendingMask |= 1<mPendingEvents[Accelerometer].acceleration.x = value * CONVERT_A_X;
break;
case EVENT_TYPE_ACCEL_Z:
mPendingMask |= 1<mPendingEvents[Accelerometer].acceleration.z = value * CONVERT_A_Z;
break;
}
}
完成后,X 軸和Y 軸的數(shù)據(jù)就互相交換了。
在 Kernel 驅(qū)動文件中交換X 軸和Y 軸
X 軸和Y 軸的數(shù)據(jù)交換可以在底層的Linux 驅(qū)動中,在剛開始讀取傳感器數(shù)據(jù)時實施。通過這種方法,無論傳感器芯片以何種方式安裝在PCB 中,或者使用各種不同類型的傳感器,HAL 文件都可以保持一致。
對于 Android 2.2 和2.3 來說,執(zhí)行該操作的最便捷的方式是修改函數(shù)report_abs()中的代碼。在該函數(shù)中,傳感器數(shù)據(jù)通過調(diào)用函數(shù)mma8452_read_data()讀取,如下所示(當(dāng)使用的傳感器為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 驅(qū)動文件為mma8452.c;對于Android 2.3,驅(qū)動文件是‘hwmon’文件夾中的mxc_mma8452.c。
在 Kernel 驅(qū)動文件中改變 X、Y 和Z 軸的方向
傳感器數(shù)據(jù)的方向也可以在Kernel 驅(qū)動文件中更改。以下帶有注釋的語句可以添加到函數(shù)report_abs()中,從而改變數(shù)據(jù)方向:
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);
總結(jié)
Android 系統(tǒng)已經(jīng)為加速度計定義了坐標(biāo)系統(tǒng),因此用戶必須轉(zhuǎn)換從實際傳感器中讀取的數(shù)據(jù),從而與其保持一致。無論是否需要轉(zhuǎn)換,都應(yīng)檢查X、Y 和Z 軸的方向以及X-Y軸坐標(biāo)。我們可以更改HAL 文件或Kernel 驅(qū)動文件來改變軸的方向,或交換X 和Y 軸,但是不要同時修改HAL 文件和Kernel 驅(qū)動。
參考資料
1. Android Coordinate System
http://developer.android.com/reference/android/hardware/SensorEvent.html
2. Reference Code and User’s Guide on ‘Android MMA8452.tar.gz’.
3. Reference Code and User’s Guide on ‘mma8452_imx51_android10_v100.tar.gz’.
4. Datasheet ‘MMA8452Q.pdf’.