MPU9255

Just came back from holiday and I need to get back into programming a rocket controller. I am having trouble getting data from my gyro/accel, an MPU9255. Using the following code I managed to get a print of numbers for a single axis which changes if i move the module. I changed the addresses in the function to test for all axis, however the temperature register doesn't respond and if I insert the magnetometer register then it prints a constant value.

#include <Wire.h>

#define MPU             0x68      //Main address

#define CONF_SAMPRATE   0x19      //registers for configuration
#define CONF_PASS       0x1A
#define CONF_GYROSCALE  0x1B
#define CONF_ACCELSCALE 0x1C

#define ACCEL_X_H       0x3B      //Accelerometer registers
#define ACCEL_X_L       0x3C
#define ACCEL_Y_H       0x3D
#define ACCEL_Y_L       0x3E
#define ACCEL_Z_H       0x3F
#define ACCEL_Z_L       0x40

#define TEMP_H          0x41      //Temperature registers
#define TEMP_L          0x42

#define GYRO_X_H        0x43      //Gyro registers
#define GYRO_X_L        0x44  
#define GYRO_Y_H        0x45
#define GYRO_Y_L        0x46
#define GYRO_Z_H        0x47
#define GYRO_Z_L        0x48

    
#define MAG_X_L         0x03      //Magnetometer registers
#define MAG_X_H         0x04
#define MAG_Y_L         0x05
#define MAG_Y_H         0x06
#define MAG_Z_L         0x07
#define MAG_Z_H         0x08



void writedata(uint8_t Add, uint8_t Reg, uint8_t Data)    //function for sending data by I2C

  {
    Wire.beginTransmission(Add);
    Wire.write(Reg);
    Wire.write(Data);
    Wire.endTransmission();
  }


byte readdata( uint8_t Add, uint8_t Reg, uint8_t N)  //function for reading data from I2C

  {
    
    Wire.beginTransmission(Add);
    Wire.write(Reg);
    Wire.endTransmission();

    Wire.requestFrom(Add, N);
    while(Wire.available()){
      uint8_t Var=Wire.read();
      return Var;
    }
    
  }

void setup()
{
  
  Wire.begin();
  Serial.begin(115200);
  
  writedata(MPU, CONF_SAMPRATE, 0x07);
  writedata(MPU, CONF_PASS, 0x06);
  writedata(MPU, CONF_GYROSCALE, 0x08);
  writedata(MPU, CONF_ACCELSCALE, 0x08);
  
}

void loop()
{
  
  uint8_t varh = readdata(MPU, MAG_X_H, 1);
  uint8_t varl = readdata(MPU, MAG_X_L, 1);
  
  int16_t var = (varh << 8 | varl);
  Serial.println(var);
  delay(100);
  
}

Your code looks good except for your configuration register addresses. They seem WAY off. Did you look at the MPU9255 register map?

Where did you get those addresses?

From some folders in their code http://www.waveshare.com/wiki/File:10-DOF-IMU-Sensor-B-code.7z

Also, what do the square brackets mean in the registers? ACCEL_XOUT_H[15:8]

Thank you very much for that document. :slight_smile:

pawelzaborek:
From some folders in their code http://www.waveshare.com/wiki/File:10-DOF-IMU-Sensor-B-code.7z

Also, what do the square brackets mean in the registers? ACCEL_XOUT_H[15:8]

Thank you very much for that document. :slight_smile:

Yeah, I wouldn't use that site. With the register map and datasheet, you shouldn't need anything else. This goes for any sensor: get the data (or verify it) directly from the documentation from the manufacturer.

The brackets stand for the bit indexes within the register(s). For instance, I2C_MST_CLK[3:0] means that I2C_MST_CLK takes up bits 0 through 3 in the I2C_MST_CTRL register. In your case, with ACCEL_XOUT_H[15:8], the numbers tell you that this register holds the most significant byte (8 bits) of the ACCEL_XOUT value. As you can see, ACCEL_XOUT_L[7:0] is the least significant byte.

Oh yeah cheers.

And with these printed numbers I have to find the maximum and map it to degrees?

pawelzaborek:
And with these printed numbers I have to find the maximum and map it to degrees?

You mean for the magnetometer, accelerometer, or gyroscope (or all three)?

Just the magnetometer.

Accelerometer should be mapped to the maximum g setting which you have to send at the start??

And the gyro is degrees per second as per the setting?

Hang on, where are the magnetometer registers!!?

BUMP

The mpu9255 is a mpu6050 plus compass.
The calibration routines available out there don't work well with the mpu9255 (it has an auto/self calibration routine not available in the mpu6050) so I made the one attached to give you offsets.

Also note that because the mpu9255 has a mpu6050 within all the mpu6050 code works well from the Jeff Rowberg library (Thanks Jeff) Library Link
To gain access to the compass just follow the instructions on the mpu6050 to link to the compass as if it were an external chip attached to the secondary i2c interface.

NOTE: the attached file MPU-9255Cal.ino uses EEPROM to store the offsets as well as displaying them. How I would use this is run the calibration which stores the offsets in EEPROM and them my sketch that needed the information would retrieve the offsets from EEPROM without having to alter code for each MPU9255 chip. Because this is not necessary and may affect other data you stored in EEPROM I am posting this as a notice. Code is for UNO (atmega328p) and may need to be modified for other chips

MPU-9255Cal.ino (44.7 KB)

Thank you very much, but I am new to arduino and I don't understand much from the code which you sent. I don't want to copy and paste code because I want to know what my code means, so I prefer to write it all out myself. Could you please explain how to access the compass? Can I just get data normally by using a different register, or do I have to do some set up first?

I used an address finder and apart from the normal address 0x68, there is a 0x77. Is that the pressure sensor?

In the attached file for calibration the mpu9255, I have simple code that also calibrates the magnetometer as well as reads its values. You will want to do both

pawelzaborek:
Hang on, where are the magnetometer registers!!?

Addresses are 0x3H through 0x8H.

See pg47 of the register map.

Stripped out all the calibration code and only left raw readings for Mag, Accel and Gyro

#include <SPI.h>
#include "Wire.h"
#define AK8963_ADDRESS 0x0C
#define AK8963_ST1 0x02
#define AK8963_XOUT_L 0x03
#define AK8963_XOUT_H 0x04
#define AK8963_YOUT_L 0x05
#define AK8963_YOUT_H 0x06
#define AK8963_ZOUT_L 0x07
#define AK8963_ZOUT_H 0x08
#define AK8963_CNTL 0x0A
#define AK8963_ASAX 0x10
#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#define GYRO_CONFIG 0x1B
#define ACCEL_CONFIG 0x1C
#define ACCEL_CONFIG2 0x1D
#define MOT_DUR 0x20
#define ZMOT_THR 0x21
#define ZRMOT_DUR 0x22
#define INT_PIN_CFG 0x37
#define INT_ENABLE 0x38
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define PWR_MGMT_1 0x6B
#define WHO_AM_I_MPU9250 0x75
#define XA_OFFSET_H 0x77
#define XA_OFFSET_L 0x78
#define YA_OFFSET_H 0x7A
#define YA_OFFSET_L 0x7B
#define ZA_OFFSET_H 0x7D
#define ZA_OFFSET_L 0x7E


#define ADO 0
#if ADO
#define MPU9250_ADDRESS 0x69
#else
#define MPU9250_ADDRESS 0x68
#define AK8963_ADDRESS 0x0C
#endif

enum Ascale {
  AFS_2G = 0,
  AFS_4G,
  AFS_8G,
  AFS_16G
};

enum Gscale {
  GFS_250DPS = 0,
  GFS_500DPS,
  GFS_1000DPS,
  GFS_2000DPS
};

enum Mscale {
  MFS_14BITS = 0,
  MFS_16BITS
};


uint8_t Gscale = GFS_250DPS;
uint8_t Ascale = AFS_2G;
uint8_t Mscale = MFS_16BITS;
uint8_t Mmode = 0x02;
float aRes, gRes, mRes;

int intPin = 12;
int myLed = 13;

int16_t accelCount[3];
int16_t gyroCount[3];
int16_t magCount[3];

int16_t tempCount; 
float temperature; 
float SelfTest[6];

void setup() {
  Wire.begin();
  TWBR = 24;
  Serial.begin(115200);
  initMPU9250();
  float magCalibration[3];
  initAK8963(magCalibration);
}

void loop() {

  delay(1000);
  while (1) {
    readAccelData(accelCount);
    Serial.print("\tX-Axis Accel "); Serial.print(accelCount[0]);
    Serial.print("\tY-Axis Accel "); Serial.print(accelCount[1]);
    Serial.print("\tZ-Axis Accel "); Serial.print(accelCount[2]);
    readGyroData(gyroCount);
    Serial.print("\tX-Axis Gyro "); Serial.print(gyroCount[0]);
    Serial.print("\tY-Axis Gyro "); Serial.print(gyroCount[1]);
    Serial.print("\tZ-Axis Gyro "); Serial.print(gyroCount[2]);
    readMagData(magCount);
    Serial.print("\tX-Axis Mag "); Serial.print(magCount[0]);
    Serial.print("\tY-Axis Mag "); Serial.print(magCount[1]);
    Serial.print("\tZ-Axis Mag "); Serial.print(magCount[2]);
    tempCount = readTempData();
    Serial.print("\tTemperature "); Serial.print(tempCount);
    Serial.println();
    delay(100);
  }
}




void writeByte(uint8_t address, uint8_t subAddress, uint8_t data)
{

  Wire.beginTransmission(address); 
  Wire.write(subAddress); 
  Wire.write(data); 
  Wire.endTransmission(); 
}

uint8_t readByte(uint8_t address, uint8_t subAddress)
{

  uint8_t data;
  Wire.beginTransmission(address); 
  Wire.write(subAddress); 
  Wire.endTransmission(false); 
  Wire.requestFrom(address, (uint8_t) 1);
  data = Wire.read(); 
  return data;
}

void readBytes(uint8_t address, uint8_t subAddress, uint8_t count, uint8_t * dest)
{
  Wire.beginTransmission(address); 
  Wire.write(subAddress); 
  Wire.endTransmission(false); 
  uint8_t i = 0;
  Wire.requestFrom(address, count); 
  while (Wire.available()) {
    dest[i++] = Wire.read();
  
  } 
}



void readAccelData(int16_t * destination)
{
  uint8_t rawData[6]; 
  readBytes(MPU9250_ADDRESS, ACCEL_XOUT_H, 6, &rawData[0]);
  destination[0] = ((int16_t)rawData[0] << 8) | rawData[1] ; 
  destination[1] = ((int16_t)rawData[2] << 8) | rawData[3] ;
  destination[2] = ((int16_t)rawData[4] << 8) | rawData[5] ;
}


void readGyroData(int16_t * destination)
{
  uint8_t rawData[6];
  readBytes(MPU9250_ADDRESS, GYRO_XOUT_H, 6, &rawData[0]);
  destination[0] = ((int16_t)rawData[0] << 8) | rawData[1] ; 
  destination[1] = ((int16_t)rawData[2] << 8) | rawData[3] ;
  destination[2] = ((int16_t)rawData[4] << 8) | rawData[5] ;
}

void readMagData(int16_t * destination)
{
  uint8_t rawData[7]; 
  if (readByte(AK8963_ADDRESS, AK8963_ST1) & 0x01) { 
    readBytes(AK8963_ADDRESS, AK8963_XOUT_L, 7, &rawData[0]); 
    uint8_t c = rawData[6]; 
    if (!(c & 0x08)) { 
      destination[0] = ((int16_t)rawData[1] << 8) | rawData[0] ;
      destination[1] = ((int16_t)rawData[3] << 8) | rawData[2] ; 
      destination[2] = ((int16_t)rawData[5] << 8) | rawData[4] ;
    }
  }
}

int16_t readTempData()
{
  uint8_t rawData[2];
  readBytes(MPU9250_ADDRESS, TEMP_OUT_H, 2, &rawData[0]); 
  return ((int16_t)rawData[0] << 8) | rawData[1] ; 
}

void initAK8963(float * destination)
{
  uint8_t rawData[3]; 
  writeByte(AK8963_ADDRESS, AK8963_CNTL, 0x00); 
  delay(10);
  writeByte(AK8963_ADDRESS, AK8963_CNTL, 0x0F);
  delay(10);
  readBytes(AK8963_ADDRESS, AK8963_ASAX, 3, &rawData[0]); 
  destination[0] = (float)(rawData[0] - 128) / 256. + 1.; 
  destination[1] = (float)(rawData[1] - 128) / 256. + 1.;
  destination[2] = (float)(rawData[2] - 128) / 256. + 1.;
  writeByte(AK8963_ADDRESS, AK8963_CNTL, 0x00);
  delay(10);
  writeByte(AK8963_ADDRESS, AK8963_CNTL, Mscale << 4 | Mmode); 
  delay(10);
}
void initMPU9250()
{
  writeByte(MPU9250_ADDRESS, PWR_MGMT_1, 0x00); 
  delay(100); 
  writeByte(MPU9250_ADDRESS, PWR_MGMT_1, 0x01); 
  delay(200);
  writeByte(MPU9250_ADDRESS, CONFIG, 0x03); 
  writeByte(MPU9250_ADDRESS, SMPLRT_DIV, 0x04); 
  uint8_t c = readByte(MPU9250_ADDRESS, GYRO_CONFIG); 
  writeByte(MPU9250_ADDRESS, GYRO_CONFIG, c & ~0x02);
  writeByte(MPU9250_ADDRESS, GYRO_CONFIG, c & ~0x18);
  writeByte(MPU9250_ADDRESS, GYRO_CONFIG, c | Gscale << 3);
  c = readByte(MPU9250_ADDRESS, ACCEL_CONFIG); 
  writeByte(MPU9250_ADDRESS, ACCEL_CONFIG, c & ~0x18); 
  writeByte(MPU9250_ADDRESS, ACCEL_CONFIG, c | Ascale << 3);
  c = readByte(MPU9250_ADDRESS, ACCEL_CONFIG2);
  writeByte(MPU9250_ADDRESS, ACCEL_CONFIG2, c & ~0x0F); 
  writeByte(MPU9250_ADDRESS, ACCEL_CONFIG2, c | 0x03); 
  writeByte(MPU9250_ADDRESS, INT_PIN_CFG, 0x22);
  writeByte(MPU9250_ADDRESS, INT_ENABLE, 0x01); 
  delay(100);
}

I know this is an old topic but anyway (:

I found an error in your code :

In MPU9255_init function you are turning on only Z axis of the gyroscope.

I replaced lines :

  writeByte(MPU9250_ADDRESS, PWR_MGMT_1, 0x00); 
  delay(100); 
  writeByte(MPU9250_ADDRESS, PWR_MGMT_1, 0x01);

With :

delay(100); 
  writeByte(MPU9250_ADDRESS, PWR_MGMT_1, 0b11111111);

And now it works correctly.

Sorry for the possible offtopic, but is there an official doc on calibration of InvenSense MPUs in general and 9255 particularly? The datasheet document says nothing about it.

vanyatka:
Sorry for the possible offtopic, but is there an official doc on calibration of InvenSense MPUs in general and 9255 particularly? The datasheet document says nothing about it.

The 9255 has additional programming that assist in calibration. This gets the offsets. It has been a while since I used this. I created it from the datasheet and other documents available. Let me know if it worked for you

MPU-9255Cal.ino (44.7 KB)

zhomeslice:
The 9255 has additional programming that assist in calibration. This gets the offsets. It has been a while since I used this. I created it from the datasheet and other documents available. Let me know if it worked for you

Thanks for the code! Yes, I saw it in this thread earlier.

What bothers me is that calibration procedure looks like an error-prone task, and the data sheet does not mention these steps at all (at least not containing word "calibration", but single occurance of word "bias" for the whole spec).

I saw some examples over the net (Whiner I guess), where there were some calibration routines with hard-coded California offsets! Whaat, I ask myself. Does it mean it would only work in there, and needed to be reprogrammed whenever it's moved around the globe?

I am new to this, and I feel I'm missing a big picture. It "feels" there must be an easier, or more documented, way to use the sensor, and it looks more like a gamble to me so far.

ps. I'll try my best to give it a try soon. The first attempt (with different code) failed, b/cause it worked when sensor was powered up in horizontal pos, and I placed it edge top which completely screwed up roll measurement...

zhomeslice:
The 9255 has additional programming that assist in calibration. This gets the offsets. It has been a while since I used this. I created it from the datasheet and other documents available. Let me know if it worked for you

I think this is the document I was able to use to develop the program See Attached
from the following URL
https://www.invensense.com/developers/software-downloads/#sla_content_45
look for:

Embedded MotionDriver 6.12
Embedded MotionDriver 6.12 is our first ever 9-axis solution not locked to a specific MCU.Version 6.1.2 is an update to 6.1 that includes bug fixes and new libraries. This release is supported across all ARM Mx core architectures and supports the InvenSense MPU-6000, 6050, 6500, 9150, and 9250. The release includes optimized libraries and example projects for M3 and M4 cores as well the generic ARM library for any Mx core and an additional library and project for the TI MSP430. eMD 6.1 also includes a Python client for both visualizing the sensor performance and commands for printing data. This solution will allow you to easily leverage and configure numerous features of the DMP and also benefit from dynamic features in the MPL software library. Libraries specific to IAR, Keil, and GCC are included.

Z

MPU HW Offset Registers 1.2.pdf (296 KB)

Hi, I'm David.

I've download the code of PCU9255, and tried to upload to ARDUINO MEGA 2560.

I know the code works okay, but just don't understand how to connect wires between MEGA and PCU9255.

Could anyone please show me how to do the connection.

Thanks.