summaryrefslogtreecommitdiff
path: root/peripheral/libupm/src/mpu9150/ak8975.h
blob: ba9788cdf1d2e0395ca201a49391018ed5d48fc4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/*
 * Author: Jon Trulson <jtrulson@ics.com>
 * Copyright (c) 2015 Intel Corporation.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#pragma once

#include <string>
#include <mraa/common.hpp>
#include <mraa/i2c.hpp>

#define AK8975_I2C_BUS 0
#define AK8975_DEFAULT_I2C_ADDR 0x0c

namespace upm {
  
  /**
   * @library mpu9150
   * @sensor ak8975
   * @comname AK8975 3-axis Magnetometer
   * @altname AK9875
   * @type compass
   * @man seeed
   * @con i2c
   *
   * @brief API for the AK8975 magnetometer
   *
   * This is a 3-axis magnetometer, which can be used alone, or
   * coupled with another device (such as the mcu9150 9-axis motion
   * sensor). 
   *
   * @snippet ak8975.cxx Interesting
   */
  class AK8975 {
  public:

    /**
     * AK8975 registers
     */
    typedef enum {
      REG_WIA                   = 0x00, // device id

      REG_INFO                  = 0x01, // undocumented (AK proprietary data)

      REG_ST1                   = 0x02, // status 1

      REG_HXL                   = 0x03, // magnetometer data, X axis low byte
      REG_HXH                   = 0x04, // magnetometer data, X axis high byte
      REG_HYL                   = 0x05,
      REG_HYH                   = 0x06,
      REG_HZL                   = 0x07,
      REG_HZH                   = 0x08,

      REG_ST2                   = 0x09, // status 2

      REG_CNTL                  = 0x0a, // control

      // REG_RSV 0x0b reserved

      REG_ASTC                  = 0x0c, // self test (internal mag field)

      // REG_TS1, REG_TS2 0x0d, 0x0e reserved/factory test

      // REG_I2CDIS 0x0f, I2C disable.  Not a good idea to use or support.
      //  write a 0x1b to disable i2c.  This requires a power cycle to undo.

      // These registers hold factory calibrated co-efficients needed to
      // properly compensate for production variations.  They can only be
      // read when device is in fuse mode.  They are used to adjust the
      // measured mag field values.
      REG_ASAX                  = 0x10, // X calibration
      REG_ASAY                  = 0x11,
      REG_ASAZ                  = 0x12
    } AK8975_REG_T;
    
    /**
     * ST1 bits
     */
    typedef enum {
      ST1_DRDY                  = 0x01 // data ready bit
    } ST1_BITS_T;
    
    /**
     * ST2 bits
     */
    typedef enum {
      ST2_DERR                  = 0x04, // data error
      ST2_HOFL                  = 0x08  // measurement overflow
    } ST2_BITS_T;
    
    /**
     * CNTL register, operating mode values
     */
    typedef enum {
      CNTL_PWRDWN               = 0x00, // power down
      CNTL_MEASURE              = 0x01, // single measurement
      CNTL_SELFTEST             = 0x08,
      CNTL_FUSE_ACCESS          = 0x0f  // access fuse (coeff) registers
    } CNTL_MODES_T;
    
    /**
     * ASTC (self test control) bits
     */
    typedef enum {
      ASTC_SELF                 = 0x40 // enable self test
    } ASTC_BITS_T;

    /**
     * ak8975 constructor
     *
     * @param bus i2c bus to use
     * @param address the address for this device
     */
    AK8975(int bus=AK8975_I2C_BUS, uint8_t address=AK8975_DEFAULT_I2C_ADDR);

    /**
     * AK8975 Destructor
     */
    ~AK8975();
    
    /**
     * set up initial values and start operation
     *
     * @param dsr the data sampling rate: one of the DSR_BITS_T values
     * @return true if successful
     */
    bool init();

    /**
     * put the chip into a specific mode
     *
     * @param mode one of the CNTL_MODES_T values
     * @return true if successful
     */
    bool setMode(CNTL_MODES_T mode);

    /**
     * check to see if the ST1_DRDY bit is set, indicating the device
     * can accept commands
     *
     * @return true if device is ready, false otherwise
     */
    bool isReady();

    /**
     * check to see if device is ready and sleep/retry if not.
     * Returns once device indicates it's ready.
     *
     * @return true if device is ready, false if retries exhausted
     */
    bool waitforDeviceReady();

    /**
     * take a measurement
     *
     * @param selfTest true if we are running a self test, false
     * (default) otherwise.
     * @return true if successful, false otherwise
     */
    bool update(bool selfTest=false);

    /**
     * do a self test sequence.  When self test is executed, the
     * device activates internal calibrated magnets, and measures
     * them, updating the measurement registers.  Once complete, the
     * data can be read as usual (getMagnetometer()) and the returned
     * values compared against the following limits to determine
     * correctness:
     *
     * -100 < X < +100; -100 < Y < +100; -1000 < Z < -300
     *
     * @return true if successful, false otherwise
     */
    bool selfTest();

    /**
     * return the compensated values for the x, y, and z axes.  The
     * unit of measurement is in micro-teslas (uT).
     *
     * @param x pointer to returned X axis value
     * @param y pointer to returned Y axis value
     * @param z pointer to returned Z axis value
     */
    void getMagnetometer(float *x, float *y, float *z);


  protected:
    /**
     * compute a compensated magnetometer axis value, based on the raw
     * axis value and a per-device, per-axis adjustment coefficient
     * that was read and stored at init() time.
     *
     * @param value the raw axis value to compensate
     * @param adj the adjustment coefficient 
     * @return true if successful
     */
    float adjustValue(float value, float adj);

    // compensation coefficients (factory set) for this device
    float m_xCoeff;
    float m_yCoeff;
    float m_zCoeff;

    // uncompensated magnetometer readings
    float m_xData;
    float m_yData;
    float m_zData;

  private:
    mraa::I2c m_i2c;
    uint8_t m_addr;

  };
}