source: rtos_arduino/trunk/arduino_lib/libraries/ZumoShield/LSM303.cpp@ 232

Last change on this file since 232 was 232, checked in by ertl-honda, 8 years ago

Zumo用ライブラリの追加

File size: 14.6 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
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
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
49void LSM303::setTimeout(unsigned int timeout)
50{
51 io_timeout = timeout;
52}
53
54unsigned int LSM303::getTimeout()
55{
56 return io_timeout;
57}
58
59bool 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/*
176Enables 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).
185Note that this function will also reset other settings controlled by
186the registers it writes to.
187*/
188void 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
259void 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
268byte 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
283void 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
292byte 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
312void 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.
327byte 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
342void 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
376void 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
437void LSM303::read(void)
438{
439 readAcc();
440 readMag();
441}
442
443/*
444Returns the angular difference in the horizontal plane between a
445default vector and north, in degrees.
446
447The default vector here is chosen to point along the surface of the
448PCB, in the direction of the top of the text on the silkscreen.
449This is the +X axis on the Pololu LSM303D carrier and the -Y axis on
450the Pololu LSM303DLHC, LSM303DLM, and LSM303DLH carriers.
451*/
452float 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
464void 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
474int 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}
Note: See TracBrowser for help on using the repository browser.