3 years, 3 months ago.

SPI Communication with L3G4200D Digital Gyroscope Sensor

I am using LPC1768 and L3G4200D digital gyroscope sensor. I need them to communicate through SPI where Mbed (Master) able to read the data from the gyroscope sensor (Slave). Can anyone help out?

SPI gyroscope

#include "mbed.h"

SPI spi(p5, p6, p7); // mosi, miso, sclk
DigitalOut cs(p8);
Serial pc(USBTX, USBRX);

int main() {
    
    // Slave select disabled //
    cs = 1;
    
    // Setup the spi for 8 bit data, high steady state clock,
    // Second edge capture, with a 1MHz clock rate
    spi.format(8,3);
    spi.frequency(500000);
 
    // Slave select enabled, it is active low // 
    cs = 0;
 
    // Send 0x8F, the command to read the WHO_AM_I register //
    // 0x8F 1000 1111, MSB = 0 means write and MSB = 1 means read // 
    spi.write(0x8F);
 
    // Send a dummy byte to receive the contents of the WHO_AM_I register //
    // The default response 1101 0011 // 
    int whoami = spi.write(0x00);
    pc.printf("WHOAMI register = 0x%X\n", whoami);
    
    // Slave select disabled //
    cs = 1;
 
    while(1) {
        
        cs = 0 ;
        // Register address for OUT_X_L of the gyroscope // 
        spi.write(0x28 & ~0x8F);
        int data = spi.write(0x00);
        pc.printf("data output 0x%X\n", data);
        
        wait(0.5);
        
        cs = 1;
        
    }
}

2 Answers

3 years, 3 months ago.

You have a number of issues with your code.

Going by the datasheet here the default startup mode for the device is in power down mode. You need to set the PD bit in CRTL_REG1 to 1 to enable things. There could well be some other startup that you need to do, check the register descriptions for any defaults that aren't what you want.

Secondly you look like you are trying to read the low byte of the X axis at address 0x28 however

0x28 = 0010 1000
0x8F = 10001111
~0x8F = 01110000
0x28 & ~0x8F = 0x00100000 = 0x20

So you will in fact be writing to the control register. Given the next transaction you are writing a 0.

If you want to perform a read by setting the most significant bit then you want spi.write(0x28 | 0x80)

Even better use some defines e.g.

#define READ 0x80
#define WRITE 0x00    // not really needed but lets you explicitly say READ or WRITE on every transfer, it makes for clearer code.
#define AUTO_ADDRESS 0x40  // auto increment address when reading/writing multiple bytes.
#define REG_OUT_X_LOW 0x28
...

spi.write(REG_OUT_X_LOW | READ | AUTO_ADDRESS );
lowByte = spi.write(0);
highByte = spi.write(0);

Make sure you do read both the high and low registers for the axis and combine them, just reading the low byte will result in a bit of a random number generator.

Accepted Answer

Hey Andy, thanks for your explanation and appreciate your effort of trying to make things clearer for me. By the way, do you know how to combine the data from high and low registers and convert it to a decimal value? Why is there both high and low registers or what is the difference between them?

posted by huiying jiang 10 Aug 2018

There are two registers because the sensor has a 16 bit output but only 8 bits per register. The high register holds the top 8 bits, the low register the bottom 8 bits.

Combining them is easy, just shift the high byte left by 8 places and then or them together.

spi.write(REG_OUT_X_LOW | READ | AUTO_ADDRESS );
lowByte = spi.write(0);
highByte = spi.write(0);
int16_t combinedValue = (int16_t)highByte<<8 | lowByte;

Mathematically this is identical to saying int16_t combinedValue = highByte * 256 + lowByte; but you will normally see this sort of thing done using the logical / bitwise operators. This is because logical operators are faster than arithmetic ones. However these days a good compiler should be able to figure out that it could make that change itself and the benefits are fairly small so feel free to use the second version if you find it makes things clearer for you.

Converting from the combined value to a decimal number depends on how you configure it. CTRL_REG4 lets you set the full scale value to either 250, 500 or 2000 degrees per second. The default is 250. Table 2.1 of the datasheet then tells you the scale factor to apply for each setting in terms of millidegrees per second.

If it is 250 then you need to multiply the combined value by (8.75 / 1000) to get a rate of turn in degrees per second. For 500 you use 17.5/1000 and for 2000 you use 70.0/1000 (70.0 not 70 so that the compiler knows this is a floating point operation not an integer one)

posted by Andy A 10 Aug 2018

I see. Thank you Andy.

posted by huiying jiang 13 Aug 2018
3 years, 3 months ago.

Hello huiying,

Can you tell us the problem you are experiencing? It seems like you might not be reading the data correctly.

Please let me know if you have any questions!

- Peter, team Mbed

If this solved your question, please make sure to click the "Thanks" link below!