Baby-O and MPU6050

I have built a large project with anlog sensors in AVRStudio and want to add gyro functionality. This sensor will allow me to gather both acceleration and gyro at a 1000 Hz rate.

Unfortunately I cannot find any TWI library to be used in AVRStudio. Switching to Arduino will need a lot of re-engineering of work I have already done, I do not prefer to go that route.

I want to make three steps:
Step 1, initialise the sensor and reading its adres

// Wake the sensor up, register 107 is default 0x40 0100 0000 which means sleep mode is enabled
message_out[0] = 0x6B;				// register 107
message_out[1] = 0x00;				// set value to 0x00 and wake up sensor

// HERE I NEED TO LEARN HOW TO WRITE TWI
// The MPU6050_ADRES is stored in register 0x75 (117)
// HERE I NEED TO LEARN HOW TO READ TWI

if (message_in[0]==0x68)
{
PORTD &= ~( 1 << DDD1 );				// LED off when “Who am I” answer is correct (or 104)
}
else
{
PORTD |= 1 << DDD1; 				// LED on when “Who am I” answer is wrong
}

Step 2, enabling the X sensor, FIFO buffer, Interrupt routine to handle the values read
Step 3, enabling the X-gyro and read H-L values
Step 4, put the result of this Proof of Concept into my application (the easy part)

So I got stuck already at step 1, does anyone have experience in using a library with AVRStudio, any sugestions are welcome. I tried AVR315 but get loads of errors when building.

Hello.

We have heard of people on our forum using the I2C Master Software Library by Peter Fleury with the Orangutan controllers, so you might try using that to communicate with your sensor.

- Jeremy

I have read a lot of positive reactions to this library, however, integrating it with my project does not go very well. These are the steps I took in AVRStudio6:

Copied the twimaster.c and i2cmaster.h to my project folder
Added existing item for both in solution explorer
Included the .h file to the newly created project
Then build all to see if all was right

/* Peter Fleury example code - an application for the Pololu Baby Orangutan B
 *
 * This application uses the Pololu AVR C/C++ Library.  For help, see:
 * -User's guide: https://www.pololu.com/docs/0J20
 * -Command reference: https://www.pololu.com/docs/0J18
 *
 * Created: 10/7/2014 8:33:02 PM
 *  Author: gebruiker
 */

#include <pololu/orangutan.h>
#include <i2cmaster.h>

int main()
{
	while(1)
	{
// Here I will make the comparison for Who am I and use the LED to tell if the answer is correct.

		red_led(1);     // Turn on the red LED.
		delay_ms(200);  // Wait for 200 ms. 

		red_led(0);     // Turn off the red LED.
		delay_ms(200);  // Wait for 200 ms.
	}
}

Before trying anything I2C I wanted to confirm that these steps were ok, build gives the following result:

Warning 1 overriding commands for target `twimaster.o’ C:\Users\gebruiker\Documents\Atmel Studio\6.1\Peter Fleury example code\Peter Fleury example code\Debug\makefile 96 1 Peter Fleury example code

Warning 2 ignoring old commands for target `twimaster.o’ C:\Users\gebruiker\Documents\Atmel Studio\6.1\Peter Fleury example code\Peter Fleury example code\Debug\makefile 89 1 Peter Fleury example code

Error 3 i2cmaster.h: No such file or directory C:\Users\gebruiker\Documents\Atmel Studio\6.1\Peter Fleury example code\Peter Fleury example code\main.c 12 23 Peter Fleury example code

Did I mis a step somewhere?

I did the following modifications:

Changed the reference #include <i2cmaster.h> to #include “i2cmaster.h” in both main.c and twimaxter.c
Removed twimaster.c in solution explorer

Now it builds ok without warnings

Question, is this the correct way to operate?

In the mean time I went to the Arduino site and got a basic example for the atmega328P with the MPU6050 sensor, wired it up and gor the raw values instantly. Now I know my hardware is ok. Sadly my project is large and in AVR SAtudio 6.1 so moving to Arduino is not an option.

I am working with AVR Studio 6.1 and need to add a MPU6050 to my project using analog sensors today. Here is what I did:

Creation of a new project for the Baby-Orangutan of Pololu
Adding the i2cmaster.h and twimaster from the Peter Fleury library to the folder where main.c is found
Using the Solution Explorer to add the existing item i2cmaster.h to the project
Copy the content of Peter Fleury test code in main, build was succesful
Enouraged I started modifying the main code to wake up the sensor and read the who am I to see if I could establish a basic connection.

/* fleury_mpu6050_test - an application for the Pololu Baby Orangutan B
 *
 * This application uses the Pololu AVR C/C++ Library.  For help, see:
 * -User's guide: https://www.pololu.com/docs/0J20
 * -Command reference: https://www.pololu.com/docs/0J18
 *
 * Created: 10/8/2014 7:23:11 PM
 *  Author: gebruiker
 */

#include <pololu/orangutan.h>

/****************************************************************************
Title:    Access serial EEPROM 24C02 using I2C interace
Author:   Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury
File:     $Id: test_i2cmaster.c,v 1.2 2003/12/06 17:07:18 peter Exp $
Software: AVR-GCC 3.3
Hardware: AT90S8515 at 4 Mhz, any AVR device can be used

Description:
    This example shows how the I2C library i2cmaster.S can be used to 
    access a serial eeprom.
    Based on Atmel Application Note AVR300, adapted to AVR-GCC C interface

*****************************************************************************/
#include <avr/io.h>
#include "i2cmaster.h"

#define MPU6050  0x68 // device adress changes to 0x69 when AD0 is pulled high

int main(void)
{
    unsigned char ret;
    
    i2c_init();     // init I2C interface
    delay_ms(200);  // Wait for 200 ms.

    while (1)
    {
       /* write 0x75 to eeprom address 0x05 (Byte Write) */
       ret = i2c_start(MPU6050+I2C_WRITE);       // set device address and write mode
       if ( ret ) {
       /* failed to issue start condition, possibly no device found */
       i2c_stop();
       }else {
       /* issuing start condition ok, device accessible */
       i2c_write(0x6B);     // go to register 107
       i2c_write(0x00);     // set value to 0x00 and wake up sensor
       i2c_stop();          // set stop conditon = release bus
 
       /* write ok, read value back from device address 0x75, wait until 
       the device is no longer busy from the previous write operation */
       i2c_start_wait(MPU6050+I2C_WRITE);     // set device address and write mode
       i2c_write(0x75);                       // who am I register
       i2c_rep_start(MPU6050+I2C_READ);       // set device address and read mode
       ret = i2c_readNak();                   // read one byte
       i2c_stop();
        
       if (ret == 0x68)
       {
           red_led(1);     // Turn on the red LED to signal the read is ok
           delay_ms(200);  // Wait for 200 ms.  
       }
       else
       {
           red_led(0);     // Turn off the red LED to signal the read is not ok
           delay_ms(200);  // Wait for 200 ms.
       }   
    }
  }
}

Hardware:
Before starting the AVR Studio 6.1 project I verified my sensor is working by using an Arduino example and an Arduino UNO with a atmega328P processor.
I do use the 4k7 pull-up resistors for SDA and SCL to 5V

Test:
Negative, no response at all, when I put the led-on in the failed to issue start condition it goes on immediately, I suspect an init problem but cannot find it
I am not familiar with the use of: if ( ret ) {
It is in the Fleury example but is this correct?
Is the procedure for making the project correct?
Is my assumption that hardware working on Arduino should work with Peter Fleury correct?
Any suggestions are welcome.

Using quotes instead of angle brackets is correct. We use a wildcard in our Atmel Studio project files to define what files to compile. When you add twimaster.c to your program, Atmel Studio sees entries for *.c and twimaster.c in the project file and does not do anything special to handle that case properly.

- Jeremy

Just one thing I missed.

The slave address is the 7 most significant bits of the byte sent. The read/write bit is the least significant.
You need to shift the 7-bit address up 1 bit. 0x68 becomes 0xD0.

Once this is done the basic communication is working.

Now the basic application is working the next step is to make a proper application.

The FIFO buffer seems the best approach together with an interrupt which allows sensor values to be processed when they are available. The main program can call the values at any time without having to wait.

These will be my parameter settings:

                        i2c_write(0x6B);	// go to register 107
			i2c_write(0x00);	// set value to 0000 0000 and wake up sensor
	                i2c_stop();		// set stop condition = release bus
	
			i2c_write(0x19);	// go to register 25 sample rate divider
			i2c_write(0x08);	// set value to 0000 1000 for 1000 Hz
			i2c_stop();		// set stop condition = release bus

			i2c_write(0x1C);	// go to register 28 accelleration configurator
			i2c_write(0x08);	// set value to 0000 1000 for 4g
			i2c_stop();		// set stop condition = release bus

			i2c_write(0x23);	// go to register 35 FIFO enable 
			i2c_write(0xF8);	// set value to 1111 1000 for all sensors not slave
			i2c_stop();		// set stop condition = release bus
			
			i2c_write(0x37);	// go to register 55 interrupt confiuration
			i2c_write(0x10);	// set value to 0001 0000 for logic level high and read clear
			i2c_stop();		// set stop condition = release bus
						
			i2c_write(0x38);	// go to register 56 interrupt enable
			i2c_write(0x01);	// set value to 0000 0001 data ready creates interrupt
			i2c_stop();		// set stop condition = release bus			
			
			i2c_write(0x6A);	// go to register 106 user control
			i2c_write(0x40);	// set value to 0100 0000 FIFO enable
			i2c_stop();		// set stop condition = release bus

Next is to read the values from the FIFO buffer and then I realised that I do not know how to read a 16 bit value out of two adresses

This is the 8 bit read we have seen earlier

	                i2c_start_wait(MPU6050+I2C_WRITE);  	// set device address and write mode
                	i2c_write(0x75);					// who am I register
			i2c_rep_start(MPU6050+I2C_READ);	        // set device address and read mode
	                ret = i2c_readNak();   				// read one byte
		        i2c_stop();

How do I make a read two bytes?

I am not familiar with the MPU-6050 sensor, but some sensors like these should be configurable so they automatically increment the register they are transmitting. This allows you to get all the data by continuously reading. You might try referring to its datasheet to see how you can have it automatically increment the register. You might also take a look at this Arduino tutorial for the MPU-6050 to see how they read multiple registers.

- Jeremy

I use an abbreviated version of Peter Fleury’s I2C routines, with Atmel Studio IV.
Here is how I use them to read out multiple bytes from one particular sensor (LPS25H). The autoincrement function is specified by setting the high order bit of the device address.

signed int I2C_ReadTempRaw(unsigned char busAddr, unsigned char deviceRegister) {

    unsigned int data = 0;
	unsigned char l;
    I2C_Start(busAddr); // send device address
    I2C_Write(deviceRegister | 0x80); // set register pointer, autoincrement
    I2C_Start(busAddr+READBIT); // restart as a read operation
    l = I2C_ReadACK(); // read the register data
	data |= I2C_ReadNACK(); //read next unsigned char
    I2C_Stop(); // stop
    return (signed int) ((data<<8)|l);
}

// read a 3 byte value, lowest order byte first

signed long I2C_ReadPressureRaw(unsigned char busAddr, unsigned char deviceRegister) {

    unsigned char pxl,pl,ph;
    I2C_Start(busAddr); // send device address
    I2C_Write(deviceRegister | 0x80); // set register pointer, autoincrement
    I2C_Start(busAddr+READBIT); // restart as a read operation
    pxl = I2C_ReadACK(); // read ls byte
    pl = I2C_ReadACK(); // read middle
    ph = I2C_ReadNACK(); // read high
    I2C_Stop(); // stop
    return (int32_t)ph << 16 | (uint16_t)pl << 8 | pxl;

Thanks for all the advice, my sensor is running now.

Here is the code that works with the unmodifed Fleury:

/*fleury_mpu6050_test - an application for the Pololu Baby Orangutan B
 *
 * This application uses the Pololu AVR C/C++ Library.  For help, see:
 * -User's guide: https://www.pololu.com/docs/0J20
 * -Command reference: https://www.pololu.com/docs/0J18
 *
 * Created: 10/8/2014 7:23:11 PM
 *  Author: gebruiker
 * Peter Fleury I2C library included in this program
 */

#include <pololu/orangutan.h>
#include <avr/io.h>
#include "i2cmaster.h"

#define MPU6050  0xD0     // (0x68 << 1) I2C slave address

unsigned char ret;            // return value

volatile uint8_t fifo;        // fifo = 1 when sensor values are available and 0 when processing is done
uint16_t acrawx;             // x axis acceleration raw value


ISR(PCINT1_vect)          // PC0..PC6 both rising and falling edge, ignore falling edge
{
 if(PINC & (1<<DDC0))
 {
  fifo = 1;
 }
}

void MPU6050_writereg(uint8_t reg, uint8_t val)
{
 i2c_start(MPU6050+I2C_WRITE);
 i2c_write(reg);  // go to register e.g. 106 user control
 i2c_write(val);  // set value e.g. to 0100 0000 FIFO enable
 i2c_stop();        // set stop condition = release bus
}


uint16_t MPU6050_readreg(uint8_t reg)
{
 i2c_start_wait(MPU6050+I2C_WRITE); // set device address and write mode
 i2c_write(reg);                                  // ACCEL_XOUT
 i2c_rep_start(MPU6050+I2C_READ);    // set device address and read mode
 int raw = i2c_readAck();                    // read one intermediate byte
 raw = (raw<<8) | i2c_readNak();        // read last byte
 i2c_stop();
 
 return raw;
} 


void Init_MPU6050()
{
 i2c_init();     // init I2C interface
 delay_ms(200);  // Wait for 200 ms.

 ret = i2c_start(MPU6050+I2C_WRITE);       // set device address and write mode
 if ( ret )
 {
  /* failed to issue start condition, possibly no device found */
  i2c_stop();
  red_led(1);    // Turn on the red LED immediately to signal the read is not ok
  while(1) {;;}  // lock program here as sensor init failed
 }
 else
 {
  /* issuing start condition ok, device accessible */
  MPU6050_writereg(0x6B, 0x00); // reg 107 set value to 0000 0000 and wake up sensor
  MPU6050_writereg(0x19, 0x08); // reg 25 sample rate divider set value to 0000 1000 for 1000 Hz
  MPU6050_writereg(0x1C, 0x08); // reg 28 acceleration configuration set value to 0000 1000 for 4g
  MPU6050_writereg(0x23, 0xF8); // reg 35 FIFO enable set value to 1111 1000 for all sensors not slave
  MPU6050_writereg(0x37, 0x10); // reg 55 interrupt configuration set value to 0001 0000 for logic level high and read clear
  MPU6050_writereg(0x38, 0x01); // reg 56 interrupt enable set value to 0000 0001 data ready creates interrupt
  MPU6050_writereg(0x6A, 0x40); // reg 106 user control set value to 0100 0000 FIFO enable
 }
}

int main(void)
{
 DDRC  &= ~(1 << DDC0);   // make pin PC3 an input
 PORTC |=  (1 << DDC0);   // enable pull-up on pin PC0 (PC0 will read as high unless driven low by INT from MPU6050)

 PCMSK1 |= (1 << PCINT8); // Pin Change Mask for PCINT8 = pin PC0
 PCICR |= (1 << PCIE1);   // Pin Change Interrupt enable interrupt on PCINT14..8 (PCINT 8 = PC0)

 acrawx = 0;        // initial value, without read led will not blink

 sei();             // Enable global interrupts 

 Init_MPU6050();    // Sensor init
 delay_ms(200);     // Wait for 200 ms.

 while (1)
 { 
  if (fifo == 1)
  {
   acrawx = MPU6050_readreg(0x3B);   // read raw X acceleration from fifo
   fifo = 0;        // wait for next delivery of sensor values
  }
  
  if (acrawx > 1)   // any value should make the led blink
  {
    red_led(1);     // Turn on the red LED
    delay_ms(200);  // Wait for 200 ms.
   red_led(0);      // Turn off the red LED
   delay_ms(200);   // Wait for 200 ms. 
  }
  else
  {
    red_led(0);     // Turn off the red LED to signal the read is not ok
    delay_ms(200);  // Wait for 200 ms.
  }
 }
}

The output is correct although the zero gravity of the x sensor is not exactly flat on the table, maxed out in one direction reads 8600, the other way 7800, total resolution of 16400 which matches the 8190 specified in the documentation for the 4g implementation.
So care has to be taken at calibration of this sensor.

I am glad you got it working. Thanks for sharing your code.

- Jeremy

The sensor showed a large amount of noise as soon as I started my motor. The noise was bigger than the sensor value. I solved this by using the internal noise filtering option. My motor runs at 6000 rpm so I was expecting some noise in the 100 Hz range. This proved wrong. The graphs were made by sending the measurement value with uart to putty and then copy pasted to excel where I made the graphs. Time span is roughly 1 second and I create 1g in both directions by turning the sensor 90 degrees left and right.

Register 26 is used for filtering

MPU6050_writereg(0x1A, 0x02);	// set filter

MPU6050 and motor noise.pdf (370 KB)

WARNING:

Not all sensors on the market have the same quality. I found out that the very cheap ones do not werk very well, see my other post for explanation.

Hello.

I am dealing same problem that you had already solve. However when I follow your steps to test my MPU6050 sensor, it turns out no respond. I am confuse why you set the PC0 be the input because the SDA should be connect to pc5.

DDRC  &= ~(1 << DDC0);   // make pin PC3 an input<-it should be PC0 right?
PORTC |=  (1 << DDC0);   // enable pull-up on pin PC0 (PC0 will read as high unless driven low by INT from MPU6050)

Did I connect it wrong or misunderstand your concept?
Tom

it should be PC0 and is connected to AD0 on board, this defines the sensor adress. SDA and SDL are connected to PC4 and PC5 on the Baby O.