L3G4200D Gyroscope in SPI Mode

Hi everyone,

I'm trying again to figure out how to get data from the Parallax L3G4200D gyroscope module in 3-wire SPI mode. The datasheet can be found here: https://www.parallax.com/sites/default/files/downloads/27911-L3G4200D-Gyroscope-Manufacturer-Datasheet.pdf. My code is below:
#include "simpletools.h"
signed char z; //Stores the gyroscope measurement
int main(){
  high(14); //CS line high (SPI inactive)
  low(13); //CLK line low (prepares chip to send brief high signals or clock pulses)
  low(14); //CS line low (start SPI)
  
  shift_out(12, 13, MSBFIRST, 8, 0b01100011); //Write MCTL register (register 4) to set 3-wire SPI
  //shift_out(12, 13, MSBFIRST, 1, 0b0); //Send don't care bit
  shift_out(12, 13, MSBFIRST, 8, 0b00000001); //Value for MCTL register (register 4) to set 3-wire SPI
  
  // Set 100Hz output rate, 25Hz cutoff freq, normal mode, enable all axes
  shift_out(12, 13, MSBFIRST, 8, 0b01100000); //Write MCTL register (register 1)
  //shift_out(12, 13, MSBFIRST, 1, 0b0); //Send don't care bit
  shift_out(12, 13, MSBFIRST, 8, 0b00011111); //Value for MCTL register (register 1)
  
  high(14); //CS high(stop SPI)
  pause(1);
  
  while(1){ //Main loop
    low(14); //CS low selects chip
    shift_out(12, 13, MSBFIRST, 8, 0b11101100); //Send read register address (OUT_Z_L)
    //shift_out(12, 13, MSBFIRST, 1, 0b0); //Send don't care value
    z = shift_in(12, 13, MSBPRE, 8); //Get value from register
    z = z-0; //Remove bias
    float zf = (float) z; //Convert z from signed char to float
    high(14); //De-select chip with CS line high
    print("z = %f\n", zf);
    pause(50); //Cycle at 20Hz
  }
}

First, I initialize SPI mode. Then, I set 3-wire SPI mode by writing to register 4. I believe there is an error in the datasheet because it says to set the SIM bit to 1 in register 2, however, SIM is in register 4, not 2. After that I write to register 1, setting a 100Hz output rate, 25Hz cutoff frequency, normal mode, and enable all axes. In the main loop I read from the OUT_Z_L register. I have commented out the "send don't care bit" lines because the datasheet doesn't say that is required, unlike the accelerometer's datasheet. When I run the code as is, I get a constant stream of z = 41.00. If I do not convert z to a float I get a constant stream of 0.

Could someone please help me figure out how to get correct data? I realize I also need to get OUT_Z_H data and do something with the two values but that can be the next step. Thank you.

Respectfully,
David

Comments

  • 10 Comments sorted by Date Added Votes
  • Hello mods,

    Perhaps my post above would be better suited for the "Robotics" or "Accessories" sections. Would you mind moving it, please?

    Thank you,
    David
  • Did you try sending the "don't care bit"?
    With some SPI devices you have to send the "deselect", "select" sequence between each command. I don't know if that is the case with this one.
    Tom
  • Hi Tom,

    Yes I just tried your suggestion and the only thing that changes is I get a different value for the steady stream of outputs. I've also tried to add the Who_Am_I register to see if the chip is actually communicating. Here is the current code:
    #include "simpletools.h"
    signed int zL; //Stores the gyroscope measurement
    signed int zH;
    int whoAmI;
    
    int main(){
      high(14); //CS line high (SPI inactive)
      low(13); //CLK line low (prepares chip to send brief high signals or clock pulses)
      low(14); //CS line low (start SPI)
      
      shift_out(12, 13, MSBFIRST, 8, 0b00001111); //Write who am I register
      shift_out(12, 13, MSBFIRST, 1, 0b0); //Send don't care bit
      shift_out(12, 13, MSBFIRST, 8, 0b11010011); //Value who am I register
      whoAmI = shift_in(12, 13, MSBPRE, 8);
      print("whoAmI = %x\n", whoAmI);
      
      shift_out(12, 13, MSBFIRST, 8, 0b01100011); //Write MCTL register (register 4)to set 3-wire SPI and 250 deg/s
      shift_out(12, 13, MSBFIRST, 1, 0b0); //Send don't care bit
      shift_out(12, 13, MSBFIRST, 8, 0b10000001); //Value for MCTL register (register 4) to set 3-wire SPI and 250 deg/s
      
      shift_out(12, 13, MSBFIRST, 8, 0b00100010); //Write MCTL register (register 3)to set data ready signal
      shift_out(12, 13, MSBFIRST, 1, 0b0); //Send don't care bit
      shift_out(12, 13, MSBFIRST, 8, 0b00001000); //Value for MCTL register (register 3) to set data ready signal
      
      // Set 100Hz output rate, 25Hz cutoff freq, normal mode, enable all axes
      shift_out(12, 13, MSBFIRST, 8, 0b01100000); //Write MCTL register (register 1)
      shift_out(12, 13, MSBFIRST, 1, 0b0); //Send don't care bit
      shift_out(12, 13, MSBFIRST, 8, 0b00011111); //Value for MCTL register (register 1)
      
      high(14); //CS high(stop SPI)
      pause(1);
      
      while(1){ //Main loop
        low(14); //CS low selects chip
        shift_out(12, 13, MSBFIRST, 8, 0b10101100); //Send read register address (OUT_Z_L)
        shift_out(12, 13, MSBFIRST, 1, 0b0); //Send don't care bit
        zL = shift_in(12, 13, MSBPRE, 8); //Get value from register
        
        shift_out(12, 13, MSBFIRST, 8, 0b10101101); //Send read register address (OUT_Z_H)
        shift_out(12, 13, MSBFIRST, 1, 0b0); //Send don't care bit
        zH = shift_in(12, 13, MSBPRE, 8); //Get value from register
    
        high(14); //De-select chip with CS line high
        print("zL = %d     zH = %d\n", zL, zH);
        pause(500); //Cycle at 20Hz
      }
    }
    

    Does it appear that I've implemented the Who_Am_I call correctly? The output is hex value ff. I have no idea what that means or what I should be getting from the Who_Am_I register.

    Thanks,
    David
  • Between each complete command, try adding
    high(14); //De-select chip with CS line high
    low(14); //CS low selects chip    
    

    See the example below in your main loop.
     while(1){ //Main loop
        low(14); //CS low selects chip
        shift_out(12, 13, MSBFIRST, 8, 0b10101100); //Send read register address (OUT_Z_L)
        shift_out(12, 13, MSBFIRST, 1, 0b0); //Send don't care bit
        zL = shift_in(12, 13, MSBPRE, 8); //Get value from register
    
    // add deselect; select statements like this after each command
    // do this also for each group of commands before the while loop 
    high(14); //De-select chip with CS line high
    low(14); //CS low selects chip    
    // **************
    
        shift_out(12, 13, MSBFIRST, 8, 0b10101101); //Send read register address (OUT_Z_H)
        shift_out(12, 13, MSBFIRST, 1, 0b0); //Send don't care bit
        zH = shift_in(12, 13, MSBPRE, 8); //Get value from register
    
        high(14); //De-select chip with CS line high
        print("zL = %d     zH = %d\n", zL, zH);
        pause(500); //Cycle at 20Hz
      }
    
  • Tom,

    Thank you so much for the suggestion!! I may be on to something here. I've implemented your recommendations and I'm getting different values for zL and zH when I move the board around. Now I think the next step is for me to figure out how to combine the zL and zH numbers to get meaningful data.

    Also, I'm still getting a hex value of ff when I try the whoAmI function. Here is that block of code:
    low(14);
      shift_out(12, 13, MSBFIRST, 8, 0b00001111); //Write who am I register
      //shift_out(12, 13, MSBFIRST, 8, 0b11010011); //Value who am I register
      whoAmI = shift_in(12, 13, MSBPRE, 8);
      high(14);
      print("whoAmI = %x\n", whoAmI);
    

    Do you know how the whoAmI register function is supposed to work? I am writing the address of the whoAmI register (0b00001111) and then reading from that register, calling the output "whoAmI". Then I attempt to print the value. I'm not sure what is supposed to be printed. I thought it might to be 11010011, which is the value given in the datasheet for that register.

    Thanks again for your help! I was getting pretty frustrated and now I can see some light at the end of the tunnel!

    Respectfully,
    David
  • Tom,

    By the way, how did you know that you need to set the chip select low before each transmission and high after each transmission? Is that something you just know, or is that in the datasheet and I should have seen it?

    Also, for anyone else, I want to mention that it only outputs a constant stream of 255 if you send the "don't care bit". I believe it's important to leave that line of code out for this gyroscope module, whereas the accelerometer datasheet specifically says to send the "don't care bit".

    David
  • Tom,

    By the way, how did you know that you need to set the chip select low before each transmission and high after each transmission? Is that something you just know, or is that in the datasheet and I should have seen it?

    Also, for anyone else, I want to mention that it only outputs a constant stream of 255 if you send the "don't care bit". I believe it's important to leave that line of code out for this gyroscope module, whereas the accelerometer datasheet specifically says to send the "don't care bit".

    David

    Glad to read that you are making progress. It's been a while, but I think I first read this SPI tutorial that showed the high-low cycle after each command group here:
    learn.parallax.com/tutorials/language/propeller-c/propeller-c-simple-protocols/spi-example

    Then I tried writing code for the 1620 temperature sensor. I tried without the high-low cycle and it didn't work. So I just use it whenever I use an SPI device. When you look at the timing diagrams, it shows the CS line going high at the end of the command sequence.

    I just looked at the data sheet in the link you provided in your original post. I don't see any reference to a don't care bit (although it's 4AM here and I might have missed it.) Usually it would be clearly shown in the timing diagram. For devices where it is shown, I've always found it necessary.

    I have no idea how the who-am-I command is supposed to work. I didn't see anything in the data sheet explaining it.

    Tom
  • I'm not sure if this helpful for your SPI effort, but this works with the L3G4200D using i2c.

    This came from the following thread:
    forums.parallax.com/discussion/155488/l3g4200d-3-axis-gyroscope

    I made some modifications to the code to work with the L3G4200D rather than the MPU6050
    /*
      Test code modified for the MPU6050 gyro
      JM - Modified for the L3G4200D gyro
    */
    
    #include "simpletools.h"
    
    unsigned char i2cAddr = 0b1101001;       //I2C address of Gyro if AD0 is high
    // unsigned char i2cAddr = 0b1101000;       //I2C address of Gyro if AD0 is low
    
    //unsigned char devId = 0x75;         //Device ID register
    unsigned char devId = 0x0f;         //Device ID register
    unsigned char reply;
    
    i2c *bus;
    
    int main()
    {
      bus = i2c_newbus(2, 4, 0);      //Set I2C bus SCL = P5, SDA = P4
    
      i2c_in(bus, i2cAddr, devId, 1, &reply, 1); 
      
      //if(reply == 0b01101000)
      if(reply == 0b11010011)
        print("Device ID confirmed: %b\n", reply);
      else
        print("Error, wrong device ID: %b\n", reply);
    }
    


    When this is loaded to an Activity Board with a L3G4200D I get the following in the serial term output.
     
      Device ID confirmed: 11010011
    
    


    This seems to match the data sheet values for 'WHO_AM_I'

    WHO_AM_I r 0F 000 1111 11010011
  • Hi Tom and Jon,

    Thank you both your help. I realized my mistake with the whoAmI register was simple - I was using a write bit (0) instead of a read bit (1). I also figured out the 2's complement operation to combine the zL and zH values. The measurements jump around quite a bit, certainly more than I would expect. In the next few days I will try implementing the data ready function and perhaps some of the other available filters to see if that helps. Thank you again. My current code is below:
    #include "simpletools.h"
    signed int whoAmI;
    signed int zL, zH, z, zSum, zAvg;
    
    int main(){
      high(14); //CS line high (SPI inactive)
      low(13); //CLK line low (prepares chip to send brief high signals or clock pulses)
      
      low(14);
      shift_out(12, 13, MSBFIRST, 8, 0b10001111); //Send read register address (whoAmI)
      whoAmI = shift_in(12, 13, MSBPRE, 8);
      high(14);
      
      if(whoAmI == 0b11010011){
        print("Device ID Correct: %b\n", whoAmI);
      }
      else{
        print("Device ID Incorrect: %b\n", whoAmI);
      } 
      
      low(14); //CS line low (start SPI)
      shift_out(12, 13, MSBFIRST, 8, 0b01100011); //Write MCTL register (register 4)to set 3-wire SPI
      shift_out(12, 13, MSBFIRST, 8, 0b00000001); //Value for MCTL register (register 4) to set 3-wire SPI
      high(14);
      
      // Set 100Hz output rate, 25Hz cutoff freq, normal mode, enable all axes
      low(14);
      shift_out(12, 13, MSBFIRST, 8, 0b01100000); //Write MCTL register (register 1)
      shift_out(12, 13, MSBFIRST, 8, 0b00011111); //Value for MCTL register (register 1)
      high(14); //CS high(stop SPI)
      
      pause(1);
      
      low(14); //CS low selects chip
      shift_out(12, 13, MSBFIRST, 8, 0b11101100); //Send read register address (OUT_Z_L)
      zL = shift_in(12, 13, MSBPRE, 8); //Get value from register
      high(14); //De-select chip with CS line high
        
      low(14);
      shift_out(12, 13, MSBFIRST, 8, 0b10101101); //Send read register address (OUT_Z_H)
      zH = shift_in(12, 13, MSBPRE, 8); //Get value from register
      high(14);
      
      //Get z-axis zero-rate offset (zAvg)
      for(int i=0; i<100; i++){
        z = (zL<<8 | zH);
        zSum += z;
      }    
      zAvg = zSum/100;
      print("zAvg = %d\n", zAvg);
      
      
      
    
      while(1){ //Main loop
        low(14); //CS low selects chip
        shift_out(12, 13, MSBFIRST, 8, 0b11101100); //Send read register address (OUT_Z_L)
        zL = shift_in(12, 13, MSBPRE, 8); //Get value from register
        high(14); //De-select chip with CS line high
        
        low(14);
        shift_out(12, 13, MSBFIRST, 8, 0b10101101); //Send read register address (OUT_Z_H)
        zH = shift_in(12, 13, MSBPRE, 8); //Get value from register
        high(14);
        
        z = (zL<<8 | zH)-zAvg; //2's complement operation
        float zf = (float) z; //Change to a float
        zf = zf*0.00875; //Multiply by sensitivity value for 250deg/s
     
        
        print("zf = %f\n", zf);
        pause(50); //Cycle at 20Hz
      }
    }
    
  • Reading through my code above, I think I found another error. It looks like the z-axis zero-rate offset (zAvg) is only getting one value from OUT_Z_L and OUT_Z_H. I need to put those read register blocks within the for loop. I'll do that this evening and get back with an update.

    Thanks,
    David
Sign In or Register to comment.