New I2C library for Teensy3

Sorry but the i2c_t3 library is only for the Teensy 3.0/3.1 devices. It won't work on AVR parts at all.

Not sure on your problem without more diagnostics of what is going on. Things that can hang the bus are if it thinks it is busy (SCL held low), or if it is blocked from sending a START/STOP (either SDA or SCL held low). This can happen if the Master/Slaves get out of sync due to missed clocks (bad wiring, bad pullups, etc).

Another way a Slave can be made to hang the bus is if a Master reads from it, but does not end the read with a NAK. If the Slave outputs a low on SDA as its last output then the bus will hang due to inability of Master to send START/STOP. A poorly made Slave I2C could possibly be made to do this also if it were made to transmit blocks of X bytes, but the Master terminated the read after reading less than X bytes. If you have access to a logic analyzer then a capture might indicate what is going on. If not, then try testing the SDA/SCL to see if either is held low when it gets stuck.
 
That library only works on Atmel chips so will work on a Teensy++2 but not on a Teensy 3.x. It is in fact a very nice library and I've used in previous projects. When you start running out of libraries it's time to really start analyzing the problem
;-)
 
Unfortunately, I don't have a logic analyzer.;) Is there no I2C library common to Teensy 3.1 and AVR besides Wire.h? I was able run the LSM9DS0 sensor on both platforms with a sketch that uses the Wire.h library, which is why this glitch on the AK8975A is so frustrating. I'll keep poking around with Wire.h and trying to get it to work. Thanks for your generous help!
 
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.
 
I think your problem has to do with whoever named the original Wire library function calls so poorly. The problem you are having may be related to the endTransmission() call depending on how the library functions internally. If it does not clear the Tx buffer after a send, or maintain the Tx buffer index across calls then if you call endTransmission() twice it will send the Tx buffer twice.

Let me explain the function calls more plainly:
  • beginTransmission() - means initialize the Tx buffer
  • write() - means fill up the Tx buffer with stuff
  • endTransmission() - means send the Tx buffer (note what it does NOT mean - anything to do with Rx)
  • requestFrom() - read stuff from a slave and fill a Rx buffer with it
This is quite common, people setup Rx functions and then end it with endTransmission(), but it's wrong, Rx functions do not use endTransmission(). It's not your fault, I blame this entirely on the poor naming of the function call.

Looking at your functions below, try removing the red sections. What may be happening is that the Tx buffer is getting re-sent and that is causing device confusion in your Slaves.

Code:
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);
[COLOR=#008000]    while (Wire.available() < 1) // Wait until data becomes available
        ;
[/COLOR]    [COLOR=#008000]data = Wire.read(); // Read register data into `data` variable[/COLOR]
    [COLOR=#ff0000]//Wire.endTransmission(); // End I2C transmission  <-- remove this[/COLOR]

    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
[COLOR=#008000]    while (Wire.available() < count);[/COLOR]
    [COLOR=#008000]// Store all `count` bytes into the given destination array.
    for (int i=0; i<count ;i++)
        dest[i] = Wire.read();[/COLOR]  
    // End I2C Transmission
    [COLOR=#ff0000]//Wire.endTransmission();  <-- remove this[/COLOR]
}

Also, the green sections are wrong for a few reasons:
  • The requestFrom() is a blocking call, it will either fill a Rx buffer or it won't (if it errors out). There is no need to wait by looping on Wire.available().
  • Also in the multi-byte read, this code assumes that the full count was received, but that may not occur:
Code:
[COLOR=#008000]while (Wire.available() < count);[/COLOR]
if it does not occur it will hang forever waiting for Wire.available() to be equal to count.

  • In both routines, the subsequent code after requestFrom() assumes the Rx buffer was filled, and then stuffs whatever was in it into the return value (or array). If the call errors out the Rx buffer could be partially filled or not filled at all. How this is dealt with varies from program to program, but at minimum what you should do is only fill the return value with whatever was received and do not block waiting for a full return, something like this:
Code:
    int i=0;    
    Wire.requestFrom(address, count);
    while(Wire.available());
        dest[i++] = Wire.read();  // note this makes the assumption dest[] size is always greater than or equal to count, or it could buffer overrun
 
Thank you for a very clear explanation. I took these functions from Jim Lindblom's LSM9DS0 sketch which works well on both the Arduino and Teensy platforms. That device, like the MPU-9150, has two slaves embedded in one sensor but it must rely on different protocols since it seems to run fine with this coding. I will try your suggestions and let you know how it works. Thank you very much for your help!
 
I changed the Wire.h routines to the following at your suggestion:

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);
	data = Wire.read(); // Read register data into `data` variable
	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
	int i=0;
        Wire.requestFrom(address, count);
	while (Wire.available()) {
        dest[i++] = Wire.read(); }
	// End I2C Transmission
}

The accelerometer and gyro readBytes work fine as before, but I am still not able to read the magnetometer without single readByte calls. Did I mis-interpret your suggestions? Thanks again for the help.
 
Perhaps I'm missing something here, I pulled a datasheet for a AK8975, and I found the following section on multi-byte I2C (click to expand):

screenshot.322.jpg

screenshot.323.jpg

Is this the right part? I don't see anything about the 0x80 address modification below. Where is that coming from?

Code:
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. [COLOR=#ff0000]OR with 0x80 to indicate multi-read[/COLOR].
    Wire.write(subAddress[COLOR=#ff0000] | 0x80[/COLOR]);
    // End write, but send a restart to keep connection alive:
    Wire.endTransmission(false);
    // Request `count` bytes of data from the device
    int i=0;
        Wire.requestFrom(address, count);
    while (Wire.available()) {
        dest[i++] = Wire.read(); }
    // End I2C Transmission
}

The way these things usually go, is that if the low-level routines work (as demonstrated by talking to the other components), then there is some higher level comm problem (eg. reading 1 byte of a 3-byte register, parsing multi-byte data backwards out of the buffer, etc). If the 0x80 address modification is necessary can you supply a link to a datasheet showing that? I don't think I've ever seen a part that worked that way.
 
I just blindly (an dumbly, it turns out) copied the wire routines from the LSM9DS0 sketch (https://github.com/sparkfun/LSM9DS0...Libraries/Arduino/SFE_LSM9DS0/SFE_LSM9DS0.cpp). In that device's data sheet (https://cdn.sparkfun.com/assets/f/6/1/f/0/LSM9DS0.pdf) we find:

6.1.1 I
2
C operation
The transaction on the bus is started through a START (ST) signal. A START condition is
defined as a HIGH to LOW transition on the data line while the SCL line is held HIGH. After
this has been transmitted by the master, the bus is considered busy. The next byte of data
transmitted after the start condition contains the address of the slave in the first 7 bits and
the eighth bit tells whether the master is receiving data from the slave or transmitting data to
the slave. When an address is sent, each device in the system compares the first seven bits
after a start condition with its own address. If they match, the device considers itself
addressed by the master.
Data transfer with acknowledge is mandatory. The transmitter must release the SDA line
during the acknowledge pulse. The receiver must then pull the data line LOW so that it
remains stable low during the HIGH period of the acknowledge clock pulse. A receiver
which has been addressed is obliged to generate an acknowledge after each byte of data
received.
The I
2
C embedded inside the LSM9DS0 behaves like a slave device and the following
protocol must be adhered to. After the start condition (ST) a slave address is sent, once a
slave acknowledge (SAK) has been returned, an 8-bit sub-address (SUB) will be
transmitted: the 7 LSb represents the actual register address while the MSB enables the
address auto increment. If the MSb of the SUB field is ‘1’, the SUB (register address) will be
automatically increased to allow multiple data read/writes.

I removed the 0x08 OR and, of course, the multiple byte read now works. Thank you very much for the education. I have usually gotten away with copying commo routines; in the future I will pay more attention to the details, which apparently, and no surprise, matter!

I would still like to try your logic analyzer if you are still interested.
 
+1 That sounds very intriguing
Yeah I shouldn't have mentioned it. I have a prototype which can do I2C traffic sniffing, but it is part of a larger project which isn't finished. When I get something which is presentable I'll start a new thread on it.
 
Good library!
For a better name that users can relate to "FastWire.h" After all, you did increase the bit-rate up pass the normal bit-rate values.

Definitely prefer "FastWire" for the library name. Am currently using this library in my TeensyNet Project, and will be using "FastWire" for my library. Already tested both I2C channels with 24LC512s and MCP23017s.
 
if you name it "WireFast" or some other name where the first 4 characters are "Wire", then it'll show up next to Wire in the File > Examples menu, and in alphabetically ordered lists on websites. Seems like that might help people see there's an alternative.
 
This is like the topic that won't die. Ok fine, I really don't have a strong opinion on the name (you guys do realize this only affects one #include line right? Usage is still Wire/Wire1).

I'll roll in wireFast on the next update, which will come out as soon as I figure out all the priority stuff.
 
I am using this library to talk with a IR sensor. Everything does work fine on the standard I2C pins 18 + 19.
However, if I want to use pin 29 & 30 on my teensy 3.1 instead (SDA to 30, SCL to 29), the sensor is not responding any more.
What I did on software side is to switch pin mode inside the constructor from "I2C_PINS_18_19" to " I2C_PINS_29_30" and change "Wire" against "Wire1".
Have I missed something, like altering pin registers anywhere else in order two activate I2C on 29 & 30 ?
 
I2C on pins 29 & 30

I am using this library to talk with a IR sensor. Everything does work fine on the standard I2C pins 18 + 19.
However, if I want to use pin 29 & 30 on my teensy 3.1 instead (SDA to 30, SCL to 29), the sensor is not responding any more.
What I did on software side is to switch pin mode inside the constructor from "I2C_PINS_18_19" to " I2C_PINS_29_30" and change "Wire" against "Wire1".
Have I missed something, like altering pin registers anywhere else in order two activate I2C on 29 & 30 ?
 
I am using this library to talk with a IR sensor. Everything does work fine on the standard I2C pins 18 + 19.
However, if I want to use pin 29 & 30 on my teensy 3.1 instead (SDA to 30, SCL to 29), the sensor is not responding any more.
What I did on software side is to switch pin mode inside the constructor from "I2C_PINS_18_19" to " I2C_PINS_29_30" and change "Wire" against "Wire1".
Have I missed something, like altering pin registers anywhere else in order two activate I2C on 29 & 30 ?

I would check the following:


  • Since Wire1 uses the SMT pads on the back, make sure the electrical connection to the pads is good. The SMT pads are easy to damage, so make sure not to twist or pull the wires.
  • If you are using external pullups, make sure they are connected to pins 29/30 also. It will not work without pullups. Make sure the pullup voltage is correct.
  • Make sure to change all Wire to Wire1, including Wire1.begin(), and all later function calls. If you are using both Wire and Wire1, make sure they both have setup(), and double check the function calls.
  • Use a voltmeter to verify pullup voltages on pins 29/30. If you have a logic analyzer or scope, use it to verify traffic on pins 29/30.
  • This is obvious, but make sure it is not a Teensy 3.0 (it does not have a Wire1 interface).
  • Check the error results of the function calls. If the connection is electrically bad the functions will error out or hang (the bus can hang if the pullups are missing).
  • If the calls appear to work, it could be that the slave is NAK'ing, or otherwise non-responsive. If the code is not setup to detect bad responses (eg. assumes all responses are correct), then it won't be immediately obvious. Again, check error results and verify response lengths from the Slaves.

If the code changes are correct then there is likely some electrical connection problem.
 
Just saw this and is soooo nice :D

Is it Still up? I got the 6b version that seems like the latest and I'm gonna try it out now. Amazing! Why you didn't host it on google code?
 
Yes the download links should work. In the next round of updates I might put it on github, but at the moment it is only hosted on the forum. Version 6b is currently the latest. I have some updates planned, but nothing newer is ready just yet.
 
Back
Top