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