Wire on Teensy 3.1

Status
Not open for further replies.

onehorse

Well-known member
I rewrote an Arduino sketch that used a simple I2C.h TWI library to use the Wire.h library instead. The sketch doesn't work on the Teensy 3.1 with the I2C.h library. When I tried to use it on the Arduino, the device kept cycling when it hit the wire.begin() and never got into the void loop. Does this have something to do with the 10 k pull-up resistors on the sensor breakout board I am using? I know there is some issue with Wire.h and external pullups but I really don't understand it. Should I expect the Wire.h library to work better with the Teensy 3.1? I guess I should have tried it before posting, sorry. I want to be able to run sketches both on the Arduino 3.3 V 8 MHz Pro Mini and the Teensy 3.1. Which I2C library is recommended for such compatibility? Thanks for your help!
 
Wire.h works fine on my Teensy 3.0's for the devices I've tested it with (Adafruit MCP23008/MCP23017, random i2c 16x2 LCD, 0.96" Oled display, PCF8591). Note, I do have 4.7K pull-up resistors installed for both SDA (A4) and SCL (A5), as was recommended by Paul S. a year ago. With the resistors in place on the Teensy, I run the same code on my 3.3v Teensy 3.0 and my 5v Arduino Uno R3 (all of my devices work with both voltages, though for the LCD, I need to feed it 5v for the backlight).
 
Thanks for the reply Michael. I will try Wire.h on the Teensy. It certainly must work since that is what I used to talk to the LSM9DS0. Even though I am using a 3.3V Pro Mini, do I have to disable the internal SDA and SCL pullups when using Wire.h? Do you have any idea what might be causing the repeated cycling behavior? It was almost as if the reset were being set low repeatedly. All of the sensors I2C slaves have 10k pullups on the breakout boards. Do I still need 4k7 external pullups on the I2C wires with the Teensy?
 
On the Teensy 3.x, I don't believe there are any internal SDA/SCL pullups (that's why you need the external pullups). Various AVR processors do have the pullups, and the library enables them by default on those systems. There is an alternative i2c library for the Teensy that is faster, and allows you to have more control, but so far, I haven't really used it. IMHO, the Arduino IDE makes it hard to use separate libraries based on processor #ifdef's, as it tries to protect the user from having to learn how to properly setup forward references in C++.
 
Thanks for your advice on the uint32_t type. That did the trick. I rewrote the I2C for Wire.h and the sketch works on both the 3.3V 8 MHz Pro Mini and the Teensy 3.1. Unfortunately on both I can no longer read from or write to the magnetometer even though I can read from its WHO_AM_I register. I set the digitalWrite(SDA, LOW) and digitalWrite(SCL, LOW) to disable internal pullups (even though the Teensy has none). Is there something about the Wire.h data speed that would prevent me reading from a slave I2C device in a bypass mode through another slave device?
 
Last edited:
I rewrote an Arduino sketch that used a simple I2C.h TWI library to use the Wire.h library instead. The sketch doesn't work on the Teensy 3.1 with the I2C.h library.

Can you post a link to where you downloaded this library?

I know there are several still out there with AVR stuff hard-coded. Since Wire and i2c_t3_lib work great, fixing those lesser-used I2C libs is a lower priority. But I'll at least put it on my list of libraries to someday patch.



Unfortunately on both I can no longer read from or write to the magnetometer even though I can read from its WHO_AM_I register.

If you can post the code, maybe someone will be able to see what's wrong?


I set the digitalWrite(SDA, LOW) and digitalWrite(SCL, LOW) to disable internal pullups (even though the Teensy has none).

On Teensy 3.1, the pins get configured for which thing they connect to inside the chip. When configured for I2C, the GPIO has no effect on the pins. Using digitalWrite does nothing to the pin if I2C has control of it. Using pinMode will reconfigure the pin to connect to GPIO, taking it away from the I2C port.

If you're used to AVR, where GPIO still has some effect on the pins even when they're in use by a peripheral, this might seem strange. But pretty much all modern chips work this way, where you configure which thing inside the chip controls the pin. When the pin is connect to the I2C, it can't be accessed as GPIO.
 
Excellent article. Thanks. I still don't know why the I2C.h library I used successfully on the Pro Mini doesn't work (won't compile) on the Teensy 3.1. I'll post a copy of it as per Paul's request.

Also, I believe the failure to communicate with the AK8975A has to do with the lack of support in Wire.h for multiple restarts, which I really don't understand yet.

Thanks for the replies and the help everyone!
 
Last edited:
Hi Paul,

You can find the I2C.h library I am currently using here:

https://github.com/kriswiner/MMA8452Q

This is an Arduino sketch I wrote for the 3.3 V 8 MHz Pro Mini. When I use the same I2C TWI for the MPU-9150 sensor, it works fine on the Pro Mini, but the Teensy complains that IIRC TWBR is undefined in this scope. This could just mean that I need to define it as uint8_t or something. I haven't played around with it yet.

I have run into some discussions that seem to indicate the AK8975A requires a multiple restart capability unsupported by Wire.h. I need to recheck the data sheet. Is this the reason I can't talk to this sensor with Wire.h on the Pro Mini (or Teensy) but I2C.h works just fine? Sorry for the newbie questions. If its any consolation to the valuable time you are spending to help me, I am learning a lot! Thanks again for your help.
 
Yup, that i2c.h file is hard-coded for AVR chips. For example:

Code:
void i2cSendStop(void)
{
// transmit stop condition
        TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}

All the code in that file will need to be rewritten. That's unlikely to happen anytime soon, if ever, but I'll put this one on my list of incompatible libraries to someday look into....

Code:
I have run into some discussions that seem to indicate the AK8975A requires a multiple restart capability unsupported by Wire.h

Long ago, Wire didn't support repeated start. It was added a couple years ago.
 
The Invensense MPU-9150 (and -9250) is a 9DoF motion sensor that combines a 6 DoF gyro/accelerometer, the MPU-6050, with an Asahi Kasei AK8975A hall-sensor magnetometer into a single device. The microcontroller can access the magnetometer directly by placing the combined MPU-9150 device into a bypass mode through a CNTL register write that connects the internal AK8975A device to the MPU-9150 TWI bus. It is then directly addressed through its own 7-bit device address just like any other I2c device on the bus. This works perfectly well for the simple I2c.h and I2cdev.h TWI libraries but for some reason, when using Wire.h, the AK8975A is no longer accessible after the initial read of its WHO_AM_I register. This latter behavior is seen on both a 3.3V 8 MHz Pro Mini and on the Teensy 3.1. Does anyone have an idea what could be happening here?
 
Last edited:
Aha. It is very device specific. Paul has mentioned an alternative I2C library specifically for Teensy3.1
While using that library may not automatically address your concern I suggest posting your question in that libraries specific thread
The author of that library is user nox771 and he appears to have a wealth of knowledge in respect to I2C (amongst other things). He may be able to answer your question.
 
Just an update on Wire.h with Teensy 3.1 (and 3.3 V 8 MHz Pro Mini Arduino). I replace the multiple byte read with several single byte reads and now everything works. This is passing strange since the multiple byte read seems to be getting data from one I2C slave but not the other. I suspect it has to do with having enough delay time to get all the data but I don't understand this library wellenough to know how to test that idea. I'm hoping someone with more experience can see what the problem is now that I have a little more info.

Here is the byte read part of the code I am using:
Code:
  if(readByte(AK8975A_ADDRESS, AK8975A_ST1) && 0x01) { // wait for magnetometer data ready bit to be set
//  readBytes(AK8975A_ADDRESS, AK8975A_XOUT_L, 6, &rawData[0]);  // Read the six raw data registers sequentially into data array
  rawData[0] = readByte(AK8975A_ADDRESS, AK8975A_XOUT_L);
  rawData[1] = readByte(AK8975A_ADDRESS, AK8975A_XOUT_H);
  rawData[2] = readByte(AK8975A_ADDRESS, AK8975A_YOUT_L);
  rawData[3] = readByte(AK8975A_ADDRESS, AK8975A_YOUT_H);
  rawData[4] = readByte(AK8975A_ADDRESS, AK8975A_ZOUT_L);
  rawData[5] = readByte(AK8975A_ADDRESS, AK8975A_ZOUT_H);
  destination[0] = (int16_t)((rawData[1] << 8) | rawData[0]) ;  // Turn the MSB and LSB into a signed 16-bit value
  destination[1] = (int16_t)((rawData[3] << 8) | rawData[2]) ;  
  destination[2] = (int16_t)((rawData[5] << 8) | rawData[4]) ;


and here are the readByte calls to the Wire.h library:

Code:
void writeByte(uint8_t address, uint8_t subAddress, uint8_t data)
{
	// Begin transmission at device write address
	Wire.beginTransmission(address);
	Wire.write(subAddress); // Write register to be written to
	Wire.write(data); // Transmit byte to write
	Wire.endTransmission(); // End I2C transmission
}

uint8_t readByte(uint8_t address, uint8_t subAddress)
{
	uint8_t data; // `data` will store the register data
	// Begin I2C transmission using device write address
	Wire.beginTransmission(address); 
	// Write the register to be read:
	Wire.write(subAddress);	
	// End write, but send a restart to keep connection alive:
	Wire.endTransmission(false);
	// Transmit device read address:
	Wire.requestFrom(address, (uint8_t) 1);
	while (Wire.available() < 1) // Wait until data becomes available
		;
	data = Wire.read(); // Read register data into `data` variable
	Wire.endTransmission(); // End I2C transmission

	return data; // Return data from register
}

void readBytes(uint8_t address, uint8_t subAddress, uint8_t count, uint8_t * dest)
{  
	// Begin I2C transmission and send device address
	Wire.beginTransmission(address);
	// Next send the register to be read. OR with 0x80 to indicate multi-read.
	Wire.write(subAddress | 0x80);
	// End write, but send a restart to keep connection alive:
	Wire.endTransmission(false);
	// Request `count` bytes of data from the device
	Wire.requestFrom(address, count);
	// Wait until the data has been read in
	while (Wire.available() < count)
		;
	// Store all `count` bytes into the given destination array.
	for (int i=0; i<count ;i++)
		dest[i] = Wire.read();
	// End I2C Transmission
	Wire.endTransmission();
}

Would a delay inserted into the readBytes function cure this problem?

Thanks for your help.

PS: This problem has been resolved. It has to do with poorly constructed Wire.h calls. See this thread if interested:

http://forum.pjrc.com/threads/21680-New-I2C-library-for-Teensy3
 
Last edited:
Status
Not open for further replies.
Back
Top