Shop OBEX P1 Docs P2 Docs Learn Events
L3G4200D 3-Axis Gyroscope? — Parallax Forums

L3G4200D 3-Axis Gyroscope?

briank77479briank77479 Posts: 36
edited 2014-06-16 19:49 in Learn with BlocklyProp
I've just finished the Propeller C tutorial on the MMA7455 3-Axis Accelerometer and was wonder if there are any plans to add the L3G4200D 3-Axis Gyroscope to the tutorial list?

Thanks Brian

Comments

  • SRLMSRLM Posts: 5,045
    edited 2014-04-30 16:48
    libpropeller has a C++ module for the L3GD20, which IIRC is code compatible (completely?) with the L3G4200D. There may be some helpful hints there:
    https://github.com/libpropeller/libpropeller/tree/master/libpropeller/l3gd20
  • briank77479briank77479 Posts: 36
    edited 2014-05-01 10:37
    As always guys very, very helpful!!!
    Thank you so much,
    Brian
  • briank77479briank77479 Posts: 36
    edited 2014-05-12 10:28
    Okay, I thought I would be a bit adventurous and see if I could take the L3G4200D code that Andy generated and combine it with the MMA7455 code in the Propeller-C tutorial. The idea was to try and read both simultaneously, see the attached code file.
    When I run the program I'm reading the Accelerometer okay and it appears that I'm reading the Z-axis of the Gyroscope; at least when I move the Gyroscope the value changes. However on the X and Y axis of the Gyroscope it reads 79 & 0 respectively.
    I get the feeling I'm looking at this incorrectly; a conflict between the two sensors.
    Any advice would be greatly appreciated.

    Thanks in advance, Brian
  • edited 2014-05-12 11:16
    I won't have a chance to test this right away. In the meantime, try this:

    - Increase stack arrays to 128
    - Only use print statements from within cog 0, (main function in your particular program)
    - Change return types of functions that run in other cogs to void

    Andy
  • briank77479briank77479 Posts: 36
    edited 2014-05-13 09:43
    Andy,
    Thank you very much for your help!!

    Will update on my results.
    Brian
  • briank77479briank77479 Posts: 36
    edited 2014-05-13 10:06
    Update. I increased the stack arrays to 128 and it worked. I compared the results of running each sensor separately with running them simultaneously and they were the same, within a nominal percentage.

    Much appreciated on your help!!

    Brian
  • c07Brian.Kesterc07Brian.Kester Posts: 36
    edited 2014-06-03 12:40
    Hey Brian, is increasing the stack arrays to 128 the only thing you changed in your code? I'm interested in trying to do something similar, so it would be great to see your final working code if you don't mind...
  • briank77479briank77479 Posts: 36
    edited 2014-06-04 10:44
    I just updated the Library folder and noticed some changes in the SimpleTools, particularly to i2c. Before the update the attached file would provide a "confirm" when the L3G4200D was connected. Now with the new library I get the "Error".
    Please guide me what I need to change to make it work again.

    Thank you
  • edited 2014-06-04 11:23
    I'm rebuilding the circuit now. The i2c_in and i2c_out use just the 7-bit I2C address and does not force you to include the read/write bit any more. So instead of:

    unsigned char addrW = 0xd2; //I2C write address
    unsigned char addrR = 0xd3; //I2C read address

    use

    unsigned char i2cAddr = 0x69; //I2C address

    The i2c_in/out functions also use the value of the register you want to access within the device. It was using the address of the variable containing the register previously. So change:

    i2c_in(bus, addrR, &devId, 1, &reply, 1);

    to

    i2c_in(bus, i2cAddr, devId, 1, &reply, 1);

    Note that I also changed addrR to i2cAddr
  • edited 2014-06-04 11:45
    My initial reply in the post above wasn't quite right, but is now corrected. Here also is the corrected and tested code:
    /**
     * This is the main L3G4200D GYROSCOPE program file.
     */
    
    #include "simpletools.h"
    
    unsigned char i2cAddr = 0x69;       //I2C address of Gyro
    unsigned char devId = 0x0f;         //Device ID register
    unsigned char reply;
    
    i2c *bus;
    
    int main()
    {
      bus = i2c_newbus(5, 4, 0);      //Set I2C bus SCL = P5, SDA = P4
    
      i2c_in(bus, i2cAddr, devId, 1, &reply, 1); 
      
      if(reply == 0b11010011)
        print("Device ID confirmed: %b\n", reply);
      else
        print("Error, wrong device ID: %b\n", reply);
    }
    
    
  • briank77479briank77479 Posts: 36
    edited 2014-06-05 10:37
    Andy,
    Thank you very much for the assistance. I tried to carry the revisions over to the L3G4200D code you created earlier to read the Gyro scope; see attached.
    Evidently I missed something as the results are x = 6, y = 4, z = 82 and they don't vary when the device is moved.

    What did I miss?

    Again, Thank you
    Brian
  • edited 2014-06-05 11:07
    Hi Brian,

    Looks like you got everything right with the code modifications. I loaded it onto mine, and it worked correctly on the first download, no bugs.

    I'm not sure what's up with yours. Re-run the previous example program. Did it confirm that the Gyro is replying? If no, check wiring. If yes, let's look at your most recent program's project settings. Click the bottom-left Show Project Manager, and check the Project Options tab. Compiler Type = C, Memory Model = CMM Main RAM Compact..., Optimization -OS Size. Click compiler. 32bit Double should be checked, and -std=c99 should be in the Other Compiler Options field. In Linker, Math Lib is checked.

    Andy
    /**
     * This is the main L3G4200D GYROSCOPE program file.
     */
    
    #include "simpletools.h"
    
    unsigned char i2cAddr = 0x69;       //I2C Gyro address
    
    //L3G4200D register addresses & commads.
    //See device datasheet section 7 for more info.
    unsigned char devId  = 0x0f;        //Device ID
    unsigned char ctrl1  = 0x20;        //Control reg1
    unsigned char cfg1   = 0b00011111;   //100 hz, 25 cutoff, power up, axes enabled
    unsigned char ctrl2  = 0x21;
    unsigned char ctrl3  = 0x22;
    unsigned char cfg3   = 0b00001000;    //Enable data poling (I2_DRDY)
    unsigned char ctrl4  = 0x23;
    unsigned char cfg4   = 0b10000000;    //Block until read, big endian
    unsigned char status = 0x27;
    unsigned char xL     = 0x28;            //Reg for x low byte
                                           //Next 5 bytes xH, yL, yH, zL, xH
    
    unsigned char reply;                //Single byte reply
    char xyz[6];                        //XYZ dat array
    int x, y, z;                       //Axis variables
    
    i2c *bus;                           //Declare I2C bus
    
    
    int main()
    {
      bus = i2c_newbus(5, 4, 0);        //New I2C bus SCL = P5, SDA = P4
    
      int n;
      n = i2c_out(bus, i2cAddr, ctrl3, 1, &cfg3, 1);
      n += i2c_out(bus, i2cAddr, ctrl4, 1, &cfg4, 1);
      n += i2c_out(bus, i2cAddr, ctrl1, 1, &cfg1, 1);
    
      if(n != 9)
      {
        print("Bytes should be 9, but was %d,", n);
        while(1);
      }
      
      while(1)                            //Main Loop
      {
        int ready = 0;                    //Wait until ready
        while (!ready)
        {
          i2c_in(bus, i2cAddr, status, 1, &ready, 1);
          ready = 1 & (ready >>= 3);
        }
    
        for(int i = 0; i < 6; i++)        //Get axis bytes
        {
          int regAddr = xL + i;
          i2c_in(bus, i2cAddr, regAddr, 1, &xyz[i], 1);
        }
    
        //Bytes to int
        x = (int) (short) ((xyz[1] << 8) + xyz[0]) / 114;
        y = (int) (short) ((xyz[3] << 8) + xyz[2]) / 114;
        z = (int) (short) ((xyz[5] << 8) + xyz[4]) / 114;
     
        printf("x = %d, y = %d z = %d\n", x, y, z);
        pause(250); 
        print(HOME, CLREOL);
      }
    }
    
  • briank77479briank77479 Posts: 36
    edited 2014-06-06 04:52
    Andy,
    This is where I slap my forehead:innocent: I was so used to running programs with the correct settings that I sometimes forget to check periodically. Sure enough a couple of settings weren't.

    Very much appreciated the help!!!

    Brian
  • Kaeru no OjisanKaeru no Ojisan Posts: 26
    edited 2014-06-15 17:29
    Dear, Andy,

    I would like to apply your code to MPU6050 Gyro + Accelerometer, and would like to know the details of your code.
    It is appreciated if you could explain the followings;

    1) What value of “ready” are you expecting in the line “i2c_in(bus, i2cAddr, status, 1, &ready, 1);” ?
    How shall I amend the next line “ready = 1 & (ready >>= 3);” for MPU6050 ?

    2) Why is 3 obtained by “n = i2c_out(bus, i2cAddr, ctrl3, 1, &cfg3, 1);” ?

    3) Why do you not use “while(i2c_busy(…,…)” instead of “if(n != 9)” ?

    Thanks Kaeru no Ojisan
  • edited 2014-06-16 16:34
    This is a little off topic, I might move it to its own thread. I will leave a link in this thread if I do.

    "1) What value of “ready” are you expecting in the line “i2c_in(bus, i2cAddr, status, 1, &ready, 1);” ?
    How shall I amend the next line “ready = 1 & (ready >>= 3);” for MPU6050 ?"

    In this Register Map document:

    http://invensense.com/mems/gyro/documents/RM-MPU-6000A.pdf

    Bit 0 of the INT_STATUS register 0x3A is 1 when data is ready, and 0 after the info is read. Depending on how other registers have been configured, it should be possible to declare status to 0x3A (instead of 0x27). Then, you could use:

    i2c_in(bus, i2cAddr, status, 1, &ready, 1);
    ready = 1 & ready;

    This also assumes that i2cAddr has been updated. I see from this Product Spec document

    http://www.cdiweb.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf

    ...that the AD0 pin can be set high for an address of 0b1101001 or low for an address of 0b1101000. One of these values should be the device's I2C address (in place of 0x69).


    "2) Why is 3 obtained by “n = i2c_out(bus, i2cAddr, ctrl3, 1, &cfg3, 1);” ?"

    3 is the total number of bytes sent. The first byte is the 7-bit I2C address + read/write bit. The second byte is ctrl3, and the third byte is cfg3. If it returns a different number, it might mean that it did not receive an ACK signal after attempting to send one of the bytes. One situation that could lead to this is if the I2C address sent doesn't match what the chip thinks its address is.


    "3) Why do you not use “while(i2c_busy(…,…)” instead of “if(n != 9)” ?"

    Some chips are designed to be polled for an ACK signal while they are busy processing. That's the case for the EEPROM in the I2C tutorial http://learn.parallax.com/propeller-c-simple-protocols/diy-i2c. The i2c_busy function accommodates that. Other devices have other ways of reporting whether they are ready. We find out these details by reading the device datasheet. If I remember correctly, the datasheet for the L3G4200D explained that bit 3 of register 0x69 would contain that information if bit 3 of register 0x22 is set. See usage of ctrl3 and cfg3 in the program. Note, it also depended on several other settings that the first three i2c_out statements configured. So, the equivalent of while(i2c_busy...)) for the L3G4200D ended up being this:

    int ready = 0;
    while (!ready)
    {
    i2c_in(bus, i2cAddr, status, 1, &ready, 1);
    ready = 1 & (ready >>= 3);
    }

    In a more direct answer to your question, I used if(n != 9) for troubleshooting while writing the code. I think I left it in there as a diagnostic in case there were problems. ...or, maybe I just forgot to remove it after getting everything running.

    Have you tried modifying the smaller program yet? After poking around in the Product Spec and Register Map documents, this is my guess at what the program would look like if modified for your chip. The if... condition was arrived at based on the description in the Register Map's WHO_AM_I register description on page 46.
    /*
      Test code modified for the MPU6050 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 reply;
    
    i2c *bus;
    
    int main()
    {
      bus = i2c_newbus(5, 4, 0);      //Set I2C bus SCL = P5, SDA = P4
    
      i2c_in(bus, i2cAddr, devId, 1, &reply, 1); 
      
      if(reply == 0b01101000)
        print("Device ID confirmed: %b\n", reply);
      else
        print("Error, wrong device ID: %b\n", reply);
    }
    

    P.S. Note that there are two versions of the i2cAddr variable declaration. I guessed that your module has AD0 set high, but it might be grounded instead. If that's the case, you'll need to comment the one declaration and uncomment the other.
  • Kaeru no OjisanKaeru no Ojisan Posts: 26
    edited 2014-06-16 19:49
    Dear Andy,

    Thank you very much and I appreciated your help.
    I made new thread relevant to this matter (I think).

    http://forums.parallax.com/showthread.php/156107-MPU6050-Quaternion?p=1273916#post1273916

    It is also very much appreciated if you can help on this problem.

    Kaeru no Ojisan
Sign In or Register to comment.