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.
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.
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
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.
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();
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.
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.
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.
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