1 /****************************************************************************** 2 * $Id: AKFS_Measure.c 580 2012-03-29 09:56:21Z yamada.rj $ 3 ****************************************************************************** 4 * 5 * Copyright (C) 2012 Asahi Kasei Microdevices Corporation, Japan 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 #include <inttypes.h> 21 22 #ifdef WIN32 23 #include "AK8975_LinuxDriver.h" 24 #else 25 #include "AK8975Driver.h" 26 #endif 27 28 #include "AKFS_Measure.h" 29 #include "AKFS_Disp.h" 30 #include "AKFS_APIs.h" 31 32 /*! 33 Read sensitivity adjustment data from fuse ROM. 34 @return If data are read successfully, the return value is #AKM_SUCCESS. 35 Otherwise the return value is #AKM_FAIL. 36 @param[out] regs The read ASA values. When this function succeeds, ASAX value 37 is saved in regs[0], ASAY is saved in regs[1], ASAZ is saved in regs[2]. 38 */ 39 int16 AKFS_ReadAK8975FUSEROM( 40 uint8 regs[3] 41 ) 42 { 43 /* Set to FUSE ROM access mode */ 44 if (AKD_SetMode(AK8975_MODE_FUSE_ACCESS) != AKD_SUCCESS) { 45 AKMERROR; 46 return AKM_FAIL; 47 } 48 49 /* Read values. ASAX, ASAY, ASAZ */ 50 if (AKD_RxData(AK8975_FUSE_ASAX, regs, 3) != AKD_SUCCESS) { 51 AKMERROR; 52 return AKM_FAIL; 53 } 54 55 /* Set to PowerDown mode */ 56 if (AKD_SetMode(AK8975_MODE_POWERDOWN) != AKD_SUCCESS) { 57 AKMERROR; 58 return AKM_FAIL; 59 } 60 61 AKMDEBUG(DBG_LEVEL2, "%s: asa(dec)=%d,%d,%d\n", 62 __FUNCTION__, regs[0], regs[1], regs[2]); 63 64 return AKM_SUCCESS; 65 } 66 67 /*! 68 Carry out self-test. 69 @return If this function succeeds, the return value is #AKM_SUCCESS. 70 Otherwise the return value is #AKM_FAIL. 71 */ 72 int16 AKFS_SelfTest(void) 73 { 74 BYTE i2cData[SENSOR_DATA_SIZE]; 75 BYTE asa[3]; 76 AKFLOAT hdata[3]; 77 int16 ret; 78 79 /* Set to FUSE ROM access mode */ 80 if (AKD_SetMode(AK8975_MODE_FUSE_ACCESS) != AKD_SUCCESS) { 81 AKMERROR; 82 return AKM_FAIL; 83 } 84 85 /* Read values from ASAX to ASAZ */ 86 if (AKD_RxData(AK8975_FUSE_ASAX, asa, 3) != AKD_SUCCESS) { 87 AKMERROR; 88 return AKM_FAIL; 89 } 90 91 /* Set to PowerDown mode */ 92 if (AKD_SetMode(AK8975_MODE_POWERDOWN) != AKD_SUCCESS) { 93 AKMERROR; 94 return AKM_FAIL; 95 } 96 97 /* Set to self-test mode */ 98 i2cData[0] = 0x40; 99 if (AKD_TxData(AK8975_REG_ASTC, i2cData, 1) != AKD_SUCCESS) { 100 AKMERROR; 101 return AKM_FAIL; 102 } 103 104 /* Set to Self-test mode */ 105 if (AKD_SetMode(AK8975_MODE_SELF_TEST) != AKD_SUCCESS) { 106 AKMERROR; 107 return AKM_FAIL; 108 } 109 110 /* 111 Wait for DRDY pin changes to HIGH. 112 Get measurement data from AK8975 113 */ 114 if (AKD_GetMagneticData(i2cData) != AKD_SUCCESS) { 115 AKMERROR; 116 return AKM_FAIL; 117 } 118 119 hdata[0] = AK8975_HDATA_CONVERTER(i2cData[2], i2cData[1], asa[0]); 120 hdata[1] = AK8975_HDATA_CONVERTER(i2cData[4], i2cData[3], asa[1]); 121 hdata[2] = AK8975_HDATA_CONVERTER(i2cData[6], i2cData[5], asa[2]); 122 123 /* Test */ 124 ret = 1; 125 if ((hdata[0] < AK8975_SELFTEST_MIN_X) || 126 (AK8975_SELFTEST_MAX_X < hdata[0])) { 127 ret = 0; 128 } 129 if ((hdata[1] < AK8975_SELFTEST_MIN_Y) || 130 (AK8975_SELFTEST_MAX_Y < hdata[1])) { 131 ret = 0; 132 } 133 if ((hdata[2] < AK8975_SELFTEST_MIN_Z) || 134 (AK8975_SELFTEST_MAX_Z < hdata[2])) { 135 ret = 0; 136 } 137 138 AKMDEBUG(DBG_LEVEL2, "Test(%s):%8.2f, %8.2f, %8.2f\n", 139 (ret ? "Success" : "fail"), hdata[0], hdata[1], hdata[2]); 140 141 if (ret) { 142 return AKM_SUCCESS; 143 } else { 144 return AKM_FAIL; 145 } 146 } 147 148 /*! 149 This function calculate the duration of sleep for maintaining 150 the loop keep the period. 151 This function calculates "minimum - (end - start)". 152 @return The result of above equation in nanosecond. 153 @param end The time of after execution. 154 @param start The time of before execution. 155 @param minimum Loop period of each execution. 156 */ 157 struct timespec AKFS_CalcSleep( 158 const struct timespec* end, 159 const struct timespec* start, 160 const int64_t minimum 161 ) 162 { 163 int64_t endL; 164 int64_t startL; 165 int64_t diff; 166 167 struct timespec ret; 168 169 endL = (end->tv_sec * 1000000000) + end->tv_nsec; 170 startL = (start->tv_sec * 1000000000) + start->tv_nsec; 171 diff = minimum; 172 173 diff -= (endL - startL); 174 175 /* Don't allow negative value */ 176 if (diff < 0) { 177 diff = 0; 178 } 179 180 /* Convert to timespec */ 181 if (diff > 1000000000) { 182 ret.tv_sec = diff / 1000000000; 183 ret.tv_nsec = diff % 1000000000; 184 } else { 185 ret.tv_sec = 0; 186 ret.tv_nsec = diff; 187 } 188 return ret; 189 } 190 191 /*! 192 Get interval of each sensors from device driver. 193 @return If this function succeeds, the return value is #AKM_SUCCESS. 194 Otherwise the return value is #AKM_FAIL. 195 @param flag This variable indicates what sensor frequency is updated. 196 @param minimum This value show the minimum loop period in all sensors. 197 */ 198 int16 AKFS_GetInterval( 199 uint16* flag, 200 int64_t* minimum 201 ) 202 { 203 /* Accelerometer, Magnetometer, Orientation */ 204 /* Delay is in nano second unit. */ 205 /* Negative value means the sensor is disabled.*/ 206 int64_t delay[AKM_NUM_SENSORS]; 207 int i; 208 209 if (AKD_GetDelay(delay) < 0) { 210 AKMERROR; 211 return AKM_FAIL; 212 } 213 AKMDATA(AKMDATA_GETINTERVAL, 214 "delay[A,M,O]=%" PRIi64 ",%" PRIi64 ",%" PRIi64 "\n", 215 delay[0], delay[1], delay[2]); 216 217 /* update */ 218 *minimum = 1000000000; 219 *flag = 0; 220 for (i=0; i<AKM_NUM_SENSORS; i++) { 221 /* Set flag */ 222 if (delay[i] >= 0) { 223 *flag |= 1 << i; 224 if (*minimum > delay[i]) { 225 *minimum = delay[i]; 226 } 227 } 228 } 229 return AKM_SUCCESS; 230 } 231 232 /*! 233 If this program run as console mode, measurement result will be displayed 234 on console terminal. 235 @return If this function succeeds, the return value is #AKM_SUCCESS. 236 Otherwise the return value is #AKM_FAIL. 237 */ 238 void AKFS_OutputResult( 239 const uint16 flag, 240 const AKSENSOR_DATA* acc, 241 const AKSENSOR_DATA* mag, 242 const AKSENSOR_DATA* ori 243 ) 244 { 245 int buf[YPR_DATA_SIZE]; 246 247 /* Store to buffer */ 248 buf[0] = flag; /* Data flag */ 249 buf[1] = CONVERT_ACC(acc->x); /* Ax */ 250 buf[2] = CONVERT_ACC(acc->y); /* Ay */ 251 buf[3] = CONVERT_ACC(acc->z); /* Az */ 252 buf[4] = acc->status; /* Acc status */ 253 buf[5] = CONVERT_MAG(mag->x); /* Mx */ 254 buf[6] = CONVERT_MAG(mag->y); /* My */ 255 buf[7] = CONVERT_MAG(mag->z); /* Mz */ 256 buf[8] = mag->status; /* Mag status */ 257 buf[9] = CONVERT_ORI(ori->x); /* yaw */ 258 buf[10] = CONVERT_ORI(ori->y); /* pitch */ 259 buf[11] = CONVERT_ORI(ori->z); /* roll */ 260 261 if (g_opmode & OPMODE_CONSOLE) { 262 /* Console mode */ 263 Disp_Result(buf); 264 } 265 266 /* Set result to driver */ 267 AKD_SetYPR(buf); 268 } 269 270 /*! 271 This is the main routine of measurement. 272 */ 273 void AKFS_MeasureLoop(void) 274 { 275 BYTE i2cData[SENSOR_DATA_SIZE]; /* ST1 ~ ST2 */ 276 int16 mag[3]; 277 int16 mstat; 278 int16 acc[3]; 279 struct timespec tsstart= {0, 0}; 280 struct timespec tsend = {0, 0}; 281 struct timespec doze; 282 int64_t minimum; 283 uint16 flag; 284 AKSENSOR_DATA sv_acc; 285 AKSENSOR_DATA sv_mag; 286 AKSENSOR_DATA sv_ori; 287 AKFLOAT tmpx, tmpy, tmpz; 288 int16 tmp_accuracy; 289 290 minimum = -1; 291 292 #ifdef WIN32 293 clock_init_time(); 294 #endif 295 296 /* Initialize library functions and device */ 297 if (AKFS_Start(CSPEC_SETTING_FILE) != AKM_SUCCESS) { 298 AKMERROR; 299 goto MEASURE_END; 300 } 301 302 while (g_stopRequest != AKM_TRUE) { 303 /* Beginning time */ 304 if (clock_gettime(CLOCK_MONOTONIC, &tsstart) < 0) { 305 AKMERROR; 306 goto MEASURE_END; 307 } 308 309 /* Get interval */ 310 if (AKFS_GetInterval(&flag, &minimum) != AKM_SUCCESS) { 311 AKMERROR; 312 goto MEASURE_END; 313 } 314 315 if ((flag & ACC_DATA_READY) || (flag & ORI_DATA_READY)) { 316 /* Get accelerometer */ 317 if (AKD_GetAccelerationData(acc) != AKD_SUCCESS) { 318 AKMERROR; 319 goto MEASURE_END; 320 } 321 322 /* Calculate accelerometer vector */ 323 if (AKFS_Get_ACCELEROMETER(acc, 0, &tmpx, &tmpy, &tmpz, &tmp_accuracy) == AKM_SUCCESS) { 324 sv_acc.x = tmpx; 325 sv_acc.y = tmpy; 326 sv_acc.z = tmpz; 327 sv_acc.status = tmp_accuracy; 328 } else { 329 flag &= ~ACC_DATA_READY; 330 flag &= ~ORI_DATA_READY; 331 } 332 } 333 334 if ((flag & MAG_DATA_READY) || (flag & ORI_DATA_READY)) { 335 /* Set to measurement mode */ 336 if (AKD_SetMode(AK8975_MODE_SNG_MEASURE) != AKD_SUCCESS) { 337 AKMERROR; 338 goto MEASURE_END; 339 } 340 341 /* Wait for DRDY and get data from device */ 342 if (AKD_GetMagneticData(i2cData) != AKD_SUCCESS) { 343 AKMERROR; 344 goto MEASURE_END; 345 } 346 /* raw data to x,y,z value */ 347 mag[0] = (int)((int16_t)(i2cData[2]<<8)+((int16_t)i2cData[1])); 348 mag[1] = (int)((int16_t)(i2cData[4]<<8)+((int16_t)i2cData[3])); 349 mag[2] = (int)((int16_t)(i2cData[6]<<8)+((int16_t)i2cData[5])); 350 mstat = i2cData[0] | i2cData[7]; 351 352 AKMDATA(AKMDATA_BDATA, 353 "bData=%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X\n", 354 i2cData[0], i2cData[1], i2cData[2], i2cData[3], 355 i2cData[4], i2cData[5], i2cData[6], i2cData[7]); 356 357 /* Calculate magnetic field vector */ 358 if (AKFS_Get_MAGNETIC_FIELD(mag, mstat, &tmpx, &tmpy, &tmpz, &tmp_accuracy) == AKM_SUCCESS) { 359 sv_mag.x = tmpx; 360 sv_mag.y = tmpy; 361 sv_mag.z = tmpz; 362 sv_mag.status = tmp_accuracy; 363 } else { 364 flag &= ~MAG_DATA_READY; 365 flag &= ~ORI_DATA_READY; 366 } 367 } 368 369 if (flag & ORI_DATA_READY) { 370 if (AKFS_Get_ORIENTATION(&tmpx, &tmpy, &tmpz, &tmp_accuracy) == AKM_SUCCESS) { 371 sv_ori.x = tmpx; 372 sv_ori.y = tmpy; 373 sv_ori.z = tmpz; 374 sv_ori.status = tmp_accuracy; 375 } else { 376 flag &= ~ORI_DATA_READY; 377 } 378 } 379 380 /* Output result */ 381 AKFS_OutputResult(flag, &sv_acc, &sv_mag, &sv_ori); 382 383 /* Ending time */ 384 if (clock_gettime(CLOCK_MONOTONIC, &tsend) < 0) { 385 AKMERROR; 386 goto MEASURE_END; 387 } 388 389 /* Calculate duration */ 390 doze = AKFS_CalcSleep(&tsend, &tsstart, minimum); 391 AKMDATA(AKMDATA_LOOP, "Sleep: %6.2f msec\n", (doze.tv_nsec/1000000.0f)); 392 nanosleep(&doze, NULL); 393 394 #ifdef WIN32 395 if (_kbhit()) { 396 _getch(); 397 break; 398 } 399 #endif 400 } 401 402 MEASURE_END: 403 /* Set to PowerDown mode */ 404 if (AKD_SetMode(AK8975_MODE_POWERDOWN) != AKD_SUCCESS) { 405 AKMERROR; 406 return; 407 } 408 409 /* Save parameters */ 410 if (AKFS_Stop(CSPEC_SETTING_FILE) != AKM_SUCCESS) { 411 AKMERROR; 412 } 413 } 414 415 416