AdSense

Saturday, 31 August 2013

Read acceleration sensor MPU-6050 with ATMega 16A

(Deutsche Version) In this post, I will show how to read the values of an MPU-6050 with an ATMega 16A. For the usage of I2C on the ATMega, I use this implementation.

If you want to use a Raspberry PI instead of an ATMega, see this post.

At first, I wrote two functions which can read from a register respectively write to a register:

void TWIM_WriteRegister(char reg, char value)
{
    TWIM_Start(addr, TWIM_WRITE); // set device address and write mode
    TWIM_Write(reg);
    TWIM_Write(value);
    TWIM_Stop();
}

char TWIM_ReadRegister(char reg)
{
    TWIM_Start(addr, TWIM_WRITE);
    TWIM_Write(reg);
    TWIM_Stop();

    TWIM_Start(addr, TWIM_READ); // set device address and read mode
    char ret = TWIM_ReadNack();
    TWIM_Stop();
    return ret;
}


As a second step, I wrote two functions which read the acceleration and gyro data from the Sensor. Please note that you have to disable the sleep mode, this can be done by using TWIM_WriteRegister(107, 0).

double MPU6050_ReadAccel(int axis)//x = 0; y = 1; z = 2
{
  char reg = axis * 2 + 59;
  char AFS_SEL = TWIM_ReadRegister(28);
  double factor = 1<<AFS_SEL;
  factor = 16384/factor;
  int val = 0;
  double double_val = 0;
  char ret = 0;

  ret = TWIM_ReadRegister(reg);
  val = ret << 8;

  ret = TWIM_ReadRegister(reg+1);  
  val += ret;

  if (val & 1<<15)
  val -= 1<<16;

  
  double_val = val;

  double_val = double_val / factor;

  return double_val;
}

double MPU6050_ReadGyro(int axis)//x = 0; y = 1; z = 2
{
  char reg = axis * 2 + 67;
  char FS_SEL = TWIM_ReadRegister(27);
  double factor = 1<<FS_SEL;
  factor = 131/factor;
  int val = 0;
  double double_val = 0;
  char ret = 0;

  ret = TWIM_ReadRegister(reg);
  val = ret << 8;

  ret = TWIM_ReadRegister(reg+1);  
  val += ret;

  if (val & 1<<15)
  val -= 1<<16;



  double_val = val;
 
  double_val = double_val / factor;

  return double_val;
}


The values for the gyrometer are in degrees per second and in units of g for the accelerometer, further information is provided in the Register Map, especially at the explaination of FS_SEL and AFS_SEL: http://www.invensense.com/mems/gyro/documents/RM-MPU-6000A.pdf

34 comments:

  1. Thanks for this tutorial... but I have one problem :((

    I done everything in Atmel Studio 6.1 but when I press Build Solution (F7) I get this errors:

    Error 1 'addr' was not declared in this scope (Line 28)
    Error 1 'addr' was not declared in this scope (Line 36)
    Warning 3 left shift count >= width of type [enabled by default] (Line 63)
    Warning 3 left shift count >= width of type [enabled by default] (Line 89)

    Can you help me?? PLEASE

    ReplyDelete
    Replies
    1. Hi David
      the addr is the I2C address of the GPU-6050. In my case, the address is 0x68, so I have
      #define addr 0x68
      on the first line in my program.
      If this address does not work, you could use an I2C Scanner which scans every I2C address and then prints out where there are devices.
      Udo

      Delete
    2. :D :D..... THANKS FOR YOUR REPLAY!!!

      And also I have one question, I make QuadCopter that uses MPU6050 for stabilization.... but I don't know how to get sensor date in characters... can you help me?? (send me example of something like that).....

      I have everything but this chip is problem for me........ Only you can help me..

      Thanks again bro :D

      Delete
    3. If you look into the ReadAccel and ReadGyro functions, you will see that I read out the values into "ret" which is a character variable, you can use this. I use int at first because both registers (8 bit each) together form the value - and thats 16 bit, more than a character variable. In the end, I convert it into a physical value (in m/s^2) in a double.

      Delete
    4. OK thank you for your help :DD I will try it..

      Delete
    5. Also.... I have written small code for test:

      //----------

      int main(void)
      {
      USARTInit(25);
      TWIM_Init(25);
      TWIM_WriteRegister(107,0);
      while(1)
      {
      MPU6050_ReadGyro(0);
      USARTWriteChar(ret);
      _delay_ms(100);
      }
      }

      //----------

      but i get this error:

      Error 3 'ret' was not declared in this scope

      P.S. Im beginner and i don't know everything in programming... im learning.. and you help will be great :DD

      Thanks!

      Delete
    6. Yeah, so you want to write "ret" to USART, but ret is nowhere defined. You could try something like
      char ret = (char)MPU6050_ReadGyro(0);
      if you want to write the Gyro out to the USART.

      Delete
    7. This comment has been removed by the author.

      Delete
  2. Hi again.. ;))

    Is here any functions to calibrate accel and gyro????

    ReplyDelete
    Replies
    1. and can i interface compass magnetometer http://www.ebay.com/itm/400362466832 >>>> and GPS http://www.ebay.com/itm/Ublox-NEO-6M-GPS-Module-Aircraft-Flight-Controller-For-Arduino-MWC-IMU-APM2-BR-/151140071016?pt=LH_DefaultDomain_0&hash=item2330a67668 >>>> with same I2C?? (and which code i need to use(PLEASE HELP :)))))

      Thx for all!

      Delete
  3. TWIM_WriteRegister(107, 0)

    Error: expected declaration specifiers or '...' before numeric constant
    Compiler: Atmel Studio 6.1

    since the function has been defined as TWIM_WriteRegister(char reg, char value), can it accept 107, 0 as parameters? Should I change char to int? Please help!!

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. what for you need to define it?
      you don't need to define it...... use this example code below...

      int main(void)
      {
      TWIM_WriteRegister(107, 0);
      while (1)
      {
      //Your code here!!
      }
      }

      Delete
  4. Its working...thanks a lot buddy :)...but I still can't understand how "TWIM_WriteRegister(107, 0);" is causing the above mentioned error outside the main function and working fine inside it...could you please explain?

    ReplyDelete
    Replies
    1. I am not sure, i don't know programming well but i think that with that code > TWIM_WriteRegister(107, 0); < you write short function instead of that long code >>

      TWIM_Start(addr, TWIM_WRITE);
      TWIM_Write(reg);
      TWIM_Write(value);
      TWIM_Stop();

      :)))

      Delete
  5. I'm not using any dev-boards...I'm making a simple circuit that interfaces ATmega16 and MPU6050...can I connect SCL and SDA pins of ATmega16 to SCL and SDA of MPU6050 directly? Or do I need level shifters?

    ReplyDelete
    Replies
    1. So... If you using 3.3V power supply for atmega than you don't need anything and you can directly connect SCL>SCL SDA>SDA... but if you have 5V power supply this product is for you >> http://www.ebay.com/itm/161108492494 << this board has it's 5V>3V3 converter... you can directly connect SCL>SCL SDA>SDA anyway... :))))))

      Delete
    2. Yes...this is the product that I have...GY-521...so I will connect SDA and SCL directly. Thanks a lot for helping me out buddy...:) :)

      Delete
    3. Hey can you send me your code...?? I am interested with that..... :))))

      Delete
    4. my email is >> daduka_k@yahoo.com

      Thanks!! :D

      Delete
    5. I will...once i finish it...:)

      Delete
  6. thanks ur code is easy to understand...

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Are there any other initialization parameters for the mpu6050 or just by calling the (107, 0) command I should be able to read from them?

    I am trying to translate from this to ATxMEGA and I am currently struggling with the code

    ReplyDelete
    Replies
    1. So basically you only have to disable the sleep mode (107,0) and then you shoud be able to read out the sensor values as described above. Keep in mind that the sensor is for 3.3V and 5V might work but might also destroy the sensor.

      Delete
  9. hello david i want to interface mpu6050 with atmega 8..can you tell me what changes i have to do with this code...and which libraries i have to include before copying this code

    ReplyDelete
  10. Hii i am also working with mpu6050 with atmega 16
    so i just have to write this code in main to read the x axis of aclro. if i am using lcd as output

    int main(void)
    {

    TWIM_WriteRegister(107,0);
    while(1)
    {
    MPU6050_ReadAccel(0);
    lcd=ret;
    _delay_ms(100);
    }
    }

    ReplyDelete
  11. but i cant fetch acelormetor reading
    my code is.
    TWIM_WriteRegister(107,0);
    TWIM_Init(25);
    while(1)
    {

    char ret = (char)MPU6050_ReadAccel(1);
    printf(" %d\n",ret);
    _delay_ms(100);
    }


    whats the problem ,gyro is comming.

    ReplyDelete
  12. by writing this TWIM_WriteRegister(107,0); my usart stop working.

    ReplyDelete