Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
9 years, 5 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,®,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,®,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,®,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
9 years, 5 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.
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 24 Jun 2015Detaching 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 24 Jun 2015