Git Product home page Git Product logo

i2c_t3's Introduction

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:

  • For Teensy 3.0, there is one I2C interface: Wire
  • For Teensy 3.1, 3.2, LC, there are two I2C interfaces: Wire, Wire1
  • For Teensy 3.5, there are three I2C interfaces: Wire, Wire1, Wire2
  • For Teensy 3.6, there are four I2C interfaces: Wire, Wire1, Wire2, Wire3


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:

  • Teensy LC - ~44k Ohms
  • Teensy 3.0/3.1/3.2 - ~190 Ohms (this is believed to be a HW bug)
  • Teensy 3.5 - ~150 Ohms (this is believed to be a HW bug)
  • Teensy 3.6 - ~25 Ohms (this is believed to be a HW bug)

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:

  • I2C_RATE_100, I2C_RATE_200, I2C_RATE_300, I2C_RATE_400, I2C_RATE_600, I2C_RATE_800, I2C_RATE_1000, I2C_RATE_1200, I2C_RATE_1500, I2C_RATE_1800, I2C_RATE_2000, I2C_RATE_2400, I2C_RATE_2800, I2C_RATE_3000

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:

  • I2C_OP_MODE_ISR - Interrupt
  • I2C_OP_MODE_DMA - DMA
  • I2C_OP_MODE_IMM - Immediate

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.

  • basic_master - this creates a Master device which is setup to talk to the Slave device given in the basic_slave sketch.
  • basic_master_mux - this creates a Master device which can communicate using the Wire bus on two sets of pins, and change pins on-the-fly. This type of operation is useful when communicating with Slaves with fixed, common addresses (allowing one common-address Slave on each set of pins).
  • basic_master_callback - this creates a Master device which acts similar to the basic_master sketch, but it uses callbacks to handle transfer results and errors.
  • basic_slave - this creates a Slave device which responds to the basic_master sketch.
  • basic_slave_range - this creates a Slave device which will respond to a range of I2C addresses. A function exists to obtain the Rx address, therefore it can be used to make a single device act as multiple I2C Slaves.
  • basic_scanner - this creates a Master device which will scan the address space and report all devices which ACK. It only scans the Wire bus.
  • basic_interrupt - this creates a Master device which is setup to periodically read/write from a Slave device using a timer interrupt.
  • basic_echo - this creates a device which listens on Wire1 and then echos that incoming data out on Wire. It demonstrates non-blocking nested Wire calls (calling Wire inside Wire1 ISR).
  • advanced_master - this creates a Master device which is setup to talk to the Slave device given in the advanced_slave sketch. It adds a protocol layer on-top of basic I2C communication and has a series of more complex tests.
  • advanced_slave - this creates a Slave device which responds to the advanced_master sketch. It responds to a protocol layer on-top of basic I2C communication.
  • advanced_scanner - this creates a Master device which will scan the address space and report all devices which ACK. It scans all existing I2C buses.
  • advanced_loopback - this creates a device using one bus as a Master (Wire) and all other buses as Slaves. When all buses are wired together (loopback) it creates a closed test environment, which is particularly useful for Master/Slave development on a single device.


Header Defines

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

  • I2C_BUS_ENABLE n - this controls how many buses are enabled. When set as "I2C_BUS_ENABLE 1" only Wire will be active and code/ram size will be reduced. When set as "I2C_BUS_ENABLE 2" then both Wire and Wire1 will be active and code/ram usage will be increased. Specifying a higher number of buses than exists is allowed, as it will be automatically limited by what is available on the device. The default is "I2C_BUS_ENABLE 4", to enable all buses on all devices by default.

  • I2C_TX_BUFFER_LENGTH n

  • I2C_RX_BUFFER_LENGTH n - these two defines control the buffers allocated to transmit/receive functions. When dealing with Slaves which don't need large communication (eg. sensors or such), these buffers can be reduced to a smaller size. Buffers should be large enough to hold: Target Addr + Data payload. Default is: 259 bytes = 1 byte Addr + 258 byte Data, as that is what some examples use.

  • I2Cx_INTR_FLAG_PIN p - these defines make the specified pin high whenever the I2C interrupt occurs (I2C0 == Wire, I2C1 == Wire1, and so on). This is useful as a trigger signal when using a logic analyzer. By default they are undefined (commented out).

  • I2C_AUTO_RETRY - this define is used to make the library automatically call resetBus() if it has a timeout while trying to send a START. This is useful for clearing a hung Slave device from the bus. If successful it will try again to send the START, and proceed normally. If not then it will exit with a timeout. Note - this option is NOT compatible with multi-master buses. By default it is disabled.

  • I2C_ERROR_COUNTERS - uncomment to make the library track error counts. Error counts can be retrieved or zeroed using the getErrorCount() and zeroErrorCount() functions respectively. When included, errors will be tracked on the following (Master-mode only): Reset Bus (auto-retry only), Timeout, Addr NAK, Data NAK, Arb Lost, Bus Not Acquired, DMA Errors. By default error counts are enabled.

  • I2C_DISABLE_PRIORITY_CHECK - uncomment to entirely disable auto priority escalation. Normally priority escalation occurs to ensure I2C ISR operates at a higher priority than the calling function (to prevent ISR stall if the calling function blocks). Uncommenting this will disable the check and cause I2C ISR to remain at default priority. It is recommended to disable this check and manually set ISR priority levels when using complex configurations. By default priority checks are enabled (this define is commented out).



Function Summary

The functions are divided into the following classifications:

  • Italic functions are compatible with the original Arduino Wire API. This allows existing Arduino sketches to compile without modification.
  • Bold functions are the added enhanced functions. They utilize the advanced capabilities of the Teensy hardware. The library provides the greatest benefit when utilizing these functions (versus the standard Wire library).
  • '^' indicates optional function arguments. When not specified default values will be used.

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

  • default pin setting SCL/SDA:
    • Wire: 19/18
    • Wire1: 29/30 (3.1/3.2), 22/23 (LC), 37/38 (3.5/3.6)
    • Wire2: 3/4 (3.5/3.6)
    • Wire3: 57/56 (3.6)
  • return: none

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

  • default pin setting SCL/SDA:
    • Wire: 19/18
    • Wire1: 29/30 (3.1/3.2), 22/23 (LC), 37/38 (3.5/3.6)
    • Wire2: 3/4 (3.5/3.6)
    • Wire3: 57/56 (3.6)
  • return: none
  • parameters:
    • address = 7bit slave address of device

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).

  • return: none
  • parameters:
    • mode = I2C_MASTER, I2C_SLAVE
    • address1 = 1st 7bit address for specifying Slave address (ignored for Master mode)
    • ^address2 = 2nd 7bit address for specifying Slave address range (ignored for Master mode)
    • ^pins - pin setting to use, refer to Pins Section above. Can be specified as either of the following:
      • pins_enum
      • pinSCL, pinSDA
    • ^pullup = I2C_PULLUP_EXT, I2C_PULLUP_INT (default I2C_PULLUP_EXT)
    • ^rate = frequency of I2C clock to use in Hz, eg. 400000 for 400kHz. Can also be specified as a I2C_RATE_xxxx enum (deprecated), refer to Clocking Section above. (default 400kHz)
    • ^opMode = I2C_OP_MODE_ISR, I2C_OP_MODE_DMA, I2C_OP_MODE_IMM. Optional setting to specify operating mode (ignored for Slave mode, defaults ISR mode)

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.

  • return: 1=success, 0=fail (bus busy)
  • parameters:
    • opMode = I2C_OP_MODE_ISR, I2C_OP_MODE_DMA, I2C_OP_MODE_IMM

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

  • return: none
  • parameters:
    • i2cFreq = desired I2C frequency in Hz, eg. 400000 for 400kHz.

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

  • return: bus frequency in Hz

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.

  • return: 1=success, 0=fail (function can no longer fail, it will auto limit at min/max bounds)
  • parameters:
    • busFreq = bus frequency, typically F_BUS unless reconfigured
    • rate = frequency of I2C clock to use in Hz, eg. 400000 for 400kHz. Can also be specified as a I2C_RATE_xxxx enum (deprecated), refer to Clocking Section above

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.

  • return: 1=success, 0=fail
  • parameters:
    • pins - pin setting to use, refer to Pins Section above. Can be specified as either of the following:
      • pins_enum
      • pinSCL, pinSDA
    • ^pullup = I2C_PULLUP_EXT, I2C_PULLUP_INT (default I2C_PULLUP_EXT)

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

  • return: none
  • parameters:
    • pin - pin setting to use, refer to Pins Section above.

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

  • return: pin used

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().

  • return: none
  • parameters:
    • timeout = timeout in microseconds

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.

  • return: none

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

  • return: none
  • parameters:
    • address = target 7bit slave 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.

  • return: 0=success, 1=data too long, 2=recv addr NACK, 3=recv data NACK, 4=other error
  • parameters:
    • ^i2c_stop = I2C_NOSTOP, I2C_STOP (default STOP)
    • ^timeout = timeout in microseconds (default 0 = infinite wait)

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).

  • return: none
  • parameters:
    • ^i2c_stop = I2C_NOSTOP, I2C_STOP (default STOP)

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.

  • return: #bytes received = success, 0=fail
  • parameters:
    • address = target 7bit slave address
    • length = number of bytes requested
    • ^i2c_stop = I2C_NOSTOP, I2C_STOP (default STOP)
    • ^timeout = timeout in microseconds (default 0 = infinite wait)

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.

  • return: none
  • parameters:
    • address = target 7bit slave address
    • length = number of bytes requested
    • ^i2c_stop = I2C_NOSTOP, I2C_STOP (default STOP)

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

  • return: 0=success, 1=data too long, 2=recv addr NACK, 3=recv data NACK, 4=other error

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

  • return:
    • I2C_WAITING
    • I2C_TIMEOUT
    • I2C_ADDR_NAK
    • I2C_DATA_NAK
    • I2C_ARB_LOST
    • I2C_BUF_OVF
    • I2C_NOT_ACQ
    • I2C_DMA_ERR
    • I2C_SENDING
    • I2C_SEND_ADDR
    • I2C_RECEIVING
    • I2C_SLAVE_TX
    • I2C_SLAVE_RX

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

  • return: 1=Tx/Rx complete (with or without errors), 0=still running

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

  • return: 1=Tx/Rx complete (Tx or Rx completed, no error), 0=fail (NAK, timeout or Arb lost)
  • parameters:
    • ^timeout = timeout in microseconds (default 0 = infinite wait)

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

  • return: #bytes written = success, 0=fail
  • parameters:
    • data = data byte

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

  • return: #bytes written = success, 0=fail
  • parameters:
    • data_array = pointer to uint8_t (or char) array of data
    • count = number of bytes to write

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

  • return: #bytes available

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

  • return: data, -1 if buffer empty

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

  • return: #bytes read
  • parameters:
    • data_array = pointer to uint8_t (or char) array of data
    • count = number of bytes to read

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

  • return: data, -1 if buffer empty

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

  • return: data, 0 if buffer empty

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

  • return: data, 0 if buffer empty

Wire.flush(); - does nothing


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

  • return: rxAddr of last received command

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.

  • return: error count / none
  • parameters:
    • counter
      • I2C_ERRCNT_RESET_BUS
      • I2C_ERRCNT_TIMEOUT
      • I2C_ERRCNT_ADDR_NAK
      • I2C_ERRCNT_DATA_NAK
      • I2C_ERRCNT_ARBL
      • I2C_ERRCNT_NOT_ACQ
      • I2C_ERRCNT_DMA_ERR


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.

i2c_t3's People

Contributors

nox771 avatar paulstoffregen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

i2c_t3's Issues

Wire status question

I have tried to find documentation for Wire status enums. In .h there is comment of stopped states.

Does states I2C_TIMEOUT, I2C_ADDR_NAK, I2C_DATA_NAK, I2C_ARB_LOST ,I2C_BUF_OVF, I2C_NOT_ACQ, I2C_DMA_ERR mean that comunication has been stopped and bus needs to be reset with resetBus to get it running again?

Or is there some better test forchecking need for reset?

I expect that I2C_WAITING is normal state - stopped, but waiting response and will change to some of above in case on error.

Arduino Uno: i2c_t3.h missing

#include "i2c_t3.h"  // I2C library

                                    ^

compilation terminated.

exit status 1
Error compiling for board Arduino/Genuino Uno.

Can you just include these files in your repository?

Teensy 3.6 multi-master configuration not working.

I have issues using this lib with multi-master configuration:

The example code provided below(if replace with Wire lib, the code does work on Arduino uno/mega)

Master1:

#include <i2c_t3.h>
//#include <Wire.h>

#define I2C_ADDRESS_OTHER 0x2
#define I2C_ADDRESS_ME 0x1

void setup() {
    Serial.begin(9600);
    Wire.begin(I2C_MASTER, I2C_ADDRESS_ME, I2C_PINS_18_19, I2C_PULLUP_EXT, 100000);
//    Wire.begin(I2C_ADDRESS_ME);
    Wire.onReceive(receiveI2C);
}

void loop() {
    delay(5000);
    Wire.beginTransmission(I2C_ADDRESS_OTHER);
    Wire.write("hello world from 0x1 to 0x2");
    Wire.endTransmission();
    Serial.println("Send to master2");
}

void receiveI2C(size_t howMany) {
    while (Wire.available() > 0) {
        char c = Wire.read();
        Serial.print(c);
    }
    Serial.println();
}

Master 2:

#include <i2c_t3.h>
//#include <Wire.h>

#define I2C_ADDRESS_OTHER 0x1
#define I2C_ADDRESS_ME 0x2

void receiveI2C(size_t count);

void setup() {
    Serial.begin(9600);
//    Wire.begin(I2C_ADDRESS_ME);
    Wire.begin(I2C_MASTER, I2C_ADDRESS_ME, I2C_PINS_18_19, I2C_PULLUP_EXT, 100000);
    Wire.onReceive(receiveI2C);
}

void loop() {
    delay(5000);
    Wire.beginTransmission(I2C_ADDRESS_OTHER);
    Wire.write("hello world from 0x2 to 0x1");
    Wire.endTransmission();
    Serial.println("Send to master1");
}

void receiveI2C(size_t count) {
    while (Wire.available() > 0) {
        char c = Wire.read();
        Serial.print(c);
    }
    Serial.println();
}

I2C bass spesifications tells me that it should be working hardware wise.
https://www.nxp.com/docs/en/user-guide/UM10204.pdf

Not an issue, but a question: Avoid memory issues due to virtual double buffering?

I'm actually dealing with a Teensy LC and a 128x64 i2c OLED. I managed to increase the speed and to get a nice ~30fps frame rate thanks to this great library using DMA. I understand that the DMA channel has to be dynamically allocated but that eats up a lot of the precious and small dynamic memory of the weak-chested Teensy LC. Thus, I'm trying to save memory in other places: I had to increase the I2C TX buffer of the library to 1027 bytes which allows to transmit a full screen in one transmission. But... I have to use another 1024 byte array to render graphics for the screen before I write it to the TX buffer. That is (in my eyes perhaps unnecessary) double buffering and I wonder if there isn't a way to hand over the frame buffer pointer directly instead of copying it to the TX buffer. That would probably allow to "economize" some CPU cycles without all the write/copy operations and I'd not have to allocate such a huge TX buffer... Any ideas?

TwoWire error when used with SparkfunHTU21D lib

This is probably simple, but I'm attempting to use the HTU21D with 12c_t3.h
(TeensyLC)

First error is:

In file included from /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/libraries/SparkFun_HTU21D/src/SparkFunHTU21D.cpp:28:0:
/Applications/Arduino.app/Contents/Java/hardware/teensy/avr/libraries/SparkFun_HTU21D/src/SparkFunHTU21D.h:54:14: error: 'TwoWire' has not been declared
   void begin(TwoWire &wirePort = Wire); //If user doesn't specificy then Wire will be used

in SparkFunHTU21D.cpp

//#include <Wire.h>
#include <i2c_t3.h>
#include "SparkFunHTU21D.h"

In SparkFunHTU21D.h:

//#include <Wire.h>
#include <i2c_t3.h>

//Start I2C communication
void HTU21D::begin(TwoWire &wirePort)
{
  _i2cPort = &wirePort; //Grab which port the user wants us to use
  
  _i2cPort->begin();
}

Using Wire with multiple devices

Tanks for the great enchances. That solved my problem with my 1-wire library, which needs to run i2c tasks under isr (callbacks).

Now I have questions. Basically I build classes, which are independent of each other. So then they does not know about others and their use of wire.

The first problem is that each device class has its own initialization for Wire. So each of them will call begin, which causes situation that several device classes may call begin. So is there reliable way to check that has begin has been called and that it matches the request the other device will use?

1-wire runs with isr, when task has been started. Others normally as blocked but can also use irs in future. So do have any way to mark wire "locked" for some system? So if I start 1-wire tasks, I should have a way to tell that this Wire is now locked for me (e.g. pointer of callback handler). Then if other class tries to use it, it needs to wait until other class has finished its tasks.

Currently I have both problems by having own Wire extension, which just adds functionality. But if there is currently no functions for above, would it be possible to add functionality to your extension?

Wire.write multiple bytes

Hi, I want to senda variable called data (int data = 500) using Wire.write(data), but this only sends one byte, so my master receives 249.

I know there is Wire.receive(array, count) in order to send more than one byte, but that solution must have the data separated into bytes inside an array.

Is there a way to send the value 500 and to receive it as 500?

Thanks for your help.

Starting new I2C transaction via master callback

Hi, I see now the last release (but I've not installed it yet). I'm thinking about a "little" issue in "acquireBus_(..)" method. The method raise the interrupt priority against the caller routine.
This prevent the use of the I2C library from the callback functions because these functions are interrupt routines themselves (I see no other issues).
As workaround, I thinking about restoring the original priority after calling the library routines, but avoid the change by "acquireBus_(..)" is better. Is there a way to do it ?
EDIT: without change the library sourcecode.

..Obviously, the callback routine need to exit fastest as possible to avoid delays on new I2C interrupts.
Thank you in advance.

Incorrect order of operations?

In void i2c_t3::resetBus_(struct i2cStruct* i2c, uint8_t bus) I see this:

digitalWrite(scl,HIGH);
pinMode(scl,OUTPUT);

Shouldn't these 2 lines be swapped?

Interrupts either not running and/or call wrong function.

I'm using a Teensy 3.5
Wire.begin(); Wire.setClock(400000); Wire.onError(I2C_0_OnError); Wire.onTransmitDone(I2C_0_TransmitDone); Wire.onReqFromDone(I2C_0_ReceiveDone);
The main loop writes to the slave, the I2C_0_TransmitDone reads from the slave and the I2C_0_ReceiveDone processes the data.

This works well when only using 1 I2C. When using Wire and Wire1 using I2C_1_***** functions, the transmit/receive work as intended but only I2C_1_OnError will get called for errors on both busses.
I got round that by using 1 onError function for both.
Then I tried 3 busses with Wire2 using I2C_2_****** for onTransmitDone/onReqFromDone as well. This time I2C_X_TransmitDone would be called correctly but only I2C_1_ReceiveDone would be called.

Is it some limitation with too many interrupts and timeouts?

10-bit Slave Addressing?

More of a feature request:

Any plans to implement communication with devices with 10-bit slave addresses? i.e. Teensy as Master requesting data from 10-bit addressed slave devices.

I took a look at the requestFrom/sendRequest methods to noodle with it myself but it's a little over my head..

Thanks!

Teensy 4.0

Hi,

I've just bought a Teensy 4.0. What I need to change to use the 3 i2c wires ? If I change only pinouts, is it enough ?
Could you please update the documentation ?

BR,

Document glitch filter values

I'm reading through the code to get an in-depth understanding and I'm hanging on this part

// Set filter
if(busFreq >= 48000000)
    *(i2c->FLT) = 4;
else
    *(i2c->FLT) = busFreq/12000000;

Could you document this better. How did you arrive at these values? Why have the glitch factor depend on the bus frequency for slower buses and have a fixed value of 4 for faster busses. Why have it depend on the bus frequency at all? Shouldn't the glitch filter depend on the I2C frequency or more precisely be based on the number of bus clocks per i2c clock pulse width?
And why such a low value? If I compute this correctly, the above code would set the glitch filter to just about 40ns for a Teensy 3.2 with F_BUS 36000000. I haven't done any experiments but my gut feeling is that just by touching a jump wire on my breadboard I could get glitches longer than that.

Wouldn't it make sense to use the highest value for the glitch filter that does not interfere with the data rate?

Simultaneous

Hi,
Is it possible to send and receive data on multiple i2c channels simultaneously?
Thank you

setSCL()/setSDA()/pinConfigure() don't work properly

All 3 functions won't work if there are no pullups connected to the default pins. I guess that's because these functions think the bus is busy. If these functions are called before Wire.begin(), they shouldn't try to look at the bus. Note that https://www.pjrc.com/teensy/td_libs_Wire.html explicitly says that setSCL() and setSDA() can be used before Wire.begin(), so for people basing their code on that page that would be the natural way to write code.

Wire.end() not implemented

I'm trying to communicate to a hybrid I2C/SPI device, which occasionally needs to be powered down. I have it isolated on its very own I2C bus, with nothing else on it, specifically so I can drop the I2C lines (in tests, the I2C lines actually (partially) power the device if I2C isn't end()-ed, SDA and SCL then get turned into outputs, and then set LOW).
In the Wire library, the interrupts get shut off, and the pins get unassigned. Should we do that manually with this library, or are there plans to implement end()?
2/26/19: I implemented Wire.end(). No worries.

Is there a way to undo the effects of Begin?

Is there a way to undo the effects of begin?

I would like to use the I2C pins as regular GPIO pins after using them as I2C pins.

Is this supported? If not, can you point me to what I would have to do to help out on that front?

Thanks!

Licensing question

I have been using you library on my boat project. One of them may become commercial and then I will be in trouble with GPL. I changed my NMEA2000 library to MIT long time ago, when someone asked me to do that. How about your library, does it need to be GPL or could it be changed to MIT?

I2C Master Issue

After uploading the Basic_Master program to the Teensy 3.2 when I check it in the Serial Monitor it shows Send Failed. Is there any change required when the slave is also a Teensy 3.2?

Teensy Audio Board I2c_t3 issues

Hello,

I am trying to use a Teensy 3.2 with Audio Board as a i2c slave
when adding the i2c_t3 library it throws errors - conflicting declaration 'i2c_t3 Wire'
extern i2c_t3 Wire;

it is stated that replacing Wire.h with i2c_t3.h should be done in the Audio library.
./hardware/teensy/avr/libraries/Audio/control_sgtl5000.cpp
• ./hardware/teensy/avr/libraries/Audio/control_wm8731.cpp
• ./hardware/teensy/avr/libraries/Audio/control_cs4272.cpp
• ./hardware/teensy/avr/libraries/Audio/control_ak4558.cpp

What do you recommend?

I2C Slave Receive Buffer

Hi, I've been having trouble with Wire.available() and Wire.read() not reading from the beginning of the receive buffer.

My setup is a Teensy LC (master) and Teensy 3.2 (slave) connected via i2c pins 18/19. Both are grounded, and there are 3.3k ohm pullups to the 3.3V pin on the Teensy 3.2. Both are also connected over serial for uploading and debugging.

The master reads from serial and passes it along line by line over i2c.

// Buffer contains some text, ending with a newline
Wire.beginTransmission(SLAVE_ID);
Wire.write(buffer, buffer_len);
Wire.endTransmission(false);

The slave reads from i2c and, for debugging, just prints it back to serial.

if (Wire.available() > 0) {
  Serial.print(Wire.read());
}

Here's where the issue is: in order for Wire.available() to be greater than 0, each line sent must be longer than the previous, and the slave only receives the characters that come after. For example if the master sends

abcd
ab
abcdefg

The slave will print

abcd
fg

I'm guessing this is because when the slave receives a new transmission, it writes from the beginning of the rxBuffer and sets rxBufferLength, however the rxBufferIndex is not reset. When a new transmission is received and is shorter, it will return rxBufferLength - rxBufferIndex as some negative number.

There's not really an easy fix here:

  • Setting rxBufferIndex to 0 would cause previous transmissions to be lost if not consumed.
  • Turning rxBuffer into a ring buffer would cause issues for code using onReceive.
  • rxBufferLength is used in many places for both master and slave.

Interestingly, this happens with the Arduino Wire library as well. I found a related issue but outdated workaround in https://forum.pjrc.com/threads/35500-Teensy-3-2-Master-and-Slave-I2C-communication-problem?p=113711&viewfull=1#post113711 .

slave_range configuration gives unexpected results

I've tried implementing your slave_range implementation, where I have 1 master device, and a slave with an address range of 0x67 to 0x99.

This seems good in principal, however when I try to run the code... it only seems to allow the address1 address to register. I've tried on 2 other adjacent addresses and there's no acknowledgement of those addresses (Error code 2).

It may also assist in responding to note that:

a) I'm using the TwoWire Wire library for another I2C device on the slave
b) I've modified the i2c_t3 library so that the Wire namespace is not duplicated (renamed to WireT3).
c) I'm using Wire1 for the master/slave setup on a LC chip.

Please advise how best to get this working.

Thanks

Teensy LC/3.5/3.6 STOP not sent / detected

Using the basic_slave and basic_master examples the handler is only triggered for the previous message, not the current one.

I.e.
master sends "#1"
master sends "#2" -> slave shows "#1" received
master sends "#3" -> slave shows "#2" received

Is this a known issue? I've been using Teensy LC and 3.5/3.6 and it seems to affect all of them.

I suspect at some point the buffer is being filled with data but the handler is not run.
On subsequent messages the buffer has additional data added to it but the count and pointer relate to the beginning of the buffer, hence the "old" data.

Currently investigating.

Thanks

Will

Multiple Bus Conflict

Hello, hope I'm posting this in the right place. I'm fairly certain the issue I'm having is a result of the library and not my hardware/software configuration, but I don't know enough about the library to be sure.

My hardware is a Teensy 3.2, and I'm trying to talk to multiple LED drivers (TLC59116) using both of the i2c busses. I have 4 drivers connected to the Wire bus, and 2 drivers connected to the Wire1 bus.

I have made a very basic sketch to demonstrate the problem I am having. Essentially, when I talk to the drivers over one bus, I can loop through setting and clearing the LEDs indefinitely. However, when I try and talk to both sets of busses, even though it's being done sequentially, I start to have issues. It will either run for a few seconds and crash, or other weird things will happen, and then it will crash.

I don't think it's a hardware issue, as when only when bus is running, it behaves correctly. Can anyone confirm success using more than one Wire interface at a time that there is no conflict?

here's my simple sketch: (in order to test one bus at a time, I comment out the Wire1 or Wire sections.
`#include <i2c_t3.h>

#define ALLCALL_ADDRESS 0x68
#define SOFTWARE_RESET_ADDR 0x6B
#define AUTO_INCREMENT_ALL_REGISTERS 0x80
#define TLC_PWM0_REG 0x02
void setup() {

Wire1.begin();
Wire1.resetBus();

Wire1.beginTransmission(SOFTWARE_RESET_ADDR);
Wire1.write(0xA5);
Wire1.write(0x5A);
Wire1.endTransmission(I2C_STOP);
delay(50);

Wire1.beginTransmission(0x68);
Wire1.write(byte(AUTO_INCREMENT_ALL_REGISTERS));
Wire1.write(byte(0x01));
Wire1.write(byte(0x88));
for (int i=0; i< 16; i++)
Wire1.write(byte(0xFF));
Wire1.write(byte(0xFF));
Wire1.write(byte(0));
for (int i=0; i< 4; i++)
Wire1.write(byte(0xFF));
Wire1.endTransmission();

// Wire.begin();
// Wire.resetBus();
//
// Wire.beginTransmission(SOFTWARE_RESET_ADDR);
// Wire.write(0xA5);
// Wire.write(0x5A);
// Wire.endTransmission(I2C_STOP);
// delay(50);
//
// Wire.beginTransmission(0x68);
// Wire.write(byte(AUTO_INCREMENT_ALL_REGISTERS));
// Wire.write(byte(0x01));
// Wire.write(byte(0x88));
// for (int i=0; i< 16; i++)
// Wire.write(byte(0xFF));
// Wire.write(byte(0xFF));
// Wire.write(byte(0));
// for (int i=0; i< 4; i++)
// Wire.write(byte(0xFF));
// Wire.endTransmission();

}

void loop() {

// //Set leds on Wire
// for(uint8_t j=0; j<4; j++) {
// for(uint8_t i=0; i<16; i++) {
// setLED(0x60+j, i, 100);
// delay(10);
// }
// }

//Set leds on Wire1
for(uint8_t j=0; j<2; j++) {
for(uint8_t i=0; i<16; i++) {
setLED1(0x60+j, i, 100);
delay(10);
}
}

// //Clear all leds
// for(uint8_t j=0; j<4; j++) {
// for(uint8_t i=0; i<16; i++)
// setLED(0x60+j, i, 0);
// }
for(uint8_t j=0; j<2; j++) {
for(uint8_t i=0; i<16; i++)
setLED1(0x60+j, i, 0);
}

}
//
//void setLED(uint8_t address, uint8_t led, uint8_t val) {
// Wire.beginTransmission(address);
// Wire.write(TLC_PWM0_REG + led);
// Wire.write(val);
// Wire.endTransmission();
//}

void setLED1(uint8_t address, uint8_t led, uint8_t val) {
Wire1.beginTransmission(address);
Wire1.write(TLC_PWM0_REG + led);
Wire1.write(val);
Wire1.endTransmission();
}

`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.