Go Down

Topic: [SOLVED]SAMD21 reading MPU6050 Gyro MSB always 255 (Read 1 time) previous topic - next topic

Billbo911

May 06, 2018, 04:28 am Last Edit: May 13, 2018, 11:33 pm by Billbo911 Reason: Solved
I'm starting the process of building an inverted pendulum robot. I had great success with a stepper motor version designed by Joop Brooking. Search "YABR" and you will be sure to find it.

My new project will be using a Sparfun SAMD21 Mini breakout board instead of the Pro Mini used for the stepper motor bot. Again I am using the same MPU6050 as in the previous bot. I will be using brushed DC motors as well.

My first phase of testing is to make sure I can communicate with the MPU6050.
While I do find it on the bus, and can get values from it, the MSB of the gyro and Accelerometers is always 255. I verified this by just printing the MSBs from all six axis. The odd thing is, if I use the same code, modified for the differences in Serial vs SerialUSB, and sensor and only swap out the micro controller to a Pro Micro, the code works perfectly and I get values in a range that are expected. Yes, I did change the board version and recompiled in between the changes. So, the only real difference I can see is the processor version.

I'm fairly certain the issue lies in the way the Wire.h handles the data with the SAMD21.


Here is the code. (I edited the sercom portion to get exactly 400Khz on the SCL line).
There is also some code in here to read a Nunchuck if it is on the buss. Just ignore that section.
Code: [Select]

#include <Wire.h>

byte error, MPU_6050_found, nunchuck_found, lowByte, highByte;
int address;
int nDevices;

void setup()
{
 Wire.begin();
 //TWBR = 12; // Non SAMD21
 sercom3.disableWIRE();                         // Disable the I2C bus
 SERCOM3->I2CM.BAUD.bit.BAUD = SystemCoreClock / ( 2 * 530000) - 1 ;   // // Set the I2C SCL frequency to 400kHz
 sercom3.enableWIRE();                          // Restart the I2C bus
 SerialUSB.begin(115200);
 Serial1.begin(115200);
}

void loop()
{
 SerialUSB.println("Scanning I2C bus...");

 nDevices = 0;
 for(address = 1; address < 127; address++ )
 {
   Wire.beginTransmission(address);
   error = Wire.endTransmission();

   if (error == 0)
   {
     SerialUSB.print("I2C device found at address 0x");
     if (address<16)SerialUSB.print("0");
     SerialUSB.println(address,HEX);
     nDevices++;
     if(address == 0x68 || address == 0x69){
       SerialUSB.println("This could be a MPU-6050");
       Wire.beginTransmission(address);
       Wire.write(0x75);
       Wire.endTransmission();
       SerialUSB.println("Send Who am I request...");
       Wire.requestFrom(address, 1);
       while(Wire.available() < 1);
       lowByte = Wire.read();
       if(lowByte == 0x68){
         SerialUSB.print("Who Am I responce is ok: 0x");
         SerialUSB.println(lowByte, HEX);
       }
       else{
         SerialUSB.print("Wrong Who Am I responce: 0x");
         if (lowByte<16)SerialUSB.print("0");
         SerialUSB.println(lowByte, HEX);
       }
       if(lowByte == 0x68 && address == 0x68){
         MPU_6050_found = 1;
         SerialUSB.println("Starting Gyro....");
         set_gyro_registers();
       }
     }
     if(address == 0x52){
       SerialUSB.println("This could be a Nunchuck");
       SerialUSB.println("Trying to initialise the device...");
       Wire.beginTransmission(0x52);
       Wire.write(0xF0);
       Wire.write(0x55);
       Wire.endTransmission();
       delay(20);
       Wire.beginTransmission(0x52);
       Wire.write(0xFB);
       Wire.write(0x00);
       Wire.endTransmission();
       delay(20);
       SerialUSB.println("Sending joystick data request...");
       Wire.beginTransmission(0x52);
       Wire.write(0x00);
       Wire.endTransmission();
       Wire.requestFrom(0x52,1);
       while(Wire.available() < 1);
       lowByte = Wire.read();
       if(lowByte > 100 && lowByte < 160){
         SerialUSB.print("Data response normal: ");
         SerialUSB.println(lowByte);
         nunchuck_found = 1;
       }
       else{
         SerialUSB.print("Data response is not normal: ");
         SerialUSB.println(lowByte);
       }
     }
   }
   else if (error==4)
   {
     SerialUSB.print("Unknown error at address 0x");
     if (address<16)
       SerialUSB.print("0");
     SerialUSB.println(address,HEX);
   }    
 }
 if (nDevices == 0)
   SerialUSB.println("No I2C devices found\n");
 else
   SerialUSB.println("done\n");
 if(MPU_6050_found){
   SerialUSB.print("Balance value: ");
   Wire.beginTransmission(0x68);
   Wire.write(0x3F);
   Wire.endTransmission();
   Wire.requestFrom(0x68,2);
   SerialUSB.println((Wire.read()<<8|Wire.read())*-1);
   delay(20);
   SerialUSB.println("Printing raw gyro values");
   for(address = 0; address < 20; address++ ){
     Wire.beginTransmission(0x68);
     Wire.write(0x43);
     Wire.endTransmission();
     Wire.requestFrom(0x68,6);
     while(Wire.available() < 6);
     SerialUSB.print("Gyro X = ");
     SerialUSB.print(Wire.read()<<8|Wire.read());
     SerialUSB.print(" Gyro Y = ");
     SerialUSB.print(Wire.read()<<8|Wire.read());
     SerialUSB.print(" Gyro Z = ");
     SerialUSB.println(Wire.read()<<8|Wire.read());
     Serial1.println("Bang!!!");
   }
   SerialUSB.println("");
 }
 else SerialUSB.println("No MPU-6050 device found at address 0x68");

 if(nunchuck_found){
   SerialUSB.println("Printing raw Nunchuck values");
   for(address = 0; address < 20; address++ ){
     Wire.beginTransmission(0x52);
     Wire.write(0x00);
     Wire.endTransmission();
     Wire.requestFrom(0x52,2);
     while(Wire.available() < 2);
     SerialUSB.print("Joystick X = ");
     SerialUSB.print(Wire.read());
     SerialUSB.print(" Joystick y = ");
     SerialUSB.println(Wire.read());
     delay(100);
   }
 }
 else SerialUSB.println("No Nunchuck device found at address 0x52");
 //while(1);  //Comment out to allow continuous testing
 delay(1500);  // Add to ing evry 1.5 sec.
}

void set_gyro_registers(){
 //Setup the MPU-6050
 Wire.beginTransmission(0x68);                                     //Start communication with the address found during search.
 Wire.write(0x6B);                                                         //We want to write to the PWR_MGMT_1 register (6B hex)
 Wire.write(0x00);                                                         //Set the register bits as 00000000 to activate the gyro
 Wire.endTransmission();                                                   //End the transmission with the gyro.
 
 Wire.beginTransmission(0x68);                                     //Start communication with the address found during search.
 Wire.write(0x1B);                                                         //We want to write to the GYRO_CONFIG register (1B hex)
 Wire.write(0x00);                                                         //Set the register bits as 00000000 (250dps full scale)
 Wire.endTransmission();                                                   //End the transmission with the gyro

 Wire.beginTransmission(0x68);                                     //Start communication with the address found during search.
 Wire.write(0x1C);                                                         //We want to write to the ACCEL_CONFIG register (1A hex)
 Wire.write(0x08);                                                         //Set the register bits as 00001000 (+/- 4g full scale range)
 Wire.endTransmission();                                                   //End the transmission with the gyro

 Wire.beginTransmission(0x68);                                     //Start communication with the address found during search
 Wire.write(0x1A);                                                         //We want to write to the CONFIG register (1A hex)
 Wire.write(0x03);                                                         //Set the register bits as 00000011 (Set Digital Low Pass Filter to ~43Hz)
 Wire.endTransmission();                                                   //End the transmission with the gyro
}

Koepel

#1
May 06, 2018, 10:06 pm Last Edit: May 06, 2018, 10:22 pm by Koepel
Hi, when you talk about something please give a link to it.
Joop Brokking YABR: http://www.brokking.net/yabr_main.html
Sparkfun SAMD21: https://www.sparkfun.com/products/13672

When something is wrong, fall back to something simple basic that you can make work. For example a minimal sketch that shows the problem. Now you have created a sketch that has already too many issues.

First of all, you could have bought a real Arduino board for better support. For example the Arduino Zero or the MKR Zero, or even a Arduino M0 clone like the Elecrow M0-SD.
Have you installed extra Sparkfun boards for the Sparkfun SAMD21 ? Did it come with its own Wire library ?

Stay away from the TWR or SERCOM. The Wire.setClock() is the function to set the clock.
Since there are problems, you should use the default 100 kHz. Increasing the clock is for when everything works perfect.

Run the I2C Scanner and find the address of the sensor. Use that with the shortest example possible: https://playground.arduino.cc/Main/MPU-6050#short. That example uses "int16_t" to create the x,y,z value. That is what is needed. You try to put them directly into parameters for the SerialUSB.print() function, that is asking for trouble.

Don't do this after a Wire.requestFrom():
Code: [Select]
while(Wire.available() < 2);Did someone tell you to do that ? or did you found a (wrong) example ? You can remove all of them, there are about 4 of those in the sketch.

You test if the I2C address is 0x68 or 0x69, but after that you assume it is 0x68.

It is not a well structured sketch. You could put more code in functions, and design a better program flow.

How is your I2C bus ? I hope just a few short wires and not a cable.
Since the SAMD21 is a 3.3V processor, you don't need level shifters anymore for the I2C or SPI bus. The MPU-6050 is noisy, you could upgrade to the MPU-9250. The MPU-9250 can be used with I2C and SPI.

Billbo911

Hi, when you talk about something please give a link to it.
Joop Brokking YABR: http://www.brokking.net/yabr_main.html
Sparkfun SAMD21: https://www.sparkfun.com/products/13672

When something is wrong, fall back to something simple basic that you can make work. For example a minimal sketch that shows the problem. Now you have created a sketch that has already too many issues.

First of all, you could have bought a real Arduino board for better support. For example the Arduino Zero or the MKR Zero, or even a Arduino M0 clone like the Elecrow M0-SD.
Have you installed extra Sparkfun boards for the Sparkfun SAMD21 ? Did it come with its own Wire library ?

Stay away from the TWR or SERCOM. The Wire.setClock() is the function to set the clock.
Since there are problems, you should use the default 100 kHz. Increasing the clock is for when everything works perfect.

Run the I2C Scanner and find the address of the sensor. Use that with the shortest example possible: https://playground.arduino.cc/Main/MPU-6050#short. That example uses "int16_t" to create the x,y,z value. That is what is needed. You try to put them directly into parameters for the SerialUSB.print() function, that is asking for trouble.

Don't do this after a Wire.requestFrom():
Code: [Select]
while(Wire.available() < 2);
Did someone tell you to do that ? or did you found a (wrong) example ? You can remove all of them, there are about 4 of those in the sketch.

You test if the I2C address is 0x68 or 0x69, but after that you assume it is 0x68.

It is not a well structured sketch. You could put more code in functions, and design a better program flow.

How is your I2C bus ? I hope just a few short wires and not a cable.
Since the SAMD21 is a 3.3V processor, you don't need level shifters anymore for the I2C or SPI bus. The MPU-6050 is noisy, you could upgrade to the MPU-9250. The MPU-9250 can be used with I2C and SPI.
If you had followed the links you provided for the Brooking.net, you would have seen exactly where this code came from. It is the hardware test script that is used for verifying that two different devices are available on the I2C bus. The test is never run with both on the buss at the same time. Thus, having the code in there for the Nunchuck is harmless.

The code, as it stands, finds the MPU6050 and even validates that it is in fact a MPU6050. It also is successful in retrieving data from the registers on the gyro.

The problem I am experiencing is that the MSB for each axis of the Gyro and Accelerometer comes back at 255, even when the gyro is perfectly still.
When I run this same code on an Arduino Pro mini or an Arduino Pro Micro, it works perfectly. It is only when it  is run on the SAMD21 that I see this error.

Yes, I did install the SAMD and the Sparkfun SAMD boards into the environment.

I2C bus is two wires no more than 6cm in length run directly between the controller and sensor boards.

One new piece of info I have found is that the SAMD21 does not appear to have pull-ups on the SCL and SDA lines. Is that correct?

Koepel

I see. The code is in a zip file. Others publish their code on Github, then I could make an issue. Even when the code is on Github, some people don't like me for telling that the Wire library is used in the wrong way and keep on doing it wrong :smiley-confuse:

I don't know if Sparkfun supplies a Wire library with the board. They use their own bootloader, but I don't see a specific Wire library: https://github.com/sparkfun/SAMD21_Dev_Breakout.

If the bootloader is compatible with the bootloader of the Arduino M0 or Arduino Zero, then you could select one of the official Arduino boards.

If the internal pullup resistors are enabled, they are weaker than with an Arduino Pro Mini.
Does the MPU-6050 module have pullup resistors on the module ?
Without pullup resistors I would expect other problems then a second byte being 255.

Can you try that short example ? You only have to change the Serial to SerialUSB.

You have to do a few tests to get closer to the problem. It is a weird problem and at the moment there are too many things that might be the cause.

Billbo911

I see. The code is in a zip file. Others publish their code on Github, then I could make an issue. Even when the code is on Github, some people don't like me for telling that the Wire library is used in the wrong way and keep on doing it wrong :smiley-confuse:

I don't know if Sparkfun supplies a Wire library with the board. They use their own bootloader, but I don't see a specific Wire library: https://github.com/sparkfun/SAMD21_Dev_Breakout.

If the bootloader is compatible with the bootloader of the Arduino M0 or Arduino Zero, then you could select one of the official Arduino boards.

If the internal pullup resistors are enabled, they are weaker than with an Arduino Pro Mini.
Does the MPU-6050 module have pullup resistors on the module ?
Without pullup resistors I would expect other problems then a second byte being 255.

Can you try that short example ? You only have to change the Serial to SerialUSB.

You have to do a few tests to get closer to the problem. It is a weird problem and at the moment there are too many things that might be the cause.
Thanks for the follow up!

I tried adding external Pullups at 4.7K. No improvement.

I created a shorter script as you suggested, but really got no improvement in the results.
I tried this new script on both the SAMD21 and also on a Teensy 3.2. Again, no good results.
I created it so it would be simple to change the I2C clock setting regardless of which board I used. And, as you said, it is a simple matter to change the "Serial..." to SerialUSB..." also making it easy to test on a variety of boards. I'm starting to believe it may have more to do with my script than the board it's self. I loaded this script onto a Pro Micro and got the same MSB of 255. If I loaded the test script that came with the YABR robot, the same one I listed previously, it works great on the Pro Micro. So, I'm kinda confused now.

Honestly, I would not be surprised if the Wire library I am using is jacked up. I do know one is provided when you install the SF boards, but that doesn't mean it is right.

So, any suggestions on what I should use?
At this point I am ready to just move forward with the Pro Micro and scrap the idea of using the SAMD21.

Here is the shorter script.
Code: [Select]

#include <Wire.h>

int address;

void setup() {
  Wire.begin();
  TWBR = 12;
  //Wire.setClock(400000);
  //sercom3.disableWIRE();                         // Disable the I2C bus
  //SERCOM3->I2CM.BAUD.bit.BAUD = SystemCoreClock / ( 2 * 470000) - 1 ;   // Set the I2C SCL frequency to 400kHz
  //sercom3.enableWIRE();                          // Restart the I2C bus
 
  Serial.begin(115200);

  Serial.println("Starting Gyro....");
  set_gyro_registers();
  delay(50);
}

void loop() {
    Serial.print("Balance value: ");
    Wire.beginTransmission(0x68);
    Wire.write(0x3F);
    Wire.endTransmission();
    Wire.requestFrom(0x68,2);
    Serial.println((Wire.read()<<8|Wire.read())*-1);
    delay(20);
    Serial.println("Printing raw gyro values");
       
      Wire.beginTransmission(0x68);
      Wire.write(0x43);
      Wire.endTransmission();
      Wire.requestFrom(0x68,6);
      while(Wire.available() < 6);
      Serial.print("Gyro X MSB = ");
      Serial.print(Wire.read());//<<8|Wire.read());
      Serial.print(" Gyro X LSB = ");
      Serial.println(Wire.read());
      Serial.print("Gyro Y MSB = ");
      Serial.print(Wire.read());//<<8|Wire.read());
      Serial.print(" Gyro Y LSB = ");
      Serial.println(Wire.read());
      Serial.print("Gyro Z MSB = ");
      Serial.print(Wire.read());//<<8|Wire.read());
      Serial.print(" Gyro Z LSB = ");
      Serial.println(Wire.read());
     
      delay(1500);  // Add to ing evry 1.5 sec.
}

void set_gyro_registers(){
  //Setup the MPU-6050
  Wire.beginTransmission(0x68);                                     //Start communication with the address found during search.
  Wire.write(0x6B);                                                         //We want to write to the PWR_MGMT_1 register (6B hex)
  Wire.write(0x00);                                                         //Set the register bits as 00000000 to activate the gyro
  Wire.endTransmission();                                                   //End the transmission with the gyro.
 
  Wire.beginTransmission(0x68);                                     //Start communication with the address found during search.
  Wire.write(0x1B);                                                         //We want to write to the GYRO_CONFIG register (1B hex)
  Wire.write(0x00);                                                         //Set the register bits as 00000000 (250dps full scale)
  Wire.endTransmission();                                                   //End the transmission with the gyro

  Wire.beginTransmission(0x68);                                     //Start communication with the address found during search.
  Wire.write(0x1C);                                                         //We want to write to the ACCEL_CONFIG register (1A hex)
  Wire.write(0x08);                                                         //Set the register bits as 00001000 (+/- 4g full scale range)
  Wire.endTransmission();                                                   //End the transmission with the gyro

  Wire.beginTransmission(0x68);                                     //Start communication with the address found during search
  Wire.write(0x1A);                                                         //We want to write to the CONFIG register (1A hex)
  Wire.write(0x03);                                                         //Set the register bits as 00000011 (Set Digital Low Pass Filter to ~43Hz)
  Wire.endTransmission();                                                   //End the transmission with the gyro
}

Billbo911

#5
May 07, 2018, 10:37 pm Last Edit: May 08, 2018, 12:18 am by Billbo911
I just took a quick look at the "Arduino" SAMD Wire.h and the "SparkFun " SAMD Wire.h. There are differences on lines 37, 74, and 77. Honestly, I'm not sure what impact they would have.

Here is line 37 from the arduino SAMD Wire.h:
Code: [Select]
void begin(uint8_t, bool enableGeneralCall = false);
and the SparkFun SAMD Wire.h:
Code: [Select]
void begin(uint8_t);

Here are lines 73-78 from Arduino:
Code: [Select]
    // RX Buffer
    RingBufferN<256> rxBuffer;

    //TX buffer
    RingBufferN<256> txBuffer;
    uint8_t txAddress;

and from SparkFun:
Code: [Select]
    // RX Buffer
    RingBuffer rxBuffer;

    //TX buffer
    RingBuffer txBuffer;
    uint8_t txAddress;




Koepel

Those are very small differences.

I have already told you what to stay away from, what to try, why "int16_t" is important, what not to do and what you could do. The shorter sketch you showed will not work. I'm afraid I can't help you further.

Billbo911

Those are very small differences.

I have already told you what to stay away from, what to try, why "int16_t" is important, what not to do and what you could do. The shorter sketch you showed will not work. I'm afraid I can't help you further.
Credit where credit is due.
This last responce pushed me in the right direction to get this project moving forward. Thank you.

Now I have to say this.

If you consider what you say to users "support", then you need to rethink your line of work!

Seriously, your condensation is ridiculous. There are much better ways to convey your thoughts. Please consider twice before you post.

Here is what finally actually helped. "why "int16_t" is important". My short script now works by assigning the Wire.reads to int16_t variables, and then printing them.



Koepel

Work ? Ha ha, are you paying me ? I am just like you, no more, no less. We are both Arduino enthusiasts.

Go Up