Home

Awesome

i2c_t3

Enhanced I2C library for Teensy 3.x & LC devices

This is an enhanced I2C library for Teensy 3.x/LC devices.

Recent discussion and a usage summary can be found in the PJRC forums here.



Description

This library is designed to operate from the Arduino/Teensyduino development system. However this is not strictly required as the files can be used independently. Recent releases of the library are bundled with the Teensyduino software available here. Follow the instructions on that page for installation.

The library can also be downloaded separately (eg. for updates), and used by unpacking the library contents into your sketchbook/libraries folder.

To use with existing Arduino sketches, simply change the #include <Wire.h> to #include <i2c_t3.h>

Example sketches can be found in the Arduino menus at: File->Examples->i2c_t3

The latest version of the library provides the following:



Pins

Some interfaces have multiple sets of pins that they can utilize. For a given interface only one set of pins can be used at a time, but for a device configured as a bus Master the pins can be changed on-the-fly when the bus is idle.

In functions that require a pin specification there are two ways to specify it. One is to use the pin enum as shown in the table below under "Pin Name". This will restrict the pin choices to the listed pin pairings. The other method is to specify the SCL, SDA pins directly (in that order), using any valid SCL or SDA pin given the device type and interface used. If pins are not given on initial setup then defaults are used as indicated below (based on device type and bus).

As an example the following functions are all valid:

Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000); // Wire bus, SCL pin 19, SDA pin 18, ext pullup, 400kHz 
Wire.begin(I2C_MASTER, 0x00, 19, 18); // equivalent to above, will default to ext pullup at 400kHz
Wire.begin(I2C_MASTER, 0x00, 16, 18); // similar to above, but using SCL pin 16 and SDA pin 18

The mapping of device types, available pins, and interfaces is as follows. Note that these are not physical pin numbers, they refer to the Teensy pin assignments, which can be viewed here: https://www.pjrc.com/teensy/pinout.html

Interface  Devices     Pin Name      SCL    SDA   Default
---------  -------  --------------  -----  -----  -------  
   Wire      All    I2C_PINS_16_17    16     17             
   Wire      All    I2C_PINS_18_19    19*    18      +
   Wire    3.5/3.6  I2C_PINS_7_8       7      8
   Wire    3.5/3.6  I2C_PINS_33_34    33     34
   Wire    3.5/3.6  I2C_PINS_47_48    47     48
  Wire1       LC    I2C_PINS_22_23    22     23      +
  Wire1    3.1/3.2  I2C_PINS_26_31    26     31
  Wire1    3.1/3.2  I2C_PINS_29_30    29     30      +
  Wire1    3.5/3.6  I2C_PINS_37_38    37     38      +
  Wire2    3.5/3.6  I2C_PINS_3_4       3      4      +
  Wire3      3.6    I2C_PINS_56_57    57*    56      +

Note: in almost all cases SCL is the lower pin #, except cases marked *

On some devices the pins for the 2nd and higher number buses (Wire1, Wire2, Wire3) may reside on surface mount backside pads. It is recommended to use a breakout expansion board to access those, as the pads are likely not mechanically robust, with respect to soldered wires pulling on them. There are a number of breakout boards for this purpose such as these:



Pullups

The I2C bus is a two-wire interface where the SDA and SCL are active pulldown and passive pullup (resistor pullup). When the bus is not communicating both line voltages should be at the high level pullup voltage.

The pullup resistor needs to be low-enough resistance to pull the line voltage up given the capacitance of the wire and the transfer speed used. For a given line capacitance, higher speed transfers will necessitate a lower resistance pullup in order to make the rising-edge rate faster. Generally the falling-edge rates are not a problem since the active pulldowns (typically NMOS) are usually quite strong. This article illustrates the effect of varying pullup resistance: http://dsscircuits.com/articles/86-articles/47-effects-of-varying-i2c-pull-up-resistors

However, if an excessively low resistance is used for the pullups then the pulldown devices may not be able to pull the line voltage low enough to be recognized as an low-level input signal. This can sometimes occur if multiple devices are connected on the bus, each with its own internal pullup. TI has a whitepaper on calculating pullup resistance here: http://www.ti.com/lit/an/slva689/slva689.pdf

In general, for a majority of simple I2C bus configurations a pullup resistance value in the range of 2k to 5k Ohms should work fine.

Teensy Pullups

Due to the situation with internal pullups, it is recommended to use external pullups for all devices in all cases (except in special cases for the 3.0/3.1/3.2 devices).

Regarding the Teensy devices, the library provides an option to use either internal pullups or external pullups (by specifiying I2C_PULLUP_INT or I2C_PULLUP_EXT on the bus configuration functions). For most cases external pullups, I2C_PULLUP_EXT, is the preferred connection simply because it is easier to configure the bus for a particular resistance value, and for a particular pullup voltage (not necessarily the same as the device voltages, more below). Note, when using external pullups all devices should be configured for external.

That said, sometimes internal pullups, I2C_PULLUP_INT, are used to simplify wiring or for simple test scenarios. When using internal pullups, generally only one device is configured for internal (typically the Master), and Slave devices are configured for external (since they rely on the Master device to pullup). It is possible to have multiple devices configured for internal on the same bus, as long as the aggregate pullup resistance does not become excessively low (the resistances will be in parallel so the aggregate will be less than the lowest value).

The internal pullup resistances of the Teensy devices are as follows:

None of these internal pullups is a particularly good value.

The Teensy 3.0/3.1/3.2 value of ~190 Ohms is very strong (it is believed to be a HW bug), however in most cases it can work fine on a short bus with a few devices. It will work at most any speed, including the max library speeds (eg. breadboard with 3.0/3.1/3.2 device and a few Slave devices usually works fine with internal pullups). That said, multiple devices configured for internal pullups on the same bus will not work well, as the line impedance will be too low. If using internal pullups make sure at most one device is internal and the rest are external.

On the other hand, the Teensy LC value of ~44k Ohms is very weak. An LC configured for internal will have trouble running at high speeds in all configurations.

The Teensy 3.6 internal pullup is essentially a short, and is unusable.

Pullup Voltages

Some consideration should be given when connecting 3.3V and 5V devices together on a common I2C bus. The bus voltage should be one or the other, and there should not be multiple pullups connecting to different voltages on a single line.

The voltage tolerance is as follows:

Voltage    Devices
-------  -----------
  3.3V   3.0, 3.6, LC
  5.0V   3.1, 3.2, 3.5

Sometimes devices supplied at 5V will communicate fine if the I2C bus is at 3.3V, because the logic high/low thresholds are biased towards ground more than supply. However if a 5V device truly requires a 5V I2C signal, whereas other devices on the bus require 3.3V signal, there is a method to accomplish this.

To connect 5V devices to 3.3V tolerant Teensy or to connect multiple voltage level I2C buses, refer to the following app note by NXP: http://www.nxp.com/documents/application_note/AN10441.pdf

There are also many bidirectional I2C level-shifter ICs and breakout boards on the market which can simplify building such connections. Many implement exactly what is shown in the NXP app note.



Clocking

The library now supports arbitrary I2C clock rate frequencies, which can be specified directly, eg. 400000 for 400kHz. The I2C clock rate is set via a divide ratio from the F_BUS frequency (except for Wire1 bus on LC device which uses F_CPU). There is a fixed list of divide ratios available, and the library will choose the nearest available ratio when attempting to produce a requested I2C rate.

The maximum I2C rate is 1/20th of F_BUS. Some examples relating F_CPU, F_BUS, and max I2C rate are below (actual device configuration depends on compile settings):

     F_CPU      F_BUS     Max I2C
     (MHz)      (MHz)       Rate
 -------------  -----    ----------
    240/120      120        6.0M    bus overclock
      216        108        5.4M    bus overclock
     192/96       96        4.8M    bus overclock
      180         90        4.5M    bus overclock
      240         80        4.0M    bus overclock
   216/144/72     72        3.6M    bus overclock
      192         64        3.2M    bus overclock
  240/180/120     60        3.0M
      168         56        2.8M
      216         54        2.7M
 192/144/96/48    48        2.4M
       72         36        1.8M
       24         24        1.2M
       16         16        800k
        8          8        400k
        4          4        200k
        2          2        100k

Previous library releases used I2C_RATE_xxxx enums. This is still supported, but is now deprecated, and specifying the frequency directly (as a uint32_t value) is now the preferred method.

Allowable I2C_RATE_xxxx enum list is as follows:

Note that at high speeds the specified clock is not necessarily equivalent to actual SCL clock speeds. The peripheral limits the actual SCL speeds to well below the theoretical speeds (both in terms of actual bit clock frequency and throughput rates).

To get a better idea of throughput the transfer time for a 128 byte transfer across different F_CPU/F_BUS/I2C Rate combinations has been measured on a Teensy 3.1 device. This behavior generally applies to all devices. This is shown below.

I2C Speed Test



Operational Modes

There are three modes of operation: Interrupt, DMA, and Immediate. The operating mode of the I2C can be set in the begin() or setOpMode() functions, using the opMode parameter which can have the following values:

Interrupt mode is the normal default mode (it was the only mode in library versions prior to v7). It supports both Master and Slave operation. The two other modes, DMA and Immediate, are for Master operation only.

DMA mode requires an available DMA channel to operate. In cases where DMA mode is specified, but there are no available channels, then the I2C will revert to operating in Interrupt mode.

Similarly, for Interrupt mode to work the I2C ISRs must run at a higher priority than the calling function. Where this is not the case, the library will first attempt to elevate the priority of the I2C ISR to a higher priority than the calling function. If that is not possible then it will revert to operating in Immediate mode.



Example List

Examples are divided into two categories, basic and advanced. Basic examples are demonstrate basic "Arduino-like" function of the library. Advanced examples demonstrate more complex scenarios, such as multi-bus, concurrent Master/Slave, and background transfer (ISR or DMA) operations.



Header Defines

These defines can be modified at the top of the i2c_t3.h file.



Function Summary

The functions are divided into the following classifications:


Wire.begin(); - initializes I2C as Master mode, external pullups, 100kHz rate, and default pin setting


Wire.begin(address); - initializes I2C as Slave mode using address, external pullups, 100kHz rate, and default pin setting


Wire.begin(mode, address1, ^(pins_enum | pinSCL,pinSDA), ^pullup, ^rate, ^opMode);
Wire.begin(mode, address1, ^address2, ^(pins_enum | pinSCL,pinSDA), ^pullup, ^rate, ^opMode); - these various forms initialize I2C as a Master or Slave device. When two addresses are used it will initialize an address-range Slave. Addresses are ignored for Master mode (however Master-mode must specify at least one 0x00 address placeholder to also specify pins/pullup/rate/opMode options).


Wire.setOpMode(opMode); - this configures operating mode of the I2C as either Immediate, ISR, or DMA. By default Arduino-style begin() calls will initialize to ISR mode. This can only be called when the bus is idle (no changing mode in the middle of Tx/Rx). Note that Slave mode can only use ISR operation.


Wire.setClock(i2cFreq); - reconfigures I2C frequency divider to get desired I2C freq.


Wire.getClock(); - return current I2C clock setting (may differ from set frequency due to divide ratio quantization)


Wire.setRate(busFreq, rate); - reconfigures I2C frequency divider based on supplied bus freq and desired rate. Rate is specified as a direct frequency value in Hz. The function will accept I2C_RATE_xxxx enums, but that form is now deprecated.


Wire.pinConfigure( (pins_enum | pinSCL,pinSDA), ^pullup); - reconfigures active I2C pins on-the-fly (only works when bus is idle). Inactive pins will switch to input mode.


Wire.setSCL(pin); - change the SCL pin
Wire.setSDA(pin); - change the SDA pin


Wire.getSCL(); - get the current SCL pin
Wire.getSDA(); - get the current SDA pin


Wire.setDefaultTimeout(timeout); - sets the default timeout applied to all function calls which do not explicitly set a timeout. The default is initially zero (infinite wait). Note that timeouts do not currently apply to background transfers, sendTransmission() and sendRequest().


Wire.resetBus(); - this is used to try and reset the bus in cases of a hung Slave device (typically a Slave which is stuck outputting a low on SDA due to a lost clock). It will generate up to 9 clocks pulses on SCL in an attempt to get the Slave to release the SDA line. Once SDA is released it will restore I2C functionality.


Wire.beginTransmission(address); - initialize Tx buffer for transmit to Slave at address


Wire.endTransmission(^i2c_stop, ^timeout); - blocking routine, transmits Tx buffer to Slave. i2c_stop parameter can be optionally specified to indicate if command should end with a STOP (I2C_STOP) or not (I2C_NOSTOP). timeout parameter can also be optionally specified.


Wire.sendTransmission(^i2c_stop); - non-blocking routine, starts transmit of Tx buffer to slave. i2c_stop parameter can be optionally specified to indicate if command should end with a STOP (I2C_STOP) or not (I2C_NOSTOP). Use done(), finish(), or onTransmitDone() callback to determine completion and status() to determine success/fail. Note that sendTransmission() does not currently support timeouts (aside from initial bus acquisition which does support it).


Wire.requestFrom(address, length, ^i2c_stop, ^timeout); - blocking routine with timeout, requests length bytes from Slave at address. Receive data will be placed in the Rx buffer. i2c_stop parameter can be optionally specified to indicate if command should end with a STOP (I2C_STOP) or not (I2C_NOSTOP). timeout parameter can also be optionally specified.


Wire.sendRequest(address, length, ^i2c_stop); - non-blocking routine, starts request for length bytes from slave at address. Receive data will be placed in the Rx buffer. i2c_stop parameter can be optionally specified to indicate if command should end with a STOP (I2C_STOP) or not (I2C_NOSTOP). Use done(), finish() or onReqFromDone() callback to determine completion and status() to determine success/fail.


Wire.getError(); - returns "Wire" error code from a failed Tx/Rx command


Wire.status(); - returns current status of I2C (enum return value)


Wire.done(); - returns simple complete/not-complete value to indicate I2C status


Wire.finish(^timeout); - blocking routine, loops until Tx/Rx is complete. timeout parameter can be optionally specified.


Wire.write(data); - write data byte to Tx buffer


Wire.write(data_array, count); - write count number of bytes from data array to Tx buffer


Wire.available(); - returns number of remaining available bytes in Rx buffer


Wire.read(); - returns next data byte (signed int) from Rx buffer


Wire.read(data_array, count); - read count number of bytes from Rx buffer to data array


Wire.peek(); - returns next data byte (signed int) from Rx buffer without removing it from Rx buffer


Wire.readByte(); - returns next data byte (uint8_t) from Rx buffer


Wire.peekByte(); - returns next data byte (uint8_t) from Rx buffer without removing it from Rx buffer


Wire.flush(); - does nothing


Wire.getRxAddr(); - returns target address of incoming I2C command. Used for Slaves operating over an address range.


Wire.onTransmitDone(function); - used to set Master Tx complete callback. Function must be of the form void function(void), refer to code examples


Wire.onReqFromDone(function); - used to set Master Rx complete callback. Function must be of the form void function(void), refer to code examples


Wire.onReceive(function); - used to set Slave Rx callback. Function must be of the form void function(size_t len), refer to code examples


Wire.onRequest(function); - used to set Slave Tx callback. Function must be of the form void function(void), refer to code examples


Wire.onError(function); - used to set callback for bus Tx/Rx errors (Master-mode only). Function must be of the form void function(void), refer to code examples


Wire.getErrorCount(counter); - Get error count from specified counter.
Wire.zeroErrorCount(counter); - Zero error count of specified counter.



Compatible Libraries

These are libraries which are known to be compatible with this I2C library. They may have been possibly modified to utilize enhanced functions (higher speed, timeouts, etc), or perhaps for general compatibility. Please contact their respective authors for questions regarding their usage.