-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathRedBotAccel.cpp
More file actions
329 lines (284 loc) · 13.4 KB
/
RedBotAccel.cpp
File metadata and controls
329 lines (284 loc) · 13.4 KB
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
/****************************************************************
Main CPP for RedBot accelerometer board.
This code is beerware; if you use it, please buy me (or any other
SparkFun employee) a cold beverage next time you run into one of
us at the local.
21 Jan 2014- Mike Hord, SparkFun Electronics
Code developed in Arduino 1.0.5, on an SparkFun Redbot v12.
****************************************************************/
#include "RedBot.h"
#include <Arduino.h>
RedBotAccel::RedBotAccel()
{
byte buffer[2];
// This sets the bit rate of the bus; I want 100kHz. See the
// datasheet for details on how I came up with this value.
TWBR = 72;
// The very first thing we want to do is reset the accelerometer. Since
// we don't know what's happened since the last time we powered the
// thing up, the best we can do is reset it by twiddling the reset bit
// (bit 6) in CTRL2 register (0x2B).
buffer[0] = 0x40;
xlWriteBytes(0x2B, buffer, 1);
// Accelerometer configuration- there are five registers (starting with
// 0x2A) that must be configured for the accelerometer to operate. We'll
// create a byte buffer and fill it with the values we need, then push it
// to the accelerometer. Only the first two registers need to be fiddled
// with, the rest are for interrupts and things we don't need to worry
// about.
// Let's set the dynamic range to max out at 8g instead of 2.
buffer[0] = 0x02;
xlWriteBytes(0x0E, buffer, 1);
// Now we'll populate our buffer.
// CTRL1 register, five settings here:
// 7:6 - sample rate during sleep mode (leave default 50Hz)
// 5:3 - data rate (leave default 800Hz)
// 2 - low noise mode (set to 1 to activate)
// 1 - 8-bit mode (leave 0 to disable)
// 0 - active/standby (leave 0, for now)
buffer[0] = 0x04;
// CTRL2 register, five more settings:
// 7 - self-test (leave 0, disabled)
// 6 - software reset (leave 0, no reset)
// 5 - no use
// 4:3 - sleep mode (leave 00, normal mode)
// 2 - sleep enable (leave 0, disable auto sleep)
// 1:0 - active mode (set 10, hi-res oversample)
buffer[1] = 0x02;
// Now that we've populated our buffer, we can push it to the accelerometer.
xlWriteBytes(0x2A, buffer, 2);
// Now that we've set that up, we can enable the part by writing 0x05 to
// CTRL1.
buffer[0] = 0x05;
xlWriteBytes(0x2A, buffer, 1);
}
// read() initiates a capture of the current x, y, and z values, and stores
// them in the appropriate class member variables.
void RedBotAccel::read()
{
// The first step, the easy step, is to grab the values. We'll put 'em in
// a byte buffer.
byte buffer[6];
xlReadBytes(0x01, buffer, 6);
// Next, we need to copy the data into the member variables so they can be
// accessed by the user.
x = buffer[0]<<8 | buffer[1];
y = buffer[2]<<8 | buffer[3];
z = buffer[4]<<8 | buffer[5];
// Adding these three calculations adds ~ 700us to this process.
// This method takes ~856 us to run w/o them in and about 1532 us with
// these floating point operations. (BH)
angleXZ = 180*atan2(x,z)/PI;
angleXY = 180*atan2(x,y)/PI;
angleYZ = 180*atan2(y,z)/PI;
}
// For bump detection, we're looking for a transient in the Z direction. The
// bump should be pretty hard, so hopefully, we'll be able to distinguish
// between a bump and a tap.
void RedBotAccel::enableBump()
{
byte buffer[8];
// The *very* first thing we need to do is disable the chip; otherwise,
// we can't change the register settings.
xlReadBytes(0x2A, buffer, 1);
buffer[0] &= 0xFE;
xlWriteBytes(0x2A, buffer, 1);
// To enable tap detection, we need to write some data to registers
// 0x21-0x28. See Freescale app note 4072 for more info about setting
// this up.
// The very first thing we'll do is enable the LPF for pulse detection.
// This is in register 0x0F.
buffer[0] = 0x10;
xlWriteBytes(0x0F, buffer, 1);
// Since tap detection and bump detection use the same system resources,
// we need to fetch the data from the accelerometer before we can set up
// tap.
xlReadBytes(0x21, buffer, 8);
// Now that we have the current settings, we can turn on z-axis tap detection
// by fiddling with the appropriate bits.
// 0x21 (PULSE_CFG)- We need to set bit 6 (ELE, latch events into register)
// and bit 0 (XSPEFE, x-axis single pulse event function enable)
buffer[0] = 0x41;
// 0x22 (PULSE_SRC)- we'll read this to check for pulses; it's read only, so
// we don't need to do anything with it here.
buffer[1] |= 0x00; // just a placeholder
// 0x23- X pulse threshold- experimentally determined to be a good value for a
// threshold.
buffer[2] = 32;
// 0x24- Y pulse threshold
// Both of these can be ignored, and shouldn't be touched, in case they're
// configured for something else.
buffer[3] |= 0x00; // placeholder
// 0x25 (PULSE_THSZ)
buffer[4] |= 0;
// 0x26 (PULSE_TMLT)- maximum length a pulse must be to be detected as a tap.
// The length is dependent upon three things: the sampling rate (800Hz),
// whether Pulse_LPF is set or clear in register 0x0F (it's not), and the
// sampling mode (Hi-res). Charts on pp34-35 of the datasheet tell us that
// the maximum pulse length here is this register value times 0.625ms.
buffer[5] = 25; // maximum pulse length of 62.5ms
// 0x27 (PULSE_LTCY)- lockout time after a pulse occurs before another one
// will be sensed. Charts for value are on page 35 of the datasheet.
buffer[6] = 50; // 125ms lockout period
// 0x28 (PULSE_WIND)- window within which a second tap must occur to register
// a double tap event. We aren't worried about double taps (yet), so let's
// leave this unchanged.
buffer[7] |= 0x00; // placeholder
// Write the values we just set up back into the accelerometer.
xlWriteBytes(0x21, buffer, 8);
// Now we need to put the device back into active mode.
xlReadBytes(0x2A, buffer, 1);
buffer[0] |= 0x01;
xlWriteBytes(0x2A, buffer, 1);
}
boolean RedBotAccel::checkBump()
{
byte buffer = 0;
xlReadBytes(0x22, &buffer, 1); // check the PULSE_SRC register to see if a
// pulse event has been registered. This
// will clear all pulse events
if ((buffer&0x10)!=0) return true; // Mask for X events.
else return false;
}
void RedBotAccel::setBumpThresh(int xThresh)
{
byte buffer;
// The *very* first thing we need to do is disable the chip; otherwise,
// we can't change the register settings.
xlReadBytes(0x2A, &buffer, 1);
buffer &= 0xFE;
xlWriteBytes(0x2A, &buffer, 1);
// 0x23- X pulse threshold- experimentally determined to be a good value for a
// threshold.
buffer = (byte)xThresh;
// Write the value we just set up back into the accelerometer.
xlWriteBytes(0x23, &buffer, 1);
// Now we need to put the device back into active mode.
xlReadBytes(0x2A, &buffer, 1);
buffer |= 0x01;
xlWriteBytes(0x2A, &buffer, 1);
}
// Private function that reads some number of bytes from the accelerometer.
void RedBotAccel::xlReadBytes(byte addr, byte *buffer, byte len)
{
unsigned int timeout = 0; // We're going to use this to set a timeout on the
// amount of time we'll wait for the bus to become
// available. The minimum period here is about 4ms
// on a 16MHz device.
// First, we need to write the address we want to read from, so issue a write
// with that address. That's two steps: first, the slave address:
TWCR = START_COND; // Send a start condition
while (!(TWCR&(1<<TWINT))) // When TWINT is set, start is complete, and we
// can initiate data transfer.
{
if (++timeout == 0) return; // time out if the bus is busy. In most cases,
} // "busy" means no sensor on the bus.
timeout = 0;
TWDR = XL_ADDR<<1; // Load the slave address
TWCR = CLEAR_TWINT; // Clear TWINT to begin transmission (I know,
// it LOOKS like I'm setting it, but this is
// how we clear that bit. Dumb.)
while (!(TWCR&(1<<TWINT))) // Wait for TWINT again.
{
if (++timeout == 0) return; // time out if the bus is busy. In most cases,
} // "busy" means no sensor on the bus.
timeout = 0;
// Now, we need to send the address we want to read from:
TWDR = addr; // Load the slave address
TWCR = CLEAR_TWINT; // Clear TWINT to begin transmission (I know,
// it LOOKS like I'm setting it, but this is
// how we clear that bit. Dumb.)
while (!(TWCR&(1<<TWINT))) // Wait for TWINT again.
{
if (++timeout == 0) return; // time out if the bus is busy. In most cases,
} // "busy" means no sensor on the bus.
timeout = 0;
TWCR = STOP_COND;
timeout = 0;
// Now, we issue a repeated start (by doing what we just did again), and this
// time, we set the READ bit as well.
TWCR = START_COND; // Send a start condition
while (!(TWCR&(1<<TWINT))) // When TWINT is set, start is complete, and we
// can initiate data transfer.
{
if (++timeout == 0) return; // time out if the bus is busy. In most cases,
} // "busy" means no sensor on the bus.
timeout = 0;
TWDR = (XL_ADDR<<1) | I2C_READ; // Load the slave address and set the read bit
TWCR = CLEAR_TWINT; // Clear TWINT to begin transmission (I know,
// it LOOKS like I'm setting it, but this is
// how we clear that bit. Dumb.)
while (!(TWCR&(1<<TWINT))) // Wait for TWINT again.
{
if (++timeout == 0) return; // time out if the bus is busy. In most cases,
} // "busy" means no sensor on the bus.
timeout = 0;
// Now, we can fetch data from the slave by clearing TWINT, waiting, and
// reading the data. Rinse, repeat, as often as needed.
for (byte i = 0; i < len; i++)
{
if (i == len-1) TWCR = CLEAR_TWINT; // Clear TWINT to begin transmission (I know,
// it LOOKS like I'm setting it, but this is
// how we clear that bit. Dumb.)
else TWCR = NEXT_BYTE;
while (!(TWCR&(1<<TWINT))) // Wait for TWINT again.
{
if (++timeout == 0) return; // time out if the bus is busy. In most cases,
} // "busy" means no sensor on the bus.
timeout = 0;
buffer[i] = TWDR; // Now our data can be fetched from TWDR.
}
// Now that we're done reading our data, we can transmit a stop condition.
TWCR = STOP_COND;
}
void RedBotAccel::xlWriteBytes(byte addr, byte *buffer, byte len)
{
unsigned int timeout = 0; // We're going to use this to set a timeout on the
// amount of time we'll wait for the bus to become
// available. The minimum period here is about 4ms
// on a 16MHz device.
// First, we need to write the address we want to read from, so issue a write
// with that address. That's two steps: first, the slave address:
TWCR = START_COND; // Send a start condition
while (!(TWCR&(1<<TWINT))) // When TWINT is set, start is complete, and we
// can initiate data transfer.
{
if (++timeout == 0) return; // time out if the bus is busy. In most cases,
} // "busy" means no sensor on the bus.
timeout = 0;
TWDR = XL_ADDR<<1; // Load the slave address
TWCR = CLEAR_TWINT; // Clear TWINT to begin transmission (I know,
// it LOOKS like I'm setting it, but this is
// how we clear that bit. Dumb.)
while (!(TWCR&(1<<TWINT))) // Wait for TWINT again.
{
if (++timeout == 0) return; // time out if the bus is busy. In most cases,
} // "busy" means no sensor on the bus.
timeout = 0;
// Now, we need to send the address we want to read from:
TWDR = addr; // Load the slave address
TWCR |= CLEAR_TWINT; // Clear TWINT to begin transmission (I know,
// it LOOKS like I'm setting it, but this is
// how we clear that bit. Dumb.)
while (!(TWCR&(1<<TWINT))) // Wait for TWINT again.
{
if (++timeout == 0) return; // time out if the bus is busy. In most cases,
} // "busy" means no sensor on the bus.
timeout = 0;
// Now, we can send data to the slave by putting data into TWDR, clearing
// TWINT, and waiting for TWINT. Rinse, repeat, as often as needed.
for (byte i = 0; i < len; i++)
{
TWDR = buffer[i]; // Now our data can be sent to TWDR.
TWCR |= CLEAR_TWINT; // Clear TWINT to begin transmission (I know,
// it LOOKS like I'm setting it, but this is
// how we clear that bit. Dumb.)
while (!(TWCR&(1<<TWINT))) // Wait for TWINT again.
{
if (++timeout == 0) return; // time out if the bus is busy. In most cases,
} // "busy" means no sensor on the bus.
timeout = 0;
}
// Now that we're done writing our data, we can transmit a stop condition.
TWCR = STOP_COND;
}