diff options
Diffstat (limited to 'libsensors/akmdfs/AKFS_Measure.c')
-rw-r--r-- | libsensors/akmdfs/AKFS_Measure.c | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/libsensors/akmdfs/AKFS_Measure.c b/libsensors/akmdfs/AKFS_Measure.c new file mode 100644 index 0000000..84c0843 --- /dev/null +++ b/libsensors/akmdfs/AKFS_Measure.c @@ -0,0 +1,410 @@ +/****************************************************************************** + * + * Copyright (C) 2012 Asahi Kasei Microdevices Corporation, Japan + * + * 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 copy 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. + * + ******************************************************************************/ +#ifdef WIN32 +#include "AK8975_LinuxDriver.h" +#else +#include "AK8975Driver.h" +#endif + +#include "AKFS_Measure.h" +#include "AKFS_Disp.h" +#include "AKFS_APIs.h" + +/*! + Read sensitivity adjustment data from fuse ROM. + @return If data are read successfully, the return value is #AKM_SUCCESS. + Otherwise the return value is #AKM_FAIL. + @param[out] regs The read ASA values. When this function succeeds, ASAX value + is saved in regs[0], ASAY is saved in regs[1], ASAZ is saved in regs[2]. + */ +int16 AKFS_ReadAK8975FUSEROM( + uint8 regs[3] +) +{ + /* Set to FUSE ROM access mode */ + if (AKD_SetMode(AK8975_MODE_FUSE_ACCESS) != AKD_SUCCESS) { + AKMERROR; + return AKM_FAIL; + } + + /* Read values. ASAX, ASAY, ASAZ */ + if (AKD_RxData(AK8975_FUSE_ASAX, regs, 3) != AKD_SUCCESS) { + AKMERROR; + return AKM_FAIL; + } + + /* Set to PowerDown mode */ + if (AKD_SetMode(AK8975_MODE_POWERDOWN) != AKD_SUCCESS) { + AKMERROR; + return AKM_FAIL; + } + + AKMDEBUG(DBG_LEVEL2, "%s: asa(dec)=%d,%d,%d\n", + __FUNCTION__, regs[0], regs[1], regs[2]); + + return AKM_SUCCESS; +} + +/*! + Carry out self-test. + @return If this function succeeds, the return value is #AKM_SUCCESS. + Otherwise the return value is #AKM_FAIL. + */ +int16 AKFS_SelfTest(void) +{ + BYTE i2cData[SENSOR_DATA_SIZE]; + BYTE asa[3]; + AKFLOAT hdata[3]; + int16 ret; + + /* Set to FUSE ROM access mode */ + if (AKD_SetMode(AK8975_MODE_FUSE_ACCESS) != AKD_SUCCESS) { + AKMERROR; + return AKM_FAIL; + } + + /* Read values from ASAX to ASAZ */ + if (AKD_RxData(AK8975_FUSE_ASAX, asa, 3) != AKD_SUCCESS) { + AKMERROR; + return AKM_FAIL; + } + + /* Set to PowerDown mode */ + if (AKD_SetMode(AK8975_MODE_POWERDOWN) != AKD_SUCCESS) { + AKMERROR; + return AKM_FAIL; + } + + /* Set to self-test mode */ + i2cData[0] = 0x40; + if (AKD_TxData(AK8975_REG_ASTC, i2cData, 1) != AKD_SUCCESS) { + AKMERROR; + return AKM_FAIL; + } + + /* Set to Self-test mode */ + if (AKD_SetMode(AK8975_MODE_SELF_TEST) != AKD_SUCCESS) { + AKMERROR; + return AKM_FAIL; + } + + /* + Wait for DRDY pin changes to HIGH. + Get measurement data from AK8975 + */ + if (AKD_GetMagneticData(i2cData) != AKD_SUCCESS) { + AKMERROR; + return AKM_FAIL; + } + + hdata[0] = AK8975_HDATA_CONVERTER(i2cData[2], i2cData[1], asa[0]); + hdata[1] = AK8975_HDATA_CONVERTER(i2cData[4], i2cData[3], asa[1]); + hdata[2] = AK8975_HDATA_CONVERTER(i2cData[6], i2cData[5], asa[2]); + + /* Test */ + ret = 1; + if ((hdata[0] < AK8975_SELFTEST_MIN_X) || + (AK8975_SELFTEST_MAX_X < hdata[0])) { + ret = 0; + } + if ((hdata[1] < AK8975_SELFTEST_MIN_Y) || + (AK8975_SELFTEST_MAX_Y < hdata[1])) { + ret = 0; + } + if ((hdata[2] < AK8975_SELFTEST_MIN_Z) || + (AK8975_SELFTEST_MAX_Z < hdata[2])) { + ret = 0; + } + + AKMDEBUG(DBG_LEVEL2, "Test(%s):%8.2f, %8.2f, %8.2f\n", + (ret ? "Success" : "fail"), hdata[0], hdata[1], hdata[2]); + + if (ret) { + return AKM_SUCCESS; + } else { + return AKM_FAIL; + } +} + +/*! + This function calculate the duration of sleep for maintaining + the loop keep the period. + This function calculates "minimum - (end - start)". + @return The result of above equation in nanosecond. + @param end The time of after execution. + @param start The time of before execution. + @param minimum Loop period of each execution. + */ +struct timespec AKFS_CalcSleep( + const struct timespec* end, + const struct timespec* start, + const int64_t minimum +) +{ + int64_t endL; + int64_t startL; + int64_t diff; + + struct timespec ret; + + endL = (end->tv_sec * 1000000000) + end->tv_nsec; + startL = (start->tv_sec * 1000000000) + start->tv_nsec; + diff = minimum; + + diff -= (endL - startL); + + /* Don't allow negative value */ + if (diff < 0) { + diff = 0; + } + + /* Convert to timespec */ + if (diff > 1000000000) { + ret.tv_sec = diff / 1000000000; + ret.tv_nsec = diff % 1000000000; + } else { + ret.tv_sec = 0; + ret.tv_nsec = diff; + } + return ret; +} + +/*! + Get interval of each sensors from device driver. + @return If this function succeeds, the return value is #AKM_SUCCESS. + Otherwise the return value is #AKM_FAIL. + @param flag This variable indicates what sensor frequency is updated. + @param minimum This value show the minimum loop period in all sensors. + */ +int16 AKFS_GetInterval( + uint16* flag, + int64_t* minimum +) +{ + /* Accelerometer, Magnetometer, Orientation */ + /* Delay is in nano second unit. */ + /* Negative value means the sensor is disabled.*/ + int64_t delay[AKM_NUM_SENSORS]; + int i; + + if (AKD_GetDelay(delay) < 0) { + AKMERROR; + return AKM_FAIL; + } + AKMDATA(AKMDATA_GETINTERVAL,"delay[A,M,O]=%lld,%lld,%lld\n", + delay[0], delay[1], delay[2]); + + /* update */ + *minimum = 1000000000; + *flag = 0; + for (i=0; i<AKM_NUM_SENSORS; i++) { + /* Set flag */ + if (delay[i] >= 0) { + *flag |= 1 << i; + if (*minimum > delay[i]) { + *minimum = delay[i]; + } + } + } + return AKM_SUCCESS; +} + +/*! + If this program run as console mode, measurement result will be displayed + on console terminal. + @return If this function succeeds, the return value is #AKM_SUCCESS. + Otherwise the return value is #AKM_FAIL. + */ +void AKFS_OutputResult( + const uint16 flag, + const AKSENSOR_DATA* acc, + const AKSENSOR_DATA* mag, + const AKSENSOR_DATA* ori +) +{ + int buf[YPR_DATA_SIZE]; + + /* Store to buffer */ + buf[0] = flag; /* Data flag */ + buf[1] = CONVERT_ACC(acc->x); /* Ax */ + buf[2] = CONVERT_ACC(acc->y); /* Ay */ + buf[3] = CONVERT_ACC(acc->z); /* Az */ + buf[4] = acc->status; /* Acc status */ + buf[5] = CONVERT_MAG(mag->x); /* Mx */ + buf[6] = CONVERT_MAG(mag->y); /* My */ + buf[7] = CONVERT_MAG(mag->z); /* Mz */ + buf[8] = mag->status; /* Mag status */ + buf[9] = CONVERT_ORI(ori->x); /* yaw */ + buf[10] = CONVERT_ORI(ori->y); /* pitch */ + buf[11] = CONVERT_ORI(ori->z); /* roll */ + + if (g_opmode & OPMODE_CONSOLE) { + /* Console mode */ + Disp_Result(buf); + } + + /* Set result to driver */ + AKD_SetYPR(buf); +} + +/*! + This is the main routine of measurement. + */ +void AKFS_MeasureLoop(void) +{ + BYTE i2cData[SENSOR_DATA_SIZE]; /* ST1 ~ ST2 */ + int16 mag[3]; + int16 mstat; + int16 acc[3]; + struct timespec tsstart= {0, 0}; + struct timespec tsend = {0, 0}; + struct timespec doze; + int64_t minimum; + uint16 flag; + AKSENSOR_DATA sv_acc; + AKSENSOR_DATA sv_mag; + AKSENSOR_DATA sv_ori; + AKFLOAT tmpx, tmpy, tmpz; + int16 tmp_accuracy; + + minimum = -1; + +#ifdef WIN32 + clock_init_time(); +#endif + + /* Initialize library functions and device */ + if (AKFS_Start(CSPEC_SETTING_FILE) != AKM_SUCCESS) { + AKMERROR; + goto MEASURE_END; + } + + while (g_stopRequest != AKM_TRUE) { + /* Beginning time */ + if (clock_gettime(CLOCK_MONOTONIC, &tsstart) < 0) { + AKMERROR; + goto MEASURE_END; + } + + /* Get interval */ + if (AKFS_GetInterval(&flag, &minimum) != AKM_SUCCESS) { + AKMERROR; + goto MEASURE_END; + } + + if ((flag & ACC_DATA_READY) || (flag & ORI_DATA_READY)) { + /* Get accelerometer */ + if (AKD_GetAccelerationData(acc) != AKD_SUCCESS) { + AKMERROR; + goto MEASURE_END; + } + + /* Calculate accelerometer vector */ + if (AKFS_Get_ACCELEROMETER(acc, 0, &tmpx, &tmpy, &tmpz, &tmp_accuracy) == AKM_SUCCESS) { + sv_acc.x = tmpx; + sv_acc.y = tmpy; + sv_acc.z = tmpz; + sv_acc.status = tmp_accuracy; + } else { + flag &= ~ACC_DATA_READY; + flag &= ~ORI_DATA_READY; + } + } + + if ((flag & MAG_DATA_READY) || (flag & ORI_DATA_READY)) { + /* Set to measurement mode */ + if (AKD_SetMode(AK8975_MODE_SNG_MEASURE) != AKD_SUCCESS) { + AKMERROR; + goto MEASURE_END; + } + + /* Wait for DRDY and get data from device */ + if (AKD_GetMagneticData(i2cData) != AKD_SUCCESS) { + AKMERROR; + goto MEASURE_END; + } + /* raw data to x,y,z value */ + mag[0] = (int)((int16_t)(i2cData[2]<<8)+((int16_t)i2cData[1])); + mag[1] = (int)((int16_t)(i2cData[4]<<8)+((int16_t)i2cData[3])); + mag[2] = (int)((int16_t)(i2cData[6]<<8)+((int16_t)i2cData[5])); + mstat = i2cData[0] | i2cData[7]; + + AKMDATA(AKMDATA_BDATA, + "bData=%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X\n", + i2cData[0], i2cData[1], i2cData[2], i2cData[3], + i2cData[4], i2cData[5], i2cData[6], i2cData[7]); + + /* Calculate magnetic field vector */ + if (AKFS_Get_MAGNETIC_FIELD(mag, mstat, &tmpx, &tmpy, &tmpz, &tmp_accuracy) == AKM_SUCCESS) { + sv_mag.x = tmpx; + sv_mag.y = tmpy; + sv_mag.z = tmpz; + sv_mag.status = tmp_accuracy; + } else { + flag &= ~MAG_DATA_READY; + flag &= ~ORI_DATA_READY; + } + } + + if (flag & ORI_DATA_READY) { + if (AKFS_Get_ORIENTATION(&tmpx, &tmpy, &tmpz, &tmp_accuracy) == AKM_SUCCESS) { + sv_ori.x = tmpx; + sv_ori.y = tmpy; + sv_ori.z = tmpz; + sv_ori.status = tmp_accuracy; + } else { + flag &= ~ORI_DATA_READY; + } + } + + /* Output result */ + AKFS_OutputResult(flag, &sv_acc, &sv_mag, &sv_ori); + + /* Ending time */ + if (clock_gettime(CLOCK_MONOTONIC, &tsend) < 0) { + AKMERROR; + goto MEASURE_END; + } + + /* Calculate duration */ + doze = AKFS_CalcSleep(&tsend, &tsstart, minimum); + AKMDATA(AKMDATA_LOOP, "Sleep: %6.2f msec\n", (doze.tv_nsec/1000000.0f)); + nanosleep(&doze, NULL); + +#ifdef WIN32 + if (_kbhit()) { + _getch(); + break; + } +#endif + } + +MEASURE_END: + /* Set to PowerDown mode */ + if (AKD_SetMode(AK8975_MODE_POWERDOWN) != AKD_SUCCESS) { + AKMERROR; + return; + } + + /* Save parameters */ + if (AKFS_Stop(CSPEC_SETTING_FILE) != AKM_SUCCESS) { + AKMERROR; + } +} + + |