LSM 303 FIFO mode programming

I would like to use FIFO mode for the LSM303 accelerometer. The idea is to collect data at a high data rate and read multiple samples back in a polled loop. I’ll want to do the same trick with the LG30 gyroscope.

I am using a baby Orangutan for the controller, and can read/write from the device one sample at a time.

Does anyone know the magic spell for this ?

#include <Wire.h>
#include <L3G.h>
#include <LSM303.h>
#include <Servo.h>


L3G gyro;
LSM303 compass;



void setup() {
   Serial.begin (115200);
   Wire.begin();
   //compass.writeAccReg(LSM303::CTRL_REG5_A, 0xc0);  // Reboot.
   compass.init();  // Figure out which device.
   compass.enableDefault();
   
   compass.writeAccReg(LSM303::CTRL_REG1_A, 0x97 );  // Enable X,y,Z registers. (1.3 khz, x,z channels enabled)
   compass.writeAccReg(LSM303::CTRL_REG4_A, 0x88);  // 2G full scale, High resolution mode.
   compass.writeAccReg(LSM303::CTRL_REG5_A, 0x40);    
   compass.writeAccReg(LSM303::FIFO_CTRL_REG_A,0x48);// Fifo mode. No triggers. 32 entries.
  // compass.writeAccReg(LSM303::REFERENCE_A,0x5f);
   Serial.println ("Setup Done");
 
}




float getCompassAngle () {
     int statusA  = compass.readAccReg(LSM303::STATUS_A); 
     int refA = compass.readAccReg(LSM303::FIFO_SRC_REG_A);  // FSS --- bottom 5 bits.
     
     int count = refA & 0x1f;
     Serial.print ("STatus A : "); Serial.print ( statusA);  Serial.print (" RefA " ) ; Serial.print(refA);
     Serial.print (" Count : " ) ; Serial.println ( count );
  
      long x=0 , y=0 , z =0; // Accumulators for X, Z values.
      for ( byte i = 0 ; i < count ; i ++ ) {
        //Serial.println ( "Start Read");
        compass.readAcc( );
        //Serial.println("Done Read");
        if ( compass.timeoutOccurred() ) {
          Serial.println("Timeout on Compass Set");
        }
        x += compass.a.x ;
        y += compass.a.y ;
        z += compass.a.z;
      }
      float xx = (float)(x >> 4 );
      float yy = (float)(y >> 4 );
      float zz = (float)(z >> 4 );
      float angle = 180.0/M_PI*asin(-xx/sqrt(xx*xx+yy*yy+zz*zz));
      return angle;
}


void loop () {
  
  // Get the number of FIFO entries to read. 
  
    static unsigned long lastTick = 0;
    unsigned long thisTick = millis () ;
    if ( thisTick >= lastTick + 10 ) {
      lastTick = thisTick;
      int dt = lastTick - thisTick;
  
      float angle = getCompassAngle ();
 
      static int ctr = 0;
      if ( ctr ++ >= 100 ){
          ctr = 0;
        //  Serial.print ("One Second  " );
         // Serial.println ( angle);
      }
  }
}

Example output form the code :

STatus A : 255 RefA 32 Count : 0
STatus A : 255 RefA 32 Count : 0
STatus A : 255 RefA 32 Count : 0

Thanks !

It turns out that if the FIFO’s on either the MSL303 and L3G get into an error condition, the fifo enable bits in the FIFO control
register need to be reset:

For the accelerometer the set-up code looks like this:

void setup() {
   Serial.begin (115200);
   Wire.begin();
   compass.init();  // Figure out which device.
   compass.enableDefault();
   compass.writeAccReg(LSM303::FIFO_CTRL_REG_A,0x00); // Reset FIFO mode.
   compass.writeAccReg(LSM303::CTRL_REG1_A, 0x97 );  // Enable X,y,Z registers. (1.3 khz, x,z channels enabled)
   compass.writeAccReg(LSM303::CTRL_REG4_A, 0x88);  // 2G full scale, High resolution mode.
   compass.writeAccReg(LSM303::CTRL_REG5_A, 0x40);   
   compass.writeAccReg(LSM303::FIFO_CTRL_REG_A,0x48);// Fifo mode. No triggers. 32 entries.
  // compass.writeAccReg(LSM303::REFERENCE_A,0x5f);
   Serial.println ("Setup Done");
 
}

where theo only change is the additional write of the FIFO_CTRL_REG_A.

The Gyroscope has a simular issue.

Hello.

Is it working the way you expect now? Which LSM303 accelerometer you are using? The bits for the FIFO register are different between versions.

- Jeremy

Yes, Its working well. I’m collecting data from the gyroscope at 800 hz, and from the accelerometer at 400 hz. I’m running the i2c bus at 400 kHz.

I’m using your MinIMU-9 v2 Gyro, Accelerometer, and Compass (L3GD20 and LSM303DLHC Carrier) sensor for the gyroscope and accelerometer.

I’ve attached example code for both the L3G20 and the LSM3030DLHC. I’ve modified my “twi.h” include file to set the speed to 400 Kbaud. I expect that this can be done by simply defining TWI_FRQ in the sketch before including wire.h:

 #define TWI_FREQ 400000L

In my case, I have this defined in twi.h:

#ifndef twi_h
#define twi_h

  #include <inttypes.h>

  //#define ATMEGA8

  #ifndef TWI_FREQ
//  #define TWI_FREQ 100000L
  #define TWI_FREQ 400000L
  #endif

Working test code for the gyroscope is:


#include <Wire.h>
#include <L3G.h>

L3G gyro;



void setup() {
   Serial.begin (115200);
   Wire.begin();
   delay(1000);

   gyro.init();
   gyro.enableDefault();
   gyro.writeReg(L3G_FIFO_CTRL_REG, 0x00);      // Force FIFO to disabled.
    
   gyro.writeReg(L3G_CTRL_REG1, 0xcf );           // 800 hz ODR, 30 hz lp cutoff. All enabled. Power up.
   gyro.writeReg(L3G_CTRL_REG5, 0x40);            //
   gyro.writeReg(L3G_FIFO_CTRL_REG,0x40);         // Fifo mode.
   gyro.writeReg(L3G_FIFO_CTRL_REG,0x30);         // FIFO, watermark = 16.
   Serial.println ("Gyro Setup Done");
 
}




float getGyroRate () {
     int statusA  = gyro.readReg(L3G_STATUS_REG); 
     int refA = gyro.readReg(L3G_FIFO_SRC_REG );  // FSS --- bottom 5 bits.
     
     int count = refA & 0x1f;
     Serial.print ("STatus A : "); Serial.print ( statusA);  Serial.print (" RefA " ) ; Serial.print(refA);
     Serial.print (" Count : " ) ; Serial.println ( count );
  
      long x=0 , y=0 , z =0; // Accumulators for X, Z values.
      for ( byte i = 0 ; i < count ; i ++ ) {
        //Serial.println ( "Start Read");
        gyro.read();
        //Serial.println("Done Read");
      
        x += gyro.g.x ;
        y += gyro.g.y ;
        z += gyro.g.z;
      }
      float angleRate = (float)y/count;
      return angleRate;
}


// Try to read FIFO on Gyro.
void loop () {
    static unsigned long lastTick = 0;
    unsigned long thisTick = millis () ;
    if ( thisTick >= lastTick + 10 ) {
      lastTick = thisTick;
      int dt = lastTick - thisTick;
  
      float rate = getGyroRate ();
 
      static int ctr = 0;
      if ( ctr ++ >= 100 ){
          ctr = 0;
        //  Serial.print ("One Second  " );
         // Serial.println ( angle);
      }
  }
}

And code that tests the accelrometer is:


#include <Wire.h>
#include <LSM303.h>

LSM303 compass;



void setup() {
   Serial.begin (115200);
   Wire.begin();
   //compass.writeAccReg(LSM303::CTRL_REG5_A, 0xc0);  // Reboot.
   compass.init();  // Figure out which device.
   compass.enableDefault();
   compass.writeAccReg(LSM303::FIFO_CTRL_REG_A,0x00); // Reset FIFO mode.
   compass.writeAccReg(LSM303::CTRL_REG1_A, 0x97 );  // Enable X,y,Z registers. (1.3 khz, x,z channels enabled)
   compass.writeAccReg(LSM303::CTRL_REG4_A, 0x88);  // 2G full scale, High resolution mode.
   compass.writeAccReg(LSM303::CTRL_REG5_A, 0x40);    
   compass.writeAccReg(LSM303::FIFO_CTRL_REG_A,0x40);// Fifo mode. No triggers. 32 entries.
  // compass.writeAccReg(LSM303::REFERENCE_A,0x5f);
   Serial.println ("Setup Done");
 
}




float getCompassAngle () {
  //int statusA  = compass.readAccReg(LSM303::STATUS_A); 
     int refA = compass.readAccReg(LSM303::FIFO_SRC_REG_A);  // FSS --- bottom 5 bits.
     
     int count = refA & 0x1f;
     // byte me = compass.readAccReg(LSM303::WHO_AM_I);
     //Serial.print ("ME : " );  Serial.print ( me ) ; 
     //Serial.print (" Status A : "); Serial.print ( statusA);  Serial.print (" RefA " ) ; Serial.print(refA);
     //Serial.print (" Count : " ) ; Serial.println ( count ); 
  
     Serial.print ("Count ") ; Serial.print ( count); Serial.print ( " " );
      long x=0 , y=0 , z =0; // Accumulators for X, Z values.
      for ( byte i = 0 ; i < count ; i ++ ) {
        //Serial.println ( "Start Read");
        compass.readAcc( );
        //Serial.println("Done Read");
        if ( compass.timeoutOccurred() ) {
          Serial.println("Timeout on Compass Set");
        }
        x += compass.a.x ;
        y += compass.a.y ;
        z += compass.a.z;
      }
      float xx = (float)x;
      float yy = (float)y;
      float zz = (float)z;
      return  180.0/M_PI*asin(-xx/sqrt(xx*xx+yy*yy+zz*zz));
}


void loop () {
  
  // Get the number of FIFO entries to read. Fifo is 14 bits.
  
    static unsigned long lastTick = 0;
    unsigned long thisTick = millis () ;
    if ( thisTick >= lastTick + 10 ) {
      lastTick = thisTick;
      int dt = lastTick - thisTick;
  
      float angle = getCompassAngle ();
      Serial.println (angle);
 
      //static int ctr = 0;
      //if ( ctr ++ >= 100 ){
      //    ctr = 0;
        //  Serial.print ("One Second  " );
         // Serial.println ( angle);
      // }
  }
}

Hope this helps.

Thanks for sharing your code!

- Jeremy

Hi sorry for the dumb question but your register CTRL_REG1_A, 0x97 is for the low-power mode (5.376 kHz) am i wrong? How is your accelerometers ODR at 400Hz?

Thank you for your question. Unfortunately, I don’t have the code environment to test this, but from the datasheet, and the value should be 0x17. The critical part of the project was the Gyros to keep the robot balanced. The 0x97 value may be a latent bug that shows up as a slow update on the accelerometer which I may not have noticed in testing.

I am interested in your findings.

I want to use similar code but for acquiring and saving acceleration data with as much precision as possible but i noticed that the lsm303 goes from 12 bit to 10 bit resolution when using FIFO. Great code and thanks for your response