New I2C library for Teensy3

I'm using Teensy 3.2; typically, I only access very simple I2C devices like RTC modules (DS1307, DS1338 and the likes).

Most of the time, I just have a single I2C device, wired to the Teensy (short wires).

I'd like to get rid of (external) resistors and only use Teensy 3.2 integrated pullups, to simplify the circuit.

How should I call i2c_t3 functions, in order to accomplish this? A short example would be great! :)

Many thanks!
 
Indications on the forum is that you cannot expect the internal pullups to work. Teensy 3.1/3.2 need external pullup on the line somewhere.
 
The pullups can work, depending on situation. The Teensy 3.x pullups are low resistance (~200 ohms), but while this value is low it can still work in most cases. I've used these pullups many times on quick breadboard setups. Where it might have problems is on long resistive bus lines if the Slave cannot pull down strong enough (cannot establish low voltage due to strong pullup).

The LC pullups on the other hand are nearly useless. They are the opposite, very resistive ~44k. They are so resistive that only the lowest signals speeds might work, if at all.

Still it costs nothing to try out either of these, just a line of code. It can be set in a couple ways, either on the initial begin() call:
Code:
Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, [B]I2C_PULLUP_INT[/B], I2C_RATE_400);
Or via pin reconfiguration command:
Code:
Wire.pinConfigure(I2C_PINS_18_19, [B]I2C_PULLUP_INT[/B]);
 
nox771:

I big THANK YOU for putting this library together. It just saved my life ;). I had just come back from an installation at a customer's site that had gone bad since there was so much electrical noise (heavy machinery and motors) in the environment that the processor kept hanging up. Little did I know that the standard Wire library does not have a timeout included. Apparently the system was catching something on the CLK line once in a while and it did cause the Teensy miss the the ACK, resulting in an eternal wait for this to happen. With your library I was able to set a timeout and while the system now once in a while misses a measurement point it at least keeps running.

By the way - in my setup I do use the internal pullups since I literally only have 1" distance to cover to feed into a P82B715TD I2C driver chip. From there I have about 15 feet to my system and on the system side I have another driver to bring the signal back down to "normal" level. Today I ran a test with a 30 foot non-shielded line between the two driver chips and still was able to securely communicate between the two stations. Mind you - I had to limit the rate setting to 600 (with the Teensy 3.1 running at 72 MHz), but that is still fast enough to get my data across in time.

Again, thanks a lot for putting this work into this library.

Wolfie
 
Hi All,
My first project with my new Teensy 3.1 is to use I2C to read a temperature sensor. I constructed the code below based on the examples, and using the library provided by nox771 (thank you!).
I checked the SDA and SCL lines with an oscilloscope. After reset they are in high imp, and after 1 sec they both go high and stay high.
The serial monitor says:
Error - Set mode: I2C timeout
Any suggestions welcome.
Thanks
Andras

The full code:
#include <i2c_t3.h>

void setup()
{
Serial.begin(9600);
ledBlink(1000);
// Setup for Master mode, pins 18/19, internal pullups, 400kHz
Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_INT, I2C_RATE_400);
Wire.setDefaultTimeout(1000);
}

void loop()
{
readTemp(0x48);
ledBlink(500);
}

void readTemp(uint8_t sensor_addr)
{
uint16_t temp =0;
Wire.beginTransmission(sensor_addr); // slave addr
Wire.write(0); //Write 0 to config register means read will return temperature value
Wire.endTransmission();
if (Wire.status()!=I2C_WAITING)
{
print_i2c_status("Error - Set mode: ");
Wire.resetBus();
return;
}
Wire.requestFrom(sensor_addr,2,I2C_STOP); // blocking read (request 2 bytes)
if (Wire.status()!=I2C_WAITING)
{
print_i2c_status("Error - Read temp: ");
Wire.resetBus();
return;
}
temp=Wire.readByte()<<8;
temp|=Wire.readByte();
temp>>=7;
Serial.printf("Temperature: %.1f \n",temp,(float)(temp) / 2);
}

void print_i2c_status(char * s)
{
Serial.print(s);
switch(Wire.status())
{
case I2C_WAITING: Serial.print("I2C waiting, no errors\n"); break;
case I2C_ADDR_NAK: Serial.print("Slave addr not acknowledged\n"); break;
case I2C_DATA_NAK: Serial.print("Slave data not acknowledged\n"); break;
case I2C_ARB_LOST: Serial.print("Bus Error: Arbitration Lost\n"); break;
case I2C_TIMEOUT: Serial.print("I2C timeout\n"); break;
case I2C_BUF_OVF: Serial.print("I2C buffer overflow\n"); break;
default: Serial.print("I2C busy\n"); break;
}
}

void ledBlink(uint16_t ms)
{
pinMode(LED_BUILTIN,OUTPUT); // LED
digitalWrite(LED_BUILTIN,HIGH); // LED on
delay(ms);
digitalWrite(LED_BUILTIN,LOW); // LED off
delay(ms);
}

 
No, I don't have external resistors. I activated the internal ones in Wire.begin, and I could also see on the scope that they are actively pulled high. I can give it a try, though I don't expect much.
Thanks
 
Hi All,
My first project with my new Teensy 3.1 is to use I2C to read a temperature sensor. I constructed the code below based on the examples, and using the library provided by nox771 (thank you!).
I checked the SDA and SCL lines with an oscilloscope. After reset they are in high imp, and after 1 sec they both go high and stay high.
The serial monitor says:
Error - Set mode: I2C timeout

It is unclear. Can you get a scope shot of the I2C traffic? You say the lines go high at 1s, which is the setup() delay. There should be some initial traffic right after that where it sends the I2C slave address 0x48. Your serial monitor only shows the above right? (there is nothing before or after?) That implies it errors on the first command. Usually this type of thing is something to do with wiring or pullups (SDA/SCL aren't cross wired or anything like that?)
 
Cross wired! Yesss! Dang, I forgot to check that earlier (I measured everything else in terms of connectivity and shortcuts...) Thank you, now it works!
 
Hi,

I am having problems running the i2c_t3 library with the following problem:

Code:
Arduino: 1.6.7 (Windows 7), TD: 1.27, Board: "Teensy 3.0, Serial, 48 MHz, US English"

C:\Users\...\Documents\Arduino\libraries\i2c_t3\i2c_t3.cpp: In static member function 'static uint8_t i2c_t3::setOpMode_(i2cStruct*, uint8_t, i2c_op_mode)':
C:\Users\...\Documents\Arduino\libraries\i2c_t3\i2c_t3.cpp:289:84: error: 'DMAMUX_SOURCE_I2C1' was not declared in this scope
i2c->DMA->triggerAtHardwareEvent((bus == 0) ? DMAMUX_SOURCE_I2C0 : DMAMUX_SOURCE_I2C1);
^
exit status 1
Error compiling.

Ah thanks, I'll look into it.

@nox771, did you ever have any luck in sorting this out? I am having the same issue. Running Latest Arduino IDE and latest Teensyduino. If so, can you elaborate wrt what is meant by, "This line should be wrapped with a preprocessor if."

Thanks,

Dylan
 
Last edited:
Sorry about that. It got pushed to the bottom of the list and forgotten (last 6 months have been hard for unrelated reasons). I'll take a look at it.

What it means is that version 3.0 of the die has no I2C1 bus (it only has I2C0), so any line referring to an I2C1 bus should be skipped (or otherwise routed around) using macro checks. Give me a little bit of time, I'll see if I can figure it out.
 
FWIW, the defines for the specific Teensy 3.x/LC's are:

  • If __MK20DX128__ is defined, it is a Teensy 3.0 (without i2c1);
  • If __MK20DX256__ is defined, it is a Teensy 3.1 or Teensy 3.2;
  • If __MKL26Z64__ is defined, it is a Teensy LC.
 
Hi,
@nox771, did you ever have any luck in sorting this out? I am having the same issue. Running Latest Arduino IDE and latest Teensyduino. If so, can you elaborate wrt what is meant by, "This line should be wrapped with a preprocessor if."

I think I've patched this, but you'll need to test it. I've pushed the update to GitHub, and I've attached a new zip to the top post.

I wasn't able to see this error when I compiled. Although trying to work with I2C1 will fail on a T3.0, the defines exist in the kinetis.h file, so I'm not sure how you got the compile to fail (although I'm not running the newest stuff, so that might explain it).
 
FWIW, the defines for the specific Teensy 3.x/LC's are:

  • If __MK20DX128__ is defined, it is a Teensy 3.0 (without i2c1);
  • If __MK20DX256__ is defined, it is a Teensy 3.1 or Teensy 3.2;
  • If __MKL26Z64__ is defined, it is a Teensy LC.

Yes the i2c_t3 lib has internal defines for the buses, however those are useful in general. That info should probably be added to the Code Tips post:
https://forum.pjrc.com/threads/25395-Teensy-Quick-Reference-Code-Examples-Tips-and-Tricks
 
Hi,

I've got an ongoing project that keeps evolving. I tried using the fast TFT for my display but it wasn't working out so now I'm experimenting with OpenVG and HDMI on a Pi A+ for the display. But my project requires hard real time too so I'm using a Teensy 3.x and my plan is to communicate by using the Pi as an i2c master and the Teensy as both an i2c master (to communicate with sensors) and as an i2c slave to the Pi.

Looking at your example code in I see that it says:

// **********************************************************************************************
// ** Note: This is a Teensy 3.1/LC ONLY sketch since it requires a device with two I2C buses. **
// **********************************************************************************************
//


but in another place I found:

Uploaded i2c_t3_lib_and_sketch_v6.zip
Added Teensy 3.1 support, including Wire1 (I2C1) interface on pins 29/30 and 26/31.
All new code structure used to maximize function sharing between the two buses. For single bus configuration the code/ram size change is minimal relative to v5 library on Teensy 3.0.
Added new header defines to control number of enabled buses and interrupt flags.
Fixed some bugs in ISR code with respect to very high speed transfers.
Added new example sketches for Teensy 3.1: quad_master and dual_bus_master_slave


Can you elaborate on this? I've got several different Teensies and if I need to I can purchase an LC and see if I can shoehorn my code into 8k. But I'm surprised that the 3.x Teensies might be providind a subset of the LC functionality, i.e. the dual i2c bus.

Thanks!
 
Looking at your example code in I see that it says:

// **********************************************************************************************
// ** Note: This is a Teensy 3.1/LC ONLY sketch since it requires a device with two I2C buses. **
// **********************************************************************************************
//


but in another place I found:

Uploaded i2c_t3_lib_and_sketch_v6.zip
Added Teensy 3.1 support, including Wire1 (I2C1) interface on pins 29/30 and 26/31.
All new code structure used to maximize function sharing between the two buses. For single bus configuration the code/ram size change is minimal relative to v5 library on Teensy 3.0.
Added new header defines to control number of enabled buses and interrupt flags.
Fixed some bugs in ISR code with respect to very high speed transfers.
Added new example sketches for Teensy 3.1: quad_master and dual_bus_master_slave


Can you elaborate on this? I've got several different Teensies and if I need to I can purchase an LC and see if I can shoehorn my code into 8k. But I'm surprised that the 3.x Teensies might be providind a subset of the LC functionality, i.e. the dual i2c bus.

The Teensy 3.0 has a single I2C bus (Wire - SDA/SCL on the pinout diagram). Refer to the pin diagrams here:
http://pjrc.com/teensy/pinout.html

The 3.1 and 3.2 devices have two I2C buses (Wire on SDA0/SCL0, and Wire1 on SDA1/SCL1), however Wire1 connections are only available on the backside surface mount pads.

The LC device also has two buses (Wire on SDA0/SCL0, and Wire1 on SDA1/SCL1), but they are both accessible on the main thru-hole connections. However LC runs at a lower internal bus frequency, so this limits the top speed you can use on I2C (still overclocked with respect to I2C spec, but not quite as fast as 3.2 device).

This is described in more detail in the "Description" and "Clocking" sections of the 1st post in this thread.
 
10K vs 2k2 pullup risetime

On a Teensy 3.1 or 3.2 with a single TMP102 temp sensor, with 10K pullups I measure about 5 usec risetime, and less than 20 nsec fall time. This is way too slow. The standard Wire library often fails, leaving SCL and/or SDA low. I wonder if it is partly because of the slow rise on clock contributing to missed data bits? With 2k2 pullups, risetime is about 500 nsec - 10X faster. This is in a white breadboard with 24" of 100-mil ribbon cable between Teensy and TMP102. I also get I2C lockups with much shorter prototype wires between a Teensy and Adafruit 14- or 7-segment display using the HT16K33 backpack. These lockups are what led me here to a hopefully more robust library.
 
Last edited:
On a Teensy 3.1 or 3.2 with a single TMP102 temp sensor, with 10K pullups I measure about 5 usec risetime, and less than 20 nsec fall time. This is way too slow. The standard Wire library often fails, leaving SCL and/or SDA low. I wonder if it is partly because of the slow rise on clock contributing to missed data bits? With 2k2 pullups, risetime is about 500 nsec - 10X faster. This is in a white breadboard with 24" of 100-mil ribbon cable between Teensy and TMP102. I also get I2C lockups with much shorter prototype wires between a Teensy and Adafruit 14- or 7-segment display using the HT16K33 backpack. These lockups are what led me here to a hopefully more robust library.

High resistance pullups can certainly cause loss of data if the edge rate is too slow. I would recommend lower resistance as you mention. I've used < 1k on occasion.

The only thing to check there is if your low-levels are low enough to register as logic-low, which is more a problem with long wires between pullup and slave (or weak slave pulldown) - essentially since pulldowns are active, you are weighting your effective pulldown strength against your pullup strength to obtain your resulting voltage.

If you have a scope or logic analyzer that would be best for verifying communication, otherwise you will just have to gauge lockups/corruption with respect to different pullup values. If your program checks return values and such for errors that can also help isolate problems.
 
I think I've patched this, but you'll need to test it. I've pushed the update to GitHub, and I've attached a new zip to the top post.

I wasn't able to see this error when I compiled. Although trying to work with I2C1 will fail on a T3.0, the defines exist in the kinetis.h file, so I'm not sure how you got the compile to fail (although I'm not running the newest stuff, so that might explain it).

Hi nox771,

Sorry for my VERY late response, I have been pulled away from this project for a while but I am back! Thank you very much for patching your library, you are a life saver!

If ever we meet, beerware is in order! :D

Kind regards,

Dylan
 
If you have a scope or logic analyzer that would be best for verifying communication, otherwise you will just have to gauge lockups/corruption with respect to different pullup values. If your program checks return values and such for errors that can also help isolate problems.

Agreed, I have a scope and also a Totalphase Beagle I2C and SPI analyzer.

Amen to the checking return values. It's amazing how much open source driver and demo app code does not do that! Astonishingly, the Adafruit 7- and 14-segment display example code never checks anything. If the display is completely missing, the demo application runs fine and reports no errors! My mind is boggled.
 
Astonishingly, the Adafruit 7- and 14-segment display example code never checks anything. If the display is completely missing, the demo application runs fine and reports no errors! My mind is boggled.
Hacking for a demo v.s. software engineering.
 
Back
Top