1 /*
2  * Author: Jon Trulson <jtrulson@ics.com>
3  * Copyright (c) 2015 Intel Corporation.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #pragma once
25 
26 #include <string>
27 #include <mraa/common.hpp>
28 #include <mraa/i2c.hpp>
29 
30 #define AK8975_I2C_BUS 0
31 #define AK8975_DEFAULT_I2C_ADDR 0x0c
32 
33 namespace upm {
34 
35   /**
36    * @library mpu9150
37    * @sensor ak8975
38    * @comname AK8975 3-axis Magnetometer
39    * @altname AK9875
40    * @type compass
41    * @man grove
42    * @con i2c
43    *
44    * @brief API for the AK8975 magnetometer
45    *
46    * This is a 3-axis magnetometer, which can be used alone, or
47    * coupled with another device (such as the mcu9150 9-axis motion
48    * sensor).
49    *
50    * @snippet ak8975.cxx Interesting
51    */
52   class AK8975 {
53   public:
54 
55     /**
56      * AK8975 registers
57      */
58     typedef enum {
59       REG_WIA                   = 0x00, // device id
60 
61       REG_INFO                  = 0x01, // undocumented (AK proprietary data)
62 
63       REG_ST1                   = 0x02, // status 1
64 
65       REG_HXL                   = 0x03, // magnetometer data, X axis low byte
66       REG_HXH                   = 0x04, // magnetometer data, X axis high byte
67       REG_HYL                   = 0x05,
68       REG_HYH                   = 0x06,
69       REG_HZL                   = 0x07,
70       REG_HZH                   = 0x08,
71 
72       REG_ST2                   = 0x09, // status 2
73 
74       REG_CNTL                  = 0x0a, // control
75 
76       // REG_RSV 0x0b reserved
77 
78       REG_ASTC                  = 0x0c, // self test (internal mag field)
79 
80       // REG_TS1, REG_TS2 0x0d, 0x0e reserved/factory test
81 
82       // REG_I2CDIS 0x0f, I2C disable.  Not a good idea to use or support.
83       //  write a 0x1b to disable i2c.  This requires a power cycle to undo.
84 
85       // These registers hold factory calibrated co-efficients needed to
86       // properly compensate for production variations.  They can only be
87       // read when device is in fuse mode.  They are used to adjust the
88       // measured mag field values.
89       REG_ASAX                  = 0x10, // X calibration
90       REG_ASAY                  = 0x11,
91       REG_ASAZ                  = 0x12
92     } AK8975_REG_T;
93 
94     /**
95      * ST1 bits
96      */
97     typedef enum {
98       ST1_DRDY                  = 0x01 // data ready bit
99     } ST1_BITS_T;
100 
101     /**
102      * ST2 bits
103      */
104     typedef enum {
105       ST2_DERR                  = 0x04, // data error
106       ST2_HOFL                  = 0x08  // measurement overflow
107     } ST2_BITS_T;
108 
109     /**
110      * CNTL register, operating mode values
111      */
112     typedef enum {
113       CNTL_PWRDWN               = 0x00, // power down
114       CNTL_MEASURE              = 0x01, // single measurement
115       CNTL_SELFTEST             = 0x08,
116       CNTL_FUSE_ACCESS          = 0x0f  // access fuse (coeff) registers
117     } CNTL_MODES_T;
118 
119     /**
120      * ASTC (self test control) bits
121      */
122     typedef enum {
123       ASTC_SELF                 = 0x40 // enable self test
124     } ASTC_BITS_T;
125 
126     /**
127      * ak8975 constructor
128      *
129      * @param bus i2c bus to use
130      * @param address the address for this device
131      */
132     AK8975(int bus=AK8975_I2C_BUS, uint8_t address=AK8975_DEFAULT_I2C_ADDR);
133 
134     /**
135      * AK8975 Destructor
136      */
137     ~AK8975();
138 
139     /**
140      * set up initial values and start operation
141      *
142      * @param dsr the data sampling rate: one of the DSR_BITS_T values
143      * @return true if successful
144      */
145     bool init();
146 
147     /**
148      * put the chip into a specific mode
149      *
150      * @param mode one of the CNTL_MODES_T values
151      * @return true if successful
152      */
153     bool setMode(CNTL_MODES_T mode);
154 
155     /**
156      * check to see if the ST1_DRDY bit is set, indicating the device
157      * can accept commands
158      *
159      * @return true if device is ready, false otherwise
160      */
161     bool isReady();
162 
163     /**
164      * check to see if device is ready and sleep/retry if not.
165      * Returns once device indicates it's ready.
166      *
167      * @return true if device is ready, false if retries exhausted
168      */
169     bool waitforDeviceReady();
170 
171     /**
172      * take a measurement
173      *
174      * @param selfTest true if we are running a self test, false
175      * (default) otherwise.
176      * @return true if successful, false otherwise
177      */
178     bool update(bool selfTest=false);
179 
180     /**
181      * do a self test sequence.  When self test is executed, the
182      * device activates internal calibrated magnets, and measures
183      * them, updating the measurement registers.  Once complete, the
184      * data can be read as usual (getMagnetometer()) and the returned
185      * values compared against the following limits to determine
186      * correctness:
187      *
188      * -100 < X < +100; -100 < Y < +100; -1000 < Z < -300
189      *
190      * @return true if successful, false otherwise
191      */
192     bool selfTest();
193 
194     /**
195      * return the compensated values for the x, y, and z axes.  The
196      * unit of measurement is in micro-teslas (uT).
197      *
198      * @param x pointer to returned X axis value
199      * @param y pointer to returned Y axis value
200      * @param z pointer to returned Z axis value
201      */
202     void getMagnetometer(float *x, float *y, float *z);
203 
204 
205   protected:
206     /**
207      * compute a compensated magnetometer axis value, based on the raw
208      * axis value and a per-device, per-axis adjustment coefficient
209      * that was read and stored at init() time.
210      *
211      * @param value the raw axis value to compensate
212      * @param adj the adjustment coefficient
213      * @return true if successful
214      */
215     float adjustValue(float value, float adj);
216 
217     // compensation coefficients (factory set) for this device
218     float m_xCoeff;
219     float m_yCoeff;
220     float m_zCoeff;
221 
222     // uncompensated magnetometer readings
223     float m_xData;
224     float m_yData;
225     float m_zData;
226 
227   private:
228     mraa::I2c m_i2c;
229     uint8_t m_addr;
230 
231   };
232 }
233 
234 
235