8 years, 10 months ago.

SPI and I2C at the same time on FRDMK22

Hello,

I have a FRDM K22F board on which I connect an accelerometer MPU9250 with SPI and a altimeter MPL3115A2 on I2C. Those two sensors are using interruptions for data collection.

When I run the altimeter alone no problem. When I run the accelerometer alone no problem. When I mix both, the altimeter works for a small duration and then stops (between 10 an 60 seconds)

Please find enclosed the code I use :

smallest code I could extract


#include "mbed.h"
#include "MPL3115A2Defs.h"
#include "MPU9250Defs.h"



DigitalOut ledRed(PTC5);

Serial usbSerial(USBTX, USBRX);

Serial serialPort(PTD3,PTD2);

Timer timer;


InterruptIn interrupt1Alti(PTC1);
InterruptIn interrupt2Alti(PTC2);
I2C i2c(PTB3,PTB2);

InterruptIn interruptAcc(PTC3);
DigitalOut cs(PTD4);
SPI spi(PTD6,PTD7,PTD5);
// SPI utils :
 void select()
{
    //Set CS low to start transmission (interrupts conversion)
    cs = 0;
}
 void deselect()
{
    //Set CS high to stop transmission (restarts conversion)
    cs = 1;
}

 uint8_t   writeByte(uint8_t WriteAddr, uint8_t WriteData )
{
    unsigned int temp_val;
    select();
    spi.write(WriteAddr);
    temp_val=spi.write(WriteData);
    deselect();
    return temp_val;
}
 uint8_t  readByte( uint8_t WriteAddr, uint8_t WriteData )
{
    return writeByte(WriteAddr | 0x80,WriteData);
}

 void readBytes( uint8_t ReadAddr, uint8_t *ReadBuf, unsigned int Bytes )
{
    unsigned int  i = 0;

    select();
    spi.write(ReadAddr | 0x80);
    for(i=0; i<Bytes; i++)
        ReadBuf[i] = spi.write(0x00);

    deselect();

}

// I2C utils :
int writeRegister(uint8_t writeAddr, uint8_t writeData)
{

    int ret;
    char cmd[2];
    cmd[0] = writeAddr;
    cmd[1] = writeData;

    ret= i2c.write(MPL3115A2_ADDRESS, cmd, 2);
    return ret;

}

bool readRegisters(uint8_t readAddr, uint8_t *readBuf, unsigned int bytes)
{
    i2c.start();
    i2c.write(MPL3115A2_ADDRESS);
    wait_ms(10);
    i2c.write(readAddr);
    wait_ms(10);
    bool ret= i2c.read(MPL3115A2_ADDRESS, (char*)readBuf, bytes);
    i2c.stop();

    return ret;
}



void readAcc()
{
    uint8_t data[512]; // data array to hold accelerometer and gyro x, y, z, data
    uint16_t ii, fifo_count;

    readBytes(MPU9250_FIFO_COUNT_H,data,2); // read FIFO sample count
    fifo_count = ((uint16_t)data[0] << 8) | data[1];

    if(fifo_count==512)
    {
        usbSerial.printf("overflow\n");
        // Reset Fifo
        writeByte(MPU9250_INT_ENABLE,0x00);
        writeByte(MPU9250_FIFO_EN,   0x00);
        writeByte(MPU9250_USER_CTRL, 0x04);
        wait_ms(100);
        writeByte(MPU9250_USER_CTRL, 0x60);
        writeByte(MPU9250_FIFO_EN,   0b01111010);
        writeByte(MPU9250_INT_ENABLE,0x01);
        return;
    }

    readBytes(MPU9250_FIFO_R_W,  data,fifo_count);


}
void readAlti()
{
    uint8_t dataBuffer[5];
    double pressure;
    double temperature;
    double altitude;

    bool resRead=readRegisters(MPL3115A2_OUT_P_MSB, dataBuffer, 5);

    if(!resRead)
    {
        int i = 0;
        pressure = (dataBuffer[i] << 16 | dataBuffer[i+1] << 8 | dataBuffer[i+2
                ] )/0.64;
        i+=3;
        temperature =(dataBuffer[i] << 8 | dataBuffer[i+1])/2.56+27315;
        i+=2;
        altitude= 4433077*(1.0-pow(pressure/(100.0*101325),0.190263));

        usbSerial.printf("pressure=%f,alti=%f,temp=%f\n", pressure/10000,altitude,temperature/100 - 273.15 );

    }
}


int main()
{


    // Setup serial ports
    //----------------------------------------------------------------------------
    usbSerial.baud(115200);
    usbSerial.printf("Usb serial enabled\n");
    serialPort.baud(115200);
    serialPort.printf("Serial Port Started\n");

    // Setup Alti
    //----------------------------------------------------------------------------
    uint8_t reg;
    i2c.frequency(400000);
    readRegisters(MPL3115A2_WHO_AM_I,&reg,1);
    usbSerial.printf("\nWhoAMI %p\n\n",reg);

    writeRegister(MPL3115A2_CTRL_REG1, 0x28);
    writeRegister(MPL3115A2_CTRL_REG1, 0b00000100);

    wait_ms(100);

    interrupt1Alti.fall(&readAlti);
    interrupt2Alti.fall(&readAlti);

    writeRegister(MPL3115A2_PT_DATA_CFG, 0x07);

    readRegisters(MPL3115A2_PT_DATA_CFG,&reg,1);
    usbSerial.printf("cfg %p\n\n",reg);


    // enable data ready and fifo interrupts
    writeRegister(MPL3115A2_CTRL_REG4,0x80);
    // set data ready on interrupt 1 and let fifo  on interrupt 2
    writeRegister(MPL3115A2_CTRL_REG5,0x80);

    // Set Active mode

    readRegisters(MPL3115A2_CTRL_REG1,&reg,1);
    reg|=0b00000001;
    writeRegister(MPL3115A2_CTRL_REG1, reg); // set the device in active mode


    // Setup Acc
    //----------------------------------------------------------------------------
    spi.frequency(1000000);
    writeByte(MPU9250_PWR_MGMT_1, 0x80); // toggle reset device
    wait_ms(1000);
    // reset FIFO :
    writeByte(MPU9250_INT_ENABLE,0x00);
    writeByte(MPU9250_FIFO_EN,   0x00);
    writeByte(MPU9250_USER_CTRL, 0x04);
    wait_ms(100);
    writeByte(MPU9250_USER_CTRL, 0x60);
    writeByte(MPU9250_FIFO_EN,   0b01111010);
    writeByte(MPU9250_INT_ENABLE,0x01);

    // get stable time source; Auto select clock source to be PLL gyroscope reference if ready
    // else use the internal oscillator, bits 2:0 = 001
    writeByte(MPU9250_PWR_MGMT_2, 0x00);
    writeByte(MPU9250_PWR_MGMT_1, 0x01); // Turn on internal clock source
    // Set SampleRate
    uint8_t div=(1000/100)-1;
    writeByte(MPU9250_SMPRT_DIV, div);
    writeByte(MPU9250_GYRO_LPF, MPU9250_GYRO_LPF_184);
    writeByte(MPU9250_ACCEL_LPF, MPU9250_ACCEL_LPF_184);
    writeByte(MPU9250_GYRO_CONFIG, MPU9250_GYROFSR_1000 );
    writeByte(MPU9250_ACCEL_CONFIG,MPU9250_ACCELFSR_2);

    // Confiugure magneto :
    // Reset magnometer
    writeByte(MPU9250_I2C_SLV0_ADDR,MPU9250_AK8963_ADDRESS);
    writeByte(MPU9250_I2C_SLV0_REG,MPU9250_AK8963_CNTL2);
    writeByte(MPU9250_I2C_SLV0_DO,0x01);
    writeByte(MPU9250_I2C_SLV0_CTRL,0x81);

    wait_ms(1500);

    // Set Mode and Sample Rate :
    writeByte(MPU9250_I2C_SLV0_ADDR,MPU9250_AK8963_ADDRESS);
    writeByte(MPU9250_I2C_SLV0_REG,MPU9250_AK8963_CNTL1);
    writeByte(MPU9250_I2C_SLV0_DO,0x12);
    writeByte(MPU9250_I2C_SLV0_CTRL,0x81); // Enable i2c and write 1 bytes

    writeByte(MPU9250_I2C_SLV1_ADDR,MPU9250_AK8963_ADDRESS|MPU9250_READ_FLAG); // Magneto address
    writeByte(MPU9250_I2C_SLV1_REG,MPU9250_AK8963_ST1); // Address of the first data register
    writeByte(MPU9250_I2C_SLV1_CTRL,0x88); // Enable reading and read 8 bytes
    writeByte(MPU9250_USER_CTRL, 0x0C); // Reset FIFO and DMP

    // Configure FIFO to capture accelerometer and gyro data for bias calculation
    writeByte(MPU9250_INT_ENABLE   , 0x01 );
    writeByte(MPU9250_USER_CTRL, 0b01100000); // Enable FIFO
    writeByte(MPU9250_FIFO_EN,   0b01111010); // Enable gyro and accelerometer sensors for FIFO (max size 512 bytes in MPU-9150)


    interruptAcc.fall(&readAcc);
    // Main loop
    //----------------------------------------------------------------------------

    while(1)
    {
        usbSerial.printf(".");
        wait_ms(100);
    }

}

Many thanks for your help.

1 Answer

8 years, 10 months ago.

The problem is more than likely that you have an interrupt coming from one device whilst servicing the other. You need to block or detach any interrupts until the other completes.

Accepted Answer

Paul,

Many thanks, you are perfectly right... In fact the accelerometer triggers a lot of interrupt whereas the altimeter very few.

What happens is an accelerometer interrupt shadows an altimeter interrupt. Because I read altimeter only when i get the interrupt (polling is bad in my case) and because altimeter stops measuring if i don't read measured values I don't get new interrupt.

I have created a time based watch dog. Do you think there is a nicer way to solve this problem ? I don't think detaching interrupt would help ?

posted by Jacques Charreyron 24 Jun 2015

Detaching and re enabling interrupts is probably a bit aggressive and may increase processing time, so if you have alternative methods try them and put timers in to check performance. There may be a way to prioritise the interrupts, but I'm not sure if there would still be collisions. Ultimately you need to find the best solution to give you the maximum amount of readings that will enhance the overall resolution of the system.

posted by Paul Staron 24 Jun 2015