notes, tips, tricks, ideas and such

The MPU6050 data sheet has a nice “marketing” section, claiming it has a DMP unit, and a couple of useful interrupts, among them a accelerometer event interrupt.

However, how to actually use these features is not documented there. And I want to use the Motion Detection Interrupt (so I can put the host MCU to sleep and wake it up when the MPU6050 senses motion – quite probably there’s a cheaper and better documented chip that can do this too, but I already bought a few MPU6050’s… “gy521” breakout board).

http://www.i2cdevlib.com/devices/mpu6050#registers is a in progress development to reverse engineer how to use the DMP. It also has a very useful register map (Thanks!), that fills the gaps in the “RM-MPU-6000A-00” document. With that register map, and a slightly older data sheet that actually has the section 8.3 that’s referred to elsewhere (found at https://dlnmh9ip6v2uc.cloudfront.net/datasheets/Components/General%20IC/PS-MPU-6000A.pdf , but that URL seems like it might not stay.), I could figure out how to enable the Motion Detection Interrupt.

After power on (0x00 to register (decimal) 107), the Motion Detection Interrupt can be enabled as follows:

  • (optionally?) Reset all internal signal paths in the MPU-6050 by writing 0x07 to register 0x68;
  • write register 0x37 to select how to use the interrupt pin. For an active high, push-pull signal that stays until register (decimal) 58 is read, write 0x20.
  • Write register 28 (==0x1C) to set the Digital High Pass Filter, bits 3:0. For example set it to 0x01 for 5Hz. (These 3 bits are grey in the data sheet, but they are used! Leaving them 0 means the filter always outputs 0.)
  • Write the desired Motion threshold to register 0x1F (For example, write decimal 20).
  • To register 0x20 (hex), write the desired motion duration, for example 40ms.
  • to register 0x69, write the motion detection decrement and a few other settings (for example write 0x15 to set both free-fall and motion decrements to 1 and accelerometer start-up delay to 5ms total by adding 1ms. )
  • write register 0x38, bit 6 (0x40), to enable motion detection interrupt.

Translate the above into code for your favourite micro controller. (Or, maybe, the existing Arduino library for MPU6050 already supports this. I don’t know, I’m using STM32 this time. But it could be translated to AVR/PIC/MSP430 etc… as long as it has I2C/TWI)

Now, INT pin should go high when MPU6050 is moved/shaken/dropped. Play with threshold, duration and filter to set sensitivity.

Relevant excerpt from the linked older data sheet (PS-MPU-6000A-00 Revision: 3.1) above:

8.3 Motion Interrupt
The MPU-60X0 provides Motion detection capability with similar functionality to Free Fall detection. Accelerometer measurements are passed through a configurable digital high pass filter (DHPF) in order to eliminate bias due to gravity. A qualifying motion sample is one where the high passed sample from any axis has an absolute value exceeding a user-programmable threshold. A counter increments for each qualifying sample, and decrements for each non-qualifying sample. Once the counter reaches a user-programmable counter threshold, a motion interrupt is triggered. The axis and polarity which caused the interrupt to be triggered is flagged in the MOT_DETECT_STATUS register.

Like Free Fall detection, Motion detection has a configurable acceleration threshold MOT_THR specified in 1mg increments. The counter threshold MOT_DUR is specified in 1 ms increments. The decrement rate has the same options as Free Fall detection, and is specified in the MOT_DETECT_CTRL register.

Because there is no big news yet, some short updates.

– Claude Schwarz pointed me to the Yahoo user group “Lecroy Owners group”, they have design files for a HHZ406 replacement. (Made by Dieter Frieauff). So maybe the ext. trigger input can be repaired as well.

– A service manual for this ‘scope (And others) can be found there as well, or alternatively here: http://www.ko4bb.com/manuals/index.php?dir=LeCroy (Or on elektrotanya.com – but that site is full of ads)

– NoTMS was caused by a missing “Vct”, I accidentally scratched trough this trace while placing the bottom cover. Took quite some time to find, then just a little wire to fix.

9450_scratchedVCT 9450_scratchedVCTFixed

– Thanks to Claude Schwarz (Again), I now have a third ADC card. So I now have spareparts, and if I get one of the 2 broken cards working again, a working 2ch 350Mhz / 400Ms/s (10Gs/s)  DSO. (Or rather: the Leidse Makerspace then has a 2ch 350Mhz DSO). The 3 ADC cards will hereafter be named “9450_3A Claude”, “9450_3A LMS-Broken” and “9450_3A LMS-Working”. (order shown in the picture, ltr: broken, Claude, working )

9450_3A ADCs

– I measured the power supplies on “9450_3A LMS-broken”. All are present. (-5V, -12V, 5V and +12V). Next up: reference voltages and tracing the signal path.

– Some more pictures:

lecroy_patchedOn some of the ADC boards, a 5V regulator is placed where others have just a cap.

lecroy_patched_2

This is another original patch (-5V regulator), both on “9450_3A LMS broken”. This board has no LeCroy repair stickers (shown below), but those patches were there when I got the ‘scope so I assume they are original.

Bot these 2 regulators and the -12V and 12V ones have the correct output voltages.

lecroy_repair

Lecroy Repair stickers on the timebase board. (This board is working fine) There are more of those stickers in the scope on other boards.

9450_errorlog

And the last one for today: The calibration error log. Chan2 has “9450_3A Claude” in this picture, but “9450_3A LMS broken” gives similar results. If I exchange the cards between the channels, ch1 gets the errors and ch2 is error-free. (The error-free channel has “9450_3A LMS working” in both cases).

‘ll keep you posted!

EDIT 2-7-2014:
“Next up: reference voltages and tracing the signal path.”

Measured on HMS403, seems to be OK. Also none of the ADC’s have stuck bits (did not log what bit connected to what line of the LA, but with no input all are 0, as long as there is no selftest / calibration running.)

Please note if you connect a logic analyser to these circuits they are negative logic (“1” is – 5V, “0” = 0V). As the 0V is connected to chassis ground, and your LA’s ground might also be (through the powersuply’s both connected to earth ground), use caution!

The scope does not do a memory test on boot up. I carefully removed one of the RAM IC’s to test this, and the scope does still show “ADC/TMS state working”.

So there might be something wrong with the memory. Fortunately this is normal TTL logic again.

EDIT 8-Aug-2014:
On slower sample rates this scope only uses 1 of its 4 ADC’s (per channel). On slower sample rates, the problem stays, 1 out of 4 points on the display (Maybe 1 out of 4 samples?) is out of line. So it’s not 1 of the 4 adc’s that’s broken (because it only uses one at that sample rate), but something in the memory or further in the data pad. The memory is also divided in 4 parts/banks, so it could just be… But for now I’m going to work on other projects for a while.

Please comment if you have any questions or suggestions!

I wanted to experiment with a optical mouse sensor, did a websearch, found this blogpost, and adapted the code for my A2620 mouse sensor.  As it might be useful to others,  below is my Arduino sketch and the processing sketch.

Use the processing sketch to view the image from the sensor, or use a terminal program to ask it for X / Y movement.

IMG_5986_optical_mouse_a2620

(Yes, that thing to the left used to be a mouse… The thing to the right is an avrdb-m328 board used as an Arduino)

[ Arduino ]
#define FRAMELENGTH 324
#define SCLK 6 // portd.6
#define SDIO 7 // portd.7

byte frame[FRAMELENGTH];
byte flop;

/*
Serial driver for ADNS2010, by Conor Peterson (robotrobot@gmail.com)
Serial I/O routines adapted from Martjin The and Beno?t Rosseau’s work.
Delay timings verified against ADNS2061 datasheet.

The serial I/O routines are apparently the same across several Avago chips.
It would be a good idea to reimplement this code in C++. The primary difference
between, say, the ADNS2610 and the ADNS2051 are the schemes they use to dump the data
(the ADNS2610 has an 18×18 framebuffer which can’t be directly addressed).

This code assumes SCLK is defined elsewhere to point to the ADNS’s serial clock,
with SDIO pointing to the data pin.

Adapted for A2620 on AVRDBM328 by Luke.
*/

const byte regConfig    = 0x40; //a2620 . A2610 =0x00
const byte regStatus    = 0x41; //a2620 . A2610 =0x01
const byte regPixelData = 0x48; //a2620 . A2610 =0x08
const byte maskNoSleep  = 0x01; // unchanged for a2620
const byte maskPID      = 0xE0; // idem

const byte regYmov = 0x42; // a2620
const byte regXmov = 0x43;  //a2620

void mouseInit(void)
{
digitalWrite(SCLK, HIGH);
delayMicroseconds(5);
digitalWrite(SCLK, LOW);
delayMicroseconds(1);
digitalWrite(SCLK, HIGH);
delay(1025);
writeRegister(regConfig, maskNoSleep); //Force the mouse to be always on.
}

void dumpDiag(void)
{
unsigned int val;

val = readRegister(regStatus);

Serial.print(“Product ID: “);
Serial.println( (unsigned int)((val & maskPID) >> 5));
Serial.println(“Ready.”);
Serial.flush();
}

void writeRegister(byte addr, byte data)
{
byte i;

addr |= 0x80; //Setting MSB high indicates a write operation.

//Write the address
pinMode (SDIO, OUTPUT);
for (i = 8; i != 0; i–)
{
digitalWrite (SCLK, LOW);
digitalWrite (SDIO, addr & (1 << (i-1) ));
digitalWrite (SCLK, HIGH);
}

//Write the data
for (i = 8; i != 0; i–)
{
digitalWrite (SCLK, LOW);
digitalWrite (SDIO, data & (1 << (i-1) ));
digitalWrite (SCLK, HIGH);
}
}

byte readRegister(byte addr)
{
byte i;
byte r = 0;

//Write the address
pinMode (SDIO, OUTPUT);
for (i = 8; i != 0; i–)
{
digitalWrite (SCLK, LOW);
digitalWrite (SDIO, addr & (1 << (i-1) ));
digitalWrite (SCLK, HIGH);
}

pinMode (SDIO, INPUT);  //Switch the dataline from output to input
delayMicroseconds(110);  //Wait (per the datasheet, the chip needs a minimum of 100 µsec to prepare the data)

//Clock the data back in
for (i = 8; i != 0; i–)
{
digitalWrite (SCLK, LOW);
digitalWrite (SCLK, HIGH);
r |= (digitalRead (SDIO) << (i-1) );
}

delayMicroseconds(110);  //Tailing delay guarantees >100 µsec before next transaction

return r;
}

//ADNS2610 dumps a 324-byte array, so this function assumes arr points to a buffer of at least 324 bytes. (A2620: unchanged)
void readFrame(byte *arr)
{
byte *pos;
byte *uBound;
unsigned long timeout;
byte val;

//Ask for a frame dump
writeRegister(regPixelData, 0x2A); // wite anything to pixeldatareg to start at first pixel (A2620)

val = 0;
pos = arr;
uBound = arr + 325;

timeout = millis() + 1000;

//There are three terminating conditions from the following loop:
//1. Receive the start-of-field indicator after reading in some data (Success!)
//2. Pos overflows the upper bound of the array (Bad! Might happen if we miss the start-of-field marker for some reason.)
//3. The loop runs for more than one second (Really bad! We’re not talking to the chip properly.)
while( millis() < timeout && pos < uBound)
{
val = readRegister(regPixelData);

//Only bother with the next bit if the pixel data is valid.
if( !(val & 64) ) {
//Serial.println(“Invalid data.”);
continue;
}

//If we encounter a start-of-field indicator, and the cursor isn’t at the first pixel,
//then stop. (‘Cause the last pixel was the end of the frame.)
if( ( val & 128 ) &&  ( pos != arr) ) {
//      Serial.println(“last pixel read.”);
break;
}

*pos = val & 63;
pos++;
}

}

void setup()
{
pinMode(SCLK, OUTPUT);
pinMode(SDIO, OUTPUT);

Serial.begin(38400);
Serial.println(“Serial established.”);
Serial.flush();

mouseInit();
dumpDiag();

}

void loop()
{
int input;
byte buff;

//readFrame(frame);

if( Serial.available() )
{
input = Serial.read();
switch( input )
{
case ‘f’:      // capture frame
Serial.println(“Frame capture.”);
readFrame(frame);
Serial.println(“Done.”);
break;
case ‘d’: // dump frame, raw data
for( input = 0; input < FRAMELENGTH; input++ )  //Reusing ‘input’ here
Serial.write( (byte) frame[input] ); // use serial.write so it does not convert to ascii, –Luke
Serial.write( (byte) 127 );
break;
case ‘p’: // Powerup sequence
mouseInit();
dumpDiag();
break;
case ‘x’:  // read X movement register
buff = readRegister(regXmov);
Serial.print((byte) buff);
Serial.print(” “);
break;
case ‘y’:  // read Y movement register
buff = readRegister(regYmov);
Serial.print((byte) buff);
Serial.print(” “);
break;
case ‘s’:  // Shutdown
writeRegister(regConfig,0xE0);
Serial.print(“Shutdown”);
break;
case ‘i’: // Dump frame values seperated and human readable

for( input = 0; input < FRAMELENGTH; input++ ){  //Reusing ‘input’ here
Serial.print( (byte) frame[input]);
Serial.print( ” “);
// maybe find something to print LF each 18th datapoint?
}
Serial.print( (byte)127 );
break;
case ‘t’: // test image

for( input = 0; input < FRAMELENGTH; input++ ){  //Reusing ‘input’ here
Serial.write( (byte) input%64);
}
Serial.write( (byte) 127 );
break;

}
Serial.flush();
}
}
[ / arduino]

[Processing]

import processing.serial.*;

final int rate = 38400;
final int off_x = 75;
final int off_y = 70;
final int sz = 22;
final int frameX = 18;
final int frameY = 18;
final int frameLen = frameX * frameY;

Serial port;
int[] frame;
int serialCounter;

int nextFrameTime;
int framePeriod = 1000;

void setup()
{
size( 550, 550 );

frameRate(12);

frame = new int[frameLen];

initSerial();

noStroke();
noSmooth();
nextFrameTime = millis();

}

void draw()
{
serialHandler();

if( millis() >= nextFrameTime )
{
requestFrame();

background(245);

for( int i = 0; i < frameLen; i++ )
{
fill( map(frame[i], 0, 63, 0, 255) );
rect(off_x + (i % frameX * sz),
off_y  + (i / frameY * sz),
sz, sz);
}

nextFrameTime = millis() + framePeriod;
}
}

void keyPressed()
{
if( key == ‘f’ )
port.write(‘f’);

if( key  == ‘ ‘ )
requestFrame();

if( key  == ‘t’ )
{
port.write(‘t’); // test frame
serialCounter = frameLen;
}

}

void initSerial()
{
String portName = “/dev/ttyUSB0”;
port = new Serial(this, portName, rate);
println(“Using ” + portName + ” as serial device.”);
}

void requestFrame()
{

port.write(‘d’); // dump normal frame
serialCounter = frameLen;
port.write(‘f’); // request new frame
}

void serialHandler()
{
int incoming;
while( port.available() != 0 )
{
incoming = port.read();
print(incoming + ” “);
if( serialCounter > 0 )
{
if( incoming == 127 )
serialCounter = 0;
else
{
frame[serialCounter – 1] = incoming;
serialCounter–;
}
}
}
}

[/processing]