Electrical – Why can I read accelerometer and gyroscope data on the LSM9DS1 with SPI, but not the magnetometer data on the same device

memspicspist

I'm currently working on a device that uses the LSM9DS1, a 9-axis motion sensor by ST Microelectronics. I’m using a PIC24FJ128GB202 microcontroller by Microchip, and communicating with the sensor using SPI. The LSM9DS1 has an accelerometer and a gyroscope that share configuration registers and SDO/SDI pins as well as a magnetometer that has its own configuration registers and SDO/SDI pins. The SCK is shared amongst them all and the CS pins are split between magnetometer and accelerometer/gyroscope (so it’s as if there are two separate devices in the one package, one for the magnetometer and the other for the accelerometer/gyroscope.) To test the device I’m using the breadboard-compatible STEVAL MKI159-V1 evaluation board, which has combined the two SDO pins and the two SDI pins.

The issue I’m having is that I’m perfectly able to receive the data from the accelerometer/gyroscope, but using the same process, when I try to read the data from the magnetometer, all I’m getting is all 1’s for some bytes and all 0’s for the others (data is separated in to high byte and low byte for each of the three axes.) I assumed that maybe the data isn’t ready so that could have been an issue, but the device ID that is supposed to return a constant value is returning 0, every time I read it.

I have already tried:

  • using another sensor of the same model (to test if the first one was broken)
  • changing the wires to check for poor connections
  • reducing/increasing the speed of data transfer
  • reducing the speed only for writing to the registers (which helped with an issue with the gyro)

Has anyone had issued with this device or the evaluation board before, or is there something that I’m not doing right? I can’t understand why one part of the device would work and the other wouldn’t using the same exact method, and I highly doubt that the issue is they’re both broken in the same way (that would be the worst luck ever.) I don’t have access to an oscilloscope but I’ve considered maybe attaching some shift registers with LEDs to see what’s being transferred out. If anyone has any feedback about this issue or any methods I could use to tackle the problem I would be super grateful.

This is the code that relates to the communication with the sensor that I wrote:

    #define LSM_CS LATBbits.LATB15 
    #define MAG_0_CS LATBbits.LATB14

    #define GET_LSM_CS PORTBbits.RB15         // I added these thinking that                 possibly it was trying to 
    #define GET_MAG_0_CS PORTBbits.RB14   // start reading before the CS was         considered ‘0’

    volatile unsigned char dataOne[64];
    volatile int beginReading = 0;
    volatile int devicesConfigured = 0;
    volatile int i = 0;
    volatile int dataReady;
    volatile int readNew = 1;

/********************* PIN INITIALIZE *******************/
void pinInit(){
TRISA = 0x0000;
TRISB = 0x0002;     //RB1 (pin 5) is input
ANSA = 0x0000;
ANSB = 0x0000;

OSCCON = 0x46;
OSCCON = 0x57;
OSCCONbits.IOLOCK = 0;  //Unlocks PPS for writing

//SET PPS
//Inputs
RPINR20bits.SDI1R = 1;  //Assigned SDI to pin 'RP1' (pin 5) B1

//Outputs
RPOR1bits.RP2R = 7;     //SDO1 is assigned to pin RP2 (pin 6) B2
RPOR1bits.RP3R = 8;     //SCK1OUT is assigned to pin RP3 (pin 7) B3

OSCCON = 0x46;
OSCCON = 0x57;
OSCCONbits.IOLOCK = 1; //Stops write;
}

    /**************************** SPI INITIALIZE ******************************/
    void SPIInit(){
    IFS0bits.SPI1IF = 0;
    IFS0bits.SPI1TXIF = 0;
    IFS3bits.SPI1RXIF = 0;

IEC0bits.SPI1IE = 0;        // SPI1 General Interrupt Disabled
IEC0bits.SPI1TXIE = 0;      // Transmit complete interrupt Disabled
IEC3bits.SPI1RXIE = 0;      // Receive complete interrupt Disabled


/*Clear ON bit*/
SPI1CON1Lbits.SPIEN = 0;

/*Clear receiver buffer*/
int clr = SPI1BUFL;

/*Clear enhanced mode bit*/
SPI1CON1Lbits.ENHBUF = 0;

/*Clear overflow bit*/
SPI1STATLbits.SPIROV = 0;

/*Set master enable*/
SPI1CON1Lbits.MSTEN = 1;

/*Other CON settings*/
SPI1BRGL = 0xFFFF;                //FPB/(2*(SPI1BRGL +1))   7999
SPI1CON1Lbits.MODE = 0x0;       //8-bit data
SPI1CON1Lbits.DISSDO = 0;       //SDO1 controlled by module
SPI1CON1Lbits.DISSDI = 0;       //SDI1 controlled by module
SPI1CON1Lbits.DISSCK = 0;       //SCK1 is controlled by module
SPI1CON1Lbits.CKP = 1;          //Clock idle is high, active low  -----_-_-_-_-_-_-_-----
SPI1CON1Lbits.CKE = 0;          //Transmit from idle to active   
SPI1CON1Lbits.SMP = 0;          //Input sampled at end of data output time

SPI1CON1Hbits.AUDEN = 0;        //Audio protocol is disabled
SPI1CON1Hbits.IGNROV = 1;       //Ignore overflow error
SPI1CON1Hbits.IGNTUR = 1;       //Ignore transmit underrun error
SPI1CON1Hbits.MSSEN = 0;        //Slave select is controlled by I/O
SPI1CON2Lbits.WLENGTH = 0x00;   //Use SPI1CON1Lbits.MODE

//SPI1IMSKLbits.BUSYEN = 1;
//SPI1IMSKLbits.SPIRBFEN = 1;

/*Set on bit*/
SPI1CON1Lbits.SPIEN = 1;

}

/*************************** MAIN *************************/
int main(void)
{   
LSM_CS = 1;
MAG_0_CS = 1;


OSCCON = 0x0000;
while(delay < 1024){delay++;}   // Wait till the oscillator stabilizes

pinInit();

SPIInit();

devicesConfigured = 0;


TRISAbits.TRISA2 = 1;

while(1)
{
        if(devicesConfigured != 1)
        {
            configureDevices();

            SPI1BRGL = 499; // Speed slower prior to ensure correct values written
                            // Clock speed increased here for SPI reading
        }

        buttonRead();  // Used to toggle data reading

        if(beginReading == 1)
        {
            LSM_CS = 0;
            while(GET_LSM_CS == 1);
            dataReady = SPIRead(0x27);
            LSM_CS = 1;
            while(GET_LSM_CS == 0);

            if((dataReady & 0x3) > 0)
            {
                read_Data_XL2();
            }

            MAG_0_CS = 0;
            while(GET_MAG_0_CS == 1);
            dataReady = SPIRead(0x27);
            MAG_0_CS = 1;
            while(GET_MAG_0_CS == 0);

            if((dataReady & 0x0F) > 0)
            {
                read_Data_M2();
            }

            LATBbits.LATB5 = 1;   
            }
        }
        else
        {
            dimmingLED();
        }

}
}

/********************* INITIAL CONFIGURATION ********************/
void configureDevices()
{    
configureLSM();

configureMagnetometer0(); 

devicesConfigured = 1;
}

void SPIWrite(int address, int data){

int clr;

/*Enable SPI*/

/*Write address + write (0) to SPI1BUF*/
SPI1BUFL = address;

/*Wait for the transfer buffer to empty*/
while(SPI1STATLbits.SPITBF);

/*Wait for the receive buffer to fill*/
while(SPI1STATLbits.SPIRBE);

/*Read value in buffer to clear it*/
clr = SPI1BUFL;

/*Write data to SPI1BUF*/
SPI1BUFL = data;

/*Wait for the transfer buffer to empty*/
while(SPI1STATLbits.SPITBF);

/*Wait for the receive buffer to fill*/
while(SPI1STATLbits.SPIRBE);

/*Read value in buffer to clear it*/
clr = SPI1BUFL;

/*Disable SPI*/    

}

void configureLSM()
{
int checkDevice = 0;

/* CTRL_REG1_G */         
LSM_CS = 0;
while(GET_LSM_CS == 1); 
SPIWrite(0x10, 0xC8);  //1100 1000 0xC8
LSM_CS = 1;
while(GET_LSM_CS == 0);

/* CTRL_REG2_G */
LSM_CS = 0;
while(GET_LSM_CS == 1); 
SPIWrite(0x11, 0x00);
LSM_CS = 1;
while(GET_LSM_CS == 0);

/* CTRL_REG3_H */
LSM_CS = 0;
while(GET_LSM_CS == 1); 
SPIWrite(0x12, 0x00); //0000 0000
LSM_CS = 1;
while(GET_LSM_CS == 0);

/* CTRL_REG4 */
LSM_CS = 0;
while(GET_LSM_CS == 1); 
SPIWrite(0x1E, 0x38);  //0011 1000
LSM_CS = 1;
while(GET_LSM_CS == 0);

/* CTRL_REG5_XL */
LSM_CS = 0;
while(GET_LSM_CS == 1); 
SPIWrite(0x1F, 0x38);  //0011 1000
LSM_CS = 1;
while(GET_LSM_CS == 0);

/* CTRL_REG6_XL */
LSM_CS = 0;
while(GET_LSM_CS == 1); 
SPIWrite(0x20, 0xD0);  //1101 0000 0xD0
LSM_CS = 1;
while(GET_LSM_CS == 0);

/* CTRL_REG7_XL */
LSM_CS = 0;
while(GET_LSM_CS == 1); 
SPIWrite(0x21, 0x00);  //0000 0000
LSM_CS = 1;
while(GET_LSM_CS == 0);

/* CTRL_REG8_XL */
LSM_CS = 0;
while(GET_LSM_CS == 1); 
SPIWrite(0x22, 0x00);
LSM_CS = 1;
while(GET_LSM_CS == 0);

/* CTRL_REG9 */
LSM_CS = 0;
while(GET_LSM_CS == 1); 
SPIWrite(0x23, 0x04); //0000 0100
LSM_CS = 1;
while(GET_LSM_CS == 0);

/* CTRL_REG10 */
LSM_CS = 0;
while(GET_LSM_CS == 1); 
SPIWrite(0x24, 0x00);
LSM_CS = 1;
while(GET_LSM_CS == 0);

/* ORIENT_CFG_G */
LSM_CS = 0;
while(GET_LSM_CS == 1); 
SPIWrite(0x13, 0x00);
LSM_CS = 1;
while(GET_LSM_CS == 0);

/* FIFO CTRL */
LSM_CS = 0;
while(GET_LSM_CS == 1);
SPIWrite(0x2E, 0x00);
LSM_CS = 1;
while(GET_LSM_CS == 0);

/*Return 41h*/
LSM_CS = 0;
while(GET_LSM_CS == 1); 
checkDevice = SPIRead(0x0F); //WHO_AM_I
LSM_CS = 1;
while(GET_LSM_CS == 0);

dataOne[21] = checkDevice;
}

void configureMagnetometer0()
{ 
int checkDevice = 0;

/* CTRL_REG3_M */
MAG_0_CS = 0;
while(GET_MAG_0_CS == 1); 
SPIWrite(0x22, 0x84);  //1000 0100 
MAG_0_CS = 1;

/* CTRL_REG1_M */ 
MAG_0_CS = 0;
while(GET_MAG_0_CS == 1);
SPIWrite(0x20, 0xDC);  //1101 1100
MAG_0_CS = 1;

/* CTRL_REG2_M */
MAG_0_CS = 0;
while(GET_MAG_0_CS == 1);
SPIWrite(0x21, 0x00);  //0000 0000
MAG_0_CS = 1;

/* CTRL_REG4_M */ //0000 00 0 0
MAG_0_CS = 0;
while(GET_MAG_0_CS == 1);
SPIWrite(0x23, 0x08); //0000 1000 
MAG_0_CS = 1;

/* CTRL_REG5_M */ 
MAG_0_CS = 0;
while(GET_MAG_0_CS == 1);
SPIWrite(0x24, 0x40); 
MAG_0_CS = 1;

/* WHO_AM_I */
MAG_0_CS = 0;
while(GET_MAG_0_CS == 1);
checkDevice = SPIRead(0x0F); 
MAG_0_CS = 1;

dataOne[22] = checkDevice;
}

/************************** READING DATA *************************/

unsigned char SPIReadSingle(unsigned char address){    

address = address | 0x80;

/*Write address + read (1) to SPI1BUF*/
SPI1BUFL = address;
while(!SPI1STATLbits.SPITBE);   // Wait till data has shifted to shift register
while(!SPI1STATLbits.SPIRBF);   // Wait until receive buffer has new data (not in SR technically)
while(SPI1STATLbits.SPIBUSY);   // Wait for SPI to be completely ready 
// USE DELAY HERE IF NOT WORKING (note to myself)
address = SPI1BUFL;             // Read data to clear receive buffer
SPI1BUFL = 0x00;                // Shift data out of sensor

while(!SPI1STATLbits.SPITBE);   // Wait till data has shifted to SR
while(!SPI1STATLbits.SPIRBF);   // Wait until receive buffer has new data not in SR technically
while(SPI1STATLbits.SPIBUSY);   // Wait for SPI to be completely ready 
// USE DELAY HERE IF NOT WORKING
return SPI1BUFL;                // Return new data to main
}


void read_Data_XL2()
{         

/* LSM9DS1*/    
LSM_CS = 0;
dataOne[0] = SPIReadSingle(0x0F); //Read Who_Am-I, 
LSM_CS = 1;
while(GET_LSM_CS == 0);
LSM_CS = 0;
dataOne[1] = SPIReadSingle(0x29); //Read Acc0 x-High
LSM_CS = 1;
while(GET_LSM_CS == 0);
LSM_CS = 0;
dataOne[2] = SPIReadSingle(0x2B); //Read Acc0 y-High
LSM_CS = 1;
while(GET_LSM_CS == 0);
LSM_CS = 0;
dataOne[3] = SPIReadSingle(0x2D); //Read Acc0 z-High
LSM_CS = 1;
while(GET_LSM_CS == 0);
LSM_CS = 0;
dataOne[4] = SPIReadSingle(0x19); //Read Gyro0 x-High
LSM_CS = 1;
while(GET_LSM_CS == 0);
LSM_CS = 0;
dataOne[5] = SPIReadSingle(0x1B); //Read Gyro0 y-High
LSM_CS = 1;
while(GET_LSM_CS == 0);
LSM_CS = 0;
dataOne[6] = SPIReadSingle(0x1D); //Read Gyro0 z-High
LSM_CS = 1;
}

void read_Data_M2()
{        
MAG_0_CS = 0;
dataOne[7] = SPIReadSingle(0x28); //Read Mag0 x-Low
MAG_0_CS = 1;
while(GET_MAG_0_CS == 0);
MAG_0_CS = 0;
dataOne[8] = SPIReadSingle(0x29); //Read Mag0 x-High
MAG_0_CS = 1;
while(GET_MAG_0_CS == 0);
MAG_0_CS = 0;
dataOne[9] = SPIReadSingle(0x2A); //Read Mag0 y-Low
MAG_0_CS = 1;
while(GET_MAG_0_CS == 0);
MAG_0_CS = 0;
dataOne[10] = SPIReadSingle(0x2B); //Read Mag0 y-High
MAG_0_CS = 1;
while(GET_MAG_0_CS == 0);
MAG_0_CS = 0;
dataOne[11] = SPIReadSingle(0x2C); //Read Mag0 z-Low
MAG_0_CS = 1;
while(GET_MAG_0_CS == 0);
MAG_0_CS = 0;
dataOne[12] = SPIReadSingle(0x2D); //Read Mag0 z-High
MAG_0_CS = 1; 
while(GET_MAG_0_CS == 0);
MAG_0_CS = 0;
dataOne[13] = SPIReadSingle(0x0F); //Read Mag0 WHO_AM_I
MAG_0_CS = 1; 

}

Best Answer

I've figured it out. The data sheet says to enter '0' at location 2 (labeled "SIM") of Control Register 3 of the magnetometer, but I entered '1' just out of desperation and it works. Perhaps the data sheet is incorrect, but if anyone has this device and communicates with SPI and you get the same result, then that'd be a good indicator that it is indeed the issue. I also left I2C enabled even though it's not using it, and it still works fine.