以屏幕的左下方為原點(diǎn)(2d編程的時(shí)候,是以屏幕左上方為原點(diǎn)的,這個(gè)值得注意一下),箭頭指向的方向?yàn)檎?10到10,以浮點(diǎn)數(shù)為等級(jí)單位,想象一下以下情形:
手機(jī)屏幕向上(z軸朝天)水平放置的時(shí)侯,(x,y,z)的值分別為(0,0,10);
手機(jī)屏幕向下(z軸朝地)水平放置的時(shí)侯,(x,y,z)的值分別為(0,0,-10);
手機(jī)屏幕向左側(cè)放(x軸朝天)的時(shí)候,(x,y,z)的值分別為(10,0,0);
手機(jī)豎直(y軸朝天)向上的時(shí)候,(x,y,z)的值分別為(0,10,0);
2013.4.2,今天提交完代碼,指南針的調(diào)試工作可以告一段落了。這段時(shí)間主要做了2項(xiàng)工作,1、寫了一個(gè)自己的函數(shù),在.c文件中去讀acc的input event,因?yàn)樵瓉淼淖x值函數(shù)會(huì)引起驅(qū)動(dòng)資源搶占。2、寫了一個(gè)有效的濾波函數(shù)。濾波函數(shù)我前前后后寫了4個(gè),之前想的很復(fù)雜,今天下午看了一篇論文,試了下,發(fā)現(xiàn)原來有效的濾波函數(shù)如此簡(jiǎn)單,完全沒有技術(shù)含量(取9次、報(bào)一次,去掉最大最小,取平均),如下(其它函數(shù)在分割下之前的版本中已經(jīng)列出,見下文):
//daiyyr add @2013.4.2
int acount, myx[9], myy[9];
//return 0 for collect; 1 for report
int Mean_filter(int16 *bData){
signed short i, x, y, z;
x = bData[1] + (bData[2] 《《 8);
y = bData[3] + (bData[4] 《《 8);
z = bData[5] + (bData[6] 《《 8);//don‘t do this
// printf(“x:%d, y:%d, z:%d\n”,x,y,z);//no z
myx[acount] = x;
myy[acount] = y;
acount++;
if (acount == 9){
signed short maxx = -1000, minx = 1000, maxy = -1000, miny = 1000, avgx = 0, avgy = 0;
acount = 0;
//do sort and average
for (i=0; i《9; i++){
if (maxx 《 myx[i])
maxx = myx[i];
else if (minx 》 myx[i])
minx = myx[i];
avgx += myx[i];
if (maxy 《 myy[i])
maxy = myy[i];
else if (miny 》 myy[i])
miny = myy[i];
avgy += myy[i];
// printf(“avgx:%d, myx[i]:%d, avgy:%d, myy[i]:%d\n”, avgx, myx[i], avgy, myy[i]);
}
avgx = (avgx - maxx - minx) / 7;
avgy = (avgy - maxy - miny) / 7;
// printf(“bdata1:%x, bdata2:%x\n”, bData[1], bData[2]);
bData[1] = avgx & ((int16)255);
bData[2] = avgx 》》 8;
bData[3] = avgy & ((int16)255);
bData[4] = avgy 》》 8;
// printf(“maxx:%d, minx:%d, avgx:%d,avgy:%d; bdata1:%x, bdata2:%x,report!*********************\n”,maxx, minx, avgx, avgy, bData[1], bData[2]);
return 0;
}
return 1;
}
----------------------------下面內(nèi)容為2013.4.2之前--------------------------------------------------------------------------------------
7023Q
https://192.168.0.220:8443/svn/coffee/trunk
驅(qū)動(dòng) coffee/kernel/drivers/misc/akm8975.c
HAL device/cct/common/libsku7sensors/AkmSensor.cpp
HAL特殊線層 device/cct/common/libsku7sensors/ak8975/
在此開啟線程system/core/rootdir
編譯生成的守護(hù)進(jìn)程的可執(zhí)行文件在手機(jī)中的位置:/system/bin/akmd8975
8000R
驅(qū)動(dòng) \\cts-server\sourcecode\rockchip-update\kernel\drivers\input\sensors\compass
sensors/sensor-dev.c
HAL:hardware/rk29/sensor/st/ak8975/…
開啟線程:device/rockchip/rk30sdk/init.rk30board.rc
板子的GPIO腳變了,所以先修改板子配置源文件:
HAL層向服務(wù)層上報(bào)數(shù)據(jù)之前,經(jīng)過以下幾個(gè)流程:
A:開機(jī)運(yùn)行一個(gè)叫akm8975的進(jìn)程。這個(gè)進(jìn)程源代碼位于HAL層。8000R是hardware/rk29/sensor/st/;7023Q是device/cct/common/libsku7sensors/ak8975/
B:當(dāng)指南針應(yīng)用被打開后,通過HAL調(diào)用到驅(qū)動(dòng)的enable函數(shù),設(shè)備開始產(chǎn)生中斷。
C:此時(shí),akm8975這個(gè)進(jìn)程捕獲這個(gè)中斷,讀取驅(qū)動(dòng)獲得的原始數(shù)據(jù),并作一番神秘的修改,具體的修改函數(shù)被封裝于HAL層的…。/ak8975/libak8975/libak8975.a這個(gè)令人蛋疼菊緊的文件中,該文件的存在褻瀆了自由軟件精神,使業(yè)界良心蕩然無存,讓代碼民工情何以堪。
D:之后這個(gè)進(jìn)程呼叫ioctl與內(nèi)核文件搞基,驅(qū)動(dòng)的ioctl去調(diào)用驅(qū)動(dòng)的報(bào)值函數(shù)AKECS_SetYPR,該函數(shù)通過input_report_abs上報(bào)。
E:HAL層通過讀文件/dev/input/compass獲取上報(bào)的值,并作最后的處理,最后報(bào)給服務(wù)層
rbuf[0] = prms-》m_theta; // yaw 航向
rbuf[1] = prms-》m_phi180; // pitch 俯仰角
rbuf[2] = prms-》m_eta90; // roll 翻滾角
該器件最終輸出到應(yīng)用層的大約是這六個(gè)值:
磁場(chǎng)強(qiáng)度X軸、y軸、z軸、航向、俯仰角、翻滾角。其中俯仰角和翻滾角是依據(jù)重力傳感器的值計(jì)算出的結(jié)果
最后調(diào)通的方法是,利用已經(jīng)由可執(zhí)行文件中的秘密函數(shù)計(jì)算出的x和y軸磁感強(qiáng)度值(即磁感線在水平面的投影值的分解值)
rbuf[9] = prms-》m_hvec.u.x; // M_x
rbuf[10] = prms-》m_hvec.u.y; // M_y
用arctan三角函數(shù)算出正北方向與手機(jī)的某個(gè)軸(x或y)的偏移角(實(shí)際上.a文件內(nèi)部也是這樣運(yùn)算的),把角度值賦予:rbuf[0] // yaw,當(dāng)設(shè)備處于水平面的時(shí)候,頂層就是憑借這一個(gè)值來判斷方向的!而設(shè)備若存在俯仰和翻滾角,則根據(jù)另外幾個(gè)數(shù)據(jù)計(jì)算補(bǔ)償。
下面是幾個(gè)可執(zhí)行文件中的關(guān)鍵函數(shù),我用它們架空了.a文件,即自己通過磁感設(shè)備和加速度感應(yīng)設(shè)備計(jì)算6個(gè)上報(bào)的值:三軸磁數(shù)據(jù),航向、俯仰、翻滾三個(gè)方位數(shù)據(jù)。
同時(shí)注意值得正負(fù),習(xí)慣上,確定了設(shè)備的“底部”后,將底部抬起,俯仰角pitch為正,反之為負(fù);將設(shè)備右側(cè)抬起,翻滾角roll為正,反之為負(fù);航向?yàn)樵O(shè)備“縱軸”與正北方向的順時(shí)針偏離角度(yaw小于360°時(shí)順時(shí)針旋轉(zhuǎn)設(shè)備,yaw遞增)。
現(xiàn)在的問題是我的Gsensor——bma020會(huì)頻繁出現(xiàn)大的尖波,這樣造成俯仰角和翻滾角也出現(xiàn)尖波。我在嘗試使用卡曼濾波算法過濾尖波。
頻繁大尖波的原因找到了。我之前一直納悶,為什么當(dāng)我運(yùn)行akmd守護(hù)進(jìn)程時(shí),ACC本身的報(bào)值會(huì)出現(xiàn)尖波影響,硬件上,AKM影響ACC的可能性可以立刻排除。那就是軟件了,我看了ACC的驅(qū)動(dòng),原來,通過input event報(bào)值和open dev/bma020 ioctl()報(bào)值,這兩個(gè)報(bào)值方式調(diào)用的是同一個(gè)函數(shù):int bma020_read_accel_xyz(bma020acc_t * acc)。而這個(gè)函數(shù)沒有用自旋鎖鎖住,所以幾乎可以肯定,當(dāng)兩個(gè)通過不同方式讀acc值的進(jìn)程同時(shí)運(yùn)行時(shí)(ACC本身使用input報(bào)值,AKM通過ioctl讀值),在上述函數(shù)里發(fā)生了內(nèi)存搶占。
解決的方式有兩個(gè),1、給驅(qū)動(dòng)函數(shù)bma020_read_accel_xyz加自旋鎖;2、改變akmd讀acc值的方式,通過input方式讀值。
這個(gè)系統(tǒng)的設(shè)定是,不輪AKMD是否運(yùn)行,ACC驅(qū)動(dòng)不停地向input報(bào)值,所以相比用ioctl去讀值,akmd去讀ACC的input不會(huì)增加內(nèi)核負(fù)擔(dān)。
下面兩個(gè)函數(shù)是用c語言寫的讀取acc的input event值的函數(shù):
//daiyyr add @2013.03.30, to getting acc data by input. begin
static int accOpened = 0, fd;
extern int16_t acc_data[3]; //defined in main.c
int getAccData(void){
float fData[3];
int err = 1;
if (!accOpened){
fd = openAccInputEvent();
if (fd 《 0){
printf(“open acc input event failed\n”);
return -1;
}
accOpened = 1;
}
struct input_event event;
while(err 》 0){
err = read(fd, &event, sizeof(event));
if (err 《 0){
printf(“read err, fd=%d,err=%d\n”, fd, err);
return -2;
}
printf(“dy-code:%d, value:%d\n”,event.code, event.value);
if(event.type == 0){
printf(“dy-data[0]:%d, data[1]:%d, data[2]:%d\n”,acc_data[0], acc_data[1], acc_data[2]);
return 0;
}
if(event.type == 2){
switch (event.code){
case 3:
acc_data[0] = event.value;
continue;
case 4:
acc_data[1] = event.value;
continue;
case 5:
acc_data[2] = event.value;
continue;
}
}
}
return 0;
}
int openAccInputEvent(void){
char *str, *p, dev[60];
int i, fd = -1;
str = “/dev/input/event”;
strcpy(dev, str);
for(i=0;i《20;i++){
p = dev + strlen(dev);
*p++ = i+48;
*p = ’\0‘;
// printf(“mybuffer:%s\n”, dev);
fd = open(dev,0);
if (fd》=0) {
char name[80];
if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) 《 1) {
name[0] = ’\0‘;
}
if (!strcmp(name, “acc”)) {
printf(“open dev succeed\n”);
return fd;
} else {
close(fd);
p--;
*p = ’\0‘;
fd = -1;
}
}
else{
printf(“err:open dev failed dev:%s\n”, dev);
return -1;
}
}
return fd;
}
//daiyyr add end
主循環(huán):
void MeasureSNGLoop(AK8975PRMS* prms)
{
BYTE i2cData[AKSC_BDATA_SIZE];
int16 i;
int16 bData[AKSC_BDATA_SIZE]; // Measuring block data
int16 ret;
int32 ch;
int32 doze;
int32_t delay;
AKMD_INTERVAL interval;
struct timespec tsstart, tsend;
if (openKey() 《 0) {
DBGPRINT(DBG_LEVEL1,
“%s:%d Error.\n”, __FUNCTION__, __LINE__);
return;
}
if (openFormation() 《 0) {
DBGPRINT(DBG_LEVEL1,
“%s:%d Error.\n”, __FUNCTION__, __LINE__);
return;
}
// Get initial interval
GetValidInterval(CSPEC_INTERVAL_SNG, &interval);
// Initialize
if(InitAK8975_Measure(prms) != AKD_SUCCESS){
return;
}
while(TRUE){
// Get start time
if (clock_gettime(CLOCK_REALTIME, &tsstart) 《 0) {
DBGPRINT(DBG_LEVEL1,
“%s:%d Error.\n”, __FUNCTION__, __LINE__);
return;
}
// Set to SNG measurement pattern (Set CNTL register)
if (AKD_SetMode(AK8975_MODE_SNG_MEASURE) != AKD_SUCCESS) {
DBGPRINT(DBG_LEVEL1,
“%s:%d Error.\n”, __FUNCTION__, __LINE__);
return;
}
// 。! : 獲取 M snesor 的原始數(shù)據(jù)。 這里可能阻塞。
// Get measurement data from AK8975
// ST1 + (HXL + HXH) + (HYL + HYH) + (HZL + HZH) + ST2
// = 1 + (1 + 1) + (1 + 1) + (1 + 1) + 1 = 8 bytes
if (AKD_GetMagneticData(i2cData) != AKD_SUCCESS) {
DBGPRINT(DBG_LEVEL1,
“%s:%d Error.\n”, __FUNCTION__, __LINE__);
return;
}
// Copy to local variable
// DBGPRINT(DBG_LEVEL3, “%s: bData(Hex)=”, __FUNCTION__);
printf(“dyyr-”);
for(i=0; i《AKSC_BDATA_SIZE; i++){
bData[i] = i2cData[i];
// DBGPRINT(DBG_LEVEL3, “%02x,”, bData[i]);
printf(“%02x,”, bData[i]);
}
printf(“\n”);
// DBGPRINT(DBG_LEVEL3, “\n”);
D_WHEN_REPEAT(100,
“raw mag x : %d, raw mag y : %d, raw mag z : %d.”,
(signed short)(bData[1] + (bData[2] 《《 8) ),
?。╯igned short)(bData[3] + (bData[4] 《《 8) ),
?。╯igned short)(bData[5] + (bData[6] 《《 8) ) );
// 。! :
// Get acceelration sensor’s measurement data.
if (GetAccVec(prms) != AKRET_PROC_SUCCEED) {
return;
}
/*
DBGPRINT(DBG_LEVEL3,
“%s: acc(Hex)=%02x,%02x,%02x\n”, __FUNCTION__,
prms-》m_avec.u.x, prms-》m_avec.u.y, prms-》m_avec.u.z);
*/
//printf(“dyyr-MeasuringEventProcess”);
ret = MeasuringEventProcess(
bData,
prms,
getFormation(),
interval.decimator,
CSPEC_CNTSUSPEND_SNG
?。?
// Check the return value
if(ret == AKRET_PROC_SUCCEED){
if(prms-》m_cntSuspend 》 0){
// Show message
DBGPRINT(DBG_LEVEL2,
“Suspend cycle count = %d\n”, prms-》m_cntSuspend);
}
else if (prms-》m_callcnt 《= 1){
// Check interval
if (AKD_GetDelay(&delay) != AKD_SUCCESS) {
DBGPRINT(DBG_LEVEL1,
“%s:%d Error.\n”, __FUNCTION__, __LINE__);
} else {
GetValidInterval(delay, &interval);
}
}
//printf(“dyyr- measureresulthook\n”);
// Display(or dispatch) the result.
Disp_MeasurementResultHook(prms);
}
//下面幾個(gè)是位于main.c 的報(bào)值函數(shù)和我的數(shù)值處理函數(shù)
/*!
Daiyyr@2013.03.29
Get acc data and convert to pitch and roll orientation
acc_data: acc data.
pitch: pitch orientation to report
roll: roll orientation to report
獲取加速度數(shù)據(jù)并轉(zhuǎn)換為俯仰角和翻滾角
acc_data:存儲(chǔ)加速度數(shù)據(jù)
pitch:將上報(bào)的俯仰角
roll:將上報(bào)的翻滾角
*/
int16_t acc_data[3];
int acc2pitch_roll(int *pitch, int *roll)
{
if(getAccData() 《 0)
return -1;
*pitch = acc_data[2] 》 0 ? (acc_data[0] 》 0 ? -11520+acc_data[0]*64/264*90 : 11520+acc_data[0]*64/248*90) : (acc_data[0] 》 0 ? -acc_data[0]*64/264*90 : -acc_data[0]*64/248*90);
*roll = acc_data[1] 》 0 ? acc_data[1]*64/242*90 : acc_data[1]*64/273*90;
return 0;
}
/*!
Daiyyr@2013.03.29
Calibration for x & y axis magnetic data.
*/
int xmax = 1, ymax = 1, xmin = 0, ymin = 0;
int mag_x_y_calibration(int *x, int *y){
int xsf, ysf, xoff, yoff;
// printf(“xy:%d,%d\n”, *x, *y);
if(*x 》 xmax)
xmax = *x;
else if(*x 《 xmin)
xmin = *x;
if(*y 》 ymax)
ymax = *y;
else if(*y 《 ymin)
ymin = *y;
xsf = 1 》 (ymax-ymin)/(2*(xmax-ymin)) ? 1 : (ymax-ymin)/(2*(xmax-ymin));
ysf = 1 》 (xmax-ymin)/(2*(ymax-ymin)) ? 1 : (xmax-ymin)/(2*(ymax-ymin));
xoff = ((xmax-xmin)/2-xmax)*xsf;
yoff = ((ymax-ymin)/2-ymax)*ysf;
// printf(“xoff:%d, xsf:%d\n”, xoff, xsf);
*x = xsf + *x + xoff;
*y = ysf + *y + yoff;
// printf(“hhll:%d,%d,%d,%d\n”, xmax, ymax, xmin, ymin);
return 0;
}
int16_t acc_data[3];
void Disp_MeasurementResultHook(AK8975PRMS * prms)
{
int err;
int16 acc[3]; /* 將緩存 acc sensor 返回的數(shù)據(jù)。 */
if (!s_opmode) {
int rbuf[12] = { 0 };
// rbuf[0] = prms-》m_theta; // yaw
// rbuf[1] = prms-》m_phi180; // pitch
// rbuf[2] = prms-》m_eta90; // roll
// rbuf[6] = prms-》m_avec.u.x; // G_Sensor x
// rbuf[7] = prms-》m_avec.u.y; // G_Sensor y
// rbuf[8] = prms-》m_avec.u.z; // G_Sensor z
acc2pitch_roll(&rbuf[1], &rbuf[2]);
rbuf[3] = 25; // tmp (AK8975 doesn‘t have temperature sensor)
rbuf[4] = prms-》m_hdst; // m_stat
rbuf[5] = 3; // g_stat
rbuf[9] = prms-》m_hvec.u.x; // M_x
rbuf[10] = prms-》m_hvec.u.y; // M_y
mag_x_y_calibration(&rbuf[9], &rbuf[10]);
rbuf[11] = prms-》m_hvec.u.z; // M_z
rbuf[0] = axis2angle(rbuf[10], -rbuf[9]); // yaw
//printf(“pitch=%d, roll=%d,\n”, rbuf[1]/64, rbuf[2]/64);
/* 。! : 將計(jì)算得到的結(jié)果回寫到驅(qū)動(dòng)。 */
err=ioctl(g_file, ECS_IOCTL_SET_YPR, &rbuf); // 之后, 驅(qū)動(dòng)會(huì)將該數(shù)據(jù)上報(bào) sensor HAL.
}
/* 否則, 。.. */
else {
Disp_MeasurementResult(prms);
}
}
下面是用三角函數(shù)求偏移角度的函數(shù)
/*!
返回地磁感線在水平面的投影與【設(shè)備水平放置時(shí)y軸】的夾角 Daiyyr@2013.02.23
*/
int axis2angle(int x, int y)
{
double dx = x, dy = y;
double angle = 180/3.1415*atan2(dx, dy);
angle = angle 》= 0 ? angle : (angle+360);
// printf(“dyyr-angle: %f\n”, angle);
return (int)(angle*64);
}