source: trunk/Arduino/GSM/libraries/LSM303/LSM303.cpp

Last change on this file was 17969, checked in by dneise, 10 years ago
adding libraries folder, with LSM303 lib, this takes precedence over any locally installed LSM303 library, e.g. in HOME/arduino-1.0.6/libraries.
File size: 16.4 KB
Line 
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 // D with SA0 high
10#define D_SA0_LOW_ADDRESS 0b0011110 // D with SA0 low or non-D magnetometer
11#define NON_D_MAG_ADDRESS 0b0011110 // D with SA0 low or non-D magnetometer
12#define NON_D_ACC_SA0_LOW_ADDRESS 0b0011000 // non-D accelerometer with SA0 low
13#define NON_D_ACC_SA0_HIGH_ADDRESS 0b0011001 // non-D accelerometer with SA0 high
14
15#define TEST_REG_NACK -1
16
17#define D_WHO_ID 0x49
18#define DLM_WHO_ID 0x3C
19
20// Constructors ////////////////////////////////////////////////////////////////
21
22LSM303::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()?
42bool LSM303::timeoutOccurred()
43{
44 bool tmp = did_timeout;
45 did_timeout = false;
46 return tmp;
47
48}
49
50void LSM303::setTimeout(unsigned int timeout)
51{
52 io_timeout = timeout;
53}
54
55unsigned int LSM303::getTimeout()
56{
57 return io_timeout;
58}
59
60bool LSM303::init(deviceType device, sa0State sa0)
61{
62 // determine device type if necessary
63 if (device == device_auto)
64 {
65 if (testReg(D_SA0_HIGH_ADDRESS, WHO_AM_I) == D_WHO_ID)
66 {
67 // device responds to address 0011101 with D ID; it's a D with SA0 high
68 device = device_D;
69 sa0 = sa0_high;
70 }
71 else if (testReg(D_SA0_LOW_ADDRESS, WHO_AM_I) == D_WHO_ID)
72 {
73 // device responds to address 0011110 with D ID; it's a D with SA0 low
74 device = device_D;
75 sa0 = sa0_low;
76 }
77 // Remaining possibilities: DLHC, DLM, or DLH. DLHC seems to respond to WHO_AM_I request the
78 // same way as DLM, even though this register isn't documented in its datasheet, so instead,
79 // guess if it's a DLHC based on acc address (Pololu boards pull SA0 low on DLM and DLH;
80 // DLHC doesn't have SA0 but uses same acc address as DLH/DLM with SA0 high).
81 else if (testReg(NON_D_ACC_SA0_HIGH_ADDRESS, CTRL_REG1_A) != TEST_REG_NACK)
82 {
83 // device responds to address 0011001; guess that it's a DLHC
84 device = device_DLHC;
85 sa0 = sa0_high;
86 }
87 // Remaining possibilities: DLM or DLH. Check acc with SA0 low address to make sure it's responsive
88 else if (testReg(NON_D_ACC_SA0_LOW_ADDRESS, CTRL_REG1_A) != TEST_REG_NACK)
89 {
90 // device responds to address 0011000 with DLM ID; guess that it's a DLM
91 sa0 = sa0_low;
92
93 // Now check WHO_AM_I_M
94 if (testReg(NON_D_MAG_ADDRESS, WHO_AM_I_M) == DLM_WHO_ID)
95 {
96 device = device_DLM;
97 }
98 else
99 {
100 device = device_DLH;
101 }
102 }
103 else
104 {
105 // device hasn't responded meaningfully, so give up
106 return false;
107 }
108 }
109
110 // determine SA0 if necessary
111 if (sa0 == sa0_auto)
112 {
113 if (device == device_D)
114 {
115 if (testReg(D_SA0_HIGH_ADDRESS, WHO_AM_I) == D_WHO_ID)
116 {
117 sa0 = sa0_high;
118 }
119 else if (testReg(D_SA0_LOW_ADDRESS, WHO_AM_I) == D_WHO_ID)
120 {
121 sa0 = sa0_low;
122 }
123 else
124 {
125 // no response on either possible address; give up
126 return false;
127 }
128 }
129 else if (device == device_DLM || device == device_DLH)
130 {
131 if (testReg(NON_D_ACC_SA0_HIGH_ADDRESS, CTRL_REG1_A) != TEST_REG_NACK)
132 {
133 sa0 = sa0_high;
134 }
135 else if (testReg(NON_D_ACC_SA0_LOW_ADDRESS, CTRL_REG1_A) != TEST_REG_NACK)
136 {
137 sa0 = sa0_low;
138 }
139 else
140 {
141 // no response on either possible address; give up
142 return false;
143 }
144 }
145 }
146
147 _device = device;
148
149 // set device addresses and translated register addresses
150 switch (device)
151 {
152 case device_D:
153 acc_address = mag_address = (sa0 == sa0_high) ? D_SA0_HIGH_ADDRESS : D_SA0_LOW_ADDRESS;
154 translated_regs[-OUT_X_L_M] = D_OUT_X_L_M;
155 translated_regs[-OUT_X_H_M] = D_OUT_X_H_M;
156 translated_regs[-OUT_Y_L_M] = D_OUT_Y_L_M;
157 translated_regs[-OUT_Y_H_M] = D_OUT_Y_H_M;
158 translated_regs[-OUT_Z_L_M] = D_OUT_Z_L_M;
159 translated_regs[-OUT_Z_H_M] = D_OUT_Z_H_M;
160 break;
161
162 case device_DLHC:
163 acc_address = NON_D_ACC_SA0_HIGH_ADDRESS; // DLHC doesn't have SA0 but uses same acc address as DLH/DLM with SA0 high
164 mag_address = NON_D_MAG_ADDRESS;
165 translated_regs[-OUT_X_H_M] = DLHC_OUT_X_H_M;
166 translated_regs[-OUT_X_L_M] = DLHC_OUT_X_L_M;
167 translated_regs[-OUT_Y_H_M] = DLHC_OUT_Y_H_M;
168 translated_regs[-OUT_Y_L_M] = DLHC_OUT_Y_L_M;
169 translated_regs[-OUT_Z_H_M] = DLHC_OUT_Z_H_M;
170 translated_regs[-OUT_Z_L_M] = DLHC_OUT_Z_L_M;
171 break;
172
173 case device_DLM:
174 acc_address = (sa0 == sa0_high) ? NON_D_ACC_SA0_HIGH_ADDRESS : NON_D_ACC_SA0_LOW_ADDRESS;
175 mag_address = NON_D_MAG_ADDRESS;
176 translated_regs[-OUT_X_H_M] = DLM_OUT_X_H_M;
177 translated_regs[-OUT_X_L_M] = DLM_OUT_X_L_M;
178 translated_regs[-OUT_Y_H_M] = DLM_OUT_Y_H_M;
179 translated_regs[-OUT_Y_L_M] = DLM_OUT_Y_L_M;
180 translated_regs[-OUT_Z_H_M] = DLM_OUT_Z_H_M;
181 translated_regs[-OUT_Z_L_M] = DLM_OUT_Z_L_M;
182 break;
183
184 case device_DLH:
185 acc_address = (sa0 == sa0_high) ? NON_D_ACC_SA0_HIGH_ADDRESS : NON_D_ACC_SA0_LOW_ADDRESS;
186 mag_address = NON_D_MAG_ADDRESS;
187 translated_regs[-OUT_X_H_M] = DLH_OUT_X_H_M;
188 translated_regs[-OUT_X_L_M] = DLH_OUT_X_L_M;
189 translated_regs[-OUT_Y_H_M] = DLH_OUT_Y_H_M;
190 translated_regs[-OUT_Y_L_M] = DLH_OUT_Y_L_M;
191 translated_regs[-OUT_Z_H_M] = DLH_OUT_Z_H_M;
192 translated_regs[-OUT_Z_L_M] = DLH_OUT_Z_L_M;
193 break;
194 }
195 return true;
196}
197
198/*
199Enables the LSM303's accelerometer and magnetometer. Also:
200- Sets sensor full scales (gain) to default power-on values, which are
201 +/- 2 g for accelerometer and +/- 1.3 gauss for magnetometer
202 (+/- 4 gauss on LSM303D).
203- Selects 50 Hz ODR (output data rate) for accelerometer and 7.5 Hz
204 ODR for magnetometer (6.25 Hz on LSM303D). (These are the ODR
205 settings for which the electrical characteristics are specified in
206 the datasheets.)
207- Enables high resolution modes (if available).
208Note that this function will also reset other settings controlled by
209the registers it writes to.
210*/
211void LSM303::enableDefault(void)
212{
213
214 if (_device == device_D)
215 {
216 // Accelerometer
217
218 // 0x57 = 0b01010111
219 // AFS = 0 (+/- 2 g full scale)
220 writeReg(CTRL2, 0x00);
221
222 // 0x57 = 0b01010111
223 // AODR = 0101 (50 Hz ODR); AZEN = AYEN = AXEN = 1 (all axes enabled)
224 writeReg(CTRL1, 0x57);
225
226 // Magnetometer
227
228 // 0x64 = 0b01100100
229 // M_RES = 11 (high resolution mode); M_ODR = 001 (6.25 Hz ODR)
230 writeReg(CTRL5, 0x64);
231
232 // 0x20 = 0b00100000
233 // MFS = 01 (+/- 4 gauss full scale)
234 writeReg(CTRL6, 0x20);
235
236 // 0x00 = 0b00000000
237 // MLP = 0 (low power mode off); MD = 00 (continuous-conversion mode)
238 writeReg(CTRL7, 0x00);
239 }
240 else if (_device == device_DLHC)
241 {
242 // Accelerometer
243
244 // 0x08 = 0b00001000
245 // FS = 00 (+/- 2 g full scale); HR = 1 (high resolution enable)
246 writeAccReg(CTRL_REG4_A, 0x08);
247
248 // 0x47 = 0b01000111
249 // ODR = 0100 (50 Hz ODR); LPen = 0 (normal mode); Zen = Yen = Xen = 1 (all axes enabled)
250 writeAccReg(CTRL_REG1_A, 0x47);
251
252 // Magnetometer
253
254 // 0x0C = 0b00001100
255 // DO = 011 (7.5 Hz ODR)
256 writeMagReg(CRA_REG_M, 0x0C);
257
258 // 0x20 = 0b00100000
259 // GN = 001 (+/- 1.3 gauss full scale)
260 writeMagReg(CRB_REG_M, 0x20);
261
262 // 0x00 = 0b00000000
263 // MD = 00 (continuous-conversion mode)
264 writeMagReg(MR_REG_M, 0x00);
265 }
266 else // DLM, DLH
267 {
268 // Accelerometer
269
270 // 0x00 = 0b00000000
271 // FS = 00 (+/- 2 g full scale)
272 writeAccReg(CTRL_REG4_A, 0x00);
273
274 // 0x27 = 0b00100111
275 // PM = 001 (normal mode); DR = 00 (50 Hz ODR); Zen = Yen = Xen = 1 (all axes enabled)
276 writeAccReg(CTRL_REG1_A, 0x27);
277
278 // Magnetometer
279
280 // 0x0C = 0b00001100
281 // DO = 011 (7.5 Hz ODR)
282 writeMagReg(CRA_REG_M, 0x0C);
283
284 // 0x20 = 0b00100000
285 // GN = 001 (+/- 1.3 gauss full scale)
286 writeMagReg(CRB_REG_M, 0x20);
287
288 // 0x00 = 0b00000000
289 // MD = 00 (continuous-conversion mode)
290 writeMagReg(MR_REG_M, 0x00);
291 }
292}
293
294// Writes an accelerometer register
295void LSM303::writeAccReg(regAddr reg, byte value)
296{
297 Wire.beginTransmission(acc_address);
298 Wire.write((byte)reg);
299 Wire.write(value);
300 last_status = Wire.endTransmission();
301}
302
303// Reads an accelerometer register
304byte LSM303::readAccReg(regAddr reg)
305{
306 byte value;
307
308 Wire.beginTransmission(acc_address);
309 Wire.write((byte)reg);
310 last_status = Wire.endTransmission();
311 Wire.requestFrom(acc_address, (byte)1);
312 value = Wire.read();
313 Wire.endTransmission();
314
315 return value;
316}
317
318// Writes a magnetometer register
319void LSM303::writeMagReg(regAddr reg, byte value)
320{
321 Wire.beginTransmission(mag_address);
322 Wire.write((byte)reg);
323 Wire.write(value);
324 last_status = Wire.endTransmission();
325}
326
327// Reads a magnetometer register
328byte LSM303::readMagReg(regAddr reg)
329{
330 byte value;
331
332 // if dummy register address (magnetometer Y/Z), look up actual translated address (based on device type)
333 if (reg < 0)
334 {
335 reg = translated_regs[-reg];
336 }
337
338 Wire.beginTransmission(mag_address);
339 Wire.write((byte)reg);
340 last_status = Wire.endTransmission();
341 Wire.requestFrom(mag_address, (byte)1);
342 value = Wire.read();
343 Wire.endTransmission();
344
345 return value;
346}
347
348void LSM303::writeReg(regAddr reg, byte value)
349{
350 // mag address == acc_address for LSM303D, so it doesn't really matter which one we use.
351 // Use writeMagReg so it can translate OUT_[XYZ]_[HL]_M
352 if (_device == device_D || reg < CTRL_REG1_A)
353 {
354 writeMagReg(reg, value);
355 }
356 else
357 {
358 writeAccReg(reg, value);
359 }
360}
361
362// Note that this function will not work for reading TEMP_OUT_H_M and TEMP_OUT_L_M on the DLHC.
363// To read those two registers, use readMagReg() instead.
364byte LSM303::readReg(regAddr reg)
365{
366 // mag address == acc_address for LSM303D, so it doesn't really matter which one we use.
367 // Use writeMagReg so it can translate OUT_[XYZ]_[HL]_M
368 if (_device == device_D || reg < CTRL_REG1_A)
369 {
370 return readMagReg(reg);
371 }
372 else
373 {
374 return readAccReg(reg);
375 }
376}
377
378// Reads the 3 accelerometer channels and stores them in vector a
379void LSM303::readAcc(void)
380{
381 Wire.beginTransmission(acc_address);
382 // assert the MSB of the address to get the accelerometer
383 // to do slave-transmit subaddress updating.
384 Wire.write(OUT_X_L_A | (1 << 7));
385 last_status = Wire.endTransmission();
386 Wire.requestFrom(acc_address, (byte)6);
387
388 unsigned int millis_start = millis();
389 while (Wire.available() < 6) {
390 if (io_timeout > 0 && ((unsigned int)millis() - millis_start) > io_timeout)
391 {
392 did_timeout = true;
393 return;
394 }
395 }
396
397 byte xla = Wire.read();
398 byte xha = Wire.read();
399 byte yla = Wire.read();
400 byte yha = Wire.read();
401 byte zla = Wire.read();
402 byte zha = Wire.read();
403
404 // combine high and low bytes
405 // This no longer drops the lowest 4 bits of the readings from the DLH/DLM/DLHC, which are always 0
406 // (12-bit resolution, left-aligned). The D has 16-bit resolution
407 a.x = (int16_t)(xha << 8 | xla);
408 a.y = (int16_t)(yha << 8 | yla);
409 a.z = (int16_t)(zha << 8 | zla);
410}
411
412// Reads the 3 magnetometer channels and stores them in vector m
413void LSM303::readMag(void)
414{
415 Wire.beginTransmission(mag_address);
416 // If LSM303D, assert MSB to enable subaddress updating
417 // OUT_X_L_M comes first on D, OUT_X_H_M on others
418 Wire.write((_device == device_D) ? translated_regs[-OUT_X_L_M] | (1 << 7) : translated_regs[-OUT_X_H_M]);
419 last_status = Wire.endTransmission();
420 Wire.requestFrom(mag_address, (byte)6);
421
422 unsigned int millis_start = millis();
423 while (Wire.available() < 6) {
424 if (io_timeout > 0 && ((unsigned int)millis() - millis_start) > io_timeout)
425 {
426 did_timeout = true;
427 return;
428 }
429 }
430
431 byte xlm, xhm, ylm, yhm, zlm, zhm;
432
433 if (_device == device_D)
434 {
435 /// D: X_L, X_H, Y_L, Y_H, Z_L, Z_H
436 xlm = Wire.read();
437 xhm = Wire.read();
438 ylm = Wire.read();
439 yhm = Wire.read();
440 zlm = Wire.read();
441 zhm = Wire.read();
442 }
443 else
444 {
445 // DLHC, DLM, DLH: X_H, X_L...
446 xhm = Wire.read();
447 xlm = Wire.read();
448
449 if (_device == device_DLH)
450 {
451 // DLH: ...Y_H, Y_L, Z_H, Z_L
452 yhm = Wire.read();
453 ylm = Wire.read();
454 zhm = Wire.read();
455 zlm = Wire.read();
456 }
457 else
458 {
459 // DLM, DLHC: ...Z_H, Z_L, Y_H, Y_L
460 zhm = Wire.read();
461 zlm = Wire.read();
462 yhm = Wire.read();
463 ylm = Wire.read();
464 }
465 }
466
467 // combine high and low bytes
468 m.x = (int16_t)(xhm << 8 | xlm);
469 m.y = (int16_t)(yhm << 8 | ylm);
470 m.z = (int16_t)(zhm << 8 | zlm);
471}
472
473// Reads all 6 channels of the LSM303 and stores them in the object variables
474void LSM303::read(void)
475{
476 readAcc();
477 readMag();
478}
479
480/*
481Returns the angular difference in the horizontal plane between a
482default vector and north, in degrees.
483
484The default vector here is chosen to point along the surface of the
485PCB, in the direction of the top of the text on the silkscreen.
486This is the +X axis on the Pololu LSM303D carrier and the -Y axis on
487the Pololu LSM303DLHC, LSM303DLM, and LSM303DLH carriers.
488*/
489float LSM303::heading(void)
490{
491 if (_device == device_D)
492 {
493 return heading((vector<int>){1, 0, 0});
494 }
495 else
496 {
497 return heading((vector<int>){0, -1, 0});
498 }
499}
500
501/*
502Returns the angular difference in the horizontal plane between the
503"from" vector and north, in degrees.
504
505Description of heading algorithm:
506Shift and scale the magnetic reading based on calibration data to find
507the North vector. Use the acceleration readings to determine the Up
508vector (gravity is measured as an upward acceleration). The cross
509product of North and Up vectors is East. The vectors East and North
510form a basis for the horizontal plane. The From vector is projected
511into the horizontal plane and the angle between the projected vector
512and horizontal north is returned.
513*/
514template <typename T> float LSM303::heading(vector<T> from)
515{
516 vector<int32_t> temp_m = {m.x, m.y, m.z};
517
518 // subtract offset (average of min and max) from magnetometer readings
519 temp_m.x -= ((int32_t)m_min.x + m_max.x) / 2;
520 temp_m.y -= ((int32_t)m_min.y + m_max.y) / 2;
521 temp_m.z -= ((int32_t)m_min.z + m_max.z) / 2;
522
523 // compute E and N
524 vector<float> E;
525 vector<float> N;
526 vector_cross(&temp_m, &a, &E);
527 vector_normalize(&E);
528 vector_cross(&a, &E, &N);
529 vector_normalize(&N);
530
531 // compute heading
532 float heading = atan2(vector_dot(&E, &from), vector_dot(&N, &from)) * 180 / M_PI;
533 if (heading < 0) heading += 360;
534 return heading;
535}
536
537template <typename Ta, typename Tb, typename To> void LSM303::vector_cross(const vector<Ta> *a,const vector<Tb> *b, vector<To> *out)
538{
539 out->x = (a->y * b->z) - (a->z * b->y);
540 out->y = (a->z * b->x) - (a->x * b->z);
541 out->z = (a->x * b->y) - (a->y * b->x);
542}
543
544template <typename Ta, typename Tb> float LSM303::vector_dot(const vector<Ta> *a, const vector<Tb> *b)
545{
546 return (a->x * b->x) + (a->y * b->y) + (a->z * b->z);
547}
548
549void LSM303::vector_normalize(vector<float> *a)
550{
551 float mag = sqrt(vector_dot(a, a));
552 a->x /= mag;
553 a->y /= mag;
554 a->z /= mag;
555}
556
557// Private Methods //////////////////////////////////////////////////////////////
558
559int LSM303::testReg(byte address, regAddr reg)
560{
561 Wire.beginTransmission(address);
562 Wire.write((byte)reg);
563 last_status = Wire.endTransmission();
564
565 Wire.requestFrom(address, (byte)1);
566 if (Wire.available())
567 return Wire.read();
568 else
569 return TEST_REG_NACK;
570}
Note: See TracBrowser for help on using the repository browser.