-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMPU6000.cpp
More file actions
1494 lines (1235 loc) · 52.5 KB
/
MPU6000.cpp
File metadata and controls
1494 lines (1235 loc) · 52.5 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
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "MPU6000.h"
const int ChipSelPin1 = 53; // On the ArduIMU+ V3, output pin D4 on the ATmega328P connects to
MPU6000::MPU6000()
{
}
/* */
void MPU6000::initialize()
{
m_dmpReady = false;
m_packetSize = 42;
m_mpuInterrupt = false;
// pinMode(SERIAL_MUX_PIN, OUTPUT); // Serial Mux Pin - SWITCHES SERIAL INPUT FROM GPS OR FTDI CHIP
// pinMode(RED_LED_PIN, OUTPUT); // Red LED
// pinMode(BLUE_LED_PIN, OUTPUT); // Blue LED
// digitalWrite(SERIAL_MUX_PIN, LOW); // When LOW , set MUX to Receive Serial data from FTDI (upload sketch)
// When HIGH, set MUX to Receive Serial data from GPS
// This MUX is a really specific ArduIMU+ V3 module. This is what 3DRobotics calls
// the "GPS port with FTDI autoswitch". This statement controls that "auto"switch. When connecting
// a GPS module to the ArduIMU+ V3, the SERIAL_MUX_PIN must be set HIGH so that GPS data can be received.
// It can't be both, so choose wisely or switch it actively at the right moments in this sketch.
while (Serial.available() > 0)
{
Serial.read(); // Flush serial buffer to clean up remnants from previous run
}
Serial.println();
Serial.println("############# MPU-6000 Data Acquisition #############");
//--- SPI settings ---//
Serial.println("Initializing SPI Protocol...");
SPI.begin(); // start the SPI library
SPI.setClockDivider(SPI_CLOCK_DIV16); // ArduIMU+ V3 board runs on 16 MHz: 16 MHz / SPI_CLOCK_DIV16 = 1 MHz
// 1 MHz is the maximum SPI clock frequency according to the MPU-6000 Product Specification
SPI.setBitOrder(MSBFIRST); // data delivered MSB first as in MPU-6000 Product Specification
SPI.setDataMode(SPI_MODE0); // latched on rising edge, transitioned on falling edge, active low
Serial.println("...SPI Protocol initializing done.");
Serial.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
delay(100);
//--- Configure the chip select pin as output ---//
pinMode(ChipSelPin1, OUTPUT);
// write & verify dmpMemory, dmpConfig and dmpUpdates into the DMP, and make all kinds of other settings
// !!! this is the main routine to make the DMP work, and get the quaternion out of the FIFO !!!
Serial.println("Initializing Digital Motion Processor (DMP)...");
byte devStatus; // return status after each device operation (0 = success, !0 = error)
devStatus = dmpInitialize();
// make sure it worked: dmpInitialize() returns a 0 in devStatus if so
if (devStatus == 0)
{
// now that it's ready, turn on the DMP
Serial.print("Enabling DMP... ");
SPIwriteBit(0x6A, 7, true, ChipSelPin1); // USER_CTRL_DMP_EN
Serial.println("done.");
// enable Arduino interrupt detection, this will execute dmpDataReady whenever there is an interrupt,
// independing on what this sketch is doing at that moment
// http://arduino.cc/en/Reference/AttachInterrupt
Serial.print("Enabling interrupt detection... ");
// attachInterrupt(interrupt, function, mode) specifies a function to call when an external interrupt occurs
// ArduIMU+ V3 has ATMEGA328 INT0 / D2 pin 32 (input) connected to MPU-6000 INT pin 12 (output)
// attachInterrupt(0, dmpDataReady, RISING); // the 0 points correctly to INT0 / D2
// -> if there is an interrupt from MPU-6000 to ATMEGA328, boolean mpuInterrupt will be made true
byte mpuIntStatus = SPIread(0x3A, ChipSelPin1); // by reading INT_STATUS register, all interrupts are cleared
Serial.println("done.");
// set our DMP Ready flag so the main loop() function knows it's okay to use it
m_dmpReady = true;
Serial.println("DMP ready! Waiting for first data from MPU-6000...");
Serial.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
}
else // have to check if this is still functional
{
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print("DMP Initialization failed (code ");
Serial.print(devStatus);
Serial.println(")");
m_dmpReady = false;
}
} // end void setup()
// Sensor orientation ArduIMU+ V3:
// (all rotations "through" the MPU-6000 on the board)
// +X is ROLL rotation along longer dimension of the board (towards GPS connector - "front")
// +Y is PITCH rotation along shorter dimension of the board (towards GPS connector - "left")
// +Z is YAW rotation around line upwards (through the MPU-6000 - "top")
void MPU6000::getYPR()
{
// if DMP initialization during setup failed, don't try to do anything
if (!m_dmpReady)
{
return;
}
// wait for MPU interrupt or extra packet(s) available
// INFO: if there is an interrupt send from the MPU-6000 to the ATmega328P (the "Arduino" processor),
// boolean variable "mpuInterrupt" will be made "true" (see explanation in void setup() )
/* while ((mpuInterrupt == false) && (fifoCount < packetSize))
{
// do nothing until mpuInterrupt = true or fifoCount >= 42
}
*/
// there has just been an interrupt, so reset the interrupt flag, then get INT_STATUS byte
m_mpuInterrupt = false;
byte mpuIntStatus = SPIread(0x3A, ChipSelPin1); // by reading INT_STATUS register, all interrupts are cleared
// get current FIFO count
m_fifoCount = getFIFOCount(ChipSelPin1);
// check for FIFO overflow (this should never happen unless our code is too inefficient or DEBUG output delays code too much)
if ((mpuIntStatus & 0x10) || m_fifoCount == 1008)
// mpuIntStatus & 0x10 checks register 0x3A for FIFO_OFLOW_INT
// the size of the FIFO buffer is 1024 bytes, but max. set to 1008 so after 24 packets of 42 bytes
// the FIFO is reset, otherwise the last FIFO reading before reaching 1024 contains only 16 bytes
// and can/will produces output value miscalculations
{
// reset so we can continue cleanly
//SPIwriteBit(0x6A, 6, false, ChipSelPin1); // FIFO_EN = 0 = disable
SPIwriteBit(0x6A, 2, true, ChipSelPin1); // FIFO_RESET = 1 = reset (ok) only when FIFO_EN = 0
//SPIwriteBit(0x6A, 6, true, ChipSelPin1); // FIFO_EN = 1 = enable
digitalWrite(BLUE_LED_PIN, HIGH); // shows FIFO overflow without disturbing output with message
DEBUG_PRINTLN("FIFO overflow! FIFO resetted to continue cleanly.");
}
// otherwise, check for DMP data ready interrupt (this should happen frequently)
else if (mpuIntStatus & 0x02)
// mpuIntStatus & 0x02 checks register 0x3A for (undocumented) DMP_INT
{
// wait for correct available data length, should be a VERY short wait
while (m_fifoCount < m_packetSize)
{
m_fifoCount = getFIFOCount(ChipSelPin1);
}
digitalWrite(BLUE_LED_PIN, LOW); // LED off again now that FIFO overflow is resolved
// read a packet from FIFO
SPIreadBytes(0x74, m_packetSize, m_fifoBuffer, ChipSelPin1);
// verify contents of fifoBuffer before use:
#ifdef DEBUG
for (byte n = 0; n < packetSize; n++)
{
Serial.print("\tfifoBuffer[");
Serial.print(n);
Serial.print("]\t: ");
Serial.println(fifoBuffer[n], HEX);
}
#endif
// track FIFO count here in case there is more than one packet (each of 42 bytes) available
// (this lets us immediately read more without waiting for an interrupt)
m_fifoCount = m_fifoCount - m_packetSize;
// ============================================================================================== //
// >>>>>>>>> - from here the 42 FIFO bytes from the MPU-6000 can be used to generate output >>>>>>>>
// >>>>>>>>> - this would be the place to add your own code into >>>>>>>>
// >>>>>>>>> - of course all the normal MPU-6000 registers can be used here too >>>>>>>>
// ============================================================================================== //
// get the quaternion values from the FIFO - needed for Euler and roll/pitch/yaw angle calculations
int raw_q_w = ((m_fifoBuffer[0] << 8) + m_fifoBuffer[1]); // W
int raw_q_x = ((m_fifoBuffer[4] << 8) + m_fifoBuffer[5]); // X
int raw_q_y = ((m_fifoBuffer[8] << 8) + m_fifoBuffer[9]); // Y
int raw_q_z = ((m_fifoBuffer[12] << 8) + m_fifoBuffer[13]); // Z
float q_w = raw_q_w / 16384.0f;
float q_x = raw_q_x / 16384.0f;
float q_y = raw_q_y / 16384.0f;
float q_z = raw_q_z / 16384.0f;
#ifdef OUTPUT_RAW_ACCEL
// print accelerometer values from fifoBuffer
int AcceX = ((fifoBuffer[28] << 8) + fifoBuffer[29]);
int AcceY = ((fifoBuffer[32] << 8) + fifoBuffer[33]);
int AcceZ = ((fifoBuffer[36] << 8) + fifoBuffer[37]);
Serial.print("Raw acceleration ax, ay, az [8192 = 1 g]: ");
Serial.print("\t\t");
Serial.print(AcceX);
Serial.print("\t");
Serial.print(AcceY);
Serial.print("\t");
Serial.println(AcceZ);
#endif
#ifdef OUTPUT_RAW_ACCEL_G
// same as OUTPUT_RAW_ACCEL but recalculated to g-force values
int AcceX = ((fifoBuffer[28] << 8) + fifoBuffer[29]);
int AcceY = ((fifoBuffer[32] << 8) + fifoBuffer[33]);
int AcceZ = ((fifoBuffer[36] << 8) + fifoBuffer[37]);
float Ax = AcceX / 8192.0f; // calculate g-value
float Ay = AcceY / 8192.0f; // calculate g-value
float Az = AcceZ / 8192.0f; // calculate g-value
Serial.print("Raw acceleration ax, ay, az [g]: ");
Serial.print("\t\t\t");
Serial.print(Ax, 3);
Serial.print("\t");
Serial.print(Ay, 3);
Serial.print("\t");
Serial.println(Az, 3);
#endif
#ifdef OUTPUT_RAW_ANGLES
// print calculated angles for roll and pitch from the raw acceleration components
// (yaw is undetermined here, this needs the use of the quaternion - see further on)
int AcceX = ((fifoBuffer[28] << 8) + fifoBuffer[29]);
int AcceY = ((fifoBuffer[32] << 8) + fifoBuffer[33]);
int AcceZ = ((fifoBuffer[36] << 8) + fifoBuffer[37]);
// atan2 outputs the value of -pi to pi (radians) - see http://en.wikipedia.org/wiki/Atan2
// We then convert it to 0 to 2 pi and then from radians to degrees - in the end it's 0 - 360 degrees
float ADegX = (atan2(AcceY, AcceZ) + PI) * RAD_TO_DEG;
float ADegY = (atan2(AcceX, AcceZ) + PI) * RAD_TO_DEG;
Serial.print("Calculated angle from raw acceleration - roll, pitch and yaw [degrees]: ");
Serial.print(ADegX);
Serial.print("\t");
Serial.print(ADegY);
Serial.print("\t");
Serial.println("undetermined");
#endif
#ifdef OUTPUT_RAW_GYRO
// print gyroscope values from fifoBuffer
int GyroX = ((fifoBuffer[16] << 8) + fifoBuffer[17]);
int GyroY = ((fifoBuffer[20] << 8) + fifoBuffer[21]);
int GyroZ = ((fifoBuffer[24] << 8) + fifoBuffer[25]);
Serial.print("Raw gyro rotation ax, ay, az [value/deg/s]: ");
Serial.print("\t\t");
Serial.print(GyroX);
Serial.print("\t");
Serial.print(GyroY);
Serial.print("\t");
Serial.println(GyroZ);
#endif
#ifdef OUTPUT_TEMPERATURE
// print calculated temperature from standard registers (not available in fifoBuffer)
// the chip temperature may be used for correction of the temperature sensitivity of the
// accelerometer and the gyroscope - not done in this sketch
byte Temp_Out_H = SPIread(0x41, ChipSelPin1);
byte Temp_Out_L = SPIread(0x42, ChipSelPin1);
int TemperatureRaw = Temp_Out_H << 8 | Temp_Out_L;
float Temperature = (TemperatureRaw / 340.00) + 36.53; // formula from datasheet chapter 4.19
Serial.print("Chip temperature for corrections [deg. Celsius]: ");
Serial.println(Temperature, 2);
#endif
#ifdef OUTPUT_READABLE_QUATERNION
Serial.print("Quaternion qw, qx, qy, qz [-1 to +1]: ");
Serial.print("\t");
Serial.print(q_w);
Serial.print("\t");
Serial.print(q_x);
Serial.print("\t");
Serial.print(q_y);
Serial.print("\t");
Serial.println(q_z);
#endif
#ifdef OUTPUT_READABLE_EULER
// calculate Euler angles
// http://en.wikipedia.org/wiki/Atan2
// http://en.wikipedia.org/wiki/Sine (-> Inverse)
float euler_x = atan2((2 * q_y * q_z) - (2 * q_w * q_x), (2 * q_w * q_w) + (2 * q_z * q_z) - 1); // phi
float euler_y = -asin((2 * q_x * q_z) + (2 * q_w * q_y)); // theta
float euler_z = atan2((2 * q_x * q_y) - (2 * q_w * q_z), (2 * q_w * q_w) + (2 * q_x * q_x) - 1); // psi
euler_x = euler_x * 180 / M_PI; // angle in degrees -180 to +180
euler_y = euler_y * 180 / M_PI; // angle in degrees
euler_z = euler_z * 180 / M_PI; // angle in degrees -180 to +180
Serial.print("Euler angles x, y, z [degrees]: ");
Serial.print(euler_x);
Serial.print("\t");
Serial.print(euler_y);
Serial.print("\t");
Serial.print(euler_z);
Serial.println();
#endif
#ifdef OUTPUT_READABLE_ROLLPITCHYAW
// display Euler angles in degrees
// dmpGetGravity
float grav_x = 2 * ((q_x * q_z) - (q_w * q_y));
float grav_y = 2 * ((q_w * q_x) + (q_y * q_z));
float grav_z = (q_w * q_w) - (q_x * q_x) - (q_y * q_y) + (q_z * q_z);
// roll: (tilt left/right, about X axis)
float rpy_rol = atan(grav_y / (sqrt((grav_x * grav_x) + (grav_z * grav_z))));
// pitch: (nose up/down, about Y axis)
float rpy_pit = atan(grav_x / (sqrt((grav_y * grav_y) + (grav_z * grav_z))));
// yaw: (rotate around Z axis)
float rpy_yaw = atan2((2 * q_x * q_y) - (2 * q_w * q_z), (2 * q_w * q_w) + (2 * q_x * q_x) - 1);
m_yaw = rpy_yaw;
m_pitch = rpy_pit;
m_roll = rpy_rol;
/* Serial.print("Roll, pitch and yaw angles [degrees]: ");
Serial.print(rpy_rol * 180 / M_PI);
Serial.print("\t");
Serial.print(rpy_pit * 180 / M_PI);
Serial.print("\t");
Serial.print(rpy_yaw * 180 / M_PI);
Serial.println();
*/ #endif
#ifdef OUTPUT_TEAPOT
// display quaternion values in InvenSense Teapot demo format:
teapotPacket[2] = fifoBuffer[0];
teapotPacket[3] = fifoBuffer[1];
teapotPacket[4] = fifoBuffer[4];
teapotPacket[5] = fifoBuffer[5];
teapotPacket[6] = fifoBuffer[8];
teapotPacket[7] = fifoBuffer[9];
teapotPacket[8] = fifoBuffer[12];
teapotPacket[9] = fifoBuffer[13];
Serial.write(teapotPacket, 14);
teapotPacket[11]++; // packetCount, loops at 0xFF on purpose
#endif
// ============================================================================================== //
// >>>>>>>>> - this would normally be the end of adding your own code into this sketch >>>>
// >>>>>>>>> - end of using the 42 FIFO bytes from the MPU-6000 to generate output >>>>
// >>>>>>>>> - after blinking the red LED, the main loop starts again (and again, and again...) >>>>
// ============================================================================================== //
}
} // end void loop()
// ################################ SPI read/write functions #################################### //
// --- Function for SPI reading one byte from sensor
// reg : MPU-6000 register number to read from
// ChipSelPin : MPU-6000 chip select pin number (in this sketch defined by ChipSelPin1)
// return > register contents
byte MPU6000::SPIread(byte reg, int ChipSelPin)
{
DEBUG_PRINT("SPI (/CS");
DEBUG_PRINT(ChipSelPin);
DEBUG_PRINT(") ");
DEBUG_PRINT("reading 1 byte from register 0x");
if (reg < 0x10)
{
DEBUG_PRINT("0"); // add leading zero - this is an Arduino bug
}
DEBUG_PRINTF(reg, HEX);
DEBUG_PRINT("... ");
digitalWrite(ChipSelPin, LOW); // select MPU-6000 for SPI transfer (low active)
SPI.transfer(reg | 0x80); // reg | 0x80 causes a "1" added as MSB to reg to denote reading from reg i.s.o. writing to it
byte read_value = SPI.transfer(0x00); // write 8-bits zero to MPU-6000, read the 8-bits coming back from reg at the same time
digitalWrite(ChipSelPin, HIGH); // deselect MPU-6000 for SPI transfer
DEBUG_PRINT("0x");
if (read_value < 0x10)
{
DEBUG_PRINT("0"); // add leading zero - this is an Arduino bug
}
DEBUG_PRINTF(read_value, HEX);
DEBUG_PRINTLN(" (done)");
return read_value;
}
// --- Function for SPI writing one byte to sensor
// reg : MPU-6000 register number to write to
// data : data to be written into reg
// ChipSelPin : MPU-6000 chip select pin number (in this sketch defined by ChipSelPin1)
// return > nothing
void MPU6000::SPIwrite(byte reg, byte data, int ChipSelPin)
{
DEBUG_PRINT("SPI (/CS");
DEBUG_PRINT(ChipSelPin);
DEBUG_PRINT(") ");
DEBUG_PRINT("writing 1 byte to register 0x");
DEBUG_PRINTF(reg, HEX);
DEBUG_PRINT("... ");
digitalWrite(ChipSelPin, LOW);
SPI.transfer(reg);
SPI.transfer(data);
digitalWrite(ChipSelPin, HIGH);
DEBUG_PRINT("0x");
if (data < 0x10)
{
DEBUG_PRINT("0"); // add leading zero - this is an Arduino bug
}
DEBUG_PRINTF(data, HEX);
DEBUG_PRINTLN(" (done)");
}
// --- Function for SPI reading one bit from sensor
// reg : MPU-6000 register number to read from
// bitNum : bit number in the register to read - 7 (MSB) to 0 (LSB)
// ChipSelPin : MPU-6000 chip select pin number (in this sketch defined by ChipSelPin1)
// return > byte 0x00 if bit is 0, otherwise byte with a 1 at bitNum (rest 0's)
byte MPU6000::SPIreadBit(byte reg, byte bitNum, int ChipSelPin)
{
byte byte_value = SPIread(reg, ChipSelPin);
byte bit_value = byte_value & (1 << bitNum); // AND result from register byte value and byte with only one "1" at place of bit to return (rest "0"'s)
#ifdef DEBUG
Serial.print(" bit_");
Serial.print(bitNum);
Serial.print(" = ");
if (bit_value == 0x00)
{
Serial.println("0");
}
else
{
Serial.println("1");
}
#endif
return bit_value;
}
//--- Function for SPI writing one bit to sensor
// reg : MPU-6000 register number to write to
// bitNum : bit number in the register to write to - 7 (MSB) to 0 (LSB)
// databit : bit value to be written into reg - false or 0 | true or non-zero (1 will be logical)
// ChipSelPin : MPU-6000 chip select pin number (in this sketch defined by ChipSelPin1)
// return > nothing
//
// first read byte, then insert bit value, then write byte:
// otherwise all other bits will be written 0, this may trigger unexpected behaviour
void MPU6000::SPIwriteBit(byte reg, byte bitNum, byte databit, int ChipSelPin)
{
byte byte_value = SPIread(reg, ChipSelPin);
if (databit == 0)
{
byte_value = byte_value &~(1 << bitNum); // AND result from register byte value and byte with only one "0" at place of bit to write (rest "1"'s)
}
else // databit is intended to be a "1"
{
byte_value = byte_value | (1 << bitNum); // OR result from register byte value and byte with only one "1" at place of bit to write (rest "0"'s)
}
SPIwrite(reg, byte_value, ChipSelPin);
#ifdef DEBUG
Serial.print(" bit_");
Serial.print(bitNum);
Serial.print(" set to ");
if (databit == 0)
{
Serial.println("0");
}
else
{
Serial.println("1");
}
#endif
}
//--- Function for SPI reading multiple bytes to sensor
// read multiple bytes from the same device register, most of the times this
// is the FIFO transfer register (which after each read, is automatically
// loaded with new data for the next read)
// reg : MPU-6000 register number to write to
// length : number of bytes to be read
// data : buffer array (starting with [0]) to store the read data in
// ChipSelPin : MPU-6000 chip select pin number (in this sketch defined by ChipSelPin1)
// return > array of data[0 - length]
void MPU6000::SPIreadBytes(byte reg, unsigned int length, byte* data, int ChipSelPin)
{
#ifdef DEBUG
Serial.print("SPI (/CS");
Serial.print(ChipSelPin);
Serial.print(") reading ");
Serial.print(length, DEC);
Serial.print(" byte(s) from 0x");
if (reg < 0x10)
{
Serial.print("0");
} // add leading zero - this is an Arduino bug
Serial.print(reg, HEX);
Serial.println("... ");
#endif
digitalWrite(ChipSelPin, LOW);
delay(10); // wait 10 ms for MPU-6000 to react on chipselect (if this is 4 ms or less, SPI.transfer fails)
SPI.transfer(reg | 0x80); // reg | 0x80 causes a "1" added as MSB to reg to denote reading from reg i.s.o. writing to it
unsigned int count = 0;
byte data_bytes_printed = 0;
for (count = 0; count < length; count++)
{
data[count] = SPI.transfer(0x00);
#ifdef DEBUG
if (data[count] < 0x10)
{
Serial.print("0"); // add leading zero - this is an Arduino bug
}
Serial.print(data[count], HEX);
Serial.print(" ");
data_bytes_printed++;
if (data_bytes_printed == 16) // print lines of 16 bytes
{
Serial.println();
data_bytes_printed = 0;
}
#endif
}
digitalWrite(ChipSelPin, HIGH);
DEBUG_PRINTLN(" (done)");
}
//--- Function for SPI reading multiple bits from sensor
// reg : MPU-6000 register number to read from
// ChipSelPin : MPU-6000 chip select pin number (in this sketch defined by ChipSelPin1)
// return > databits
//
// 01101001 read byte
// 76543210 bit numbers
// xxx bitStart = 4, length = 3
// 010 masked
// -> 010 shifted
byte MPU6000::SPIreadBits(byte reg, byte bitStart, byte length, int ChipSelPin)
{
byte b = SPIread(reg, ChipSelPin);
byte mask = ((1 << length) - 1) << (bitStart - length + 1);
b = b & mask;
b = b >> (bitStart - length + 1);
return b;
}
//--- Function for SPI writing multiple bits to sensor
// reg : MPU-6000 register number to write to
// ChipSelPin : MPU-6000 chip select pin number (in this sketch defined by ChipSelPin1)
//
// bbbbb010 -> data (bits to write - leading 0's)
// 76543210 bit numbers
// xxx bitStart = 4, length = 3
// 00011100 mask byte
// 10101111 original reg value (read)
// 10100011 original reg value & ~mask
// 10101011 masked | original reg value
//
// first read byte, then insert bit values, then write byte:
// otherwise all other bits will be written 0, this may trigger unexpected behaviour
void MPU6000::SPIwriteBits(byte reg, byte bitStart, byte length, byte data, int ChipSelPin)
{
byte byte_value = SPIread(reg, ChipSelPin1);
byte mask = ((1 << length) - 1) << (bitStart - length + 1); // create mask
data <<= (bitStart - length + 1); // shift data into correct position
data &= mask; // zero all non-important bits in data (just to make sure)
byte_value &= ~(mask); // zero all important bits in existing byte, maintain the rest
byte_value |= data; // combine data with existing byte
SPIwrite(reg, byte_value, ChipSelPin);
#ifdef DEBUG
Serial.print(" bits set: ");
for (byte i = 0; i < (7 - bitStart); i++)
{
Serial.print("x");
}
for (byte j = 0; j < length; j++)
{
Serial.print(bitRead(data, bitStart - j));
}
for (byte k = 0; k < (bitStart - length + 1); k++)
{
Serial.print("x");
}
Serial.println();
#endif
}
// If you like to know how it works, please read on. Otherwise, just FIRE AND FORGET ;-)
void MPU6000::setMemoryBank(byte bank, boolean prefetchEnabled, boolean userBank, int ChipSelPin)
{
// - the value in 0x6D activates a specific bank in the DMP
// - the value in 0x6E sets the read/write pointer to a specific startaddress within the specified DMP bank
// - register 0x6F is the register from which to read or to which to write the data
// (after each r/w autoincrement address within the specified DMP bank starting from startaddress)
bank = bank & 0x1F; // 0x1F = 00011111
// bank 0: 0 & 0x1F = 00000000 $ 00011111 = 00000000
// bank 1: 1 & 0x1F = 00000001 $ 00011111 = 00000001
// bank 2: 2 & 0x1F = 00000010 $ 00011111 = 00000010
// bank 3: 3 & 0x1F = 00000011 $ 00011111 = 00000011
// bank 4: 4 & 0x1F = 00000100 $ 00011111 = 00000100
// bank 5: 5 & 0x1F = 00000101 $ 00011111 = 00000101
// bank 6: 6 & 0x1F = 00000110 $ 00011111 = 00000110
// bank 7: 7 & 0x1F = 00000111 $ 00011111 = 00000111
// is this to maximize the number of banks to 00011111 is 0x1F = 31 ?
if (userBank)
{
bank |= 0x20;
}
if (prefetchEnabled)
{
bank |= 0x40;
}
SPIwrite(0x6D, bank, ChipSelPin);
}
//***********************************************************//
void MPU6000::setMemoryStartAddress(byte startaddress, int ChipSelPin)
{
// - the value in 0x6D activates a specific bank in the DMP
// - the value in 0x6E sets the read/write pointer to a specific startaddress within the specified DMP bank
// - register 0x6F is the register from which to read or to which to write the data
// (after each r/w autoincrement address within the specified DMP bank starting from startaddress)
SPIwrite(0x6E, startaddress, ChipSelPin);
}
//***********************************************************//
boolean MPU6000::writeDMPMemory()
{
// - the value in 0x6D activates a specific bank in the DMP
// - the value in 0x6E sets the read/write pointer to a specific startaddress within the specified DMP bank
// - register 0x6F is the register from which to read or to which to write the data
// (after each r/w autoincrement address within the specified DMP bank starting from startaddress)
Serial.print("\tWriting DMP memory.......... ");
unsigned int i;
unsigned int j;
byte dmp_byte;
// ### there are 8 DMP banks (numbers 0 to 7)
// DMP banks 0 - 6 are completely filled with 256 bytes:
for (i = 0; i < 7; i++)
{
DEBUG_PRINT("@@@ write bank ");
DEBUG_PRINTLN(i);
setMemoryBank(i, false, false, ChipSelPin1); // bank number = i
setMemoryStartAddress(0, ChipSelPin1); // startaddress = 0 so start writing every DMP bank from the beginning
digitalWrite(ChipSelPin1, LOW);
SPI.transfer(0x6F);
for (j = 0; j < 256; j++) // max. 256 bytes of data fit into one DMP bank
{
dmp_byte = pgm_read_byte(dmpMemory + (i * 256) + j);
SPI.transfer(dmp_byte);
#ifdef DEBUG
if (dmp_byte < 0x10)
{
Serial.print("0"); // add leading zero - this is an Arduino bug
}
Serial.println(dmp_byte, HEX);
#endif
}
digitalWrite(ChipSelPin1, HIGH);
DEBUG_PRINTLN();
}
// DMP bank 7 gets only 137 bytes:
DEBUG_PRINTLN("@@@ write bank 7");
setMemoryBank(7, false, false, ChipSelPin1); // bank number = 7
setMemoryStartAddress(0, ChipSelPin1); // startaddress = 0 so start writing also this DMP bank from the beginning
digitalWrite(ChipSelPin1, LOW);
SPI.transfer(0x6F);
for (j = 0; j < 137; j++) // only 137 bytes of data into DMP bank 7
{
dmp_byte = pgm_read_byte(dmpMemory + (7 * 256) + j);
SPI.transfer(dmp_byte);
#ifdef DEBUG
if (dmp_byte < 0x10)
{
Serial.print("0"); // add leading zero - this is an Arduino bug
}
Serial.println(dmp_byte, HEX);
#endif
}
digitalWrite(ChipSelPin1, HIGH);
DEBUG_PRINTLN();
Serial.println("done.");
return true; // end of writeDMPMemory reached
}
//***********************************************************//
boolean MPU6000::verifyDMPMemory()
{
// - the value in 0x6D activates a specific bank in the DMP
// - the value in 0x6E sets the read/write pointer to a specific startaddress within the specified DMP bank
// - register 0x6F is the register from which to read or to which to write the data
// (after each r/w autoincrement address within the specified DMP bank starting from startaddress)
Serial.print("\tVerifying DMP memory.......... ");
unsigned int i;
unsigned int j;
byte dmp_byte;
byte check_byte;
boolean verification = true;
// ### there are 8 DMP banks (numbers 0 to 7)
// DMP banks 0 - 6 are completely read, all 256 bytes:
for (i = 0; i < 7; i++)
{
DEBUG_PRINT(">>> read bank ");
DEBUG_PRINTLN(i);
setMemoryBank(i, false, false, ChipSelPin1); // bank number = i
setMemoryStartAddress(0, ChipSelPin1); // startaddress = 0 so start reading every DMP bank from the beginning
digitalWrite(ChipSelPin1, LOW);
SPI.transfer(0x6F | 0x80); // 0x6F | 0x80 causes a "1" added as MSB to 0x6F to denote reading from reg i.s.o. writing to it
for (j = 0; j < 256; j++) // max. 256 bytes of data fit into one DMP bank
{
check_byte = pgm_read_byte(dmpMemory + (i * 256) + j);
dmp_byte = SPI.transfer(0x00);
if (dmp_byte != check_byte)
{
Serial.println("$$$ dmpMemory: byte verification error");
verification = false;
}
#ifdef DEBUG
if (dmp_byte < 0x10)
{
Serial.print("0"); // add leading zero - this is an Arduino bug
}
Serial.println(dmp_byte, HEX);
#endif
}
digitalWrite(ChipSelPin1, HIGH);
DEBUG_PRINTLN();
}
// DMP bank 7 only read first 137 bytes:
DEBUG_PRINTLN(">>> read bank 7");
setMemoryBank(7, false, false, ChipSelPin1); // bank number = 7
setMemoryStartAddress(0, ChipSelPin1); // startaddress = 0 so start reading also this DMP bank from the beginning
digitalWrite(ChipSelPin1, LOW);
SPI.transfer(0x6F | 0x80); // 0x6F | 0x80 causes a "1" added as MSB to 0x6F to denote reading from reg i.s.o. writing to it
for (j = 0; j < 137; j++) // only 137 bytes of data into DMP bank 7
{
check_byte = pgm_read_byte(dmpMemory + (7 * 256) + j);
dmp_byte = SPI.transfer(0x00);
if (dmp_byte != check_byte)
{
Serial.println("$$$ dmpMemory: byte verification error");
verification = false;
}
#ifdef DEBUG
if (dmp_byte < 0x10)
{
Serial.print("0"); // add leading zero - this is an Arduino bug
}
Serial.println(dmp_byte, HEX);
#endif
}
digitalWrite(ChipSelPin1, HIGH);
DEBUG_PRINTLN();
if (verification == true)
{
Serial.println("success!");
}
if (verification == false)
{
Serial.println("FAILED!");
}
return verification; // true if DMP correctly written, false if not
}
//***********************************************************//
boolean MPU6000::writeDMPConfig()
{
byte progBuffer;
byte success;
byte special;
unsigned int i;
unsigned int j;
// config set dmpConfig is a long string of blocks with the following structure:
// [bank] [offset] [length] [byte[0], byte[1], ..., byte[length]]
byte bank;
// config set dmpConfig is a long string of blocks with the following structure:
byte offset;
// config set dmpConfig is a long string of blocks with the following structure:
byte length;
Serial.print("\tWriting DMP configuration... ");
for (i = 0; i < MPU6050_DMP_CONFIG_SIZE;)
{
bank = pgm_read_byte(dmpConfig + i++); // pgm_read_byte() is a macro that reads a byte of data stored in a specified address(PROGMEM area)
offset = pgm_read_byte(dmpConfig + i++);
length = pgm_read_byte(dmpConfig + i++);
if (length > 0) // regular block of data to write
{
DEBUG_PRINT("!! bank : ");
DEBUG_PRINTLNF(bank, HEX);
setMemoryBank(bank, false, false, ChipSelPin1); // bank number = bank
DEBUG_PRINT("!! offset: ");
DEBUG_PRINTLNF(offset, HEX);
setMemoryStartAddress(offset, ChipSelPin1); // startaddress = offset from the beginning (0) of the bank
DEBUG_PRINT("!! length: ");
DEBUG_PRINTLNF(length, HEX);
digitalWrite(ChipSelPin1, LOW);
SPI.transfer(0x6F);
for (j = 0; j < length; j++)
{
progBuffer = pgm_read_byte(dmpConfig + i + j);
SPI.transfer(progBuffer);
DEBUG_PRINTLNF(progBuffer, HEX);
}
digitalWrite(ChipSelPin1, HIGH);
i = i + length;
}
else // length = 0; special instruction to write
{
// NOTE: this kind of behavior (what and when to do certain things)
// is totally undocumented. This code is in here based on observed
// behavior only, and exactly why (or even whether) it has to be here
// is anybody's guess for now.
special = pgm_read_byte(dmpConfig + i++);
DEBUG_PRINTLN("!! Special command code ");
DEBUG_PRINTF(special, HEX);
DEBUG_PRINTLN(" found...");
if (special == 0x01)
{
// enable DMP-related interrupts (ZeroMotion, FIFOBufferOverflow, DMP)
SPIwrite(0x38, 0x32, ChipSelPin1); // write 00110010: ZMOT_EN, FIFO_OFLOW_EN, DMP_INT_EN true
// by the way: this sets all other interrupt enables to false
success = true;
}
else
{
// unknown other special command if this may be needed in the future, but for now this should not happen
success = false;
}
}
}
Serial.println("done.");
return true;
}
//***********************************************************//
boolean MPU6000::verifyDMPConfig()
{
byte check_byte;
byte progBuffer;
byte success;
byte special;
unsigned int i;
unsigned int j;
// config set dmpConfig is a long string of blocks with the following structure:
// [bank] [offset] [length] [byte[0], byte[1], ..., byte[length]]
byte bank;
// config set dmpConfig is a long string of blocks with the following structure:
byte offset;
// config set dmpConfig is a long string of blocks with the following structure:
byte length;
boolean verification = true;
Serial.print("\tVerifying DMP configuration... ");
for (i = 0; i < MPU6050_DMP_CONFIG_SIZE;)
{
bank = pgm_read_byte(dmpConfig + i++); // pgm_read_byte() is a macro that reads a byte of data stored in a specified address(PROGMEM area)
offset = pgm_read_byte(dmpConfig + i++);
length = pgm_read_byte(dmpConfig + i++);
if (length > 0) // regular block of data to read
{
DEBUG_PRINT("!! bank : ");
DEBUG_PRINTLNF(bank, HEX);
setMemoryBank(bank, false, false, ChipSelPin1); // bank number = bank
DEBUG_PRINT("!! offset: ");
DEBUG_PRINTLNF(offset, HEX);
setMemoryStartAddress(offset, ChipSelPin1); // startaddress = offset from the beginning (0) of the bank
DEBUG_PRINT("!! length: ");
DEBUG_PRINTLNF(length, HEX);
digitalWrite(ChipSelPin1, LOW);
SPI.transfer(0x6F | 0x80); // 0x6F | 0x80 causes a "1" added as MSB to 0x6F to denote reading from reg i.s.o. writing to it
for (j = 0; j < length; j++)
{
progBuffer = pgm_read_byte(dmpConfig + i + j);
check_byte = SPI.transfer(0x00);
if (progBuffer != check_byte)
{
DEBUG_PRINTLN("$$$ dmpConfig: byte verification error");
verification = false;
}
#ifdef DEBUG
if (check_byte < 0x10)
{
Serial.print("0"); // add leading zero - this is an Arduino bug
}
Serial.println(check_byte, HEX);
#endif
}
digitalWrite(ChipSelPin1, HIGH);
i = i + length;
}
else // length = 0; special instruction to write
{
// NOTE: this kind of behavior (what and when to do certain things)
// is totally undocumented. This code is in here based on observed
// behavior only, and exactly why (or even whether) it has to be here
// is anybody's guess for now.
special = pgm_read_byte(dmpConfig + i++);
DEBUG_PRINT("!! Special command code ");
DEBUG_PRINTF(special, HEX);
DEBUG_PRINTLN(" found...");
if (special == 0x01)
{
// enable DMP-related interrupts (ZeroMotion, FIFOBufferOverflow, DMP)
check_byte = SPIread(0x38, ChipSelPin1); // shoudl read 00110010: ZMOT_EN, FIFO_OFLOW_EN, DMP_INT_EN true
if (check_byte != 0x32)
{
DEBUG_PRINTLN("$$$ dmpConfig: byte verification error");
verification = false;
}
#ifdef DEBUG
if (check_byte < 0x10)
{
Serial.print("0"); // add leading zero - this is an Arduino bug
}