Teensy LC - MPU6050 I2C. How to pullup +3.3V?

Status
Not open for further replies.
Hello,
I recently switched from an Arduino UNO to a Teensy LC for my project which makes use of the MPU6050 accelerometer. After some testing I realized that the output of the sensor is very inconsistent when using the teensy (I tested this with a default example sketch), the same sensor works fine with the Arduino.

So I went back to pjrc and found following quote (regarding I2C communication):
"Teensy LC & 3.0-3.6 requires pullup resistors to +3.3V. The on-chip pullups are not used. 4.7K resistors are recommended for most applications."

Thats probably what I'm missing but I'm a bit confused. First of all, what exactly is a pullup resister? Is it a part I have to buy? Can I build one my own using resistors? I looked it up and (I think) I understand what they are needed for but in the past I simply hooked pins that needed a pullup to ground, which feels kinda wrong to do on a +3.3V pin.

Apart from that I'm powering my circuit with that pin... so yeah I'm lost.
Hope somebody can explain this to me (preferebaly as if I'm 5 :)).
 
There are a lot of good references on Pull-up or Pull-down resistors on the web. Example: https://learn.sparkfun.com/tutorials/pull-up-resistors

With I2C, especially with the data line (SDA), you may have several components on the line that will try to control the output level (high or low).
To put it simply, with nothing else driving the circuits you need some default voltage on it. In these cases you need a 3.3v signal on the line.
So you connect a resistor connected to the SDA data pin and to a +3.3v pin. This will drive the circuit to high when nothing else is driving it low.
The value of the resistor is a trade off between, how much current runs through it when you drive the circuit low versus how long it takes for the power to run through it to drive the IO pins level high again. And on a 3.3v circuit 4.7k appears to work well.

May I2C devices already have a PULL up resistor on them. Often in the range of 10K. But sometimes you need an external one to get the right values, and you can use most any type of resistors. On circuit boards I mostly use surface mounted resistors, but for quick hook ups I use some through the hole types. I have several sitting around, but for ease of use, I have a set of them near by: https://www.sparkfun.com/products/10969
 
Thanks alot. I totaly misundertood that one at the first place, things are much clearer now. Unfortunately I still get inconsistent results from the sensor. I added a picture of my setup, maybe I did something wrong. Since I didn't had a 4.7k resistor laying around I chained 2k, 2k, 330, 330, 10, 10, 10 and 10 resistors.
IMG_1796.jpg
 
The 4.7k is a ballpark value... You were probably fine with just the 4K... A lot of setups just use a 10K but that is usually better for 5v systems.

Might help others if you mentioned which library and exact code you are using. Also maybe a link to the actual board you have.

The inconsistent results could be due to several things, like the Pull up resistors you mentioned, but also timing.
 
what is the vendor/part number for your MPU6050. From your photo, I think the MPU breakout has onboard pullups (as do most breakout boards for I2c devices), so you may not need to add any external pullups. The other issue is power. if you have been testing with UNO, that is 5v based, so the breakout may or may not require 5v. If breakout requires 5v, that may not be good for your LC. Hopefully, you haven't hooked up any 5v devices to your LC.

see discussion of GY-521 breakout at https://playground.arduino.cc/Main/MPU-6050
 
Last edited:
The MPU6050 has GY-521 printed on the backside.
The discussion at https://playground.arduino.cc/Main/MPU-6050 suggests powering the GY-521 with 5v. To be safe you could first just hook up 5v and GND to the breakout (no other wires from the LC), and then with voltmeter, measure the voltage on the SDA and SCL breakout pins. If it shows 3.3v, you are safe to hook the I2C wires to the LC and run your sketch. The schematic http://playground.arduino.cc/uploads/Main/MPU6050-V1-SCH.jpg shows pullups to 3.3v on the breakout, so you shouldn't need any other pullups.
 
So it maybe is this unit? https://www.amazon.com/MPU-6050-MPU6050-Accelerometer-Gyroscope-Converter/dp/B008BOPN40

And if you look at the GY-521 section of the Playgound post it mentions:
This sensor board has a voltage regulator. When using 3.3V to the VCC the resulting voltage (after the onboard voltage regulator) might be too low for a good working I2C bus. It is preferred to apply 5V to the VCC pin of the sensor board. The board has pull-up resistors on the I2C-bus. The value of those pull-up resistors are sometimes 10k and sometimes 2k2. The 2k2 is rather low. If it is combined with other sensor board which have also pull-up resistors, the total pull-up impedance might be too low.
Some GY-521 modules have the wrong capacitor (or a bad capacitor) and that result
 
On the picture in post #3, I’m seeing only one pull-up resistor while 2 are needed for I2C, one on the data line and one on the clock line. You might use an oscilloscope to check the signal quality and to adapt the resistance depending on the I2C transmission rate, wire length and parasitic capacitances.
 
Thanks for all the answers,
I dont have a level-shifter neither a multimeter at hand to properly test this atm. I'll check it tomorrow at university and report back here after that.
 
Thanks for all the answers,
I dont have a level-shifter neither a multimeter at hand to properly test this atm. I'll check it tomorrow at university and report back here after that.

You can use your UNO as a voltmeter. Power the MPU breakout with 5v/grnd from UNO, then jumper breakout's I2C pins to A0 and A1 on UNO, and run a little sketch to print values of analogRead() for A0 and A1. Values around 675 would indicate 3v3, values of 1023 would mean 5v from breakout's SDA and SCL pins

FYI, here is some I2C pullup info with scope tests of different pullup resistances:
http://www.gammon.com.au/i2c
 
You can use your UNO as a voltmeter.

Great idea thanks! The values were exactly around 675.
So I hooked up the sensor to the 5V pin now but the results are still wrong (with or without the external pullup).

I actually didn't expected the libary to be the problem (since it works fine on my UNO) but I will see if I can find a different one and try it out.
 
Ok I've done some more testing on the UNO now. It appears that the sensor will not work without external pullup on sda and scl if powered with 5V. I read A4 and A5 of the UNO while the program was running and the results were 699 using 10k pullup resistor (~800 with 4k resistor). That should be around 3.4V right? Would this be save to try on the Teensy or do I really need a level-shifter here? I have no experience with those.

Edit: Kris's libary is a little bit to advanced for my purpose (atm) and I figured that the one from jarzebski does basicly the same at it's core part.
 
Last edited:
So you are saying the sensor will not work running from the UNO without additional pullup resistors? And it used to work OK from the UNO with no additional pullups?

Your UNO "voltmeter" tests seemed to indicate that the sensor breakout when powered with 5v has pullups to 3.3v. The schematic from the playground URL indicates that the breakout has a voltage regulator to convert 5v to 3.3v and that there are 4.7K pullups to 3.3v on SDA and SCL lines. You could experiment with adding additional pullups to SDA and SCL (as noted in post #10, you need to have pullups resistors for BOTH SDA and SCL). I would try smaller resistance, say 2.2K ohm. Power the sensor with 5v, but the pullups should attach to 3.3v (the MPU is 3.3v device).

I have only worked with the newer MPU9250
 
I've likely used that same MPU6050 breakout board. I got 10 of them off of ebay once.
https://www.ebay.com/itm/MPU-6050-6...045005?hash=item2ee545af8d:g:OSwAAOSwDwtUm4WP

I had no problems running it off of 3.3V, although it may have shared the i2c bus with another sensor that probably had 10k pull-up resistor.

But I'm pretty sure I tested first w/o anything else connected. I don't think you need any other additional pull-up resistors.

If you are using i2c_t3, you might want to slow the i2c bus down.
 
If you are using i2c_t3, you might want to slow the i2c bus down.

I switched to i2c_t3 now and tried a rate of 100khz and 50khz, the results were still off and the program froze after a few moments. I also tried to set different pins for SDA and SCL (22 & 23 or 16 & 17) but the sketch seems to crash somewhere in the setup method when I do that.

Since I haven't broken a board yet (afaik) I can't really tell if this behaviour may indicate that something is wrong with my Teensy. I'd expect that the board wouldn't do anything at all if broken to be honest. I actually consider to order myself another Teensy just to check.
 
Did you setup i2c_t3 with the I2C_PULLUP_EXT in the begin function?

I'll try and post a simple sketch later. I might have done more of my testing on 3.2, but I'll try it on LC.

I doubt there is anything wrong with the breakout board or the LC.

Also, I've bought some cheap breadboards that were a mess internally and creating lots of problems, so you might want to consider that.
 
Last edited:
Well, I must've forgotten how I wired it. It definitely doesn't work as-is. For testing, look under i2c_t3 menu for the "basic_scanner" sketch. It checks all i2c addresses for an acknowledgement.

It didn't find anything with the mpu6050 board hooked up. I plugged in another i2c board and it came up. I'll test with some other resistors and see if it comes up.

Also, remember to wire ADO to GND.

I bet if you wired 2 of them up it would work. ADO->GND for one, and ADO->3.3V (not sure if that's need to be with a resistor) for the other.
 
Ok, just wired up a 2k ohm resistor to 3.3V on SCL and SDA pins. You need a 2k resistor for the SCL pin to 3.3V, and you need another 2k resistor for the SDA pin to 3.3V. Then it came up, and this with a T-LC.

I just used the 3.3V pin on T-LC to power the MPU6050 board. No 5V needed.

BTW, the board has 2.2k resistors for pull-ups already, so you need a little more. If you have 2 breakout boards, it would probably be fine.

Here's my basic code:
Code:
#include <i2c_t3.h>

#define MPU6050         3

#ifdef MPU6050
  //3 values, 2 bytes
  uint8_t mpuBytes[3*2];
  int16_t mpuAcXTmp, mpuAcYTmp, mpuAcZTmp;
  int32_t mpuAcX, mpuAcY, mpuAcZ;
#endif

struct i2cConfig {
  uint32_t clock;     // I2C clock frequency
  uint8_t port;       // I2C bus number
  i2c_pins pins;      // I2C pins
  i2c_pullup pullups; // I2C pullups
  uint8_t address;    // I2C address
};

#ifdef MPU6050
struct mpu6050Config {
  char signalName[8];
  uint8_t sampleDivisor = 1;
  uint8_t sensorType = MPU6050;
  uint8_t ID = 0x68;
  uint8_t numSensor=1;
  i2cConfig i2c;
};
#endif

#ifdef MPU6050
  mpu6050Config mpu6050Cfg;
#endif

void setup() {
  while (!Serial && (millis () <= 3000));
  Serial.begin(9600);
  for (int i=0;i<5;i++) {
    Serial.println(i);
    delay(500);
  }
  
 #ifdef MPU6050
    mpu6050Cfg.i2c.port=0;
    mpu6050Cfg.i2c.address=0x68;
    mpu6050Cfg.numSensor=1;
 #endif

Serial.println("setup1");
i2c_t3(mpu6050Cfg.i2c.port).begin(I2C_MASTER, 0x00, I2C_PINS_16_17, I2C_PULLUP_EXT, I2C_RATE_100); //tLC
//  i2c_t3(mpu6050Cfg.i2c.port).begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_100); //3.2

Serial.println("setup2");
  #ifdef MPU6050
//    mpu6050Init(&mpu6050Cfg);
  #endif

 writeByteWire(0, 0x68, 0x6B, 0);

Serial.println("setup3");
}

void loop() {
 #ifdef MPU6050
      readMPU6050(mpu6050Cfg.i2c.port,mpu6050Cfg.i2c.address,&mpuBytes[0]);
      mpuAcXTmp = mpuBytes[0]<<8|mpuBytes[1];
      mpuAcYTmp = mpuBytes[2]<<8|mpuBytes[3];
      mpuAcZTmp = mpuBytes[4]<<8|mpuBytes[5];
      mpuAcX = mpuAcXTmp;
      mpuAcY = mpuAcYTmp;
      mpuAcZ = mpuAcZTmp;
  #endif

  Serial.print(mpuAcX);
  Serial.print(",");
  Serial.print(mpuAcY);
  Serial.print(",");
  Serial.print(mpuAcZ);
  Serial.println();
  delay(100);
}

void readMPU6050(uint8_t i2cPort, uint8_t i2cAddress, uint8_t *dest) {
    readBytesWire(i2cPort, i2cAddress, 0X3B, (3*2), dest);
}

#ifdef MPU6050
void mpu6050Init(struct mpu6050Config *cfg) {
      writeByteWire(cfg->i2c.port, cfg->i2c.address, 0x6B, 0);
}
#endif

/*
* read a chunk of data from sensor
*/
void readBytesWire(uint8_t i2cPort, uint8_t address, uint8_t subAddress, uint8_t count, uint8_t * dest) {  
  i2c_t3(i2cPort).beginTransmission(address);   // Initialize the Tx buffer
  i2c_t3(i2cPort).write(subAddress);            // Put slave register address in Tx buffer
  i2c_t3(i2cPort).endTransmission(I2C_NOSTOP);  // Send the Tx buffer, but send a restart to keep connection alive
  i2c_t3(i2cPort).requestFrom(address, (size_t) count);  // Read bytes from slave register addr`ess 
  for (int i=0;i2c_t3(i2cPort).available();i++) {
    dest[i] = i2c_t3(i2cPort).read();
  }
}

/**
* write data to sensor
*/
void writeByteWire(uint8_t port, uint8_t address, uint8_t subAddress, uint8_t data) {
  i2c_t3(port).beginTransmission(address);  // Initialize the Tx buffer
  i2c_t3(port).write(subAddress);           // Put slave register address in Tx buffer
  i2c_t3(port).write(data);                 // Put data in Tx buffer
  i2c_t3(port).endTransmission();           // Send the Tx buffer
}
 
Last edited:
Huge thanks linuxgeek! I wired it the way you did and it works with your sketch. It still doesn't work with the lib I was using before though, so that was probably the cause of all trouble...

I actually tried to change the lib using i2c_t3 instead of Wire but the sensor keeps giving wrong results no matter what. Not sure whats different there since the lib works fine on my UNO and everything looks right as far as I can tell.

For anyone interested, these are the only changes I made:
Code:
#include <i2c_t3.h> // instead of #include <Wire.h>
#include <math.h>

#include <MPU6050.h>

bool MPU6050::begin(mpu6050_dps_t scale, mpu6050_range_t range, int mpua)
{
    // Set Address
    mpuAddress = mpua;

    Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_100); // instead of Wire.begin()
I linked the rest of the libary aswell as the test sketch in post #6.

For now I wont bother to find out why this lib doesn't work and rather write my own simple one.
Again thanks to everyone who helped me tracking this down, I learned quite a bit during the process.
 
I'm trying to hook up an mpu6050 to a Teensy LC, but my inexperience is getting in the way. So far I've managed to get readings of 000000 pretty consistently :) I'm not sure what the problem is, but I'm not sure if I'm using pull-up resistors properly, and I don't know what AD0 is, so that probably isn't helping my cause.
I haven't tried the code from the previous two posts yet - I guess I want to be sure I have my circuit set up properly so it isn't making all of my code efforts useless anyway.

Circuit:
I've got 3.3V and GND hooked up to the mpy6050 - that's evidently working; then I have Teensy SCL through a 5K1 resistor to SCL on the mpu6050, and Teensy SDA through a 5K1 resistor to SDA on the mpu6050. Does that sound right?

Code:

Code:
// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class
// 10/7/2011 by Jeff Rowberg <jeff@rowberg.net>
// Updates should (hopefully) always be available at https://github.com/ajrowberg/i2cdevlib
//
// Changelog:
//      2013-05-08 - added multiple output formats
//                 - added seamless Fastwire support
//      2011-10-07 - initial release

/* ============================================
I2Cdev device library code is placed under the MIT license
Copyright (c) 2011 Jeff Rowberg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
===============================================
*/

// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
// for both classes must be in the include path of your project
#include "I2Cdev.h"
#include "MPU6050.h"

// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for InvenSense evaluation board)
// AD0 high = 0x69
//MPU6050 accelgyro;
MPU6050 accelgyro(0x69); // <-- use for AD0 high

int16_t ax, ay, az;
int16_t gx, gy, gz;



// uncomment "OUTPUT_READABLE_ACCELGYRO" if you want to see a tab-separated
// list of the accel X/Y/Z and then gyro X/Y/Z values in decimal. Easy to read,
// not so easy to parse, and slow(er) over UART.
#define OUTPUT_READABLE_ACCELGYRO

// uncomment "OUTPUT_BINARY_ACCELGYRO" to send all 6 axes of data as 16-bit
// binary, one right after the other. This is very fast (as fast as possible
// without compression or data loss), and easy to parse, but impossible to read
// for a human.
//#define OUTPUT_BINARY_ACCELGYRO


#define LED_PIN 13
bool blinkState = false;

void setup() {
    // join I2C bus (I2Cdev library doesn't do this automatically)
    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
        Wire.begin();
    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
        Fastwire::setup(400, true);
    #endif

    // initialize serial communication
    // (38400 chosen because it works as well at 8MHz as it does at 16MHz, but
    // it's really up to you depending on your project)
//    Keyboard.begin(38400);

    // initialize device
    Keyboard.print("Initializing I2C devices...");
    accelgyro.initialize();
  
    // verify connection
    Keyboard.println("Testing device connections...");
    Keyboard.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");

    // use the code below to change accel/gyro offset values
    /*
    Keyboard.println("Updating internal sensor offsets...");
    // -76  -2359 1688  0 0 0
    Keyboard.print(accelgyro.getXAccelOffset()); Keyboard.print("\t"); // -76
    Keyboard.print(accelgyro.getYAccelOffset()); Keyboard.print("\t"); // -2359
    Keyboard.print(accelgyro.getZAccelOffset()); Keyboard.print("\t"); // 1688
    Keyboard.print(accelgyro.getXGyroOffset()); Keyboard.print("\t"); // 0
    Keyboard.print(accelgyro.getYGyroOffset()); Keyboard.print("\t"); // 0
    Keyboard.print(accelgyro.getZGyroOffset()); Keyboard.print("\t"); // 0
    Keyboard.print("\n");
    accelgyro.setXGyroOffset(220);
    accelgyro.setYGyroOffset(76);
    accelgyro.setZGyroOffset(-85);
    Keyboard.print(accelgyro.getXAccelOffset()); Keyboard.print("\t"); // -76
    Keyboard.print(accelgyro.getYAccelOffset()); Keyboard.print("\t"); // -2359
    Keyboard.print(accelgyro.getZAccelOffset()); Keyboard.print("\t"); // 1688
    Keyboard.print(accelgyro.getXGyroOffset()); Keyboard.print("\t"); // 0
    Keyboard.print(accelgyro.getYGyroOffset()); Keyboard.print("\t"); // 0
    Keyboard.print(accelgyro.getZGyroOffset()); Keyboard.print("\t"); // 0
    Keyboard.print("\n");
    */

    // configure Arduino LED pin for output
    pinMode(LED_PIN, OUTPUT);
}

void loop() {
  accelgyro.initialize();
    // read raw accel/gyro measurements from device
//    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

    // these methods (and a few others) are also available
    accelgyro.getAcceleration(&ax, &ay, &az);
    //accelgyro.getRotation(&gx, &gy, &gz);

    #ifdef OUTPUT_READABLE_ACCELGYRO
        // display tab-separated accel/gyro x/y/z values
        Keyboard.print("a/g:\t");
        Keyboard.print(ax); Keyboard.print("\t");
        Keyboard.print(ay); Keyboard.print("\t");
        Keyboard.print(az); Keyboard.print("\t");
        Keyboard.print(gx); Keyboard.print("\t");
        Keyboard.print(gy); Keyboard.print("\t");
        Keyboard.println(gz);
    #endif
//
//    #ifdef OUTPUT_BINARY_ACCELGYRO
//        Keyboard.write((uint8_t)(ax >> 8)); Keyboard.write((uint8_t)(ax & 0xFF));
//        Keyboard.write((uint8_t)(ay >> 8)); Keyboard.write((uint8_t)(ay & 0xFF));
//        Keyboard.write((uint8_t)(az >> 8)); Keyboard.write((uint8_t)(az & 0xFF));
//        Keyboard.write((uint8_t)(gx >> 8)); Keyboard.write((uint8_t)(gx & 0xFF));
//        Keyboard.write((uint8_t)(gy >> 8)); Keyboard.write((uint8_t)(gy & 0xFF));
//        Keyboard.write((uint8_t)(gz >> 8)); Keyboard.write((uint8_t)(gz & 0xFF));
//    #endif

    // blink LED to indicate activity
    blinkState = !blinkState;
    digitalWrite(LED_PIN, blinkState);
    delay(5000);
}

Can anybody help?
 
I've got 3.3V and GND hooked up to the mpy6050 - that's evidently working; then I have Teensy SCL through a 5K1 resistor to SCL on the mpu6050, and Teensy SDA through a 5K1 resistor to SDA on the mpu6050. Does that sound right?

This really depends on what you mean by "through".

Can anybody help?

We can help so much more if you show a photo or accurate diagram of how you're really connected the wires & parts.

Several times we've seen a misunderstanding where the resistors were connected between the Teensy pins and the I2C device, which is why I'm asking about "through". The SDA pins are supposed to connect directly to each other, and then a resistor connects between SDA and 3.3V. Likewise for SCL, connect them together and then add a resistor to 3.3V.
 
Status
Not open for further replies.
Back
Top