1/* 2 $License: 3 Copyright 2011 InvenSense, Inc. 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 $ 17 */ 18 19/****************************************************************************** 20 * 21 * $Id: mputest.c 5637 2011-06-14 01:13:53Z mcaramello $ 22 * 23 *****************************************************************************/ 24 25/* 26 * MPU Self Test functions 27 * Version 2.4 28 * May 13th, 2011 29 */ 30 31/** 32 * @defgroup MPU_SELF_TEST 33 * @brief MPU Self Test functions 34 * 35 * These functions provide an in-site test of the MPU 3xxx chips. The main 36 * entry point is the inv_mpu_test function. 37 * This runs the tests (as described in the accompanying documentation) and 38 * writes a configuration file containing initial calibration data. 39 * inv_mpu_test returns INV_SUCCESS if the chip passes the tests. 40 * Otherwise, an error code is returned. 41 * The functions in this file rely on MLSL and MLOS: refer to the MPL 42 * documentation for more information regarding the system interface 43 * files. 44 * 45 * @{ 46 * @file mputest.c 47 * @brief MPU Self Test routines for assessing gyro sensor status 48 * after surface mount has happened on the target host platform. 49 */ 50 51#include <stdio.h> 52#include <time.h> 53#include <string.h> 54#include <math.h> 55#include <stdlib.h> 56#ifdef LINUX 57#include <unistd.h> 58#endif 59 60#include "mpu.h" 61#include "mldl.h" 62#include "mldl_cfg.h" 63#include "accel.h" 64#include "mlFIFO.h" 65#include "slave.h" 66#include "ml.h" 67#include "ml_stored_data.h" 68#include "checksum.h" 69 70#include "mlsl.h" 71#include "mlos.h" 72 73#include "log.h" 74#undef MPL_LOG_TAG 75#define MPL_LOG_TAG "MPL-mpust" 76 77#ifdef __cplusplus 78extern "C" { 79#endif 80 81/* 82 Defines 83*/ 84 85#define VERBOSE_OUT 0 86 87/*#define TRACK_IDS*/ 88 89/*--- Test parameters defaults. See set_test_parameters for more details ---*/ 90 91#define DEF_MPU_ADDR (0x68) /* I2C address of the mpu */ 92 93#if (USE_SENSE_PATH_TEST == 1) /* gyro full scale dps */ 94#define DEF_GYRO_FULLSCALE (2000) 95#else 96#define DEF_GYRO_FULLSCALE (250) 97#endif 98 99#define DEF_GYRO_SENS (32768.f / DEF_GYRO_FULLSCALE) 100 /* gyro sensitivity LSB/dps */ 101#define DEF_PACKET_THRESH (75) /* 600 ms / 8ms / sample */ 102#define DEF_TOTAL_TIMING_TOL (.03f) /* 3% = 2 pkts + 1% proc tol. */ 103#define DEF_BIAS_THRESH (40 * DEF_GYRO_SENS) 104 /* 40 dps in LSBs */ 105#define DEF_RMS_THRESH (0.4f * DEF_GYRO_SENS) 106 /* 0.4 dps-rms in LSB-rms */ 107#define DEF_SP_SHIFT_THRESH_CUST (.05f) /* 5% */ 108#define DEF_TEST_TIME_PER_AXIS (600) /* ms of time spent collecting 109 data for each axis, 110 multiple of 600ms */ 111#define DEF_N_ACCEL_SAMPLES (20) /* num of accel samples to 112 average from, if applic. */ 113 114#define ML_INIT_CAL_LEN (36) /* length in bytes of 115 calibration data file */ 116 117/* 118 Macros 119*/ 120 121#define CHECK_TEST_ERROR(x) \ 122 if (x) { \ 123 MPL_LOGI("error %d @ %s|%d\n", x, __func__, __LINE__); \ 124 return (-1); \ 125 } 126 127#define SHORT_TO_TEMP_C(shrt) (((shrt+13200.f)/280.f)+35.f) 128 129#define USHORT_TO_CHARS(chr,shrt) (chr)[0]=(unsigned char)(shrt>>8); \ 130 (chr)[1]=(unsigned char)(shrt); 131 132#define UINT_TO_CHARS(chr,ui) (chr)[0]=(unsigned char)(ui>>24); \ 133 (chr)[1]=(unsigned char)(ui>>16); \ 134 (chr)[2]=(unsigned char)(ui>>8); \ 135 (chr)[3]=(unsigned char)(ui); 136 137#define FLOAT_TO_SHORT(f) ( \ 138 (fabs(f-(short)f)>=0.5) ? ( \ 139 ((short)f)+(f<0?(-1):(+1))) : \ 140 ((short)f) \ 141 ) 142 143#define CHARS_TO_SHORT(d) ((((short)(d)[0])<<8)+(d)[1]) 144#define CHARS_TO_SHORT_SWAPPED(d) ((((short)(d)[1])<<8)+(d)[0]) 145 146#define ACCEL_UNPACK(d) d[0], d[1], d[2], d[3], d[4], d[5] 147 148#define CHECK_NACKS(d) ( \ 149 d[0]==0xff && d[1]==0xff && \ 150 d[2]==0xff && d[3]==0xff && \ 151 d[4]==0xff && d[5]==0xff \ 152 ) 153 154/* 155 Prototypes 156*/ 157 158static inv_error_t test_get_data( 159 void *mlsl_handle, 160 struct mldl_cfg *mputestCfgPtr, 161 short *vals); 162 163/* 164 Types 165*/ 166typedef struct { 167 float gyro_sens; 168 int gyro_fs; 169 int packet_thresh; 170 float total_timing_tol; 171 int bias_thresh; 172 float rms_threshSq; 173 float sp_shift_thresh; 174 unsigned int test_time_per_axis; 175 unsigned short accel_samples; 176} tTestSetup; 177 178/* 179 Global variables 180*/ 181static unsigned char dataout[20]; 182static unsigned char dataStore[ML_INIT_CAL_LEN]; 183 184static tTestSetup test_setup = { 185 DEF_GYRO_SENS, 186 DEF_GYRO_FULLSCALE, 187 DEF_PACKET_THRESH, 188 DEF_TOTAL_TIMING_TOL, 189 (int)DEF_BIAS_THRESH, 190 DEF_RMS_THRESH * DEF_RMS_THRESH, 191 DEF_SP_SHIFT_THRESH_CUST, 192 DEF_TEST_TIME_PER_AXIS, 193 DEF_N_ACCEL_SAMPLES 194}; 195 196static float adjGyroSens; 197static char a_name[3][2] = {"X", "Y", "Z"}; 198 199/* 200 NOTE : modify get_slave_descr parameter below to reflect 201 the DEFAULT accelerometer in use. The accelerometer in use 202 can be modified at run-time using the inv_test_setup_accel API. 203 NOTE : modify the expected z axis orientation (Z axis pointing 204 upward or downward) 205*/ 206 207signed char g_z_sign = +1; 208struct mldl_cfg *mputestCfgPtr = NULL; 209 210#ifndef LINUX 211/** 212 * @internal 213 * @brief usec precision sleep function. 214 * @param number of micro seconds (us) to sleep for. 215 */ 216static void usleep(unsigned long t) 217{ 218 unsigned long start = inv_get_tick_count(); 219 while (inv_get_tick_count()-start < t / 1000); 220} 221#endif 222 223/** 224 * @brief Modify the self test limits from their default values. 225 * 226 * @param slave_addr 227 * the slave address the MPU device is setup to respond at. 228 * The default is DEF_MPU_ADDR = 0x68. 229 * @param sensitivity 230 * the read sensitivity of the device in LSB/dps as it is trimmed. 231 * NOTE : if using the self test as part of the MPL, the 232 * sensitivity the different sensitivity trims are already 233 * taken care of. 234 * @param p_thresh 235 * number of packets expected to be received in a 600 ms period. 236 * Depends on the sampling frequency of choice (set by default to 237 * 125 Hz) and low pass filter cut-off frequency selection (set 238 * to 42 Hz). 239 * The default is DEF_PACKET_THRESH = 75 packets. 240 * @param total_time_tol 241 * time skew tolerance, taking into account imprecision in turning 242 * the FIFO on and off and the processor time imprecision (for 243 * 1 GHz processor). 244 * The default is DEF_TOTAL_TIMING_TOL = 3 %, about 2 packets. 245 * @param bias_thresh 246 * bias level threshold, the maximun acceptable no motion bias 247 * for a production quality part. 248 * The default is DEF_BIAS_THRESH = 40 dps. 249 * @param rms_thresh 250 * the limit standard deviation (=~ RMS) set to assess whether 251 * the noise level on the part is acceptable. 252 * The default is DEF_RMS_THRESH = 0.2 dps-rms. 253 * @param sp_shift_thresh 254 * the limit shift applicable to the Sense Path self test 255 * calculation. 256 */ 257void inv_set_test_parameters(unsigned int slave_addr, float sensitivity, 258 int p_thresh, float total_time_tol, 259 int bias_thresh, float rms_thresh, 260 float sp_shift_thresh, 261 unsigned short accel_samples) 262{ 263 mputestCfgPtr->addr = slave_addr; 264 test_setup.gyro_sens = sensitivity; 265 test_setup.gyro_fs = (int)(32768.f / sensitivity); 266 test_setup.packet_thresh = p_thresh; 267 test_setup.total_timing_tol = total_time_tol; 268 test_setup.bias_thresh = bias_thresh; 269 test_setup.rms_threshSq = rms_thresh * rms_thresh; 270 test_setup.sp_shift_thresh = sp_shift_thresh; 271 test_setup.accel_samples = accel_samples; 272} 273 274#define X (0) 275#define Y (1) 276#define Z (2) 277 278#ifdef CONFIG_MPU_SENSORS_MPU3050 279/** 280 * @brief Test the gyroscope sensor. 281 * Implements the core logic of the MPU Self Test. 282 * Produces the PASS/FAIL result. Loads the calculated gyro biases 283 * and temperature datum into the corresponding pointers. 284 * @param mlsl_handle 285 * serial interface handle to allow serial communication with the 286 * device, both gyro and accelerometer. 287 * @param gyro_biases 288 * output pointer to store the initial bias calculation provided 289 * by the MPU Self Test. Requires 3 elements for gyro X, Y, 290 * and Z. 291 * @param temp_avg 292 * output pointer to store the initial average temperature as 293 * provided by the MPU Self Test. 294 * @param perform_full_test 295 * If 1: 296 * calculates offset, drive frequency, and noise and compare it 297 * against set thresholds. 298 * Report also the final result using a bit-mask like error code 299 * as explained in return value description. 300 * When 0: 301 * skip the noise and drive frequency calculation and pass/fail 302 * assessment; simply calculates the gyro biases. 303 * 304 * @return 0 on success. 305 * On error, the return value is a bitmask representing: 306 * 0, 1, 2 Failures with PLLs on X, Y, Z gyros respectively 307 * (decimal values will be 1, 2, 4 respectively). 308 * 3, 4, 5 Excessive offset with X, Y, Z gyros respectively 309 * (decimal values will be 8, 16, 32 respectively). 310 * 6, 7, 8 Excessive noise with X, Y, Z gyros respectively 311 * (decimal values will be 64, 128, 256 respectively). 312 * 9 If any of the RMS noise values is zero, it could be 313 * due to a non-functional gyro or FIFO/register failure. 314 * (decimal value will be 512). 315 * (decimal values will be 1024, 2048, 4096 respectively). 316 */ 317int inv_test_gyro_3050(void *mlsl_handle, 318 short gyro_biases[3], short *temp_avg, 319 uint_fast8_t perform_full_test) 320{ 321 int retVal = 0; 322 inv_error_t result; 323 324 int total_count = 0; 325 int total_count_axis[3] = {0, 0, 0}; 326 int packet_count; 327 short x[DEF_TEST_TIME_PER_AXIS / 8 * 4] = {0}; 328 short y[DEF_TEST_TIME_PER_AXIS / 8 * 4] = {0}; 329 short z[DEF_TEST_TIME_PER_AXIS / 8 * 4] = {0}; 330 unsigned char regs[7]; 331 332 int temperature; 333 float Avg[3]; 334 float RMS[3]; 335 int i, j, tmp; 336 char tmpStr[200]; 337 338 temperature = 0; 339 340 /* sample rate = 8ms */ 341 result = inv_serial_single_write( 342 mlsl_handle, mputestCfgPtr->addr, 343 MPUREG_SMPLRT_DIV, 0x07); 344 CHECK_TEST_ERROR(result); 345 346 regs[0] = 0x03; /* filter = 42Hz, analog_sample rate = 1 KHz */ 347 switch (DEF_GYRO_FULLSCALE) { 348 case 2000: 349 regs[0] |= 0x18; 350 break; 351 case 1000: 352 regs[0] |= 0x10; 353 break; 354 case 500: 355 regs[0] |= 0x08; 356 break; 357 case 250: 358 default: 359 regs[0] |= 0x00; 360 break; 361 } 362 result = inv_serial_single_write( 363 mlsl_handle, mputestCfgPtr->addr, 364 MPUREG_DLPF_FS_SYNC, regs[0]); 365 CHECK_TEST_ERROR(result); 366 result = inv_serial_single_write( 367 mlsl_handle, mputestCfgPtr->addr, 368 MPUREG_INT_CFG, 0x00); 369 CHECK_TEST_ERROR(result); 370 371 /* 1st, timing test */ 372 for (j = 0; j < 3; j++) { 373 374 MPL_LOGI("Collecting gyro data from %s gyro PLL\n", a_name[j]); 375 376 /* turn on all gyros, use gyro X for clocking 377 Set to Y and Z for 2nd and 3rd iteration */ 378 result = inv_serial_single_write( 379 mlsl_handle, mputestCfgPtr->addr, 380 MPUREG_PWR_MGM, j + 1); 381 CHECK_TEST_ERROR(result); 382 383 /* wait for 2 ms after switching clock source */ 384 usleep(2000); 385 386 /* we will enable XYZ gyro in FIFO and nothing else */ 387 result = inv_serial_single_write( 388 mlsl_handle, mputestCfgPtr->addr, 389 MPUREG_FIFO_EN2, 0x00); 390 CHECK_TEST_ERROR(result); 391 /* enable/reset FIFO */ 392 result = inv_serial_single_write( 393 mlsl_handle, mputestCfgPtr->addr, 394 MPUREG_USER_CTRL, BIT_FIFO_EN | BIT_FIFO_RST); 395 CHECK_TEST_ERROR(result); 396 397 tmp = (int)(test_setup.test_time_per_axis / 600); 398 while (tmp-- > 0) { 399 /* enable XYZ gyro in FIFO and nothing else */ 400 result = inv_serial_single_write(mlsl_handle, 401 mputestCfgPtr->addr, MPUREG_FIFO_EN1, 402 BIT_GYRO_XOUT | BIT_GYRO_YOUT | BIT_GYRO_ZOUT); 403 CHECK_TEST_ERROR(result); 404 405 /* wait for 600 ms for data */ 406 usleep(600000); 407 408 /* stop storing gyro in the FIFO */ 409 result = inv_serial_single_write( 410 mlsl_handle, mputestCfgPtr->addr, 411 MPUREG_FIFO_EN1, 0x00); 412 CHECK_TEST_ERROR(result); 413 414 /* Getting number of bytes in FIFO */ 415 result = inv_serial_read( 416 mlsl_handle, mputestCfgPtr->addr, 417 MPUREG_FIFO_COUNTH, 2, dataout); 418 CHECK_TEST_ERROR(result); 419 /* number of 6 B packets in the FIFO */ 420 packet_count = CHARS_TO_SHORT(dataout) / 6; 421 sprintf(tmpStr, "Packet Count: %d - ", packet_count); 422 423 if ( abs(packet_count - test_setup.packet_thresh) 424 <= /* Within +/- total_timing_tol % range */ 425 test_setup.total_timing_tol * test_setup.packet_thresh) { 426 for (i = 0; i < packet_count; i++) { 427 /* getting FIFO data */ 428 result = inv_serial_read_fifo(mlsl_handle, 429 mputestCfgPtr->addr, 6, dataout); 430 CHECK_TEST_ERROR(result); 431 x[total_count + i] = CHARS_TO_SHORT(&dataout[0]); 432 y[total_count + i] = CHARS_TO_SHORT(&dataout[2]); 433 z[total_count + i] = CHARS_TO_SHORT(&dataout[4]); 434 if (VERBOSE_OUT) { 435 MPL_LOGI("Gyros %-4d : %+13d %+13d %+13d\n", 436 total_count + i, x[total_count + i], 437 y[total_count + i], z[total_count + i]); 438 } 439 } 440 total_count += packet_count; 441 total_count_axis[j] += packet_count; 442 sprintf(tmpStr, "%sOK", tmpStr); 443 } else { 444 if (perform_full_test) 445 retVal |= 1 << j; 446 sprintf(tmpStr, "%sNOK - samples ignored", tmpStr); 447 } 448 MPL_LOGI("%s\n", tmpStr); 449 } 450 451 /* remove gyros from FIFO */ 452 result = inv_serial_single_write( 453 mlsl_handle, mputestCfgPtr->addr, 454 MPUREG_FIFO_EN1, 0x00); 455 CHECK_TEST_ERROR(result); 456 457 /* Read Temperature */ 458 result = inv_serial_read(mlsl_handle, mputestCfgPtr->addr, 459 MPUREG_TEMP_OUT_H, 2, dataout); 460 CHECK_TEST_ERROR(result); 461 temperature += (short)CHARS_TO_SHORT(dataout); 462 } 463 464 MPL_LOGI("\n"); 465 MPL_LOGI("Total %d samples\n", total_count); 466 MPL_LOGI("\n"); 467 468 /* 2nd, check bias from X and Y PLL clock source */ 469 tmp = total_count != 0 ? total_count : 1; 470 for (i = 0, 471 Avg[X] = .0f, Avg[Y] = .0f, Avg[Z] = .0f; 472 i < total_count; i++) { 473 Avg[X] += 1.f * x[i] / tmp; 474 Avg[Y] += 1.f * y[i] / tmp; 475 Avg[Z] += 1.f * z[i] / tmp; 476 } 477 MPL_LOGI("bias : %+13.3f %+13.3f %+13.3f (LSB)\n", 478 Avg[X], Avg[Y], Avg[Z]); 479 if (VERBOSE_OUT) { 480 MPL_LOGI(" : %+13.3f %+13.3f %+13.3f (dps)\n", 481 Avg[X] / adjGyroSens, 482 Avg[Y] / adjGyroSens, 483 Avg[Z] / adjGyroSens); 484 } 485 if(perform_full_test) { 486 for (j = 0; j < 3; j++) { 487 if (fabs(Avg[j]) > test_setup.bias_thresh) { 488 MPL_LOGI("%s-Gyro bias (%.0f) exceeded threshold " 489 "(threshold = %d)\n", 490 a_name[j], Avg[j], test_setup.bias_thresh); 491 retVal |= 1 << (3+j); 492 } 493 } 494 } 495 496 /* 3rd, check RMS */ 497 if (perform_full_test) { 498 for (i = 0, 499 RMS[X] = 0.f, RMS[Y] = 0.f, RMS[Z] = 0.f; 500 i < total_count; i++) { 501 RMS[X] += (x[i] - Avg[X]) * (x[i] - Avg[X]); 502 RMS[Y] += (y[i] - Avg[Y]) * (y[i] - Avg[Y]); 503 RMS[Z] += (z[i] - Avg[Z]) * (z[i] - Avg[Z]); 504 } 505 for (j = 0; j < 3; j++) { 506 if (RMS[j] > test_setup.rms_threshSq * total_count) { 507 MPL_LOGI("%s-Gyro RMS (%.2f) exceeded threshold " 508 "(threshold = %.2f)\n", 509 a_name[j], sqrt(RMS[j] / total_count), 510 sqrt(test_setup.rms_threshSq)); 511 retVal |= 1 << (6+j); 512 } 513 } 514 515 MPL_LOGI("RMS : %+13.3f %+13.3f %+13.3f (LSB-rms)\n", 516 sqrt(RMS[X] / total_count), 517 sqrt(RMS[Y] / total_count), 518 sqrt(RMS[Z] / total_count)); 519 if (VERBOSE_OUT) { 520 MPL_LOGI("RMS ^ 2 : %+13.3f %+13.3f %+13.3f\n", 521 RMS[X] / total_count, 522 RMS[Y] / total_count, 523 RMS[Z] / total_count); 524 } 525 526 if (RMS[X] == 0 || RMS[Y] == 0 || RMS[Z] == 0) { 527 /* If any of the RMS noise value returns zero, 528 then we might have dead gyro or FIFO/register failure, 529 the part is sleeping, or the part is not responsive */ 530 retVal |= 1 << 9; 531 } 532 } 533 534 /* 4th, temperature average */ 535 temperature /= 3; 536 if (VERBOSE_OUT) 537 MPL_LOGI("Temperature : %+13.3f %13s %13s (deg. C)\n", 538 SHORT_TO_TEMP_C(temperature), "", ""); 539 540 /* load into final storage */ 541 *temp_avg = (short)temperature; 542 gyro_biases[X] = FLOAT_TO_SHORT(Avg[X]); 543 gyro_biases[Y] = FLOAT_TO_SHORT(Avg[Y]); 544 gyro_biases[Z] = FLOAT_TO_SHORT(Avg[Z]); 545 546 return retVal; 547} 548 549#else /* CONFIG_MPU_SENSORS_MPU3050 */ 550 551/** 552 * @brief Test the gyroscope sensor. 553 * Implements the core logic of the MPU Self Test but does not provide 554 * a PASS/FAIL output as in the MPU-3050 implementation. 555 * @param mlsl_handle 556 * serial interface handle to allow serial communication with the 557 * device, both gyro and accelerometer. 558 * @param gyro_biases 559 * output pointer to store the initial bias calculation provided 560 * by the MPU Self Test. Requires 3 elements for gyro X, Y, 561 * and Z. 562 * @param temp_avg 563 * output pointer to store the initial average temperature as 564 * provided by the MPU Self Test. 565 * 566 * @return 0 on success. 567 * A non-zero error code on error. 568 */ 569int inv_test_gyro_6050(void *mlsl_handle, 570 short gyro_biases[3], short *temp_avg) 571{ 572 inv_error_t result; 573 574 int total_count = 0; 575 int total_count_axis[3] = {0, 0, 0}; 576 int packet_count; 577 short x[DEF_TEST_TIME_PER_AXIS / 8 * 4] = {0}; 578 short y[DEF_TEST_TIME_PER_AXIS / 8 * 4] = {0}; 579 short z[DEF_TEST_TIME_PER_AXIS / 8 * 4] = {0}; 580 unsigned char regs[7]; 581 582 int temperature = 0; 583 float Avg[3]; 584 int i, j, tmp; 585 char tmpStr[200]; 586 587 /* sample rate = 8ms */ 588 result = inv_serial_single_write( 589 mlsl_handle, mputestCfgPtr->addr, 590 MPUREG_SMPLRT_DIV, 0x07); 591 if (result) { 592 LOG_RESULT_LOCATION(result); 593 return result; 594 } 595 596 regs[0] = 0x03; /* filter = 42Hz, analog_sample rate = 1 KHz */ 597 switch (DEF_GYRO_FULLSCALE) { 598 case 2000: 599 regs[0] |= 0x18; 600 break; 601 case 1000: 602 regs[0] |= 0x10; 603 break; 604 case 500: 605 regs[0] |= 0x08; 606 break; 607 case 250: 608 default: 609 regs[0] |= 0x00; 610 break; 611 } 612 result = inv_serial_single_write( 613 mlsl_handle, mputestCfgPtr->addr, 614 MPUREG_CONFIG, regs[0]); 615 if (result) { 616 LOG_RESULT_LOCATION(result); 617 return result; 618 } 619 result = inv_serial_single_write( 620 mlsl_handle, mputestCfgPtr->addr, 621 MPUREG_INT_ENABLE, 0x00); 622 if (result) { 623 LOG_RESULT_LOCATION(result); 624 return result; 625 } 626 627 /* 1st, timing test */ 628 for (j = 0; j < 3; j++) { 629 MPL_LOGI("Collecting gyro data from %s gyro PLL\n", a_name[j]); 630 631 /* turn on all gyros, use gyro X for clocking 632 Set to Y and Z for 2nd and 3rd iteration */ 633#ifdef CONFIG_MPU_SENSORS_MPU6050A2 634 result = inv_serial_single_write( 635 mlsl_handle, mputestCfgPtr->addr, 636 MPUREG_PWR_MGMT_1, BITS_PWRSEL | (j + 1)); 637#else 638 result = inv_serial_single_write( 639 mlsl_handle, mputestCfgPtr->addr, 640 MPUREG_PWR_MGMT_1, j + 1); 641#endif 642 if (result) { 643 LOG_RESULT_LOCATION(result); 644 return result; 645 } 646 647 /* wait for 2 ms after switching clock source */ 648 usleep(2000); 649 650 /* enable/reset FIFO */ 651 result = inv_serial_single_write( 652 mlsl_handle, mputestCfgPtr->addr, 653 MPUREG_USER_CTRL, BIT_FIFO_EN | BIT_FIFO_RST); 654 if (result) { 655 LOG_RESULT_LOCATION(result); 656 return result; 657 } 658 659 tmp = (int)(test_setup.test_time_per_axis / 600); 660 while (tmp-- > 0) { 661 /* enable XYZ gyro in FIFO and nothing else */ 662 result = inv_serial_single_write(mlsl_handle, 663 mputestCfgPtr->addr, MPUREG_FIFO_EN, 664 BIT_GYRO_XOUT | BIT_GYRO_YOUT | BIT_GYRO_ZOUT); 665 if (result) { 666 LOG_RESULT_LOCATION(result); 667 return result; 668 } 669 670 /* wait for 600 ms for data */ 671 usleep(600000); 672 /* stop storing gyro in the FIFO */ 673 result = inv_serial_single_write( 674 mlsl_handle, mputestCfgPtr->addr, 675 MPUREG_FIFO_EN, 0x00); 676 if (result) { 677 LOG_RESULT_LOCATION(result); 678 return result; 679 } 680 /* Getting number of bytes in FIFO */ 681 result = inv_serial_read( 682 mlsl_handle, mputestCfgPtr->addr, 683 MPUREG_FIFO_COUNTH, 2, dataout); 684 if (result) { 685 LOG_RESULT_LOCATION(result); 686 return result; 687 } 688 /* number of 6 B packets in the FIFO */ 689 packet_count = CHARS_TO_SHORT(dataout) / 6; 690 sprintf(tmpStr, "Packet Count: %d - ", packet_count); 691 692 if (abs(packet_count - test_setup.packet_thresh) 693 <= /* Within +/- total_timing_tol % range */ 694 test_setup.total_timing_tol * test_setup.packet_thresh) { 695 for (i = 0; i < packet_count; i++) { 696 /* getting FIFO data */ 697 result = inv_serial_read_fifo(mlsl_handle, 698 mputestCfgPtr->addr, 6, dataout); 699 if (result) { 700 LOG_RESULT_LOCATION(result); 701 return result; 702 } 703 x[total_count + i] = CHARS_TO_SHORT(&dataout[0]); 704 y[total_count + i] = CHARS_TO_SHORT(&dataout[2]); 705 z[total_count + i] = CHARS_TO_SHORT(&dataout[4]); 706 if (VERBOSE_OUT) { 707 MPL_LOGI("Gyros %-4d : %+13d %+13d %+13d\n", 708 total_count + i, x[total_count + i], 709 y[total_count + i], z[total_count + i]); 710 } 711 } 712 total_count += packet_count; 713 total_count_axis[j] += packet_count; 714 sprintf(tmpStr, "%sOK", tmpStr); 715 } else { 716 sprintf(tmpStr, "%sNOK - samples ignored", tmpStr); 717 } 718 MPL_LOGI("%s\n", tmpStr); 719 } 720 721 /* remove gyros from FIFO */ 722 result = inv_serial_single_write( 723 mlsl_handle, mputestCfgPtr->addr, 724 MPUREG_FIFO_EN, 0x00); 725 if (result) { 726 LOG_RESULT_LOCATION(result); 727 return result; 728 } 729 730 /* Read Temperature */ 731 result = inv_serial_read(mlsl_handle, mputestCfgPtr->addr, 732 MPUREG_TEMP_OUT_H, 2, dataout); 733 if (result) { 734 LOG_RESULT_LOCATION(result); 735 return result; 736 } 737 temperature += (short)CHARS_TO_SHORT(dataout); 738 } 739 740 MPL_LOGI("\n"); 741 MPL_LOGI("Total %d samples\n", total_count); 742 MPL_LOGI("\n"); 743 744 /* 2nd, check bias from X and Y PLL clock source */ 745 tmp = total_count != 0 ? total_count : 1; 746 for (i = 0, 747 Avg[X] = .0f, Avg[Y] = .0f, Avg[Z] = .0f; 748 i < total_count; i++) { 749 Avg[X] += 1.f * x[i] / tmp; 750 Avg[Y] += 1.f * y[i] / tmp; 751 Avg[Z] += 1.f * z[i] / tmp; 752 } 753 MPL_LOGI("bias : %+13.3f %+13.3f %+13.3f (LSB)\n", 754 Avg[X], Avg[Y], Avg[Z]); 755 if (VERBOSE_OUT) { 756 MPL_LOGI(" : %+13.3f %+13.3f %+13.3f (dps)\n", 757 Avg[X] / adjGyroSens, 758 Avg[Y] / adjGyroSens, 759 Avg[Z] / adjGyroSens); 760 } 761 762 temperature /= 3; 763 if (VERBOSE_OUT) 764 MPL_LOGI("Temperature : %+13.3f %13s %13s (deg. C)\n", 765 SHORT_TO_TEMP_C(temperature), "", ""); 766 767 /* load into final storage */ 768 *temp_avg = (short)temperature; 769 gyro_biases[X] = FLOAT_TO_SHORT(Avg[X]); 770 gyro_biases[Y] = FLOAT_TO_SHORT(Avg[Y]); 771 gyro_biases[Z] = FLOAT_TO_SHORT(Avg[Z]); 772 773 return INV_SUCCESS; 774} 775 776#endif /* CONFIG_MPU_SENSORS_MPU3050 */ 777 778#ifdef TRACK_IDS 779/** 780 * @internal 781 * @brief Retrieve the unique MPU device identifier from the internal OTP 782 * bank 0 memory. 783 * @param mlsl_handle 784 * serial interface handle to allow serial communication with the 785 * device, both gyro and accelerometer. 786 * @return 0 on success, a non-zero error code from the serial layer on error. 787 */ 788static inv_error_t test_get_mpu_id(void *mlsl_handle) 789{ 790 inv_error_t result; 791 unsigned char otp0[8]; 792 793 794 result = 795 inv_serial_read_mem(mlsl_handle, mputestCfgPtr->addr, 796 (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0) << 8 | 797 0x00, 6, otp0); 798 if (result) 799 goto close; 800 801 MPL_LOGI("\n"); 802 MPL_LOGI("DIE_ID : %06X\n", 803 ((int)otp0[1] << 8 | otp0[0]) & 0x1fff); 804 MPL_LOGI("WAFER_ID : %06X\n", 805 (((int)otp0[2] << 8 | otp0[1]) & 0x03ff ) >> 5); 806 MPL_LOGI("A_LOT_ID : %06X\n", 807 ( ((int)otp0[4] << 16 | (int)otp0[3] << 8 | 808 otp0[2]) & 0x3ffff) >> 2); 809 MPL_LOGI("W_LOT_ID : %06X\n", 810 ( ((int)otp0[5] << 8 | otp0[4]) & 0x3fff) >> 2); 811 MPL_LOGI("WP_ID : %06X\n", 812 ( ((int)otp0[6] << 8 | otp0[5]) & 0x03ff) >> 7); 813 MPL_LOGI("REV_ID : %06X\n", otp0[6] >> 2); 814 MPL_LOGI("\n"); 815 816close: 817 result = 818 inv_serial_single_write(mlsl_handle, mputestCfgPtr->addr, MPUREG_BANK_SEL, 0x00); 819 return result; 820} 821#endif /* TRACK_IDS */ 822 823/** 824 * @brief If requested via inv_test_setup_accel(), test the accelerometer biases 825 * and calculate the necessary bias correction. 826 * @param mlsl_handle 827 * serial interface handle to allow serial communication with the 828 * device, both gyro and accelerometer. 829 * @param bias 830 * output pointer to store the initial bias calculation provided 831 * by the MPU Self Test. Requires 3 elements to store accel X, Y, 832 * and Z axis bias. 833 * @param perform_full_test 834 * If 1: 835 * calculates offsets and noise and compare it against set 836 * thresholds. The final exist status will reflect if any of the 837 * value is outside of the expected range. 838 * When 0; 839 * skip the noise calculation and pass/fail assessment; simply 840 * calculates the accel biases. 841 * 842 * @return 0 on success. A non-zero error code on error. 843 */ 844int inv_test_accel(void *mlsl_handle, 845 short *bias, long gravity, 846 uint_fast8_t perform_full_test) 847{ 848 int i; 849 850 short *p_vals; 851 float x = 0.f, y = 0.f, z = 0.f, zg = 0.f; 852 float RMS[3]; 853 float accelRmsThresh = 1000000.f; /* enourmous so that the test always 854 passes - future deployment */ 855 unsigned short res; 856 unsigned long orig_requested_sensors; 857 struct mpu_platform_data *mputestPData = mputestCfgPtr->pdata; 858 859 p_vals = (short*)inv_malloc(sizeof(short) * 3 * test_setup.accel_samples); 860 861 /* load the slave descr from the getter */ 862 if (mputestPData->accel.get_slave_descr == NULL) { 863 MPL_LOGI("\n"); 864 MPL_LOGI("No accelerometer configured\n"); 865 return 0; 866 } 867 if (mputestCfgPtr->accel == NULL) { 868 MPL_LOGI("\n"); 869 MPL_LOGI("No accelerometer configured\n"); 870 return 0; 871 } 872 873 /* resume the accel */ 874 orig_requested_sensors = mputestCfgPtr->requested_sensors; 875 mputestCfgPtr->requested_sensors = INV_THREE_AXIS_ACCEL | INV_THREE_AXIS_GYRO; 876 res = inv_mpu_resume(mputestCfgPtr, 877 mlsl_handle, NULL, NULL, NULL, 878 mputestCfgPtr->requested_sensors); 879 if(res != INV_SUCCESS) 880 goto accel_error; 881 882 /* wait at least a sample cycle for the 883 accel data to be retrieved by MPU */ 884 inv_sleep(inv_mpu_get_sampling_period_us(mputestCfgPtr) / 1000); 885 886 /* collect the samples */ 887 for(i = 0; i < test_setup.accel_samples; i++) { 888 short *vals = &p_vals[3 * i]; 889 if (test_get_data(mlsl_handle, mputestCfgPtr, vals)) { 890 goto accel_error; 891 } 892 x += 1.f * vals[X] / test_setup.accel_samples; 893 y += 1.f * vals[Y] / test_setup.accel_samples; 894 z += 1.f * vals[Z] / test_setup.accel_samples; 895 } 896 897 mputestCfgPtr->requested_sensors = orig_requested_sensors; 898 res = inv_mpu_suspend(mputestCfgPtr, 899 mlsl_handle, NULL, NULL, NULL, 900 INV_ALL_SENSORS); 901 if (res != INV_SUCCESS) 902 goto accel_error; 903 904 MPL_LOGI("Accel biases : %+13.3f %+13.3f %+13.3f (LSB)\n", x, y, z); 905 if (VERBOSE_OUT) { 906 MPL_LOGI("Accel biases : %+13.3f %+13.3f %+13.3f (gee)\n", 907 x / gravity, y / gravity, z / gravity); 908 } 909 910 bias[0] = FLOAT_TO_SHORT(x); 911 bias[1] = FLOAT_TO_SHORT(y); 912 zg = z - g_z_sign * gravity; 913 bias[2] = FLOAT_TO_SHORT(zg); 914 915 MPL_LOGI("Accel correct.: %+13d %+13d %+13d (LSB)\n", 916 bias[0], bias[1], bias[2]); 917 if (VERBOSE_OUT) { 918 MPL_LOGI("Accel correct.: " 919 "%+13.3f %+13.3f %+13.3f (gee)\n", 920 1.f * bias[0] / gravity, 921 1.f * bias[1] / gravity, 922 1.f * bias[2] / gravity); 923 } 924 925 if (perform_full_test) { 926 /* accel RMS - for now the threshold is only indicative */ 927 for (i = 0, 928 RMS[X] = 0.f, RMS[Y] = 0.f, RMS[Z] = 0.f; 929 i < test_setup.accel_samples; i++) { 930 short *vals = &p_vals[3 * i]; 931 RMS[X] += (vals[X] - x) * (vals[X] - x); 932 RMS[Y] += (vals[Y] - y) * (vals[Y] - y); 933 RMS[Z] += (vals[Z] - z) * (vals[Z] - z); 934 } 935 for (i = 0; i < 3; i++) { 936 if (RMS[i] > accelRmsThresh * accelRmsThresh 937 * test_setup.accel_samples) { 938 MPL_LOGI("%s-Accel RMS (%.2f) exceeded threshold " 939 "(threshold = %.2f)\n", 940 a_name[i], sqrt(RMS[i] / test_setup.accel_samples), 941 accelRmsThresh); 942 goto accel_error; 943 } 944 } 945 MPL_LOGI("RMS : %+13.3f %+13.3f %+13.3f (LSB-rms)\n", 946 sqrt(RMS[X] / DEF_N_ACCEL_SAMPLES), 947 sqrt(RMS[Y] / DEF_N_ACCEL_SAMPLES), 948 sqrt(RMS[Z] / DEF_N_ACCEL_SAMPLES)); 949 } 950 951 return 0; /* success */ 952 953accel_error: /* error */ 954 bias[0] = bias[1] = bias[2] = 0; 955 return 1; 956} 957 958/** 959 * @brief an user-space substitute of the power management function(s) 960 * in mldl_cfg.c for self test usage. 961 * Wake up and sleep the device, whether that is MPU3050, MPU6050A2, 962 * or MPU6050B1. 963 * @param mlsl_handle 964 * a file handle for the serial communication port used to 965 * communicate with the MPU device. 966 * @param power_level 967 * the power state to change the device into. Currently only 0 or 968 * 1 are supported, for sleep and full-power respectively. 969 * @return 0 on success; a non-zero error code on error. 970 */ 971static inv_error_t inv_device_power_mgmt(void *mlsl_handle, 972 uint_fast8_t power_level) 973{ 974 inv_error_t result; 975 static unsigned char pwr_mgm; 976 unsigned char b; 977 978 if (power_level != 0 && power_level != 1) { 979 return INV_ERROR_INVALID_PARAMETER; 980 } 981 982 if (power_level) { 983 result = inv_serial_read( 984 mlsl_handle, mputestCfgPtr->addr, 985 MPUREG_PWR_MGM, 1, &pwr_mgm); 986 if (result) { 987 LOG_RESULT_LOCATION(result); 988 return result; 989 } 990 991 /* reset */ 992 result = inv_serial_single_write( 993 mlsl_handle, mputestCfgPtr->addr, 994 MPUREG_PWR_MGM, pwr_mgm | BIT_H_RESET); 995#ifndef CONFIG_MPU_SENSORS_MPU6050A2 996 if (result) { 997 LOG_RESULT_LOCATION(result); 998 return result; 999 } 1000#endif 1001 inv_sleep(5); 1002 1003 /* re-read power mgmt reg - 1004 it may have reset after H_RESET is applied */ 1005 result = inv_serial_read( 1006 mlsl_handle, mputestCfgPtr->addr, 1007 MPUREG_PWR_MGM, 1, &b); 1008 if (result) { 1009 LOG_RESULT_LOCATION(result); 1010 return result; 1011 } 1012 1013 /* wake up */ 1014#ifdef CONFIG_MPU_SENSORS_MPU6050A2 1015 if ((b & BITS_PWRSEL) != BITS_PWRSEL) { 1016 result = inv_serial_single_write( 1017 mlsl_handle, mputestCfgPtr->addr, 1018 MPUREG_PWR_MGM, BITS_PWRSEL); 1019 if (result) { 1020 LOG_RESULT_LOCATION(result); 1021 return result; 1022 } 1023 } 1024#else 1025 if (pwr_mgm & BIT_SLEEP) { 1026 result = inv_serial_single_write( 1027 mlsl_handle, mputestCfgPtr->addr, 1028 MPUREG_PWR_MGM, 0x00); 1029 if (result) { 1030 LOG_RESULT_LOCATION(result); 1031 return result; 1032 } 1033 } 1034#endif 1035 inv_sleep(60); 1036 1037#if defined(CONFIG_MPU_SENSORS_MPU6050A2) || \ 1038 defined(CONFIG_MPU_SENSORS_MPU6050B1) 1039 result = inv_serial_single_write( 1040 mlsl_handle, mputestCfgPtr->addr, 1041 MPUREG_INT_PIN_CFG, BIT_BYPASS_EN); 1042 if (result) { 1043 LOG_RESULT_LOCATION(result); 1044 return result; 1045 } 1046#endif 1047 } else { 1048 /* restore the power state the part was found in */ 1049#ifdef CONFIG_MPU_SENSORS_MPU6050A2 1050 if ((pwr_mgm & BITS_PWRSEL) != BITS_PWRSEL) { 1051 result = inv_serial_single_write( 1052 mlsl_handle, mputestCfgPtr->addr, 1053 MPUREG_PWR_MGM, pwr_mgm); 1054 if (result) { 1055 LOG_RESULT_LOCATION(result); 1056 return result; 1057 } 1058 } 1059#else 1060 if (pwr_mgm & BIT_SLEEP) { 1061 result = inv_serial_single_write( 1062 mlsl_handle, mputestCfgPtr->addr, 1063 MPUREG_PWR_MGM, pwr_mgm); 1064 if (result) { 1065 LOG_RESULT_LOCATION(result); 1066 return result; 1067 } 1068 } 1069#endif 1070 } 1071 1072 return INV_SUCCESS; 1073} 1074 1075/** 1076 * @brief The main entry point of the MPU Self Test, triggering the run of 1077 * the single tests, for gyros and accelerometers. 1078 * Prepares the MPU for the test, taking the device out of low power 1079 * state if necessary, switching the MPU secondary I2C interface into 1080 * bypass mode and restoring the original power state at the end of 1081 * the test. 1082 * This function is also responsible for encoding the output of each 1083 * test in the correct format as it is stored on the file/medium of 1084 * choice (according to inv_serial_write_cal() function). 1085 * The format needs to stay perfectly consistent with the one expected 1086 * by the corresponding loader in ml_stored_data.c; currectly the 1087 * loaded in use is inv_load_cal_V1 (record type 1 - initial 1088 * calibration). 1089 * 1090 * @param mlsl_handle 1091 * serial interface handle to allow serial communication with the 1092 * device, both gyro and accelerometer. 1093 * @param provide_result 1094 * If 1: 1095 * perform and analyze the offset, drive frequency, and noise 1096 * calculation and compare it against set threshouds. Report 1097 * also the final result using a bit-mask like error code as 1098 * described in the inv_test_gyro() function. 1099 * When 0: 1100 * skip the noise and drive frequency calculation and pass/fail 1101 * assessment. It simply calculates the gyro and accel biases. 1102 * NOTE: for MPU6050 devices, this parameter is currently 1103 * ignored. 1104 * 1105 * @return 0 on success. A non-zero error code on error. 1106 * Propagates the errors from the tests up to the caller. 1107 */ 1108int inv_mpu_test(void *mlsl_handle, uint_fast8_t provide_result) 1109{ 1110 int result = 0; 1111 1112 short temp_avg; 1113 short gyro_biases[3] = {0, 0, 0}; 1114 short accel_biases[3] = {0, 0, 0}; 1115 1116 unsigned long testStart = inv_get_tick_count(); 1117 long accelSens[3] = {0}; 1118 int ptr; 1119 int tmp; 1120 long long lltmp; 1121 uint32_t chk; 1122 1123 MPL_LOGI("Collecting %d groups of 600 ms samples for each axis\n", 1124 DEF_TEST_TIME_PER_AXIS / 600); 1125 MPL_LOGI("\n"); 1126 1127 result = inv_device_power_mgmt(mlsl_handle, TRUE); 1128 1129#ifdef TRACK_IDS 1130 result = test_get_mpu_id(mlsl_handle); 1131 if (result != INV_SUCCESS) { 1132 MPL_LOGI("Could not read the device's unique ID\n"); 1133 MPL_LOGI("\n"); 1134 return result; 1135 } 1136#endif 1137 1138 /* adjust the gyro sensitivity according to the gyro_sens_trim value */ 1139 adjGyroSens = test_setup.gyro_sens * mputestCfgPtr->gyro_sens_trim / 131.f; 1140 test_setup.gyro_fs = (int)(32768.f / adjGyroSens); 1141 1142 /* collect gyro and temperature data */ 1143#ifdef CONFIG_MPU_SENSORS_MPU3050 1144 result = inv_test_gyro_3050(mlsl_handle, 1145 gyro_biases, &temp_avg, provide_result); 1146#else 1147 MPL_LOGW_IF(provide_result, 1148 "Self Test for MPU-6050 devices is for bias correction only: " 1149 "no test PASS/FAIL result will be provided\n"); 1150 result = inv_test_gyro_6050(mlsl_handle, gyro_biases, &temp_avg); 1151#endif 1152 1153 MPL_LOGI("\n"); 1154 MPL_LOGI("Test time : %ld ms\n", inv_get_tick_count() - testStart); 1155 if (result) 1156 return result; 1157 1158 /* collect accel data. if this step is skipped, 1159 ensure the array still contains zeros. */ 1160 if (mputestCfgPtr->accel != NULL) { 1161 float fs; 1162 RANGE_FIXEDPOINT_TO_FLOAT(mputestCfgPtr->accel->range, fs); 1163 accelSens[0] = (long)(32768L / fs); 1164 accelSens[1] = (long)(32768L / fs); 1165 accelSens[2] = (long)(32768L / fs); 1166#if defined CONFIG_MPU_SENSORS_MPU6050B1 1167 if (MPL_PROD_KEY(mputestCfgPtr->product_id, 1168 mputestCfgPtr->product_revision) == MPU_PRODUCT_KEY_B1_E1_5) { 1169 accelSens[2] /= 2; 1170 } else { 1171 unsigned short trim_corr = 16384 / mputestCfgPtr->accel_sens_trim; 1172 accelSens[0] /= trim_corr; 1173 accelSens[1] /= trim_corr; 1174 accelSens[2] /= trim_corr; 1175 } 1176#endif 1177 } else { 1178 /* would be 0, but 1 to avoid divide-by-0 below */ 1179 accelSens[0] = accelSens[1] = accelSens[2] = 1; 1180 } 1181#ifdef CONFIG_MPU_SENSORS_MPU3050 1182 result = inv_test_accel(mlsl_handle, accel_biases, accelSens[2], 1183 provide_result); 1184#else 1185 result = inv_test_accel(mlsl_handle, accel_biases, accelSens[2], 1186 FALSE); 1187#endif 1188 if (result) 1189 return result; 1190 1191 result = inv_device_power_mgmt(mlsl_handle, FALSE); 1192 if (result) 1193 return result; 1194 1195 ptr = 0; 1196 dataStore[ptr++] = 0; /* total len of factory cal */ 1197 dataStore[ptr++] = 0; 1198 dataStore[ptr++] = 0; 1199 dataStore[ptr++] = ML_INIT_CAL_LEN; 1200 dataStore[ptr++] = 0; 1201 dataStore[ptr++] = 5; /* record type 5 - initial calibration */ 1202 1203 tmp = temp_avg; /* temperature */ 1204 if (tmp < 0) tmp += 2 << 16; 1205 USHORT_TO_CHARS(&dataStore[ptr], tmp); 1206 ptr += 2; 1207 1208 /* NOTE : 2 * test_setup.gyro_fs == 65536 / (32768 / test_setup.gyro_fs) */ 1209 lltmp = (long)gyro_biases[0] * 2 * test_setup.gyro_fs; /* x gyro avg */ 1210 if (lltmp < 0) lltmp += 1LL << 32; 1211 UINT_TO_CHARS(&dataStore[ptr], (uint32_t)lltmp); 1212 ptr += 4; 1213 lltmp = (long)gyro_biases[1] * 2 * test_setup.gyro_fs; /* y gyro avg */ 1214 if (lltmp < 0) lltmp += 1LL << 32; 1215 UINT_TO_CHARS(&dataStore[ptr], (uint32_t)lltmp); 1216 ptr += 4; 1217 lltmp = (long)gyro_biases[2] * 2 * test_setup.gyro_fs; /* z gyro avg */ 1218 if (lltmp < 0) lltmp += 1LL << 32; 1219 UINT_TO_CHARS(&dataStore[ptr], (uint32_t)lltmp); 1220 ptr += 4; 1221 1222 lltmp = (long)accel_biases[0] * 65536L / accelSens[0]; /* x accel avg */ 1223 if (lltmp < 0) lltmp += 1LL << 32; 1224 UINT_TO_CHARS(&dataStore[ptr], (uint32_t)lltmp); 1225 ptr += 4; 1226 lltmp = (long)accel_biases[1] * 65536L / accelSens[1]; /* y accel avg */ 1227 if (lltmp < 0) lltmp += 1LL << 32; 1228 UINT_TO_CHARS(&dataStore[ptr], (uint32_t)lltmp); 1229 ptr += 4; 1230 lltmp = (long)accel_biases[2] * 65536L / accelSens[2]; /* z accel avg */ 1231 if (lltmp < 0) lltmp += 1LL << 32; 1232 UINT_TO_CHARS(&dataStore[ptr], (uint32_t)lltmp); 1233 ptr += 4; 1234 1235 /* add a checksum for data */ 1236 chk = inv_checksum( 1237 dataStore + INV_CAL_HDR_LEN, 1238 ML_INIT_CAL_LEN - INV_CAL_HDR_LEN - INV_CAL_CHK_LEN); 1239 UINT_TO_CHARS(&dataStore[ptr], chk); 1240 ptr += 4; 1241 1242 if (ptr != ML_INIT_CAL_LEN) { 1243 MPL_LOGI("Invalid calibration data length: exp %d, got %d\n", 1244 ML_INIT_CAL_LEN, ptr); 1245 return -1; 1246 } 1247 1248 return result; 1249} 1250 1251/** 1252 * @brief The main test API. Runs the MPU Self Test and, if successful, 1253 * stores the encoded initial calibration data on the final storage 1254 * medium of choice (cfr. inv_serial_write_cal() and the MLCAL_FILE 1255 * define in your mlsl implementation). 1256 * 1257 * @param mlsl_handle 1258 * serial interface handle to allow serial communication with the 1259 * device, both gyro and accelerometer. 1260 * @param provide_result 1261 * If 1: 1262 * perform and analyze the offset, drive frequency, and noise 1263 * calculation and compare it against set threshouds. Report 1264 * also the final result using a bit-mask like error code as 1265 * described in the inv_test_gyro() function. 1266 * When 0: 1267 * skip the noise and drive frequency calculation and pass/fail 1268 * assessment. It simply calculates the gyro and accel biases. 1269 * 1270 * @return 0 on success or a non-zero error code from the callees on error. 1271 */ 1272inv_error_t inv_factory_calibrate(void *mlsl_handle, 1273 uint_fast8_t provide_result) 1274{ 1275 int result; 1276 1277 result = inv_mpu_test(mlsl_handle, provide_result); 1278 if (provide_result) { 1279 MPL_LOGI("\n"); 1280 if (result == 0) { 1281 MPL_LOGI("Test : PASSED\n"); 1282 } else { 1283 MPL_LOGI("Test : FAILED %d/%04X - Biases NOT stored\n", result, result); 1284 return result; /* abort writing the calibration if the 1285 test is not successful */ 1286 } 1287 MPL_LOGI("\n"); 1288 } else { 1289 MPL_LOGI("\n"); 1290 if (result) { 1291 LOG_RESULT_LOCATION(result); 1292 return result; 1293 } 1294 } 1295 1296 result = inv_serial_write_cal(dataStore, ML_INIT_CAL_LEN); 1297 if (result) { 1298 MPL_LOGI("Error : cannot write calibration on file - error %d\n", 1299 result); 1300 return result; 1301 } 1302 1303 return INV_SUCCESS; 1304} 1305 1306 1307 1308/* ----------------------------------------------------------------------- 1309 accel interface functions 1310 -----------------------------------------------------------------------*/ 1311 1312/** 1313 * @internal 1314 * @brief Reads data for X, Y, and Z axis from the accelerometer device. 1315 * Used only if an accelerometer has been setup using the 1316 * inv_test_setup_accel() API. 1317 * Takes care of the accelerometer endianess according to how the 1318 * device has been described in the corresponding accelerometer driver 1319 * file. 1320 * @param mlsl_handle 1321 * serial interface handle to allow serial communication with the 1322 * device, both gyro and accelerometer. 1323 * @param slave 1324 * a pointer to the descriptor of the slave accelerometer device 1325 * in use. Contains the necessary information to operate, read, 1326 * and communicate with the accelerometer device of choice. 1327 * See the declaration of struct ext_slave_descr in mpu.h. 1328 * @param pdata 1329 * a pointer to the platform info of the slave accelerometer 1330 * device in use. Describes how the device is oriented and 1331 * mounted on host platform's PCB. 1332 * @param vals 1333 * output pointer to return the accelerometer's X, Y, and Z axis 1334 * sensor data collected. 1335 * @return 0 on success or a non-zero error code on error. 1336 */ 1337static inv_error_t test_get_data( 1338 void *mlsl_handle, 1339 struct mldl_cfg *mputestCfgPtr, 1340 short *vals) 1341{ 1342 inv_error_t result; 1343 unsigned char data[20]; 1344 struct ext_slave_descr *slave = mputestCfgPtr->accel; 1345#ifndef CONFIG_MPU_SENSORS_MPU3050 1346 struct ext_slave_platform_data *pdata = &mputestCfgPtr->pdata->accel; 1347#endif 1348 1349#ifdef CONFIG_MPU_SENSORS_MPU3050 1350 result = inv_serial_read(mlsl_handle, mputestCfgPtr->addr, 0x23, 1351 6, data); 1352#else 1353 result = inv_serial_read(mlsl_handle, pdata->address, slave->read_reg, 1354 slave->read_len, data); 1355#endif 1356 if (result) { 1357 LOG_RESULT_LOCATION(result); 1358 return result; 1359 } 1360 1361 if (VERBOSE_OUT) { 1362 MPL_LOGI("Accel : 0x%02X%02X 0x%02X%02X 0x%02X%02X (raw)\n", 1363 ACCEL_UNPACK(data)); 1364 } 1365 1366 if (CHECK_NACKS(data)) { 1367 MPL_LOGI("Error fetching data from the accelerometer : " 1368 "all bytes read 0xff\n"); 1369 return INV_ERROR_SERIAL_READ; 1370 } 1371 1372 if (slave->endian == EXT_SLAVE_BIG_ENDIAN) { 1373 vals[0] = CHARS_TO_SHORT(&data[0]); 1374 vals[1] = CHARS_TO_SHORT(&data[2]); 1375 vals[2] = CHARS_TO_SHORT(&data[4]); 1376 } else { 1377 vals[0] = CHARS_TO_SHORT_SWAPPED(&data[0]); 1378 vals[1] = CHARS_TO_SHORT_SWAPPED(&data[2]); 1379 vals[2] = CHARS_TO_SHORT_SWAPPED(&data[4]); 1380 } 1381 1382 if (VERBOSE_OUT) { 1383 MPL_LOGI("Accel : %+13d %+13d %+13d (LSB)\n", 1384 vals[0], vals[1], vals[2]); 1385 } 1386 return INV_SUCCESS; 1387} 1388 1389#ifdef __cplusplus 1390} 1391#endif 1392 1393/** 1394 * @} 1395 */ 1396 1397