Pololu Zumo Shield Arduino Library
LSM303.cpp
1 #include <LSM303.h>
2 #include <Wire.h>
3 #include <math.h>
4 
5 // Defines ////////////////////////////////////////////////////////////////
6 
7 // The Arduino two-wire interface uses a 7-bit number for the address,
8 // and sets the last bit correctly based on reads and writes
9 #define D_SA0_HIGH_ADDRESS 0b0011101
10 #define D_SA0_LOW_ADDRESS 0b0011110
11 #define DLHC_DLM_DLH_MAG_ADDRESS 0b0011110
12 #define DLHC_DLM_DLH_ACC_SA0_HIGH_ADDRESS 0b0011001
13 #define DLM_DLH_ACC_SA0_LOW_ADDRESS 0b0011000
14 
15 #define TEST_REG_ERROR -1
16 
17 #define D_WHO_ID 0x49
18 #define DLM_WHO_ID 0x3C
19 
20 // Constructors ////////////////////////////////////////////////////////////////
21 
22 LSM303::LSM303(void)
23 {
24  /*
25  These values lead to an assumed magnetometer bias of 0.
26  Use the Calibrate example program to determine appropriate values
27  for your particular unit. The Heading example demonstrates how to
28  adjust these values in your own sketch.
29  */
30  m_min = (LSM303::vector<int16_t>){-32767, -32767, -32767};
31  m_max = (LSM303::vector<int16_t>){+32767, +32767, +32767};
32 
33  _device = device_auto;
34 
35  io_timeout = 0; // 0 = no timeout
36  did_timeout = false;
37 }
38 
39 // Public Methods //////////////////////////////////////////////////////////////
40 
41 // Did a timeout occur in readAcc(), readMag(), or read() since the last call to timeoutOccurred()?
42 bool LSM303::timeoutOccurred()
43 {
44  bool tmp = did_timeout;
45  did_timeout = false;
46  return tmp;
47 }
48 
49 void LSM303::setTimeout(unsigned int timeout)
50 {
51  io_timeout = timeout;
52 }
53 
54 unsigned int LSM303::getTimeout()
55 {
56  return io_timeout;
57 }
58 
59 bool LSM303::init(deviceType device, sa0State sa0)
60 {
61  // perform auto-detection unless device type and SA0 state were both specified
62  if (device == device_auto || sa0 == sa0_auto)
63  {
64  // check for LSM303D if device is unidentified or was specified to be this type
65  if (device == device_auto || device == device_D)
66  {
67  // check SA0 high address unless SA0 was specified to be low
68  if (sa0 != sa0_low && testReg(D_SA0_HIGH_ADDRESS, WHO_AM_I) == D_WHO_ID)
69  {
70  // device responds to address 0011101 with D ID; it's a D with SA0 high
71  device = device_D;
72  sa0 = sa0_high;
73  }
74  // check SA0 low address unless SA0 was specified to be high
75  else if (sa0 != sa0_high && testReg(D_SA0_LOW_ADDRESS, WHO_AM_I) == D_WHO_ID)
76  {
77  // device responds to address 0011110 with D ID; it's a D with SA0 low
78  device = device_D;
79  sa0 = sa0_low;
80  }
81  }
82 
83  // check for LSM303DLHC, DLM, DLH if device is still unidentified or was specified to be one of these types
84  if (device == device_auto || device == device_DLHC || device == device_DLM || device == device_DLH)
85  {
86  // check SA0 high address unless SA0 was specified to be low
87  if (sa0 != sa0_low && testReg(DLHC_DLM_DLH_ACC_SA0_HIGH_ADDRESS, CTRL_REG1_A) != TEST_REG_ERROR)
88  {
89  // device responds to address 0011001; it's a DLHC, DLM with SA0 high, or DLH with SA0 high
90  sa0 = sa0_high;
91  if (device == device_auto)
92  {
93  // use magnetometer WHO_AM_I register to determine device type
94  //
95  // DLHC seems to respond to WHO_AM_I request the same way as DLM, even though this
96  // register isn't documented in its datasheet. Since the DLHC accelerometer address is the
97  // same as the DLM with SA0 high, but Pololu DLM boards pull SA0 low by default, we'll
98  // guess that a device whose accelerometer responds to the SA0 high address and whose
99  // magnetometer gives the DLM ID is actually a DLHC.
100  device = (testReg(DLHC_DLM_DLH_MAG_ADDRESS, WHO_AM_I_M) == DLM_WHO_ID) ? device_DLHC : device_DLH;
101  }
102  }
103  // check SA0 low address unless SA0 was specified to be high
104  else if (sa0 != sa0_high && testReg(DLM_DLH_ACC_SA0_LOW_ADDRESS, CTRL_REG1_A) != TEST_REG_ERROR)
105  {
106  // device responds to address 0011000; it's a DLM with SA0 low or DLH with SA0 low
107  sa0 = sa0_low;
108  if (device == device_auto)
109  {
110  // use magnetometer WHO_AM_I register to determine device type
111  device = (testReg(DLHC_DLM_DLH_MAG_ADDRESS, WHO_AM_I_M) == DLM_WHO_ID) ? device_DLM : device_DLH;
112  }
113  }
114  }
115 
116  // make sure device and SA0 were successfully detected; otherwise, indicate failure
117  if (device == device_auto || sa0 == sa0_auto)
118  {
119  return false;
120  }
121  }
122 
123  _device = device;
124 
125  // set device addresses and translated register addresses
126  switch (device)
127  {
128  case device_D:
129  acc_address = mag_address = (sa0 == sa0_high) ? D_SA0_HIGH_ADDRESS : D_SA0_LOW_ADDRESS;
130  translated_regs[-OUT_X_L_M] = D_OUT_X_L_M;
131  translated_regs[-OUT_X_H_M] = D_OUT_X_H_M;
132  translated_regs[-OUT_Y_L_M] = D_OUT_Y_L_M;
133  translated_regs[-OUT_Y_H_M] = D_OUT_Y_H_M;
134  translated_regs[-OUT_Z_L_M] = D_OUT_Z_L_M;
135  translated_regs[-OUT_Z_H_M] = D_OUT_Z_H_M;
136  break;
137 
138  case device_DLHC:
139  acc_address = DLHC_DLM_DLH_ACC_SA0_HIGH_ADDRESS; // DLHC doesn't have configurable SA0 but uses same acc address as DLM/DLH with SA0 high
140  mag_address = DLHC_DLM_DLH_MAG_ADDRESS;
141  translated_regs[-OUT_X_H_M] = DLHC_OUT_X_H_M;
142  translated_regs[-OUT_X_L_M] = DLHC_OUT_X_L_M;
143  translated_regs[-OUT_Y_H_M] = DLHC_OUT_Y_H_M;
144  translated_regs[-OUT_Y_L_M] = DLHC_OUT_Y_L_M;
145  translated_regs[-OUT_Z_H_M] = DLHC_OUT_Z_H_M;
146  translated_regs[-OUT_Z_L_M] = DLHC_OUT_Z_L_M;
147  break;
148 
149  case device_DLM:
150  acc_address = (sa0 == sa0_high) ? DLHC_DLM_DLH_ACC_SA0_HIGH_ADDRESS : DLM_DLH_ACC_SA0_LOW_ADDRESS;
151  mag_address = DLHC_DLM_DLH_MAG_ADDRESS;
152  translated_regs[-OUT_X_H_M] = DLM_OUT_X_H_M;
153  translated_regs[-OUT_X_L_M] = DLM_OUT_X_L_M;
154  translated_regs[-OUT_Y_H_M] = DLM_OUT_Y_H_M;
155  translated_regs[-OUT_Y_L_M] = DLM_OUT_Y_L_M;
156  translated_regs[-OUT_Z_H_M] = DLM_OUT_Z_H_M;
157  translated_regs[-OUT_Z_L_M] = DLM_OUT_Z_L_M;
158  break;
159 
160  case device_DLH:
161  acc_address = (sa0 == sa0_high) ? DLHC_DLM_DLH_ACC_SA0_HIGH_ADDRESS : DLM_DLH_ACC_SA0_LOW_ADDRESS;
162  mag_address = DLHC_DLM_DLH_MAG_ADDRESS;
163  translated_regs[-OUT_X_H_M] = DLH_OUT_X_H_M;
164  translated_regs[-OUT_X_L_M] = DLH_OUT_X_L_M;
165  translated_regs[-OUT_Y_H_M] = DLH_OUT_Y_H_M;
166  translated_regs[-OUT_Y_L_M] = DLH_OUT_Y_L_M;
167  translated_regs[-OUT_Z_H_M] = DLH_OUT_Z_H_M;
168  translated_regs[-OUT_Z_L_M] = DLH_OUT_Z_L_M;
169  break;
170  }
171 
172  return true;
173 }
174 
175 /*
176 Enables the LSM303's accelerometer and magnetometer. Also:
177 - Sets sensor full scales (gain) to default power-on values, which are
178  +/- 2 g for accelerometer and +/- 1.3 gauss for magnetometer
179  (+/- 4 gauss on LSM303D).
180 - Selects 50 Hz ODR (output data rate) for accelerometer and 7.5 Hz
181  ODR for magnetometer (6.25 Hz on LSM303D). (These are the ODR
182  settings for which the electrical characteristics are specified in
183  the datasheets.)
184 - Enables high resolution modes (if available).
185 Note that this function will also reset other settings controlled by
186 the registers it writes to.
187 */
188 void LSM303::enableDefault(void)
189 {
190 
191  if (_device == device_D)
192  {
193  // Accelerometer
194 
195  // 0x00 = 0b00000000
196  // AFS = 0 (+/- 2 g full scale)
197  writeReg(CTRL2, 0x00);
198 
199  // 0x57 = 0b01010111
200  // AODR = 0101 (50 Hz ODR); AZEN = AYEN = AXEN = 1 (all axes enabled)
201  writeReg(CTRL1, 0x57);
202 
203  // Magnetometer
204 
205  // 0x64 = 0b01100100
206  // M_RES = 11 (high resolution mode); M_ODR = 001 (6.25 Hz ODR)
207  writeReg(CTRL5, 0x64);
208 
209  // 0x20 = 0b00100000
210  // MFS = 01 (+/- 4 gauss full scale)
211  writeReg(CTRL6, 0x20);
212 
213  // 0x00 = 0b00000000
214  // MLP = 0 (low power mode off); MD = 00 (continuous-conversion mode)
215  writeReg(CTRL7, 0x00);
216  }
217  else
218  {
219  // Accelerometer
220 
221  if (_device == device_DLHC)
222  {
223  // 0x08 = 0b00001000
224  // FS = 00 (+/- 2 g full scale); HR = 1 (high resolution enable)
225  writeAccReg(CTRL_REG4_A, 0x08);
226 
227  // 0x47 = 0b01000111
228  // ODR = 0100 (50 Hz ODR); LPen = 0 (normal mode); Zen = Yen = Xen = 1 (all axes enabled)
229  writeAccReg(CTRL_REG1_A, 0x47);
230  }
231  else // DLM, DLH
232  {
233  // 0x00 = 0b00000000
234  // FS = 00 (+/- 2 g full scale)
235  writeAccReg(CTRL_REG4_A, 0x00);
236 
237  // 0x27 = 0b00100111
238  // PM = 001 (normal mode); DR = 00 (50 Hz ODR); Zen = Yen = Xen = 1 (all axes enabled)
239  writeAccReg(CTRL_REG1_A, 0x27);
240  }
241 
242  // Magnetometer
243 
244  // 0x0C = 0b00001100
245  // DO = 011 (7.5 Hz ODR)
246  writeMagReg(CRA_REG_M, 0x0C);
247 
248  // 0x20 = 0b00100000
249  // GN = 001 (+/- 1.3 gauss full scale)
250  writeMagReg(CRB_REG_M, 0x20);
251 
252  // 0x00 = 0b00000000
253  // MD = 00 (continuous-conversion mode)
254  writeMagReg(MR_REG_M, 0x00);
255  }
256 }
257 
258 // Writes an accelerometer register
259 void LSM303::writeAccReg(byte reg, byte value)
260 {
261  Wire.beginTransmission(acc_address);
262  Wire.write(reg);
263  Wire.write(value);
264  last_status = Wire.endTransmission();
265 }
266 
267 // Reads an accelerometer register
268 byte LSM303::readAccReg(byte reg)
269 {
270  byte value;
271 
272  Wire.beginTransmission(acc_address);
273  Wire.write(reg);
274  last_status = Wire.endTransmission();
275  Wire.requestFrom(acc_address, (byte)1);
276  value = Wire.read();
277  Wire.endTransmission();
278 
279  return value;
280 }
281 
282 // Writes a magnetometer register
283 void LSM303::writeMagReg(byte reg, byte value)
284 {
285  Wire.beginTransmission(mag_address);
286  Wire.write(reg);
287  Wire.write(value);
288  last_status = Wire.endTransmission();
289 }
290 
291 // Reads a magnetometer register
292 byte LSM303::readMagReg(int reg)
293 {
294  byte value;
295 
296  // if dummy register address (magnetometer Y/Z), look up actual translated address (based on device type)
297  if (reg < 0)
298  {
299  reg = translated_regs[-reg];
300  }
301 
302  Wire.beginTransmission(mag_address);
303  Wire.write(reg);
304  last_status = Wire.endTransmission();
305  Wire.requestFrom(mag_address, (byte)1);
306  value = Wire.read();
307  Wire.endTransmission();
308 
309  return value;
310 }
311 
312 void LSM303::writeReg(byte reg, byte value)
313 {
314  // mag address == acc_address for LSM303D, so it doesn't really matter which one we use.
315  if (_device == device_D || reg < CTRL_REG1_A)
316  {
317  writeMagReg(reg, value);
318  }
319  else
320  {
321  writeAccReg(reg, value);
322  }
323 }
324 
325 // Note that this function will not work for reading TEMP_OUT_H_M and TEMP_OUT_L_M on the DLHC.
326 // To read those two registers, use readMagReg() instead.
327 byte LSM303::readReg(int reg)
328 {
329  // mag address == acc_address for LSM303D, so it doesn't really matter which one we use.
330  // Use readMagReg so it can translate OUT_[XYZ]_[HL]_M
331  if (_device == device_D || reg < CTRL_REG1_A)
332  {
333  return readMagReg(reg);
334  }
335  else
336  {
337  return readAccReg(reg);
338  }
339 }
340 
341 // Reads the 3 accelerometer channels and stores them in vector a
342 void LSM303::readAcc(void)
343 {
344  Wire.beginTransmission(acc_address);
345  // assert the MSB of the address to get the accelerometer
346  // to do slave-transmit subaddress updating.
347  Wire.write(OUT_X_L_A | (1 << 7));
348  last_status = Wire.endTransmission();
349  Wire.requestFrom(acc_address, (byte)6);
350 
351  unsigned int millis_start = millis();
352  while (Wire.available() < 6) {
353  if (io_timeout > 0 && ((unsigned int)millis() - millis_start) > io_timeout)
354  {
355  did_timeout = true;
356  return;
357  }
358  }
359 
360  byte xla = Wire.read();
361  byte xha = Wire.read();
362  byte yla = Wire.read();
363  byte yha = Wire.read();
364  byte zla = Wire.read();
365  byte zha = Wire.read();
366 
367  // combine high and low bytes
368  // This no longer drops the lowest 4 bits of the readings from the DLH/DLM/DLHC, which are always 0
369  // (12-bit resolution, left-aligned). The D has 16-bit resolution
370  a.x = (int16_t)(xha << 8 | xla);
371  a.y = (int16_t)(yha << 8 | yla);
372  a.z = (int16_t)(zha << 8 | zla);
373 }
374 
375 // Reads the 3 magnetometer channels and stores them in vector m
376 void LSM303::readMag(void)
377 {
378  Wire.beginTransmission(mag_address);
379  // If LSM303D, assert MSB to enable subaddress updating
380  // OUT_X_L_M comes first on D, OUT_X_H_M on others
381  Wire.write((_device == device_D) ? translated_regs[-OUT_X_L_M] | (1 << 7) : translated_regs[-OUT_X_H_M]);
382  last_status = Wire.endTransmission();
383  Wire.requestFrom(mag_address, (byte)6);
384 
385  unsigned int millis_start = millis();
386  while (Wire.available() < 6) {
387  if (io_timeout > 0 && ((unsigned int)millis() - millis_start) > io_timeout)
388  {
389  did_timeout = true;
390  return;
391  }
392  }
393 
394  byte xlm, xhm, ylm, yhm, zlm, zhm;
395 
396  if (_device == device_D)
397  {
398  // D: X_L, X_H, Y_L, Y_H, Z_L, Z_H
399  xlm = Wire.read();
400  xhm = Wire.read();
401  ylm = Wire.read();
402  yhm = Wire.read();
403  zlm = Wire.read();
404  zhm = Wire.read();
405  }
406  else
407  {
408  // DLHC, DLM, DLH: X_H, X_L...
409  xhm = Wire.read();
410  xlm = Wire.read();
411 
412  if (_device == device_DLH)
413  {
414  // DLH: ...Y_H, Y_L, Z_H, Z_L
415  yhm = Wire.read();
416  ylm = Wire.read();
417  zhm = Wire.read();
418  zlm = Wire.read();
419  }
420  else
421  {
422  // DLM, DLHC: ...Z_H, Z_L, Y_H, Y_L
423  zhm = Wire.read();
424  zlm = Wire.read();
425  yhm = Wire.read();
426  ylm = Wire.read();
427  }
428  }
429 
430  // combine high and low bytes
431  m.x = (int16_t)(xhm << 8 | xlm);
432  m.y = (int16_t)(yhm << 8 | ylm);
433  m.z = (int16_t)(zhm << 8 | zlm);
434 }
435 
436 // Reads all 6 channels of the LSM303 and stores them in the object variables
437 void LSM303::read(void)
438 {
439  readAcc();
440  readMag();
441 }
442 
443 /*
444 Returns the angular difference in the horizontal plane between a
445 default vector and north, in degrees.
446 
447 The default vector here is chosen to point along the surface of the
448 PCB, in the direction of the top of the text on the silkscreen.
449 This is the +X axis on the Pololu LSM303D carrier and the -Y axis on
450 the Pololu LSM303DLHC, LSM303DLM, and LSM303DLH carriers.
451 */
452 float LSM303::heading(void)
453 {
454  if (_device == device_D)
455  {
456  return heading((vector<int>){1, 0, 0});
457  }
458  else
459  {
460  return heading((vector<int>){0, -1, 0});
461  }
462 }
463 
464 void LSM303::vector_normalize(vector<float> *a)
465 {
466  float mag = sqrt(vector_dot(a, a));
467  a->x /= mag;
468  a->y /= mag;
469  a->z /= mag;
470 }
471 
472 // Private Methods //////////////////////////////////////////////////////////////
473 
474 int LSM303::testReg(byte address, regAddr reg)
475 {
476  Wire.beginTransmission(address);
477  Wire.write((byte)reg);
478  if (Wire.endTransmission() != 0)
479  {
480  return TEST_REG_ERROR;
481  }
482 
483  Wire.requestFrom(address, (byte)1);
484  if (Wire.available())
485  {
486  return Wire.read();
487  }
488  else
489  {
490  return TEST_REG_ERROR;
491  }
492 }
LSM303::vector< int16_t >