PDA

View Full Version : New I2C library for Teensy3



Pages : [1] 2 3

nox771
03-14-2013, 12:29 AM
Hello all,

December 2, 2018:: As below see github for the latest download and details (https://github.com/nox771/i2c_t3) - the ZIP below is old …

All - … uploaded a new release on GitHub:
https://github.com/nox771/i2c_t3 … It applies some fixes and changes the license to MIT.



This thread details an enhanced I2C library for the Teensy 3.x, and LC devices (it does not support AVR based Teensy devices). The historical content of this post has been moved to the i2c_t3_history.txt file, which is linked below. This post details usage of the library below. If anyone has problems or requests please post to this thread.

Have fun,
- Brian (nox771)

----------------------------------------------------------------------------------------------------------------------
Download

This library is available from GitHub at the following link, or via the attached zip download at the bottom of this post.
https://github.com/nox771/i2c_t3

Older releases can be found in the following archive folder on GitHub, or inside the archive folder of the attached zip download below.
https://github.com/nox771/i2c_t3/tree/master/archive

----------------------------------------------------------------------------------------------------------------------
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 (http://pjrc.com/teensy/td_download.html). 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:


Clever slotted board: https://www.oshpark.com/shared_projects/ttl7D5iT
Full kit board: https://www.tindie.com/products/loglow/teensy-32-breakout/

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

3343

----------------------------------------------------------------------------------------------------------------------
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 two classifications:

Black functions are compatible with the original Arduino Wire API. This allows existing Arduino sketches to compile without modification.
Green functions are the added enhanced functions. They utilize the advanced capabilities of the Teensy 3.0/3.1 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
return: none

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)

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

return: none
parameters:

address = 7bit slave address of device
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)

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 frequency.
return: none
parameters:

i2cFreq = 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.getClock(); - return current I2C clock setting (may differ from set frequency due to divide ratio quantization)
return: bus frequency in Hz

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.


Arduino sketch for MPU-9250 9DoF with AHRS sensor fusion - https://github.com/kriswiner/MPU-9250
LSM9DS0 9DOF sensor AHRS sketch - https://github.com/kriswiner/LSM9DS0
Adafruit FRAM board - https://bitbucket.org/JezWeston/adafruit_fram_i2c_t3
Adafruit BNO055 library - http://forums.adafruit.com/viewtopic.php?f=19&t=92153&sid=883bc291b6b08f4a51e018675e15dd69
Micro Crystal Real Time Clock Module RV-3029-C2 (https://forum.pjrc.com/threads/21680-New-I2C-library-for-Teensy3?p=109412&viewfull=1#post109412) - https://mega.nz/#!NRsGBbwT!ImKEaTJhMQPQYlgDswXHYw7203Hw25h0yY6zY7M Stmc
MPU9250 - https://github.com/bolderflight/MPU9250
BME280 - https://github.com/bolderflight/BME280
AMS5812 - https://github.com/bolderflight/AMS5812
AMS5915 - https://github.com/bolderflight/AMS5915

----------------------------------------------------------------------------------------------------------------------
Edits/Changelog

Uploaded i2c_t3_lib_and_sketch_v2.zip

It adds a pinConfigure() function for reconfiguring pins on-the-fly (only possible when bus is not busy).

Uploaded i2c_t3_lib_and_sketch_v3.zip

Adds timeout functions for endTransmission(), requestFrom(), finish() and additional baud rates for 200kHz and 300kHz.

Uploaded i2c_t3_lib_and_sketch_v4.zip

Reworked ISR timeout code to better handle ending slave communication in the middle of Rx

Uploaded i2c_t3_lib_and_sketch_v5.zip

Zip file structure changed to Arduino standard format. Unpack zip contents to sketchbook/libraries folder. Example sketches can be found from Arduino menus at: File->Examples->i2c_t3
Debug routines now use IntervalTimer and are completely defined in library code
New Slave mode added to support address ranges, and new getRxAddr() function added for use in that mode
New example sketches added: slave_range and scanner
Fixed bugs in ISR timeout code and Slave-mode sda_rising_isr attach

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

Uploaded i2c_t3_lib_and_sketch_v6b.zip

Fixed i2c_t3.h header - commented out interrupt pin defines (I2Cx_INTR_FLAG_PIN)

Uploaded i2c_t3_lib_and_sketch_v7.zip

Added support for F_BUS frequencies: 60MHz, 56MHz, 48MHz, 36MHz, 24MHz, 16MHz, 8MHz, 4MHz, 2MHz
Added new rates: I2C_RATE_1800, I2C_RATE_2800, I2C_RATE_3000
Added new priority escalation - in cases where I2C ISR is blocked by having a lower priority than calling function, the I2C will either adjust I2C ISR to a higher priority, or switch to Immediate mode as needed.
Added new operating mode control - I2C can be set to operate in ISR mode, DMA mode (Master only), or Immediate Mode (Master only)
Added new begin() functions to allow setting the initial operating mode:
- begin(i2c_mode mode, uint8_t address, i2c_pins pins, i2c_pullup pullup, i2c_rate rate, i2c_op_mode opMode)
- begin(i2c_mode mode, uint8_t address1, uint8_t address2, i2c_pins pins, i2c_pullup pullup, i2c_rate rate, i2c_op_mode opMode)
Added new functions:
- uint8_t setOpMode(i2c_op_mode opMode) - used to change operating mode on the fly (only when bus is idle)
- void sendTransmission() - non-blocking Tx with implicit I2C_STOP, added for symmetry with endTransmission()
- uint8_t setRate(uint32_t busFreq, i2c_rate rate) - used to set I2C clock dividers to get desired rate, i2c_rate argument
- uint8_t setRate(uint32_t busFreq, uint32_t i2cFreq) - used to set I2C clock dividers to get desired SCL freq, uint32_t argument (quantized to nearest i2c_rate)
Added new Wire compatibility functions:
- void setClock(uint32_t i2cFreq) - (note: degenerate form of setRate() with busFreq == F_BUS)
- uint8_t endTransmission(uint8_t sendStop)
- uint8_t requestFrom(uint8_t addr, uint8_t len)
- uint8_t requestFrom(uint8_t addr, uint8_t len, uint8_t sendStop)
Fixed bug in Slave Range code whereby onRequest() callback occurred prior to updating rxAddr instead of after
Removed I2C1 defines (now included in kinetis.h)
Removed all debug code (eliminates rbuf dependency)

Uploaded i2c_t3_lib_and_sketch_v8.zip

Added support for Teensy LC:
- fully supported (Master/Slave modes, IMM/ISR/DMA operation)
- Wire: pins 16/17 or 18/19 (rate limited to I2C_RATE_1200)
- Wire1: pins 22/23 (rate limited to I2C_RATE_2400)
Added timeout on acquiring bus (prevents lockup when bus cannot be acquired, eg. stuck Slave)
Added setDefaultTimeout() function for setting default timeout to apply to all commands
Added resetBus() function for toggling SCL to release a stuck Slave device
Added setRate(rate) function, does not require specifying bus frequency
Added I2C_AUTO_RETRY user define

Uploaded i2c_t3_lib_and_sketch_v8.1.zip

Patched setOpMode() function for T3.0 device

Uploaded i2c_t3_lib_v9.zip

Added support for Teensy 3.5/3.6:
- fully supported (Master/Slave modes, IMM/ISR/DMA operation)
- supports all available pin/bus options on Wire/Wire1/Wire2/Wire3
Fixed LC slave bug, whereby it was incorrectly detecting STOPs directed to other slaves
I2C rate is now set by directly specifying frequency. I2C rate enums are no longer needed and their use is now deprecated.
Archive folder has been added which contains all prior releases

Uploaded i2c_t3_lib_v9_1.zip

Bugfixes:
- Removed I2C_F_DIV120 setting
- I2C_AUTO_RETRY disabled by default

Uploaded i2c_t3_lib_v9_2.zip

Improved resetBus() function to reset C1 state

Uploaded i2c_t3_lib_v9_3.zip

Fixed Slave ISR bug on LC/3.5/3.6 devices

Uploaded i2c_t3_lib_v9_4.zip

Fixed Slave ISR for LC/3.5/3.6 not properly recognizing RepSTART
Fixed nested Wire calls during Slave ISR receive (calling Wire inside Wire1 Slave ISR)
Added uint8_t and char array read functions - Wire.read(databuf, count);
Updated examples to demonstrate read/write array functions
Added basic_echo example

Uploaded i2c_t3_lib_v10_0.zip

Unbound SCL/SDA pin assignment. Pins can be specified with either i2c_pins enum or by direct SCL,SDA pin definition (using any valid SCL and SDA pin). New function summary is:
- begin(mode, address1, pinSCL, pinSDA, i2c_pullup, rate, i2c_op_mode)
- pinConfigure(pinSCL, pinSDA, pullup)
- setSCL(pin)
- setSDA(pin)
- getSCL()
- getSDA()
Note: internal to i2c structure, currentPins has been replaced by currentSCL and currentSDA
Added Master callback functions (works for sendTransmission/sendRequest, and also endTransmission/requestFrom):
- onTransmitDone(function) - where function() is called when Master Transmit is complete
- onReqFromDone(function) - where function() is called when Master Receive is complete
- onError(function) - where function() is called upon any I2C bus error
Added error counters which may be optionally enabled via I2C_ERROR_COUNTERS define.
- i2c_err_count enum, getErrorCount(), and zeroErrorCount() functions added
Default assignments have been added to many functions for pins/pullup/rate/op_mode, so many parameters are now optional
Fixed blocking conditions that could occur in immediate mode

Uploaded i2c_t3_lib_v10_1.zip

Added User #define to disable priority checks entirely
Added flag to dynamically disable priority checks during ISR & callbacks

PaulStoffregen
03-14-2013, 01:45 AM
Nice. I've added a link to this topic on the Wire library page.

http://www.pjrc.com/teensy/td_libs_Wire.html

Hopefully that will help people find this and use your version. Maybe at some point it should have a dedicated page?

ploveday
03-14-2013, 02:18 AM
Thanks for this.

I'm using I2C to 'network' two Teensy3s in my project, and I have been manually changing the pins and speed, but this will be much cleaner.

Async transfers will be nice, also.

nox771
03-14-2013, 03:46 AM
Sure, hope it works for everyone. Originally I also wanted to make it use DMA, but I ran out of time. Maybe in the future I can get that into it.

t3andy
03-15-2013, 11:58 AM
Good library!


The library is called i2c_t3

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.

What size pull-ups did you use for your high speed testing?

nox771
03-15-2013, 05:52 PM
When I ran my debug tests, I was using just the internal pullups (approx 190 ohms). I wasn't sure the Teensy3 pulldown capability would be strong enough to work with that, but in fact on the scope I was getting around 3Vpp swings (IIRC, maybe even a bit more), and the edges were pretty square. There is a cost of course, when the bus lines are low, each wire is dumping 15mA or so, but unless it gets stuck in that state it is just transient.

As far as the name I used i2c_t3 just because it is Teensy3 specific. There is no AVR code in it, so a generic name didn't seem appropriate. I'm not familiar with Arduino library conventions though, so maybe it doesn't matter.

t3andy
03-15-2013, 08:02 PM
I was using just the internal pullups (approx 190 ohms).

So the I2C recommended pullups are from 4.7K to 10K. (10k used for the higher bit-rates) And you are only using 190 ohms pullups? Something is not quite right!

nox771
03-15-2013, 09:13 PM
The I2C internal pullups are messed up in this part. This was noted on the original TwoWire code also. In non-I2C mode if the pins are configured for pullups they have reasonably high impedance, but when you switch on I2C it becomes a low impedance.

The resistance amount is kind of weird, it's really too high for just the on-resistance of the I/O FET and perhaps some ESD series R. You can actually get it even lower than 190 ohms using the high drive option. But even then it's still too high for a I/O driver on-resistance. It looks to me like someone used the wrong doping on the pullup resistors for that block (eg. they used silicided poly instead of high-R poly, perhaps a mask screwup).

In any event the bus can run with low resistance pullups just fine, as long as the current drain isn't a problem and the slave pulldowns are strong enough.

Constantin
03-15-2013, 09:14 PM
So the I2C recommended pullups are from 4.7K to 10K. (10k used for the higher bit-rates) And you are only using 190 ohms pullups? Something is not quite right!

I thought smaller resistors were needed for faster pullups. See the research Nick Gammon posted near the bottom of his I2C page (http://www.gammon.com.au/forum/?id=10896). I regularly use 2k2 resistors on my I2C buses based on this research. By comparison, the 10K 'square' pulses are look more like shark fins!

t3andy
03-17-2013, 01:56 PM
Yep, the lower the better.
A much better explanation...
http://dsscircuits.com/articles/effects-of-varying-i2c-pull-up-resistors.html

ggood
03-19-2013, 06:11 AM
I have an application where I need to use both I2C buses (interfacing two I2C devices that have the same address, so I assume they can't be on the same bus). Looking at the number of static private variables in the Wire/i2c_t3 classes, I'm guessing it was written when there was only one I2C bus. Is there some way to make this work, e.g. adding a method to set the currentPins member, or maybe calling begin() before reading, or would this involve moving all the stuff that's static into instance variables?

t3andy
03-19-2013, 12:34 PM
I have an application where I need to use both I2C buses (interfacing two I2C devices that have the same address

What you need is a I2C multiplexer ...

PaulStoffregen
03-19-2013, 12:51 PM
If you're going to use a mux, a 74HC4052 might be cheapest option...

nox771
03-19-2013, 06:18 PM
Looking at the number of static private variables in the Wire/i2c_t3 classes, I'm guessing it was written when there was only one I2C bus. Is there some way to make this work, e.g. adding a method to set the currentPins member, or maybe calling begin() before reading, or would this involve moving all the stuff that's static into instance variables?

It's important to understand there is only one I2C block in the part. The different pin configurations are only internal muxing to the same I2C. Understanding that however, it seems possible to do this by reconfiguring the pins, but you have to be a bit clever about it. Since the I2C hardware partially knows the state of the bus (busy, not busy, Tx/Rx, etc), you should only do this while the bus is idle (not busy).

I've managed to get a test of this working. I've added a function to the code:


//
// Configure I2C pins - only works when the bus is in a non-busy state
// return: 1=success, 0=fail (bus busy)
//
uint8_t i2c_t3::pinConfigure(i2c_pins pins, i2c_pullup pullup)


That checks to see if the bus is busy, and if not reconfigures the I2C output on the indicated pins with the indicated pullup method. When the switch happens the other pins get configured as inputs using the same indicated pullup method. I've also included my test sketch - i2c_multi_master_t3. You can refer to that for an example, but it's very simple, just make sure to reconfigure between completed I2C commands (not in the middle of a command).

I've uploaded the new code, a link to it is in the top of the thread.

ggood
03-19-2013, 09:32 PM
Ah, I was afraid that the part might behave like that. But thanks a million for finding a workaround. I'll give it a whirl tonight. I'll bet it'll work for me, since the devices are just Wii Nunchucks and I'm just polling them periodically.

And if all else fails, I'm pretty sure I have a 74HC4052 in my parts box. Thanks for the suggestion, Paul.

ggood
03-20-2013, 06:15 AM
Seems to work correctly with my two Nunchucks. Thanks, Brian!

nox771
03-20-2013, 05:30 PM
Seems to work correctly with my two Nunchucks. Thanks, Brian!
Sure, good to hear it worked :)

ggood
04-07-2013, 04:00 AM
Sure, good to hear it worked :)

Here's what I was able to make with your help: https://www.youtube.com/watch?v=j1emiN2XLCU

Wozzy
04-08-2013, 01:16 AM
Here's what I was able to make...

That's awesome!

Nantonos
04-08-2013, 06:36 AM
Here's what I was able to make with your help: https://www.youtube.com/watch?v=j1emiN2XLCU

Excellent! This is an interesting project and there are lots of Freescale Pressure Sensors (http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=PRESSURE_SENSORS) so why not start another thread describing it in more detail? With the Yamaha BC3A being discontinued, people are looking for alrernatives for breath controllers.

ggood
04-09-2013, 05:26 PM
Excellent! This is an interesting project and there are lots of Freescale Pressure Sensors (http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=PRESSURE_SENSORS) so why not start another thread describing it in more detail? With the Yamaha BC3A being discontinued, people are looking for alrernatives for breath controllers.

I'm partway through writing a series of blog posts about that subject: http://gordophone.blogspot.com/2013/01/a-series-basics-of-diy-wind-controllers.html

And this post directly discusses one of the Freescale pressure sensors that I've found that works well (Freescale MPXV4006GP): http://gordophone.blogspot.com/2013/01/breath-sensing-101.html

In general, you're going to want a sensor that maxes out at about 6 kPa (kiloPascals), which is 0.87 psi. Any higher pressure and you're likely to have an anyeurism trying to make the sensor go full-scale.

Headroom
05-11-2013, 01:35 PM
This library is excellent! Given the range of frequencies the I2C bus can be configure to run at I am guessing the I2C hardware on the Teensy3 is a lot more capable of than the one on the Teensy++2 boards.

A question: Is if there is a timeout/reset feature in case of bus lockups ? With the Teensy++2 boards I have previously worked with the I2CMaster library (http://dsscircuits.com/articles/arduino-i2c-master-library.html) from DSS circuits and it has such a feature. When I started wit my 1st I2C project I encountered problems and this was a god send feature that helped me get my project on its feet - albeit a little shaky - until I had the means in terms of knowledge and analyze and fix the problem.

nox771
05-15-2013, 08:07 PM
The timeouts are a good idea. I had a recent email exchange with someone who needed something similar. It is not a difficult thing to do since the library already has background Tx/Rx capability. It did require a little cleanup to prevent hanging the ISR though. I've augmented the endTransmission(), requestFrom() and finish() functions to incorporate timeouts. To use this requires the long form of the function calls, where timeout will be specified in microseconds:

1) Blocking transmit with timeout:
Wire.endTransmission(i2c_stop, timeout);

2) Blocking receive with timeout:
Wire.requestFrom(address, length, i2c_stop, timeout);

3) Non-blocking Tx/Rx followed sometime later by finish() with timeout:
Wire.finish(timeout);

To test it I wrote an example sketch. I've uploaded a "v3" revision library, see the original post (inside it refer to the i2c_timeout_t3 sketch). It runs through the methods above.

Note that timeouts are not foolproof, if you abort a READ in the middle and a slave gets stuck outputting a low on the SDA line it will hang the bus no matter what you do, since the master cannot transmit a START or STOP (since SDA is held low). Timeouts can however help diagnose a problem if you dump messages when they occur.

Regarding that here is how to check for the timeout error condition on exit. In this case I did not append to the Arduino Wire library's idiotic and inconsistent error codes. There are various methods to do this, but a simple one is to directly do a status check after the call:


...
Wire.endTransmission(I2C_STOP, 100); // 100us timeout
if(Wire.status() == I2C_TIMEOUT) {
// print error message, try again, whatever
}
...

Alternatively you can also just ignore the timeout errors, and it will function to prevent blocks (for instance when hot plugging/removing something on the bus).

Headroom
05-15-2013, 09:53 PM
Thank you very much for supporting this. Outstanding, really!

nox771
05-15-2013, 10:41 PM
Well v3 was short lived. It turns out my method of ending the communication, particularly in the middle of Rx with a slave, was a bit too crude. When receiving from a slave the ISR needs to NAK the slave to get it to reliably release the bus, but this can't be done in a single receive byte (sending a NAK occurs on the next receive byte).

I reworked the ISR code to better handle that case. I've uploaded a v4 version, attached to the top post. If anyone runs into bugs let me know.

nox771
05-15-2013, 11:14 PM
Thank you very much for supporting this. Outstanding, really!

Sure, hope it works for you.

Also, for everyone, I've added a function list on the top post, which details all the new functions and which ones have been added. That can be used as a guide if you need portable code which has to work on AVR. The green functions will only work in this library, which only works on Teensy3.

natri
06-07-2013, 05:05 PM
Hi,

I'm trying to get the debug feature of the examples working, but I keep failing so far.

Adding "#define I2C_DEBUG" to the top of 'i2c_slave'_t3 produces the following compiling error:


Arduino: 1.0.5 (Windows NT (unknown)), Board: "Teensy 3.0"
IntervalTimer.cpp.o: In function `IntervalTimer::end()':
L:\Program Files (x86)\Arduino\hardware\teensy\cores\teensy3/IntervalTimer.cpp:39: multiple definition of `pit0_isr'
i2c_slave_t3.cpp.o:L:\Program Files (x86)\Arduino/i2c_slave_t3.ino:144: first defined here
i2c_slave_t3.cpp.o: In function `pit0_isr':
i2c_slave_t3.cpp:(.text.pit0_isr+0xa): undefined reference to `printI2CDebug()'
i2c_slave_t3.cpp:(.text.pit0_isr+0x18): undefined reference to `i2cDebug'
collect2.exe: error: ld returned 1 exit status

What I'm doing wrong?

nox771
06-07-2013, 05:31 PM
A couple things I can think of. The top error appears to be due to the new IntervalTimer library which is for the PIT timer. It appears to be conflicting with my explicit use of the PIT timer. I haven't got my build on the latest Teensyduino yet. I'll need to study and rework it.

The second thing (last line) looks like a link error, which might be because a lib is not getting included in the build process. What is your build enviro, are you using Arduino to build?

Give me some time, I'll try to look into it.

PaulStoffregen
06-07-2013, 07:07 PM
What I'm doing wrong?

I'm going to guess you defined I2C_DEBUG in your own code. The library appears to intend for you to uncomment this in i2c_t3.h, so additional code inside the library (specifically those undefined functions) gets compiled. If you define it only within your program, it isn't defined with i2c_t3.cpp is compiled.

Regarding the PIT troubles, yes, it looks like this code was made before IntervalTimer became part of the core library.

In the code, try this:



#ifdef I2C_DEBUG
Serial.begin(115200);
IntervalTimer *t = new IntervalTimer;
t->begin(debugPrint, 1000);
#endif


and thisL:



void debugPrint(void)
{
if(i2cDebug.len())
printI2CDebug();
}


That should fix the PIT issue. If you also edit i2c_t3.h to define I2C_DEBUG, I believe it should compile without error.

PaulStoffregen
06-07-2013, 07:24 PM
@nox771 - if you publish version 5, could I talk you into rearranging the files to the convention most Arduino libraries use?

Normally the zip file extracts to a directory with the same name as the library, which contains all the .cpp and .h files the library needs. Normally all the examples are in an "examples" subdirectory (where "examples" is all lowercase which matters on Linux). Following that convention, the zip file might extract like this:

i2c_t3/i2c_t3.cpp
i2c_t3/i2c_t3.h
i2c_t3/rbuf.cpp
i2c_t3/rbuf.h
i2c_t3/examples/i2c_slave_t3/i2c_slave_t3.ino
i2c_t3/examples/i2c_multi_master_t3/i2c_multi_master_t3.ino
i2c_t3/examples/i2c_timeout_t3/i2c_timeout_t3.ino
i2c_t3/examples/i2c_master_t3/i2c_master_t3.ino

Normally the examples would not need the "i2c_" prefix and "_t3" suffix, since they'll appear in the Arduino's menu under File > Examples > i2c_t3.

The one other strong convention for libraries is a keywords.txt file, which lets the Arduino editor highlight your library's keywords nicely. Here's a quick first attempt I made, which gets most of your library's keywords.

nox771
06-07-2013, 07:47 PM
Thanks Paul, I'll take your suggestions and repackage/rebuild everything clean against the latest Teensyduino.

Question for you though. I don't want to drag this thread too far off topic, but one thing I was wondering is regarding things like the PIT ISR being defined as an integral part of the Teensyduino environment. Were you ever planning on wrapping defines around those types of things so they could be modularly included/excluded from an end-user program?

I'm not sure the best way to do that, as it would perhaps require a default include or such in order to build normally. It would seem to me that having ISRs (or any resource like that) already "taken" by the libraries sort of makes it difficult if anyone wants to override it in an end-user program. It requires going down into the Teensyduino library code and explicitly removing sections (which while it is do-able, makes things a pain across a set of end-user programs if only a few need it). It also, for instance in this I2C case, creates dependencies, in that the I2C won't build properly unless IntervalTimer is there. I haven't delved into the DMA stuff yet, but I kind of wonder if it is the same type of situation.

PaulStoffregen
06-07-2013, 08:23 PM
IntervalTimer vs direct PIT access is a trade-off, having a built-in library "consumes" the 4 ISR functions with some extra overhead, but it offers a standard API that many different libraries can use to effectively share the hardware resources. It was discussed quite a bit in another thread here, and I actually had some reservations about this for a while.

In the long term, facilitating sharing of the resources offers a lot of benefit. Originally one of the PIT interrupts was tied up by the tone() function, whether you were using tone or not. Now all 4 PIT timers are available, but through this API. Already 3 libraries (SoftPWM, VirtualWire and DmxSimple) are ported to use IntervalTimer. I recently worked on one of Adafruit's RGB matrix libraries, which also needed a timer for running a function (but no actual hardware needs). A couple other libraries will probably be ported to use it too. IntervalTimer really opens up the possibility for all these libraries to coexist without hard-wired dependencies. The ideal situation would be all combinations of libraries "just work" without editing any library code.

The one other resource where this sort of API probably makes a lot of sense is DMA. So far, very little work as happened on an API for dynamically allocating DMA channels. OctoWS2811 is probably the only widely used library using DMA at this point... so when/if this happens, I'll be the first one to suffer having to rewrite a library!

One idea that's come up many times on the Arduino developer mail list is allowing sketches to #define symbols that affect how the core library is built. There are a lot of really compelling features that could offer. But the general consensus, which I must admit I believe is wise, is opening that up will create chaos in the Arduino world. Already there are lots of widely used libraries (including at least one I wrote) that depend heavily on quirks of Arduino's build process. If the build process changed so sketches could reconfigure the core, it's felt the long-term effect would be many example programs that are very incompatible with each other. A lot of the value of the Arduino platform is the ability to copy and paste bits and pieces of code and include libraries from different sources and generally have things work as expected. If programs and libraries could reconfigure the core library, it would become a very different platform. Still, I've considered doing this sort of thing on a limited scale with Teensyduino, but I generally do try to follow what the Arduino folks are doing too. It's a very difficult decision, to balance powerful features with the desire for a base platform that's a dependable known quantity.

This ended up rambling without any good answers, because quite honestly these are very difficult questions. Specifically on IntervalTimer, I do lament the loss of direct access to the PIT interrupts, but already it's allowing all 4 timers to be used and several libraries to be used together in any combination without needing to worry about which one needs to allocate with interrupt. I think that's worth the small overhead, and unfortunately the headache of reworking all the earlier code which directly used the PIT interrupts.

natri
06-08-2013, 01:37 PM
Many thanks to the both of you!

@PaulStoffregen: Your assessment of the problem was 'uncannily' accurate. :)

I got the slave device program working now, at least in combination with an Arduino as a master.
Sadly, the problem which led me to this point did not evaporate magically: Iím trying to swap an I≤C controlled LCD-display with a Teensy 3, in order to read the data and display it elsewhere. I donít know for sure, which driver is built in the LCD, but I know the address (0x38) and the communication rate (about 350 kHz according to logic analysis). Also the driver as I≤C slave only receives data and gets no requests. So I thought, that activating the debug mode and changing the address in your example would yield me the stream of bytes intended for the display (because the I≤C master should not be able to tell the difference between the display driver and the Teensy).

But the only thing I get via serial is (with I2C_DEBUG_VERBOSE2):

I A1:70 F:85 C1:C0 S:E2 C2:20 FLT:04 ASR
I A1:70 F:85 C1:C0 S:E2 C2:20 FLT:04 RSTART ASR
And so onÖ.

Can you tell me, what the information means? Additionally I would be very grateful for any clue to the source of the problem, of course. I have only a not so educated guess: Between master (5V) and Teensy is a logic level shifter, may this be in some way the cause?

nox771
06-08-2013, 04:20 PM
I don't think the debug mode is what you want. It is dumping the low-level I2C registers from the I2C module, specifically these (you can get more information on these in the datasheet):

// Chapter 44: Inter-Integrated Circuit (I2C) - Page 1012
// I2C0_A1 // I2C Address Register 1
// I2C0_F // I2C Frequency Divider register
// I2C0_C1 // I2C Control Register 1
// I2C0_S // I2C Status register
// I2C0_D // I2C Data I/O register
// I2C0_C2 // I2C Control Register 2
// I2C0_FLT // I2C Programmable Input Glitch Filter register


What you really want is the data received, not all those registers. However this Slave setup is actually a useful test case for many things. When I rework the library today I think I'll build in a new Slave demo app, which just dumps receive data to Serial.

Regardless the information is a bit useful, as I can see the below stream looks a little odd:



I A1:70 F:85 C1:C0 S:E2 C2:20 FLT:04 ASR
I A1:70 F:85 C1:C0 S:E2 C2:20 FLT:04 RSTART ASR
And so on….


The A1 register is the address, which is 7-bit Addr + R/W, so 70h = 38h + WRITE (write is '0'). So that part is correct, and the Slave ACKs. ASR = Addressed Slave Receive, meaning the Slave understands that it was addressed, and it changes into a receive mode.

Odd part is that the next thing the Master sent is a RSTART = Repeated-START, so the Master sent the address byte then for some reason sent it again - meaning it never sent any data. It looks like it did not see the Slave ACK, are your pullups too low resistance?

Since you have an analyzer you should be able to put the SDA/SCL on it and see if the ACK registers from the Slave device at the end of the address byte transmission (the last bit should pull low).

EDIT: Sorry I missed the last line in your post - yes if you have a one-directional level shifter between Master and Slave, then the Master won't be able to see the ACK from the Slave. Slave needs to signal that it recv'd the byte by pulling SDA low, a one-directional level shifter will block the signal getting back to the Master.

Another thing - a logical level-shift should be unnecessary - I2C uses resistive pullups, just change the pullup voltage to effect a level shift.

natri
06-08-2013, 05:21 PM
I just revised the logic level converter. Even the old design has been bidirectional, but now it is working better and I get one or two bytes before RSTART and sometimes a STOP. Itís still gibberish, but at least the first byte seems to be a correct control byte for LCD-Drivers from NXP.
It seems very plausible, that the problem is with the SCL. I canít look up the pullups right now since they are on the back of the circuit board (which is the instrument panel of a car) and the whole experiment set-up is very delicate Ė and Iíd like to try another few things first. For example: I tried ĎI2C_PULLUP_INTĎ, that yielded no results at all.
Concerning your edit:


Another thing - a logical level-shift should be unnecessary - I2C uses resistive pullups, just change the pullup voltage to effect a level shift.

So you say that the 5V SDA/SCL (presumably) wonít harm the Teensy 3? Iím sorry for being daft, but what exactly do you mean by ďjust change the pullup voltage to effect a level shiftĒ?

nox771
06-08-2013, 06:06 PM
Well if you had control over the pullups you could connect them to whatever voltage you thought was appropriate (eg. dropping the voltage to 3.3V on the pullups), but from your description it sounds like an embedded system and the pullups are inaccessible.

I would not try a 5V pullup connection directly to the Teensy.

(As an ASIC designer if I were going to try such a thing I would first attempt to figure out the ESD structure of the I/O pins. If they have reverse diodes to supply then a resistive connection to 5V would be fine, but they may be using grounded-gate NMOS or similar for ESD, in which case a sustained high voltage could conceivably blow an input gate.)

Take a look at this app note from NXP, if you have some discrete NMOS you can try this:
http://www.nxp.com/documents/application_note/AN10441.pdf

natri
06-08-2013, 07:00 PM
Thanks for the link! I copied this design https://www.sparkfun.com/datasheets/BreakoutBoards/Level-Converter-v10.pdf and I used the same MOSFETs, so I suppose the thing should be working correctly.


Well if you had control over the pullups you could connect them to whatever voltage you thought was appropriate (eg. dropping the voltage to 3.3V on the pullups), but from your description it sounds like an embedded system and the pullups are inaccessible.

Meddling with the SMD resistors on the circuit board would not be my first choice, but if nothing else works - why not give it a try :)

natri
06-09-2013, 04:31 PM
I examined the circuit board again: Only the SDA line has an external pullup (4,7k), the SCA line is probably pulled up internally by the controller. Seems a little curious to meÖ
Furthermore did a little testing with the logic analyser, but I canít figure why the acknowledging isnít working.

555

Especially this example (Controller & Teensy) confuses me:

554

The terminal read out is:


I RSTART ASR
I SR D:00
^S:20
^S:20 Ėx-
The first ACK after the address is seemingly working, so the Controller sends the next byte. But this byte should be 10000000 and not 00000000. And after that I canít interpret whatís happening.

nox771
06-09-2013, 08:42 PM
I examined the circuit board again: Only the SDA line has an external pullup (4,7k), the SCA line is probably pulled up internally by the controller.

It may be that the SDA is driven as normal via resistive pullup but the SCL is driven as a CMOS signal. In a single-master setup with no clock stretching by the slave I suppose that could work, but it probably isn't a good way to do it.

One thing that I notice in the 3rd line in your 1st plot, is the fractional send by the master, it only sent 5 out of 9 clocks. At that same point of time I also notice that the SDA and SCL both going high at the same time. What might be happening is that the SDA is slower than SCL and if the Master sees SDA rising-edge while SCL is already high (because SDA is slightly lagging) it might interpret it as a STOP. Really SDA and SCL should not be changing at the same time.

Your 2nd plot is similar, only 7 out of 9 clocks on the last byte.

It could be that the capacitive loading is different than with the LCD so the edge behavior is different. You might try slowing SCL down a little bit (add some cap) to see if the problem goes away.



The terminal read out is:


I RSTART ASR
I SR D:00
^S:20
^S:20 Ėx-
The first ACK after the address is seemingly working, so the Controller sends the next byte. But this byte should be 10000000 and not 00000000. And after that I canít interpret whatís happening.
This isn't enough diagnostics to tell what is going on. The lines beginning with '^' are the SDA rising-edge ISR attempting to detect STOP conditions (for reasons too long to describe, the Slave mode needs help in this regard, so there is a separate ISR).

What is shown doesn't indicate a problem, did it output anything else? It may not have as this situation (partial byte received) is a bit unusual (it is actually difficult to generate this kind of error without some kind of bit-banging I2C wherein one can create malformed signals on purpose).

natri
06-09-2013, 10:53 PM
You might try slowing SCL down a little bit (add some cap) to see if the problem goes away.


Thanks for that idea! I'll try it tomorrow; what scale would you suggest, some pF?

nox771
06-10-2013, 07:36 AM
Thanks for that idea! I'll try it tomorrow; what scale would you suggest, some pF?

I would try something like that. If you had access to a scope you might be able to take a look at the problem edges and see what is going on a little better, but if not then iterating through some small cap values like that might work. It might be tricky - if you slow SCL too much it might fix the rising-edge SDA/SCL and give you a problem on the falling-edge SDA/SCL (if SDA falling-edge occurs when SCL is high it would create a START signal).

I'm not sure exactly if that is what is going on, but I can't think of other things that would kill a transmission in the middle of a byte.

nox771
06-10-2013, 07:55 AM
I've uploaded a new version (v5) of the I2C library, it has the following changes:


The zip file structure has been changed to a more standard format. Just drop the zip contents into your Sketchbook/libraries folder. Examples sketches are now under the library path, and can be found from Arduino menus under: File->Examples->i2c_t3


The debug routines now use the IntervalTimer library instead of using the PIT interrupts directly.


Debug routines are now completely defined in the library code, no end-user sketch code is required (other than the linker fix #include for rbuf).


Added a new Slave mode whereby Slaves can operate over a range of Addresses. This allows a device to act multiple targets on the bus, or as a general catch-all for any unmapped address. This is done via the following:

A new begin() function: begin(mode, address1, address2, pins, pullup, rate);
whereby the Slave will respond to the range given by address1 to address2 (high/low addr order can be either way).
Also: getRxAddr() This function is for use in the Slave callback functions. It returns the target address of the incoming I2C command. This is so devices which are acting as multiple targets can decide what to do based on the incoming target address.


I've included two new example sketches:

slave_range - this Slave-mode sketch uses the above Slave address range method to capture all incoming traffic on the bus. It will output any receive data on Serial, and will respond to Read requests from the Master by sending zeroes. (note: this is not a sniffer since it will respond to the traffic, I haven't figured out a way to make the hardware behave as a sniffer)
scanner - this is a Master-mode sketch. It will scan the entire 7-bit I2C address range and report all devices which ACK. It will output the results to Serial.


Fixed a bug in the ISR timeout code, which in certain conditions the timeout would not properly reset on the next command.


Fixed a bug in Slave mode in sda_rising_isr attach, whereby it was not getting attached on the Addr byte.


Also, just a note for anyone using the Slave address range method above - I would avoid addresses from 0x00-0x07, and 0x78-0x7F. Those are special/reserved according to the I2C spec (things like General Call, High-speed mode, 10-bit mode, etc - refer to the spec for details).

natri
06-15-2013, 07:46 PM
I think I figured it out Ö sort of:

575

The Teensy seems to stretch the clock after the acknowledgment of the address byte, while the master is trying to give the first two clock signals for the second byte anyway (A). Master and slave are then asynchronous, i.e. at point B the master is awaiting acknowledgment for byte 2, but the Teensy has missed two clock pulses and instead keep SDA low for acknowledgment a point C, while the master is applying its internal pull up in order to transmit 1 (the second bit of the third byte).
So far, so bad (for my project). What puzzles me is that the master seems to support clock stretching when communicating with the original LCD (Iíve seen it not right after the address byte, but later during the data bytes).
Furthermore the first bit of the second byte should be 1, but SDA isnít high during the clock pulse. Is it kept low like SCL during clock stretching? Unfortunately I canít understand the inner workings of the library concerning this aspect.

nox771
06-17-2013, 04:37 PM
It's a little odd that the Master doesn't recognize the stretching. The voltage seems adequately low. First thing I would do, if you haven't already, is recompile with the debug routines turned OFF. The debug adds latency into the ISR, and while stretching is not explicitly done, my suspicion is that the I2C hardware will keep the ACK low until the data is read, so that latency could be the cause of the problem.

Overall the latency is very high (about ~10us), so something seems a bit wrong here. What version of the lib are you running, and what is your slave begin() command?

I'll have to hook up the logic analyzer and see if I can benchmark some different setup conditions. I'm pretty sure when I was running my back-to-back setup at the highest rate I wasn't getting 10us between data bytes. A longer term goal I had was to try to speed optimize the ISR (eventually using DMA) but I haven't had a chance to get to that. Let me know your setup command and I'll see if I can do some testing.

natri
06-17-2013, 05:47 PM
Deactivating the debug routine didn't change much. I then scraped most of the program, that brought a little improvement, but still at least one clock pulse is being suppressed.
The command is: Wire.begin(I2C_SLAVE, 0x38, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_400);
Thanks for the continued help!

MrWhippy
07-11-2013, 05:23 AM
I just wanted to say a big thank you to Brian and to Paul of course, for all your hard work. The tools and information you've provided here are extremely useful and it's great to see people still identifying shortcomings and working to fix them. In case the previous posts haven't made it abundantly obvious, I would like to highlight to people still using the older Wire library that they should download and use the i2c_t3 library if they are having trouble with I2C communication using the ďRead word protocolĒ (see figure 5-13 of SMBus specifications etc.). This library helped me immensely in this area!

nox771
07-11-2013, 05:36 PM
Glad it worked for you. And I wanted to mention to Natri that I haven't forgotten about your problem. I'm still working to analyze and/or optimize the latency between bytes. I know it's been weeks, but I've been absolutely crushed under work from my day job, so it's going to take me a bit more time.

nox771
07-23-2013, 12:40 AM
I think I figured it out … sort of:

575

The Teensy seems to stretch the clock after the acknowledgment of the address byte, while the master is trying to give the first two clock signals for the second byte anyway (A). Master and slave are then asynchronous, i.e. at point B the master is awaiting acknowledgment for byte 2, but the Teensy has missed two clock pulses and instead keep SDA low for acknowledgment a point C, while the master is applying its internal pull up in order to transmit 1 (the second bit of the third byte).
So far, so bad (for my project). What puzzles me is that the master seems to support clock stretching when communicating with the original LCD (I’ve seen it not right after the address byte, but later during the data bytes).
Furthermore the first bit of the second byte should be 1, but SDA isn’t high during the clock pulse. Is it kept low like SCL during clock stretching? Unfortunately I can’t understand the inner workings of the library concerning this aspect.

I finally got a chance to look at this a little bit. Indeed the clock stretching after the received byte is due to the ISR interrupt time. I typically see ISR times of about 3.5us with some a bit lower down to 2us. For a Slave this can also be highly variable depending on what is in the onReceive/onRequest callbacks.

I coded up a small bit-banged Master which uses the SCL in a forced mode (CMOS output, no pullups, and no clock stretching), and I can see a similar type of response as you show above. It was very easy to corrupt or drop clock bits which messed up the whole state of the Slave. You mention that the LCD was clock stretching on data, but you also mention that only SDA had a pullup. Given what I've seen, I'm inclined to think the Master in your setup might be intentionally putting out stretched clocks (perhaps to meet some LCD timing criteria), and that it doesn't actually support true clock stretching. I'm thinking this because I see the same thing as you when I use a forced SCL.

Unfortunately what this all comes down to is I don't think there is a fix for you using the code as-is right now. The operations in the ISR are necessary and in a particular order for proper functioning, and I don't see a lot of optimization potential. However that said, there might be some benefit from a DMA approach, but I'll have to spend some time studying that.

----------

One other thing I did notice - the small delay between bytes is due to the ISR, but the Slave also seems to enforce a particular bit timing given whatever baud rate it is initialized with. So if a Slave is initialized at 100kHz and a Master tries to talk to it at 400kHz it will stretch every bit. Therefore it is important to have a Slave match or exceed the Master rate.

glasspusher
08-07-2013, 03:35 AM
Hi nox, Paul, et. al,

Thanks, nox, very much for this library!

I'm currently trying to get a MLX90620 16x4 IR sensor talking with a Teensy 3.0.
The old code for this used what looks to be an outdated i2c lib, so I'm trying
to get it up to date. I've gotten some of the stuff to work, but am flummoxed
by various attempts at reading the IR sensor array from RAM. The thing that has
me hung up is a command to the sensor at Slave Address 0x60. The command format is

0x60; //slave address
0x02; //read RAM command
0xYY; // start address of ram to read
0xZZ; // byte step to make- this seems to be 1 for more than one address to be read, there's an MSB and LSB
0xWW; // number or addresses to read (two byte chunks, it seems, highest value shown is 0x40, 64,(16x4)

Sending it a bunch of writes of the appropriate values doesn't work, even for
the one value read that the old code uses.

I was able to get a similar command (read bytes in eeprom) to work by doing thusly:

____________
Wire.beginTransmission(0x50); // slave address of eeprom chip on IR reader
Wire.write(0x00); //dump all eeprom command
Wire.endTransmission();

//this was put inside a read loop, and I read everything just fine.
Wire.requestFrom(0x40,1,I2C_NOSTOP);
theDatum = Wire.readByte();
________

One other caveat I just noticed, these guys say to specify the Slave address (0x60)
plus a write bit (0xC0)- looks like it needs another zero bit before continuing? If
I put in C0 for the address I get an "slave address not acknowledged", so it does look like it's
talking to 0x60 when it's set to such...any way to slip in another bit at the end of that
before the next (command)byte?

They say:

1. Start condition (Falling edge of SDA while SCL is high)
2. Slave address (SA=0x60) plus write bit = 0xC0
3. Command = 0x02
...


The datasheet for the sensor is
http://www.melexis.com/Assets/Datasheet-IR-thermometer-16X4-sensor-array-MLX90620-6099.aspx
if any kind soul wants to get a better look at the commands.

Any and all help, greatly appreciated. Beer is offered. More details if you need them, just ask.

Thanks.

glasspusher
08-07-2013, 03:46 AM
Oh, in case it isn't clear

Wire.beginTransmission(0x60); // slave address (sensor)
Wire.write(0x02); //command
...more relevant command bytes

Wire.endTransmission();

this will be at least recognized as a valid slave address. If I try to sneak in an 0xCO after the beginTransmission, it gives me a slave address error.

thx

glasspusher
08-07-2013, 03:39 PM
I got it to work! Seems using an I2C_NOSTOP for the Wire.endTransmission was the trick. See below.


Wire.beginTransmission(dev);// + 0x60); // slave addr
Wire.write(0x02); // command, read
Wire.write(address); // start address
Wire.write(0x00); // address step
Wire.write(0x01); // number of reads requested
success = Wire.endTransmission(I2C_NOSTOP, 100);
Serial.print("endTrans returned : ");
Serial.print(success,DEC);
Serial.print("\n\r---------------------------------------------------\n\r");

success = 0;

Serial.print(" bytes coming back\n\r");

Wire.requestFrom(dev,2,I2C_NOSTOP);
while(Wire.available())
{
if(!success)
{
lowByte = Wire.readByte();
++success;
}
else
{
hiByte = Wire.readByte();
++ success;
}
}

nox771
08-08-2013, 06:57 PM
I got it to work! Seems using an I2C_NOSTOP for the Wire.endTransmission was the trick.

Yes, it is common for Slave devices to require a Repeated-START (implicitly caused by not sending a STOP after the Write operaion) when doing a read operation.

There is a valid reason for this. If an I2C bus had multiple Masters and a STOP was sent after the Write operation then the bus would be in a released state (not busy). A 2nd Master could then step in the middle of the Write->Read sequence and start talking to the same Slave, which would invariably corrupt the state of that Slave. Eventually when the 1st Master tried to do its Read then the result could be complete garbage. By keeping the bus in a "busy" state the 1st Master can take as long as needed to do the Read.

In your code above you could use a STOP on the Read (requestFrom) to release the bus at the end of the Read, but in a single Master setup it is optional.

beyon
09-02-2013, 08:42 PM
glasspusher: did you get everything working with the MLX90620? I've been using it successfully with arduino but am planning to switch to teensy 3 and this i2c library to use two sensors per teensy. If you could share any code or insights I'd appreciate it!

glasspusher
09-05-2013, 01:13 AM
I sort of got it working with a teensy, but I think I ended up frying it before finishing the job, a couple of connection screw ups. I just received a new one, will post when I get it working.

I was trying to follow the iPhone app augmented reality setup, but got confused enough by how he did it(and wanting to understand what was going on) that I was starting to go over to an all-arduino type code base when the mlx died on me.

Short answer is, yes, I was talking to it with a teensy 3...

glasspusher
09-22-2013, 04:42 PM
Hey nox,
Now I'm having trouble...tried every which way. Seems the Teensies are hanging on master writes. At first I thought I fried my sensor (entirely possible) but I tried two teensy 3s with your master/slave examples and no matter which teensy I use as the master, it always fails on the first write test (haven't tried others). I updated to teensyduino 1.16 and Arduino 1.05 just to play it safe, but still the same...

I get this:

---------------------------------------------------
I2C WRITE 1 byte at MemAddr 0
Writing: 5

///

So, it's hanging on
Wire.endTransmission(); // blocking I2C Tx (when not specified I2C_STOP is implicit)

I take it?

Any ideas?

thanks for any and all.

nox771
09-23-2013, 06:55 AM
Usually when I've seen it hang up it is related to the pullup resistors either not being there, or being completely wrong values. I would say check the pullup resistors first. If you are only using T3's with no resistors, then one must be set to use internal pullups.

glasspusher
09-23-2013, 01:08 PM
Hi Nox,
Thanks for getting back to me. So, even a write would hang on this? What value would you recommend for external pullups? I was using a 6.8k resistor for a pull _down_ on the IR sensor, maybe that was part of my problem. Worked for a while...

nox771
09-23-2013, 04:53 PM
Hi Nox,
Thanks for getting back to me. So, even a write would hang on this? What value would you recommend for external pullups? I was using a 6.8k resistor for a pull _down_ on the IR sensor, maybe that was part of my problem. Worked for a while...

Yes, the SDA/SCL lines in I2C are normally pulled up to a high value (in this case 3.3V). The line drivers in the IC will usually be open-drain, so they can pull down, but not up (which is why the resistors are there). This structure is necessary to implement some of the bus features such as bidirectional data, multi-master, and clock stretching.

More specifically, the data line in I2C is bidirectional, and even for writes from the Master it will be expecting an ACK response from the Slave. For the ACK the Slave will pull the voltage down, but if no pullups are present it will stay low and hang the bus. Same situation on the clock line, if the Master pulls it low or the Slave does clock stretching it will pull low and stay there, again hanging the bus.

Pullup resistors don't have to be a particular value unless you are running a high-capacitance bus or really high speeds. Probably anything in the 1-10kOhm range will work (I normally use 1-2kOhm resistors just because I have them around).

looper89
10-10-2013, 08:25 PM
I am having issues using a T3 to communicate with a Newhaven LCD. (http://www.newhavendisplay.com/specs/NHD-0420D3Z-NSW-BBW-V3.pdf). I am using a NXP PCA9306 (http://dlnmh9ip6v2uc.cloudfront.net/datasheets/BreakoutBoards/PCA9306.pdf) to get the voltages to the proper levels. 1k pull ups are used.

The display works perfectly, verified with Arduino Duemilanove.

Every 100-200 write attempts the a letter may be displayed when using the T3.

Code:

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

void setup() {

pinMode(13,OUTPUT); // LED

//Wire.begin();
Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_100);

Serial.begin(9600);
delay(50);

// turns on backlight
Wire.beginTransmission(0x28);

Wire.write(0xFE); // enter command mode
Wire.write(0x53); // set back level
Wire.write(0x08); // to highest value
Wire.endTransmission();
delay(50);

// clear screen
Wire.beginTransmission(0x28);
Wire.write(0xFE); // enter command mode
Wire.write(0x51); // clear screen
Wire.endTransmission();
delay(50);


}

void loop() {

digitalWrite(13,HIGH);

Wire.beginTransmission(0x28);

Wire.write(0x41); // display 'A'

Wire.endTransmission();

digitalWrite(13,LOW);

delay(50);

}


1018
The above picture shows what the command to display an 'A' looks like from the Arduino.


1019
Above pictures shows what the same command looks like from the T3. Notice the rise in the SDA after the ACK, it happens after every ACK. I think it is the ACK, it rises just after the 9th bit.

I must also add that I can use the T3 to communicate to a different I2c board (ERE relay board) and it is successful and the same characteristic rise after the 9th (ACK) bit still exists.

Any help would be appreciated. Thanks for your time.

Addition: I enabled I2C_DEBUG and the display started to work. This isn't a solution but may help.

Addition 2013-10-12 : Evidently the Newhaven display can't handle the narrow pulse width after the ACK/NACK. I couldn't find a referenced that said exactly what the width had to be. I ended up putting a loop in the i2c0_isr that just counted to use some time.

for(xx=0; xx<10; xx++){ }, xx had to be declared as a volatile

The width widened and the display began to work. There has to be a better fix but for now this will work.

nox771
10-16-2013, 02:06 PM
Sorry for the delay, I didn't get a forum notice (probably due to the recent spamming).

That is a curious thing. The "spike" is happening at the ACK handover. Basically at that point in the communication the Master releases the data line and the Slave pulls it low (that is the ACK). As long as the SDA spike occurs when SCL is low it is not in violation of the spec. That appears to be the case.

My guess here is that the resistor pullups are too low for the given communication rate (the pullups are too fast - the edge rate is too high, which is why the voltage jumps up quickly at the ACK handover). For a 100KHz rate (as given in your code), the resistors can be a higher value. Try using 10K or higher and see what happens.

The other alternative is to perhaps create a new slower rate (it would require digging into the I2C settings). To some extent the I2C module enforces latency timing according to the rate setup. Or as you have done, manipulate the ISR timing to slightly delay when things occur. But I really think the easier solution is to change the resistors.


EDIT: Sorry I looked at it wrong, the spike is after the ACK, not before it as I initially thought. It is when the Slave releases the bus and the Master takes over again. It really should not be causing a Slave problem. It is not a spec violation (since SCL is low), perhaps there is some wiring crosstalk to the SCL line on the Slave side? Regardless I would still try some slower resistors, 1Kohm for low rate like 100KHz seems too low for me.

Another minor thing - it looks like your are running 5V I2C bus. The datasheets are confusing, but I've read elsewhere that the I/Os really should be limited to 3.3V. See this thread. (http://forum.pjrc.com/threads/554-Teensy-3-0-UART-voltage-tolerance-5v?p=1246&viewfull=1#post1246) Since I2C uses a pullup resistor, it has inherent current limiting. Actually replacing your 1Kohm with 10Kohm will improve this situation as it limits the ESD diode current further.

astroyam
10-19-2013, 09:43 PM
Nox, thanks mucho for the very cool library.

Using the Teensy 3 as a master device, should the timeout feature in endTransmission and requestFrom allow code to not hang if the slave device SDA or SCL (or power) lines are physically disconnected, as in the case where the slave device is a sensor which has been removed entirely? Using the attached code, when I pull the SDA or SCL line out, the Teensy hangs unrecoverably.

looper89
10-20-2013, 04:22 PM
nox771 - Thanks for your reply. You are correct, spike is after the ack. When I lengthened the 'spike' communication began to work, well mostly. I have concluded that some of the issues are with the display. I tested the display with a couple different controllers and now they all give me the same, albeit inconsistent, results.

You are also correct, the display is 5V. I am using a NXP PCA9306 device to change the levels. 3.3 on the T3 side and 5V on the display side. I am confident that the level shifter isn't causing problems.

I will try different pullups and report back.

Thanks for your time.

nox771
10-21-2013, 01:55 PM
Using the Teensy 3 as a master device, should the timeout feature in endTransmission and requestFrom allow code to not hang if the slave device SDA or SCL (or power) lines are physically disconnected, as in the case where the slave device is a sensor which has been removed entirely? Using the attached code, when I pull the SDA or SCL line out, the Teensy hangs unrecoverably.

A few things to try:

First, the timeout is intended only to prevent a particular command from hanging right at that point. The initial part of your readByte() routine is writing to the device. However the way I2C works, if the device is not present it will not ACK - so the Master (in your case the T3) will interpret it as a NAK. It will not timeout, since ACK/NAK is a normal part of the I2C communication it will occur very quickly. Timeouts are more for the case where you send a long data set to a Slave and it takes longer than it is supposed to (due to clock stretching, interference, whatever).

Looking at your code I don't think the Write section is needed at all. I can see that you are testing if the device is present, but it is not needed, the requestFrom() routine will get a NAK if the device is missing.

Also, the readByte() routine appears to be keeping the bus in a non-released state. I don't know if that can cause a problem during disconnect (if you get glitches on the wire I'm not sure what would happen). I would try changing I2C_NOSTOP to I2C_STOP on the requestFrom() command.

Finally, your routine looks like it will hang unless it gets read data (this is probably the problem). The following:

while(!Wire.available());
will hang if the Rx buffer is empty.

You need to decide what return value you want from the readByte() routine in cases where there is no device present, or for whatever reason it doesn't read anything. If you are ok with a zero return value in those cases then the routine can simplify to this:

byte readByte(byte address)
{
Wire.requestFrom(VCNL4000_ADDRESS,1,I2C_STOP,1000) ; // this is a blocking command, so no need to poll Wire.available()
return Wire.readByte(); // will return 0 if buffer empty
}


IIRC, the old Arduino lib used to return a signed int equal to -1 in this case. If you want a -1 value in cases where the buffer is empty, then use this:

int readByte(byte address)
{
Wire.requestFrom(VCNL4000_ADDRESS,1,I2C_STOP,1000) ; // this is a blocking command, so no need to poll Wire.available()
return Wire.read(); // will return -1 if buffer empty
}


Check if either of those can work for you.

EDIT: Sorry I see that your readByte() routine is feeding a memory address into the device, not just checking for connectivity. Try this instead:

// readByte(address) reads a single byte of data from address
byte readByte(byte address)
{
Wire.beginTransmission(VCNL4000_ADDRESS);
Wire.send(address);
Wire.endTransmission(I2C_NOSTOP,1000);
if(Wire.status() == I2C_WAITING)
{
Wire.requestFrom(VCNL4000_ADDRESS,1,I2C_STOP,1000) ; // this is a blocking command, so no need to poll Wire.available()
return Wire.readByte(); // will return 0 if buffer empty
}
return 0; // some kind of error occured (NAK, Timeout), return 0
}

bboyes
12-09-2013, 11:48 PM
Wow! Thanks for doing this library! Will definitely try it on a project where the current Wire library is not quite what we want. In particular the Wire "wait forever if bus is low" behaviour, which according to the above post is *not* helped by timeout. This can happen if I2C has no pullups: then the slave makes no reply at all, as if the bus is not connected to anything.

nox771
12-10-2013, 04:08 PM
Wow! Thanks for doing this library! Will definitely try it on a project where the current Wire library is not quite what we want. In particular the Wire "wait forever if bus is low" behaviour, which according to the above post is *not* helped by timeout. This can happen if I2C has no pullups: then the slave makes no reply at all, as if the bus is not connected to anything.

Yes it is a bit vague which situations are handled by the timeout and which are not. I'll try and run some experiments and annotate which conditions should be detected. I believe some of the hanging bus conditions should work, but I forget off the top of my head, so I'll need to verify it first.

Some behaviors are not always fixable. Part of the problem with these things is that the chip vendors try to make the I2C hardware "smart", but in doing so it sometimes boxes in the behavior, at times in erroneous and uncorrectable ways. If a bus is missing it's pullups the Master device can't even output it's own clock, so something like that may hang the hardware.

When I get a chance I'll run some test cases and post results.

nox771
12-11-2013, 06:19 PM
I did some testing on the timeout conditions. For a properly wired bus the timeouts should work as intended.

However if the pullups are missing this is what I've found:
- If SDA pullup is missing, but SCL pullup is connected, then the timeout should work ok.
- If SCL pullup is missing, then the code will hang at the endTransmission()/requestFrom() command. More specifically it is hanging at this point:


// we are not currently the bus master, so wait for bus ready
while(I2C0_S & I2C_S_BUSY);


The reason this occurs is that the device believes a 2nd master is controlling the SCL (this is by design, it is a part of the arbitration process). It is possible to modify the code to exit with an error at that point, but it would require checking and possibly looping on a return value from endTransmission()/requestFrom(). But that is just pushing the problem somewhere else - it is already effectively looping internally.

I think the better solution is really to make sure the bus is properly wired, even if there were some failure mode, the communication will ultimately not work anyway unless pullups are used.

lionstatic
01-02-2014, 06:40 PM
Thanks so much for this library. I have used it in the past with a Teensy 3.0 board.

Forgive me if this is a stupid question, as I'm fairly new to the Arduino and Teensy environments. I recently purchased a Teensy 3.1 board and was trying to get a sketch that I had working on the Teensy 3.0 board on the new board. When I tried to do that, I get a whole bunch of errors:

TeensyAsJoystick.cpp.o: In function `SendByte(unsigned char, unsigned char)':
C:\arduino-1.0.5/TeensyAsJoystick.ino:141: undefined reference to `i2c_t3::beginTransmission(int)'
C:\arduino-1.0.5/TeensyAsJoystick.ino:142: undefined reference to `i2c_t3::write(unsigned char)'
C:\arduino-1.0.5/TeensyAsJoystick.ino:143: undefined reference to `i2c_t3::write(unsigned char)'
C:\arduino-1.0.5/TeensyAsJoystick.ino:144: undefined reference to `i2c_t3::endTransmission()'
TeensyAsJoystick.cpp.o: In function `SendByte':
C:\arduino-1.0.5/TeensyAsJoystick.ino:145: undefined reference to `Wire'
TeensyAsJoystick.cpp.o: In function `loop':
C:\arduino-1.0.5/TeensyAsJoystick.ino:29: undefined reference to `i2c_t3::requestFrom(int, int)'
C:\arduino-1.0.5/TeensyAsJoystick.ino:33: undefined reference to `i2c_t3::read()'
C:\arduino-1.0.5/TeensyAsJoystick.ino:31: undefined reference to `i2c_t3::available()'
C:\arduino-1.0.5/TeensyAsJoystick.ino:49: undefined reference to `i2c_t3::begin()'
TeensyAsJoystick.cpp.o: In function `usb_joystick_class::hat(int)':
C:\arduino-1.0.5\hardware\teensy\cores\teensy3/usb_joystick.h:112: undefined reference to `Wire'
TeensyAsJoystick.cpp.o: In function `setup':
C:\arduino-1.0.5/TeensyAsJoystick.ino:16: undefined reference to `i2c_t3::begin()'
C:\arduino-1.0.5/TeensyAsJoystick.ino:21: undefined reference to `Wire'
collect2.exe: error: ld returned 1 exit status

I have #include <i2c_t3.h> at the top of my sketch.

I'm probably doing something stupid, but I just wanted to check first - is this library compatible with the Teensy 3.1?

Thanks

Headroom
01-02-2014, 10:15 PM
That would be much easier to answer if you would include a short sketch that would allow us to reproduce the problem!
I work on a mac and to me the mixture of backslash and forward slash is really irritating.

manitou
01-02-2014, 11:11 PM
I haven't played with the new library but the file i2c_t3.cpp in the v5 zip file needs to allow for the 3.1 board, modify it to read

#if defined(__MK20DX128__) || defined(__MK20DX256__)

(or there may be newer versions of the library that have already fixed this ...)

nox771
01-04-2014, 07:51 AM
I'm probably doing something stupid, but I just wanted to check first - is this library compatible with the Teensy 3.1?


I recently got in a couple of the new 3.1 boards. I haven't tried them yet. As mentioned above the defines have changed, so it may be as simple as what manitou suggests. I've got a more complicated update planned, but I'll try and get a basic 3.1 update soon. For now, just try changing the define line manitou shows above (I expect that will probably work).

JonHylands
01-04-2014, 03:22 PM
I hooked up a Pololu MiniIMU-9 v2 (http://www.pololu.com/product/1268), compiled the code from here: https://github.com/pololu/minimu-9-ahrs-arduino and it worked fine with my Teensy 3.1.

lionstatic
01-06-2014, 02:02 PM
I haven't played with the new library but the file i2c_t3.cpp in the v5 zip file needs to allow for the 3.1 board, modify it to read

#if defined(__MK20DX128__) || defined(__MK20DX256__)

(or there may be newer versions of the library that have already fixed this ...)

Thanks manitou! That seemed to do the trick - the sketch now compiles without errors.

nox771
01-20-2014, 07:14 PM
All, I've uploaded a new version of the library (v6) which has the following changes:


It now includes Teensy 3.1 support, including the 2nd I2C interface (I2C1) which works the same as Wire but is called Wire1.
The Wire1 interface can be configured to use pins 29/30 or pins 26/31. Unfortunately both sets of these are on the backside surface mount pads. Refer to the top post and code examples for information on setup.
Added a new header define to allow the library to be compiled as a single interface (I2C0-only) for cases where code/ram space is tight.
Also added some interrupt "flag" defines, which when set will toggle pins high when the I2C interrupts occur. This can be useful as a logic-analyzer trigger, or in determining interrupt timing.
Added two new examples, specifically for Teensy 3.1: quad_master and dual_bus_master_slave.

Regarding the new examples:

quad_master - this one creates a device capable of being a Master on 4 separate I2C buses. It uses both I2C interfaces and multiplexes on both sets of output pins for each interface. I think this might be useful for anyone using multiple Slaves having fixed identical addresses (maybe eliminate some I2C muxes).

dual_bus_master_slave - this creates a Master on one bus and a Slave on the other. These can then be connected together so it talks to itself. It seems silly at first - why would anyone want a device which talks to itself - but this actually creates a closed-loop test environment on a single-device. It is useful for developing Master/Slaves. One nice thing, is that when the code uploads it automatically resets both Master and Slave (I've had many cases of hung Slaves, which required pulling/reconnecting USB to restart them, so to me this is a bonus).


Although externally this will seem little changed, it was actually a pretty big overhaul on the code. I didn't want to have completely separate code for the two buses, so it was all rewritten to use dereferenced pointers instead of hardcoding. This allowed me to maximize function sharing, including the ISRs. In the end, this new structure allowed the multi-bus code to be only incrementally different size-wise from the old v5 code (on the order of ~100 bytes). Ram is a similar case when configured for single-bus. When configured as a dual-bus I get an increase of 320 bytes Flash and 628 bytes Ram (using default buffer sizes, which are fairly large but can be changed).

I've been running tests on it for a few days, and did manage to find and fix a couple bugs (mostly bugs only affecting the very high rates). As far as I know everything is working now. So I'll extrapolate and say that it's functioning perfectly :) (if anyone runs into problems let me know...)

Headroom
01-20-2014, 08:01 PM
Where's the "donate" button ?

nox771
01-20-2014, 08:27 PM
Where's the "donate" button ?
Heh, I think you found the first problem :p

geekguy
02-07-2014, 11:34 PM
All, I've uploaded a new version of the library (v6) which has the following changes:


It now includes Teensy 3.1 support, including the 2nd I2C interface (I2C1) which works the same as Wire but is called Wire1.
The Wire1 interface can be configured to use pins 29/30 or pins 26/31. Unfortunately both sets of these are on the backside surface mount pads. Refer to the top post and code examples for information on setup.
Added a new header define to allow the library to be compiled as a single interface (I2C0-only) for cases where code/ram space is tight.
Also added some interrupt "flag" defines, which when set will toggle pins high when the I2C interrupts occur. This can be useful as a logic-analyzer trigger, or in determining interrupt timing.
Added two new examples, specifically for Teensy 3.1: quad_master and dual_bus_master_slave.

Great work on this library! I have created a slightly renamed version of it, called FastWire (from a suggestion somebody else made). The naming is the only difference. Everything else is as you wrote it so far. :) I hope to try it out soon.

Have you considered getting a GitHub.com or Bitbucket.org account and using source control for your stuff?

8-Dale

nox771
02-08-2014, 06:17 AM
Have you considered getting a GitHub.com or Bitbucket.org account and using source control for your stuff?

I do not currently have a github or otherwise similar setup yet. The reason is more historical and has to do with my development setup. Most of my heavy-use programs are "portablized", in that I run almost everything off a USB flash drive. This allows me to plug into almost any computer and do work without the hassle of installing things. It includes the bulk of my devel apps (including big stuff like Eclipse, WinPython, Code::Blocks, wxFormBuilder), with the exception of a few big ones which just can't be setup that way (Visual Studio, Atmel Studio, CodeWarrior, DesignSpark, etc...). I also run a Virtualbox Linux install off the drive also, but I mostly use that for Verilog work, not this stuff.

Anyway point being, until within probably the last year, there wasn't any good portable SCM that fit into my flow. I did have a beta Mercurial portable, but I never got going with it. Now there appears to be a portable Git: http://code.google.com/p/msysgit/downloads/detail?name=PortableGit-1.8.5.2-preview20131230.7z
I might try utilizing that on future releases if I get it all figured out.

Regarding the name, it was chosen because Wire is really a rather bad name for the library (even the Arduino guys think so (http://arduino.cc/en/Reference/APIStyleGuide)). When I first heard it I thought it was referring to 1-Wire, obviously a very different thing. A more accurate name would either be I2C or TWI. The suffix is just because it is highly specific to the Teensy3 (well actually the Freescale K20-series, there is no AVR code) so a generic name like just "i2c" could be confusing to someone with say a Teensy2 (these are after all shared forums).

I may in the future rename the lib, whenever a Teensy4 or Teensy3++ or whatever comes out, and at that time I might ask for names. Not sure if it would be FastWire though as it is still very generic. In fact there is already a Fastwire on github, which predictably is AVR code. One might be tempted to stick ARM in the name somewhere, but on github that would probably be confusing to users of other similar parts (the I2C registers in the K20 are nothing like in say the Atmel SAM or NXP LPC parts even though they are all ARM Cortex-M3/4 parts).

rbotman
02-12-2014, 04:58 AM
I ran into a tiny bug in V6 i2c_t3.h:



// Interrupt flag - uncomment and set below to make the specified pin high whenever the
// I2C interrupt occurs. This is useful as a trigger signal when using a logic analyzer.
//
#define I2C0_INTR_FLAG_PIN 8
#define I2C1_INTR_FLAG_PIN 9



Took me a bit of time to find out why I was having trouble using pin 8 for something else. Looks like these two #defines were intended to be commented out.

Thanks for the great work nox771.

nox771
02-12-2014, 03:40 PM
I ran into a tiny bug in V6 i2c_t3.h:



// Interrupt flag - uncomment and set below to make the specified pin high whenever the
// I2C interrupt occurs. This is useful as a trigger signal when using a logic analyzer.
//
#define I2C0_INTR_FLAG_PIN 8
#define I2C1_INTR_FLAG_PIN 9



Took me a bit of time to find out why I was having trouble using pin 8 for something else. Looks like these two #defines were intended to be commented out.

Doh! Sorry, you are right, it was supposed to be commented out. Thanks for pointing it out. I've removed the previous v6 rev and uploaded a fixed version.

christoph
02-15-2014, 09:24 AM
Hi,

I'm having serious problems with an LSM303D. In the first version of my code I didn't have error checking, and the bus seemed to hang from time to time. So I added error checking. This is what I'm doing now:


static const uint8_t SLA = 0x1D;
static const uint16_t timeout = 100000; // this was 100, but I wanted 100 ms (!)
bool LSM303D::begin()
{
// configure
Wire.beginTransmission(SLA); // slave addr
Wire.write(0x20); // memory address (CTRL1)
Wire.write((0b0101 << 4) | (1 << 3) | 0b111) ; // accel normal mode, 50 Hz, block data update, all axes enabled
if (Wire.endTransmission(I2C_STOP, timeout) != 0)
{
return false;
}

Wire.beginTransmission(SLA); // slave addr
Wire.write(0x24); // memory address (CTRL5)
Wire.write((0b11 << 5) | (0b100 << 2) | 0b111) ; // magn high resolution, 50 Hz
if (Wire.endTransmission(I2C_STOP, timeout) != 0)
{
return false;
}

Wire.beginTransmission(SLA); // slave addr
Wire.write(0x25); // memory address (CTRL5)
Wire.write(0b00 << 5); // magn full scale: +-2 gauss
if (Wire.endTransmission(I2C_STOP, timeout) != 0)
{
return false;
}

Wire.beginTransmission(SLA); // slave addr
Wire.write(0x26); // memory address (CTRL5)
Wire.write(0); // magn enable
if (Wire.endTransmission(I2C_STOP, timeout) != 0)
{
return false;
}
return true;
}

I also have code for reading values from the chip, but begin() already fails with a timeout error. The wiring is correct, the pullups are correct as well - otherwise the previous code without error checking wouldn't have worked. My first question is: am I using the i2c_t3 methods correctly in the first place?

Here is my reading code:

bool LSM303D::read()
{
// read accel
Wire.beginTransmission(SLA);
Wire.write(0x28 | 0x80); // read accel, continuous
Wire.endTransmission(I2C_NOSTOP, timeout);
Wire.requestFrom(SLA, 6, I2C_STOP, timeout);
uint8_t i = Wire.available();
std::array<uint8_t, 6> buf;
if(i == 6)
{
for(uint8_t i = 0; i < 6; i++)
{
buf[i] = Wire.readByte();
}

... process data in buf ...

}
else
{
for (uint8_t j = 0; j < i; j++)
{
// empty buffer and discard data
Wire.readByte();
}
return false;
}

... more read operations ...
}


To be honest, I'm not sure which version of the library I'm using. The last modification note in i2c_t3.h is
- Modified 09Jun13 by Brian
EDIT: I have downloaded the latest Version and it works slightly better, with occasional lockups lasting until I cycle power.

Summed up questions:

Am I using the i2c_t3 methods correctly?
Has anyone used the LSM303D? What might cause it to hang?
What can I do to "unhang" the bus again? I know that is a complex issue (it already was on AVRs), but there might be readily available code that attempts to it on a Teensy 3.0

Regards

Christoph

geekguy
02-15-2014, 04:24 PM
Summed up questions:

Am I using the i2c_t3 methods correctly?
Has anyone used the LSM303D? What might cause it to hang?
What can I do to "unhang" the bus again? I know that is a complex issue (it already was on AVRs), but there might be readily available code that attempts to it on a Teensy 3.0

I have an LSM303DLHC that is part of the 10DOF IMU Breakout Board (http://www.adafruit.com/products/1604) from Adafruit. It seems to work fine with the standard Wire library for both regular Arduino and TeensyDuino, although I am still testing this to be sure everything is right. Some of my orientation readings seem to be off with the Teensy 3.1. I have not tried using this new I2C library for the Teensy 3.1 yet so don't know if there are any differences.

8-Dale

christoph
02-15-2014, 05:03 PM
I'm not sure how different they are, but the LSM303DLHC is different from the LSM303D. They definitely have different interfaces. I can also try a different library altogether, does the Wire library work with the Teensy 3.0? I've never tried it.

geekguy
02-15-2014, 06:16 PM
I'm not sure how different they are, but the LSM303DLHC is different from the LSM303D. They definitely have different interfaces. I can also try a different library altogether, does the Wire library work with the Teensy 3.0? I've never tried it.
Try the LSM303 library (https://github.com/adafruit/Adafruit_LSM303) from Adafruit. That's what I have been using for both Arduino and Teensy 3.1, with the standard Wire libraries for both. However, I am not sure it is returning correct results for the Teensy 3.1. My orientation readings (pitch, roll, and heading) aren't correct for the Teensy 3.1. Maybe you can get better results using this new I2C library. I would definitely like to hear about your results with the Adafruit LSM303 library using the new I2C stuff.

8-Dale

nox771
02-16-2014, 12:32 AM
There are several ways to hang the bus, one is to hang a slave device, another is to get the hardware to think it has sent an I2C STOP when in fact it did not (this case would not be something that is your fault, it would be the ISR code).

I have personally seen the I2C interface mangle the STOP when doing a receive using max baud rates (2400 setting), in fact I had to insert a delay in the latest rev (v6) to help with this. It is possible your problems are related to the STOP that occurs at the end of a Tx or Rx, although I have never seen this occur on a Tx, and your begin() function is only Tx. There have been other people complaining about sporadic hangs, so if this could be identified as the cause I would be interested to know.

For Rx problem I have seen, what is happening is that it is trying to change from a Rx mode back to Tx (as happens at the end of a receive) too quickly, which confuses the I2C hardware. I had a test case at the 2400 rate that would cause this fairly reliably which is what I used to figure out my delay patch. However if this is happening at lower rates also then it will have to be fixed.

Some questions:

What baud rate are you using?
Can you be more specific about the hang conditions:
When it hangs, is it exiting your begin() with a false, or it is hanging inside the begin() function? If it is STOP related it will hang inside the begin(), specifically on an endTransmmission() call, because it will think the bus is busy and will be waiting inside that call.
If it does exit begin() with a false, which Tx command does it hang on (first, second, third, fourth?) and what is the I2C error return value?
Do you have any kind of a logic analyzer or scope dump of the I2C traffic? Your commands are short, so if you trigger on the START (SDA falling-edge while SCL high) then you should be able to capture the entire command.


One thing I notice right off:


static const uint16_t timeout = 100000; // this was 100, but I wanted 100 ms (!)

You have over-ranged this. The largest delay you can get with a uint16 will be 65535us. If you really want a timeout that long I can try to rework the routines to maybe support milliseconds. I did not expect people would use timeouts that long.



I'm having serious problems with an LSM303D. In the first version of my code I didn't have error checking, and the bus seemed to hang from time to time. So I added error checking. This is what I'm doing now:
...
I also have code for reading values from the chip, but begin() already fails with a timeout error. The wiring is correct, the pullups are correct as well - otherwise the previous code without error checking wouldn't have worked. My first question is: am I using the i2c_t3 methods correctly in the first place?

The code looks ok at first glance. If you can provide more information I'll think about it some more. I am not familiar with a LSM303D, I'll need to pull a datasheet on it. If we can't identify a solution then I can probably acquire one and experiment on it. What specifically are you using?



Has anyone used the LSM303D? What might cause it to hang?

Slaves can hang if the clocking gets somehow confused between Master and Slave. For instance on a bit-banged Master (not what we are using here, just an example), if the Master drives the SCL line as a CMOS line, not a open-drain, then it will not recognize Slave clock-stretching. So on a Rx command (Slave sending data to Master) the Slave thinks it is stretching the clock and the Master keeps banging away, they become completely out-of-sync and eventually the Slave ends up getting a partial clock and hangs in the middle of transmitting a byte. At that point you are done, the bus is hung. There are other similar ways, basically anything that ends up holding SCL or SDA low indefinitely will hang the bus.

The problem I described at the top is an internal problem due to the I2C hardware. Basically the ISR tells it to send a STOP, it doesn't do it (but it thinks it did), and on the next command it hangs because SCL/SDA is low, it thinks its no longer the Master, and it hangs waiting for the "other" (non-existant) Master to release the bus.



What can I do to "unhang" the bus again? I know that is a complex issue (it already was on AVRs), but there might be readily available code that attempts to it on a Teensy 3.0
Well the real solution is to not hang the bus in the first place. The less correct (but probably equally workable) solution is for single-Master setups. In the i2c_t3.cpp file look for lines like this (I think there are two spots):

// we are not currently the bus master, so wait for bus ready
while(*(i2c->S) & I2C_S_BUSY);
Comment it out, it will no longer hang waiting for the bus to release. I would be interested to know if that works, I haven't tried it. If it does, it implies there is some condition where the STOP isn't properly being sent.

christoph
02-16-2014, 11:44 AM
What baud rate are you using?

I'm running the bus at 400 kHz, this is my call to begin():

Wire.begin(I2C_MASTER, 0x00, I2C_PINS_16_17, I2C_PULLUP_EXT, I2C_RATE_400);


When it hangs, is it exiting your begin() with a false, or it is hanging inside the begin() function?

begin() was hanging in the first call to endTransmission(), which I didn't expect at all, because I thought that the timeout would make endTransmission() return after the given time.


Do you have any kind of a logic analyzer or scope dump of the I2C traffic?

Unfortunately, I don't.


Well the real solution is to not hang the bus in the first place. The less correct (but probably equally workable) solution is for single-Master setups. In the i2c_t3.cpp file look for lines like this (I think there are two spots):I'll try that, thanks for that! It's a single-Master setup and it will stay that way. I might more slaves, though, but that shouldn't make a difference (my experience is that more slaves *will* make a difference).

The board I'm using is the LSM303D board from pololu (http://www.pololu.com/product/2127)

I'll do some more testing and will come back when I can tell what call exactly is hanging during reading, because begin() is quite reliable now.

nox771
02-16-2014, 06:07 PM
begin() was hanging in the first call to endTransmission(), which I didn't expect at all, because I thought that the timeout would make endTransmission() return after the given time.

This is very suspicious. I'm assuming that you start with a power-cycle on the Teensy3. If so, the hardware cannot think that it is a busy bus, so it should never hang on the first Tx call. There is no prior I2C activity to it hanging on the first call to endTransmission() is that right?

If it is truly hanging on the first call, and the first call occurs right after a power-cycle then this implies to me that a slave device is hung and is holding the bus low. If that is the case there is no fix other than to reset the slave device via power-cycle or whatever.

I have had this situation occur myself when I get a slave hung and my normal upload/power-cycle procedure does not include the slave device (eg. it stays hung).

Looking at the schematic I can see that the board has integrated 4.7k pullups (plus a level-shift system). Out of curiosity, do you have many other things connected on the same bus? Are there other pullup devices in parallel, and if so are the voltages matched?

If the bus capacitance is too high (or pullups too large) it could cause the pulses to be truncated (shark fins vs square waves). That could possibly cause problems also. One thing to test this - if you change the bus speed to 100kHz does anything about the behavior change? Does it still get stuck?



The board I'm using is the LSM303D board from pololu (http://www.pololu.com/product/2127)

Ok, well if we can't get your system stable I'll look into getting one of these. I use this library in my own stuff, so I'm definitely interested in making it reliable.



I'll do some more testing and will come back when I can tell what call exactly is hanging during reading, because begin() is quite reliable now.
Ok, more information is better. Another thing to try - if you can isolate the problem to a repeatable case, say the first Tx to occur after a Rx, then just after the Rx (prior to the Tx), you can have the system pause, and then probe SCL/SDA with a voltmeter. They should both be high. In the general case, if either line is low after a STOP has been issued (at the end of any Tx or Rx command, except when I2C_NOSTOP is used), then something is wrong.

christoph
02-16-2014, 07:47 PM
OK, some more information about my system:

Currently the LSM303D is the only slave device on the bus.
Power cycling means disconnecting the Teensy's USB cable and reconnecting it. There are no huge caps or batteries in my circuit, USB is the only way to supply my circuit with power.
The pololu board is powered with 3.3V via VDD, not VIN. That means that the pullups added by pololu shouldn't be pulling the bus high on the Teensy side.
I have external 4k7 pullups on the Teensy side of the board.


With the newest version of the library the hang during begin() disappeared. Now it hangs occasionally during read() calls, which I tracked with debug messages over Serial. This is my current read() code, where I marked the last debug message that is printed (100% reproducible with 100 kHz and 400 kHz bus speed):
bool LSM303D::read()
{
// read accel
Serial.println("LSM303::read() 1"); Serial.flush();
Wire.beginTransmission(SLA);
Wire.write(0x28 | 0x80); // read accel, continuous

Serial.print("LSM303::read() 2"); Serial.flush();
if (Wire.endTransmission(I2C_NOSTOP, timeout) != 0)
{
Serial.println("error"); Serial.flush();
return false;
}

Serial.println("\nLSM303::read() 3"); Serial.flush(); // <========== When it hangs, this is the last message
if (Wire.requestFrom(SLA, 6, I2C_STOP, timeout) == 0) // <========== So this must be the bad one that doesn't return
{
Serial.println("error"); Serial.flush();
return false;
}
uint8_t i = Wire.available();
std::array<uint8_t, 6> buf;
if(i == 6)
{
for(uint8_t i = 0; i < 6; i++)
{
buf[i] = Wire.readByte();
}
... data processing ...
}
else
{
for (uint8_t j = 0; j < i; j++)
{
// empty buffer and discard data
Wire.readByte();
}
return false;
}

// read magn
Serial.println("LSM303::read() 4"); Serial.flush();
Wire.beginTransmission(SLA);
Wire.write(0x08 | 0x80); // read magn, continuous

Serial.println("LSM303::read() 5"); Serial.flush();
if (Wire.endTransmission(I2C_NOSTOP, timeout) != 0)
{
Serial.println("error"); Serial.flush();
return false;
}

Serial.println("LSM303::read() 6"); Serial.flush();
if (Wire.requestFrom(SLA, 6, I2C_STOP, timeout) == 0)
{
Serial.println("error"); Serial.flush();
return false;
}
i = Wire.available();
if(i == 6)
{
for(uint8_t i = 0; i < 6; i++)
{
buf[i] = Wire.readByte();
}
... data processing ...
}
else
{
for (uint8_t j = 0; j < i; j++)
{
// empty buffer and discard data
Wire.readByte();
}
return false;
}
return true;
}


I couldn't probe SCL and SDA prior to the call that hangs, because I only have my breadboard here and literally no tools at all. This would be very tedious work, as the board happily runs in an endless loop (about 30 loops per second) for a few minutes before hanging. Probing the lines in every loop would drive me insane, and I'm sure the results wouldn't be correct either.

Regards

Christoph

nox771
02-17-2014, 05:18 AM
Ok, no tools will make it more challenging, so it might take some additional trial and error.

These kinds of intermittent bugs are really the worst kind. The fact that there is apparently no dependence on baud rate makes me wonder if there is some subtle timing problem occurring during the C1 mode changes (specifically if the ISR running at 96MHz is changing the I2C peripheral registers too quickly). For it to hang on a requestFrom() when using a timeout, there is only one possibility - I2C_C1_MST must be not set (which should be impossible, since just prior you did a Tx with a NOSTOP), and it must be hanging waiting for the I2C_S_BUSY flag to clear. Ah, wouldn't JTAG be nice right about now :p

Can you comment out this line in the sendRequest_() function, and tell me if anything changes:

void i2c_t3::sendRequest_(struct i2cStruct* i2c, uint8_t addr, size_t len, i2c_stop sendStop)
{
// exit immediately if request for 0 bytes
if(len == 0) return;

i2c->reqCount=len; // store request length
i2c->rxBufferIndex = 0; // reset buffer
i2c->rxBufferLength = 0;

// clear the status flags
*(i2c->S) = I2C_S_IICIF | I2C_S_ARBL;

// now take control of the bus...
if(*(i2c->C1) & I2C_C1_MST)
{
// we are already the bus master, so send a repeated start
I2C_DEBUG_STR("RSTART"); // Repeated START
*(i2c->C1) = I2C_C1_IICEN | I2C_C1_MST | I2C_C1_RSTA | I2C_C1_TX;
}
else
{
// we are not currently the bus master, so wait for bus ready
//while(*(i2c->S) & I2C_S_BUSY);
// become the bus master in transmit mode (send start)
I2C_DEBUG_STR("START"); // START
i2c->currentMode = I2C_MASTER;
*(i2c->C1) = I2C_C1_IICEN | I2C_C1_MST | I2C_C1_TX;
}

// send 1st data and enable interrupts
i2c->currentStatus = I2C_SEND_ADDR;
i2c->currentStop = sendStop;
*(i2c->C1) = I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TX; // enable intr
uint8_t target = (addr << 1) | 1; // address + READ
I2C_DEBUG_STR("T:"); I2C_DEBUG_HEX(target); I2C_DEBUG_STRB("\n"); // target addr
*(i2c->D) = target;
}

If this can be narrowed to the cause, I2C_C1_MST being wrong, then I can work backwards and try to find out how it is getting cleared.

You mention you have no test equipment available at the moment, do you happen to have a spare LED and resistor? Might be able to use that in combination with digitalWrite() to a pin as a signal probe.

Also regarding the timeout, I rechecked it, the i2c code is already using a uint32_t type on the timeout delays, so it can handle 100ms already. Just alter your timeout definition to use uint32_t:

static const uint32_t timeout = 100000; // this was 100, but I wanted 100 ms (!)

christoph
02-17-2014, 07:32 AM
Nox, have a break!

I have a different suspect that might be related to the LSM303, but not to your code: I have timed code that only runs if the acceleration derivative exceeds a certain threshold. This code is in an ISR that apparently is always entered between messages 3 and 4 (where the I2C code seems to hang) and I will check if this might cause the hang. Your code is more mature than mine, so chances are that the bug is in mine.

Commenting out this/these (I found 2) loops didn't change the behavior:

//while(*(i2c->S) & I2C_S_BUSY);

I've already fixed the uint16_t to be a uint32_t, this was a copy/paste error. I got acompilerwarning as soon as I changed the constant to a value bigger than 65535.

Regards

Christoph

christoph
02-18-2014, 06:36 PM
Nox, your code works perfectly. Thank you for your support!

This is what happened: When my device is moved, the LSM values are filltered differently than while it is more or less still. This special filter had an integer overflow after 32768 samples, which caused a division by zero, causing my device to "die". The timing was such that this always occured between my debug messages 3 and 4. The threshold for triggering this special filtering was a bit too low, so that it kicked in even when I was typing on my table with the breadboard next to me. This is why it occured after a seemingly random time. What a debugging adventure...

Regards

Christoph

nox771
02-18-2014, 07:46 PM
Excellent, glad to see you worked it all out.

Codefella
03-25-2014, 02:11 PM
Hi guys, i'm facing problems using the i2c_t3 code from within an IntervallTimer callback (need that for sampling).

The following part of code causes the application to hang:
i2c_t3.cpp:


uint8_t i2c_t3::finish_(struct i2cStruct* i2c, uint32_t timeout)
{
deltaT = elapsedMicros();
int i =0;
while(!done_(i2c) && (timeout == 0 || deltaT < timeout)) ;

...

}


done_(i2c) never gets true because ic2-->status is WAITING.
This is NOT happening when calling the same functions from outside the IntervallTimer callback.

I called endTransmission without a timeout, if called with, the following beginTransmission fails because
of the ic2-->status WAITING.

Any ideas, suggestion what i code to to narrow it furhter down? Maybe a known issue?

Thanks in advance, Andi

By the way: really great work nox :)

I2C Bus init:

Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_800);

Request device:


Wire.beginTransmission(devAddr);
Wire.write(regAddr);
Wire.endTransmission();
Wire.beginTransmission(devAddr);
Wire.requestFrom((int) devAddr, (int)min

nox771
03-25-2014, 05:56 PM
done_(i2c) never gets true because ic2-->status is WAITING.

Well, something here is off, if status is WAITING then done_() will return true (it is one of the true conditions).



This is NOT happening when calling the same functions from outside the IntervallTimer callback.

My first thought is that IntervalTimer is interrupt based, and so is i2c_t3. I'm not totally clear on how ARM ISRs handle interrupting each other, but on many micros this would be a blocking problem (IntervalTimer ISR blocking the i2c_t3 ISR). I don't know what timing tolerances you need, but one way to unblock it is to move the waiting part out of the ISR, and instead use the ISR to set flags.

Something like this:

volatile uint8_t flag=0;

void timerISR() // <-- IntervalTimer ISR
{
flag=1; // set flag based on IntervalTimer timing
}

void loop() // <-- main loop
{
if(flag)
{
// do I2C stuff here
flag=0;
}
}

This unblocks the i2c_t3 ISR, and will function largely the same as putting the code in the ISR, except with somewhat more timing variability depending on what else is in the main loop. However if you put wait states into ISRs, you are in a sense already introducing a lot of timing variability.






Wire.beginTransmission(devAddr);
Wire.write(regAddr);
Wire.endTransmission();
Wire.beginTransmission(devAddr); // <-- this line is unnecessary, it is only for Transmit, not Receive
Wire.requestFrom((int) devAddr, (int)min

FYI - You don't need the line above. A lot of people do this, but beginTransmission is actually just a Tx buffer setup routine. This is really the fault of bad function naming on the original Arduino API.

Codefella
03-25-2014, 07:22 PM
Thanks for your answer!


Well, something here is off, if status is WAITING then done_() will return true (it is one of the true conditions).

I will double check this, maybe as was just a bit confused because of to less coffee and to much debugging ;)



My first thought is that IntervalTimer is interrupt based, and so is i2c_t3. I'm not totally clear on how ARM ISRs handle interrupting each other, but on many micros this would be a blocking problem (IntervalTimer ISR blocking the i2c_t3 ISR). I don't know what timing tolerances you need, but one way to unblock it is to move the waiting part out of the ISR, and instead use the ISR to set flags.

Timing is rather critical because the code collects samples to run a FFT on it. Allready tried your suggestion before but (without further optimization) results are not very promising.





FYI - You don't need the line above. A lot of people do this, but beginTransmission is actually just a Tx buffer setup routine. This is really the fault of bad function naming on the original Arduino API.

Good point. The line was not in my original version of code by searching around the web i found this line (or similar) in a lot of code so i tried it out: same result.

I'm not sure but i think i read a posting of you where you wrote that you moved some routines to be handled by interrupt routines, is there a version of ic2_3t without ISR?
Apart from that i will go a bit deeper into ARM ISR handling, maybe i could work out a solution :confused:

nox771
03-25-2014, 07:43 PM
I'm not sure but i think i read a posting of you where you wrote that you moved some routines to be handled by interrupt routines, is there a version of ic2_3t without ISR?

For Master mode I think the original Wire library does not use ISR, however currently i2c_t3 always uses ISR.

Now that you bring this up though I'm thinking I'll go back and perhaps add a compile flag which would allow optional compile of Master mode as a non-ISR method. Actually I could do it that way or I could just add on another function (or overload), to indicate Tx/Rx without ISR (eg. immediate and blocking). It will possibly take some time before I can do this.

As an immediate fix, you could take the original Wire library, and graft on some of the setup code from i2c_t3, as far as setting the rate. Regarding your earlier begin() line, ext pullup and pins 18/19 are already the defaults IIRC, so you only need to patch in the rate adjustment to get it to more/less match what you are doing.

nox771
03-25-2014, 09:05 PM
Another option, you can leave the code as-is, and try altering the interrupt priority levels. I haven't tried this before in my code, but perhaps it is an easy fix.

From this (http://forum.pjrc.com/threads/17760-AVR-like-ISR%28%29?p=28111&viewfull=1#post28111) post, I saw the following:


The NVIC implements interrupt priority levels, from 0 to 255 (however, only the upper 4 bits are implemented for 16 different levels, so 0-15 are the same, 16-31 are the same, and so on). The lower the number, the higher the priority. By default, all interrupts are priority 0, which means no other interrupt is allowed until it returns. The NVIC keeps track of the current priority level as interrupts are run and when they return. So it doesn't matter if you use sei() inside your interrupt routine (or an ISR macro which puts a SEI instruction early in the function epilogue). The NVIC doesn't allow another interrupt of the same or lower priority until your function returns. If you want to use this feature to allow some interrupts to interrupt others, instead of fiddling with state at run-time, you just assign each interrupt a priority ahead of time. The NVIC and ARM hardware automatically manages everything.

So, it looks like the original thought is probably right, the ISRs are blocking each other. Now since the IntervalTimer is PIT, and PIT has 4 interrupts, you can try altering the priority on those. In the mk20dx128.h file you will see this near the bottom:

// 0 = highest priority
// Cortex-M4: 0,16,32,48,64,80,96,112,128,144,160,176,192,208,22 4,240
// Cortex-M0: 0,64,128,192
#define NVIC_SET_PRIORITY(irqnum, priority) (*((volatile uint8_t *)0xE000E400 + (irqnum)) = (uint8_t)(priority))
#define NVIC_GET_PRIORITY(irqnum) (*((uint8_t *)0xE000E400 + (irqnum)))

Also defined in there are the IRQ number definitions. So perhaps something like the following in your setup() routine:


void setup()
{
...
// probably do this after Wire and IntervalTimer setup (just so nothing resets to defaults)
NVIC_SET_PRIORITY(IRQ_PIT_CH0,16);
NVIC_SET_PRIORITY(IRQ_PIT_CH1,16);
NVIC_SET_PRIORITY(IRQ_PIT_CH2,16);
NVIC_SET_PRIORITY(IRQ_PIT_CH3,16);
// and just for completeness
NVIC_SET_PRIORITY(IRQ_I2C0,0);
NVIC_SET_PRIORITY(IRQ_I2C1,0);
...
}

I would be interested to know if something like that works.

Codefella
03-26-2014, 10:24 AM
It works :D

By setting the I2C IRQ Priority the code works without issues.

NVIC_SET_PRIORITY(IRQ_I2C0,0);

It doesn't matter if you change the NVIC Priority before or after the start of the intervall timer but i think set the priority before is more safe.

I have no success setting the Priority of the PIT IRQ below (which means higher value) the I2C IRQ priority...

Best Regards, Andi

PaulStoffregen
03-26-2014, 01:46 PM
Recently I added code to the hardware serial objects write() functions, to detect if their main program code is running at the same or higher priority than their interrupt code. I must admit, this started as something of an experiment. So far, the results seem very positive. Perhaps someday the various USB interfaces, Wire library and this improved I2C library might someday do the same?

The idea is to automatically fall back to automatically fall back to polling the hardware, because you know the interrupt you'd normally depend upon can't happen. It's tricky to get right, and certainly not an optimal way to do things. But if the user has structured their program in such a way that they're calling functions to transmit data while running within an interrupt, hogging the CPU to successfully complete the transmission is FAR better than a deadlock.

I put a nvic_execution_priority() function into the core library some time ago. It does the thorny work of figuring out the current priority level. It's not perfect, attempting a balance between speed and accuracy, but I'd highly recommend using this function if you try to implement this feature. In the future, as Teensy3 develops as a platform, my goal is to keep that function as fast as it can be, but if more complexities of deducing the ARM NVIC priority level are needed to make Arduino programs "just work" even in the cases where users do lots of I/O inside interrupts, that nvic_execution_priority() function will grow to accommodate the wider platform needs.

Using it just involves comparing the return value from nvic_execution_priority() with the priority your own interrupt uses. If it's less or equal, you can know your interrupt can't happen. Figuring out what to do in that case is tricky. For example, here's Serial3:



void serial3_putchar(uint32_t c)
{
uint32_t head;

if (!(SIM_SCGC4 & SIM_SCGC4_UART2)) return;
head = tx_buffer_head;
if (++head >= TX_BUFFER_SIZE) head = 0;
while (tx_buffer_tail == head) {
int priority = nvic_execution_priority();
if (priority <= IRQ_PRIORITY) {
if ((UART2_S1 & UART_S1_TDRE)) {
uint32_t tail = tx_buffer_tail;
if (++tail >= TX_BUFFER_SIZE) tail = 0;
UART2_D = tx_buffer[tail];
tx_buffer_tail = tail;
}
} else if (priority >= 256) {
yield(); // wait
}
}
tx_buffer[head] = c;
transmitting = 1;
tx_buffer_head = head;
UART2_C2 = C2_TX_ACTIVE;
}


You can see it falls back to manually checking the TDRE bit in the status register and manually takes a byte out of the full buffer and shoves it into the UART data register if the transmitter is ready. Normally you'd carefully disable interrupts when doing this sort of thing, but obviously in this case you know the interrupt can't occur.

Eventually I'm going to try doing something similar in the Wire library, the many USB interfaces, and other code that transmits data using interrupts. In fact the main point of this message is to serve as a reminder to myself to someday work on this (when I have lots of extra time.....)

I have no idea if this would be possible for this improved I2C library, but perhaps it's something to think about?

nox771
03-26-2014, 04:30 PM
Paul, I have to admit, I am now thoroughly confused.

Starting with Codefella's comment, changing the I2C priority to 0 fixes things for him (in which it appeared the PIT and I2C priorities were equal, so PIT was blocking I2C), but according to your earlier post here (http://forum.pjrc.com/threads/17760-AVR-like-ISR%28%29?p=28111&viewfull=1#post28111), the default priorities are already 0, so this by itself should not have changed anything. Are the default priorities set to something else? Could you perhaps direct me to where in the main code priorities are originally getting set so I can try to understand it? I think I'll have to study that and your Serial3 code for a while.

Further I was completely unaware that the main loop even had a priority level. I can understand it in terms of nested interrupts, but for the main loop it confuses me. Is this for some kind of RTOS/preemption system?

I can try to add in an adaptive transmit/receive system for the Master mode, but obviously it will take some time and studying. I seem to recall somewhere that ARM has a manual describing the NVIC, is that the place to read up on this?

onehorse
04-22-2014, 08:50 PM
Hi nox771,

Headroom suggests you might understand this problem I am currently having with the Wire.h TWI library. I know that one possible solution is to try out your new I2c_t3 library. Will it work on an AVR device like the 3.3V 8 MHz Pro Mini (ATMEGA 328-P)? I'd like to maintain compatibility between Arduino and Teensy, if possible. Here is a description of my problem:

The Invensense MPU-9150 (and -9250) is a 9DoF motion sensor that combines a 6 DoF gyro/accelerometer, the MPU-6050, with an Asahi Kasei AK8975A hall-sensor magnetometer into a single device. The microcontroller can access the magnetometer directly by placing the combined MPU-9150 device into a bypass mode through a CNTL register write that connects the internal AK8975A device to the MPU-9150 TWI bus. It is then directly addressed through its own 7-bit device address just like any other I2c device on the bus. This works perfectly well for the simple I2c.h and I2cdev.h TWI libraries but for some reason, when using Wire.h, the AK8975A is no longer accessible after the initial read of its WHO_AM_I register. This latter behavior is seen on both a 3.3V 8 MHz Pro Mini and on the Teensy 3.1. Does anyone have an idea what could be happening here?

nox771
04-22-2014, 10:47 PM
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.

onehorse
04-22-2014, 11:03 PM
Thanks for the reply. I think I will try this library and see if I have a different result:

http://www.dsscircuits.com/images/code/I2C_Rev5.zip

Headroom
04-23-2014, 12:11 AM
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
;-)

onehorse
04-23-2014, 01:42 AM
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!

nox771
04-23-2014, 03:03 AM
Unfortunately, I don't have a logic analyzer.

Do you happen to have a spare Teensy3.1 in addition to your project components? I have an unfinished project which might be able to solve that problem for I2C.

onehorse
04-23-2014, 04:42 AM
Yes, I have three Teensy 3.1s. What did you have in mind?

nox771
04-23-2014, 05:01 AM
Ok, I'll PM you the details (it is both unfinished and off-topic for this thread).

onehorse
04-24-2014, 02:16 AM
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:


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:



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.

nox771
04-24-2014, 03:37 AM
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.





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 <-- remove this

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(); <-- remove this
}



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:



while (Wire.available() < count);


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:




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

onehorse
04-24-2014, 05:07 AM
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!

onehorse
04-24-2014, 05:24 AM
I changed the Wire.h routines to the following at your suggestion:




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.

nox771
04-24-2014, 06:36 AM
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):

1870

1871

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





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

onehorse
04-24-2014, 04:09 PM
I just blindly (an dumbly, it turns out) copied the wire routines from the LSM9DS0 sketch (https://github.com/sparkfun/LSM9DS0_Breakout/blob/master/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.

Headroom
04-24-2014, 08:34 PM
I would still like to try your logic analyzer if you are still interested.


+1 That sounds very intriguing

nox771
04-24-2014, 08:42 PM
+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.

jimmayhugh
04-28-2014, 10:05 PM
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.

PaulStoffregen
04-28-2014, 11:29 PM
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.

nox771
04-29-2014, 03:50 AM
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.

maxbot
05-11-2014, 01:44 PM
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 ?

maxbot
05-11-2014, 01:44 PM
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 ?

nox771
05-11-2014, 04:18 PM
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.

esapode688
05-28-2014, 04:45 PM
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?

nox771
05-28-2014, 05:00 PM
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.

esapode688
05-28-2014, 06:19 PM
Your library Solved all My troubles with I2c and Teensy 3.1.

http://forum.pjrc.com/threads/25844-I2c-get-stuck-with-daisy-chained-Attiny85

THANK YOUUUUUU

nox771
05-28-2014, 08:20 PM
Great, good to hear that it worked for you.

robsoles
05-28-2014, 10:47 PM
... Why you didn't host it on google code?

Google smoogle. Github ftw.

victorg
06-26-2014, 09:27 AM
Hi,

I have a Teensy 3.1 and I am using this library to read gyro and accelerometer values from a MPU-9150.

I works just fine right after the code has been downloaded to the Teensy, but the values become incorrect (constant zero) if I switch to external power or unplug and reinserts the USB power. SDA and SDL signals are similar in both cases, so I suspect the issue to be timing related.

This is the code used to retrieve the 7bit value from the MPU-9150:



int16_t MPU9150_readSensor(int addrL, int addrH){
Wire.beginTransmission(MPU9150_I2C_ADDRESS);

Wire.write(addrL);
Wire.endTransmission(I2C_NOSTOP);
Wire.requestFrom(MPU9150_I2C_ADDRESS, 1, I2C_STOP);

byte L = Wire.read();

Wire.beginTransmission(MPU9150_I2C_ADDRESS);
Wire.write(addrH);
Wire.endTransmission(I2C_NOSTOP);

Wire.requestFrom(MPU9150_I2C_ADDRESS, 1, I2C_STOP);
while (Wire.available() < 1);
byte H = Wire.read();
return (H<<8)+L;
}


Do you have any idea what could be causing this issue? Or know if there is way to reset the Teensy every time is powers on?

Thanks

nox771
06-27-2014, 06:16 PM
Hi,
I works just fine right after the code has been downloaded to the Teensy, but the values become incorrect (constant zero) if I switch to external power or unplug and reinserts the USB power. SDA and SDL signals are similar in both cases, so I suspect the issue to be timing related.


I'm not sure, perhaps the MPU9150 needs time to reset/restart after the power supply change. You might try dumping the status after a transfer to get more information:



void print_i2c_status(void)
{
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;
default: Serial.print("I2C busy\n"); break;
}
}

int16_t MPU9150_readSensor(int addrL, int addrH){
Wire.beginTransmission(MPU9150_I2C_ADDRESS);
Wire.write(addrL);
Wire.endTransmission(I2C_NOSTOP);
print_i2c_status();
Wire.requestFrom(MPU9150_I2C_ADDRESS, 1, I2C_STOP);
print_i2c_status();

byte L = Wire.read();

Wire.beginTransmission(MPU9150_I2C_ADDRESS);
Wire.write(addrH);
Wire.endTransmission(I2C_NOSTOP);
print_i2c_status();

Wire.requestFrom(MPU9150_I2C_ADDRESS, 1, I2C_STOP);
print_i2c_status();
while (Wire.available() < 1);
byte H = Wire.read();
return (H<<8)+L;
}

onehorse
06-27-2014, 06:24 PM
Why do you read sequentially and not both data bytes with one Wire.read? This is the equivalent read I am using. It is similar but I am reading multiple bytes, up to 14, with the same read call:



void readBytes(uint8_t address, uint8_t subAddress, uint8_t count, uint8_t * dest)
{
Wire.beginTransmission(address); // Initialize the Tx buffer
Wire.write(subAddress); // Put slave register address in Tx buffer
Wire.endTransmission(I2C_NOSTOP); // Send the Tx buffer, but send a restart to keep connection alive
uint8_t i = 0;
Wire.requestFrom(address, count); // Read bytes from slave register address
while (Wire.available()) {
dest[i++] = Wire.read(); } // Put read results in the Rx buffer
}

onehorse
06-27-2014, 06:27 PM
I started using the i2c_t3 wire library and everything is working well, except I am getting some compile warnings:


In file included from MPU6050IMU.ino:29:0:
C:\Documents and Settings\Kris\My Documents\Arduino\libraries\i2c_t3/i2c_t3.h: In function 'uint8_t readByte(uint8_t, uint8_t)':
C:\Documents and Settings\Kris\My Documents\Arduino\libraries\i2c_t3/i2c_t3.h:592:19: note: candidate 1: size_t i2c_t3::requestFrom(int, int)
C:\Documents and Settings\Kris\My Documents\Arduino\libraries\i2c_t3/i2c_t3.h:590:19: note: candidate 2: size_t i2c_t3::requestFrom(uint8_t, size_t)
C:\Documents and Settings\Kris\My Documents\Arduino\libraries\i2c_t3/i2c_t3.h: In function 'void readBytes(uint8_t, uint8_t, uint8_t, uint8_t*)':
C:\Documents and Settings\Kris\My Documents\Arduino\libraries\i2c_t3/i2c_t3.h:592:19: note: candidate 1: size_t i2c_t3::requestFrom(int, int)
C:\Documents and Settings\Kris\My Documents\Arduino\libraries\i2c_t3/i2c_t3.h:590:19: note: candidate 2: size_t i2c_t3::requestFrom(uint8_t, size_t)
Binary sketch size: 37,560 bytes (of a 262,144 byte maximum)
Estimated memory use: 5,436 bytes (of a 65,536 byte maximum)


Any idea what it is unhappy about?

onehorse
06-27-2014, 06:30 PM
Just answered my own question. Apparently I needed to change the uint8_t to size_t in the request from call.

nox771
06-28-2014, 01:38 AM
Just answered my own question. Apparently I needed to change the uint8_t to size_t in the request from call.

Right, I use size_t for indexes to allow it to use large memory buffers. Using a uint8_t as the buffer index would limit it to 256 byte buffers.

onehorse
07-07-2014, 04:56 AM
I designed an add-on for Teensy 3.1 that solders onto the back surface pads. Before I soldered the first successful prototype I tested it on a breadboard using I2C pins 16&17; everything works. Then I "dry" mounted it on the back pads and rewrote the sketch I have been using to use Wire1 everywhere I had Wire and set the I2C pin designation to I2C_PINS_29_30. I could get no Serial output; usually, with a sensor attached to the Teensy or not, I expect to get a Serial message that the WHO_AM_I register is 0xFF or the correct value. I tried using a brand new Teensy and an old Teensy 3.1 workhorse, I tried both dry mounting the sensor board and using a bare Teensy all with the same null results. After I changed the settings back to Wire.begin(...I2C_PINS_16_17,..) and Wire for Wire1 everywhere, I got the Serial output I expected both with no sensor attached and with. So my question is, how well vetted is the Wire1 mode and the use of the I2C_PINS29_30? I can't seem to get it to work.

nox771
07-07-2014, 06:53 AM
So my question is, how well vetted is the Wire1 mode and the use of the I2C_PINS29_30? I can't seem to get it to work.

I've used Wire1 on pins 29/30 many times. The included dual_bus_master_slave and quad_master sketches were done using it and I tested those up to max rate. I would recommend using a simple sketch like the mentioned examples to see if the pins toggle at all (ideally using a scope, analyzer, or I2C traffic analyzer). If your sketch has additional complexity it could be interfering in other ways (priority levels, etc). Make sure I2C_BUS_ENABLE is set to 2 in the i2c_t3.h header file.

onehorse
07-07-2014, 04:30 PM
Thanks, I'm glad it's me and not the library! I'll take your advice and try it with a simple program using the dual_bus_master_slave and quad_master sketches as a guide and report on what I find. I suspect it is this:


Make sure I2C_BUS_ENABLE is set to 2 in the i2c_t3.h header file.

that is tripping me up.

onehorse
07-08-2014, 04:43 AM
It looks like the I2C_BUS_ENABLE defaults to 2 in the v6b 12c_t3.h library so that wasn't the problem. Instead, it was that I had no external pullups on SDA/SCL even though I was asking for them in the Wire1 call. I got everything to work once I figured this out; the little sensor board is happily i2c-ing away on the back Teensy 3.1 pads! Thanks for such a great library, without it I wouldn't have been able to finish this part of the project.

digitalCowboy
07-08-2014, 08:09 PM
It looks like the I2C_BUS_ENABLE defaults to 2 in the v6b 12c_t3.h library so that wasn't the problem. Instead, it was that I had no external pullups on SDA/SCL even though I was asking for them in the Wire1 call. I got everything to work once I figured this out; the little sensor board is happily i2c-ing away on the back Teensy 3.1 pads! Thanks for such a great library, without it I wouldn't have been able to finish this part of the project.
Glad you got it working. I've been soldering right angle headers to the back of the Teensy on my test boards and using the master/slave function - very useful library.

onehorse
07-08-2014, 08:38 PM
Agreed. I have LSM9DS0 and MPU9250 sensor read/fusion programs that run with i2c_t3 on Teensy 3.1 if you are interested. Most are on GitHub. (https://github.com/kriswiner/MPU-9250)

nox771
07-08-2014, 09:16 PM
Instead, it was that I had no external pullups on SDA/SCL even though I was asking for them in the Wire1 call.

Ah, the "pullups don't materialize when I summon them" problem, I think we have all run into that bug. Good to hear you got it working.

happyinmotion
08-17-2014, 09:33 AM
Is there a repository of libraries that have been changed from using Wire to using i2c_t3?

I ask as I've been using the Adafruit FRAM board and library (https://learn.adafruit.com/adafruit-i2c-fram-breakout). Switching this from Wire to i2c_t3 and implementing multiple byte reads and writes increased the speed by a factor of 300.

So how best to share this changed code?

Headroom
08-17-2014, 01:41 PM
I'd say post your code on Github and provide a link to this thread in the readme and perhaps in your code as a comment.

onehorse
08-17-2014, 05:23 PM
Perhaps in the same way Jeff Rowberg et al. have been trying to collect sketches for various sensors using their i2cdev.h library, it would make sense to have a dedicated i2c_t3.h library repository. I have several sketches that use i2c_t3.h; they are all on github but sorted by sensor or device not library. Maybe more generally it would make sense to have a Teensy-3.1-specific repository.

happyinmotion
08-17-2014, 07:50 PM
Yes, a dedicated repository seems like the best option to me.

I personally don't have time to host that and will put my code up on my BitBucket (git does my head in), but would be keen to contribute to a repository.

nox771
08-18-2014, 04:51 PM
Guys, if you can provide a link and description I can add it to the top post. Perhaps a "compatible libraries" section at the end of the post.

onehorse
08-18-2014, 05:12 PM
Here's a start:

https://github.com/kriswiner/MPU-9250

https://github.com/kriswiner/LSM9DS0

nox771
08-19-2014, 04:57 AM
Here's a start:

https://github.com/kriswiner/MPU-9250

https://github.com/kriswiner/LSM9DS0

Got it, thanks. I cleaned the top post and added a section at the end.

Constantin
08-19-2014, 02:28 PM
Hi Nox771,

Just wanted to thank you for your efforts. I recently switched to the new I2C library for humidity and temperature measurements (Si7021, HTU21) and everything worked as expected. No compile errors, etc. Next steps may include stepping up the bus speed to 400 to reduce the time the I2C interrogation of the sensor takes. But that presumes this non-CS grad will figure out a good non-blocking approach to extracting the info and turn off the internal heater also. Maybe use Metro as a quasi RTOS? Anyhow, that's for me to figure out.

Your efforts are super appreciated since they 'just work' and are easy to adopt.

nox771
08-20-2014, 03:44 AM
Hi Nox771,

Just wanted to thank you for your efforts. I recently switched to the new I2C library for humidity and temperature measurements (Si7021, HTU21) and everything worked as expected. No compile errors, etc. Next steps may include stepping up the bus speed to 400 to reduce the time the I2C interrogation of the sensor takes. But that presumes this non-CS grad will figure out a good non-blocking approach to extracting the info and turn off the internal heater also. Maybe use Metro as a quasi RTOS? Anyhow, that's for me to figure out.

Your efforts are super appreciated since they 'just work' and are easy to adopt.

Sure, glad it works well for you. Always better to hear that than bug reports. I do have more things I want to do with the library, but recently I've been overloaded with day job stuff, so unfortunately will take longer...

happyinmotion
09-13-2014, 07:14 AM
Here's a start:

https://github.com/kriswiner/MPU-9250

https://github.com/kriswiner/LSM9DS0

And here's one from me:

adafruit_fram_i2c_t3 (https://bitbucket.org/JezWeston/adafruit_fram_i2c_t3)

Uses i2c_t3 to access the Adafruit FRAM board (https://learn.adafruit.com/adafruit-i2c-fram-breakout). Read speeds of 12 microseconds per byte are possible, a major speed-up over using Wire.

nox771
09-16-2014, 02:35 PM
And here's one from me:

adafruit_fram_i2c_t3 (https://bitbucket.org/JezWeston/adafruit_fram_i2c_t3)

Uses i2c_t3 to access the Adafruit FRAM board (https://learn.adafruit.com/adafruit-i2c-fram-breakout). Read speeds of 12 microseconds per byte are possible, a major speed-up over using Wire.

Thanks, got it.

esapode688
09-30-2014, 02:29 PM
I've got 5 teensy 3.1 linked together on SDA0-SCL0

Each teensy got 11 attiny 85 bootloaded with arduino linked on SDA1-SCL1

I would like to exchange data between the teensys at 2.4 mhz and then having each teensy talking to the tinys at 100 khz on the second i2c line. If i include both i2c t3 and wire.h it gives me a compile error.

How do i make this?

Headroom
09-30-2014, 02:38 PM
You really have not provided enough info to answer the question wether it will be possible to run all the Teesny 3.1s at 2.4 MHz.
Annyway, there is no reason to run the two libraries in parallel on the Teesny 3.1. The i2c_t3 library will communicate fine with the ATTinys at 100KHz.
You'll use the wire.h library only when programming the ATTinys.

esapode688
09-30-2014, 03:23 PM
what do you need as info?

nox771
09-30-2014, 03:55 PM
As Headroom mentioned there is no reason to run i2c_t3 and Wire together. The i2c_t3 library is a superset of the Wire API, so it implements all the functions already. Moreover they use the same hardware peripheral, so running both of them at once is likely to cause problems.

You should be able to use both I2C interfaces from the T3.1 using i2c_t3, just refer to the examples (see top post in thread). However the library only works on Teensy3, so you will need to use a different library on the ATTiny devices.

MichaelMeissner
09-30-2014, 05:06 PM
You probably know this already, but in general for ATtiny85, there is no Wire.h either, but instead there is TinyWireM.h or TinyWireS.h, depending on whether your ATtiny85 is master or slave. In this case, I would imagine the ATtiny85's are slaves.

At times I've thought of writing a combined wire library for use in library functions, that would use virtual functions to do the actual i2c transfers. On a Teensy 3.x in the main .ino/.pde file you would include i2c_t3.h, in ATtiny85's you would include either TinyWire<x>.h, and in Uno's/Lenoardo's/Teensy 2.x's you would include Wire.h. That way you don't have to maintain 3 separate libraries of functions that do i2c stuff. Due to the way the Arduino IDE works, you would need to have 3 separate main programs, but you could move most of the code to a library if you routinely move projects from one processor to another.

The libraries that use i2c would need to take an argument for the Wire device to allow communication with more than one i2c controller.

<rant>
I realize that these libraries were often hacked together to meet an initial need, or were written for really small constraints, but at times I wish the infrastructure had been designed with an eye towards future growth. I also hate having to edit header files to specify things like screen sizes (for OLED monitors), when the user should be able to specify it as a parameter in the constructor.
</rant> :(

PaulStoffregen
09-30-2014, 07:51 PM
TinyWire.h seems like a pretty foolish design decision to me.

I'm sure it made good technical sense to a programmer, who wanted to make sure people knew it wasn't really the same software. We engineers are like that, generally risk adverse, so we tend to rationalize design decisions like this, that somehow it will be better for end users if it's clearly not the same code as Arduino's normal Wire.h. In truth, it's really just avoiding taking responsibility for a decision and committment to craft a drop-in replacement for such a well established API.

Of course, the net result is most of Adafruit's customers suffer needlessly when using Trinket with a huge Arduino code base with Wire.h in widespread use. If they merely changed the name, their product would "just work" in so many more cases.


Ok, there, I ranted too. Normally I don't rant like this, especially about other boards, and especially about awesome companies like Adafruit. But in this case, it's truly a silly decision that only hurts their own customers. I'm amazed it's gone on this long.

MichaelMeissner
09-30-2014, 08:53 PM
I don't recall if the Adafruit ATtiny85 supported TinyWire*.h (I don't have it in my trinket/gemma ide directories). They seem to have gone to using something called i2c.h instead of Wire.h/TinyWireM.h with some of the latest releases, which of course is yet another file.

Digispark and the MIT ATtiny85 distributions were the ones I saw TinyWireM.h. There were other Tiny*.h (TinyServo.h for instance) files that put me off doing more with the ATtiny85's.

I believe this is the source for the original TinyWireM.h: http://playground.arduino.cc/Code/USIi2c

And while we are ranting about incompatible changes others made, the decision of the Due 'designers' to put the SDA/SCL in the standard shield layout as the 2nd i2c device, means whenever I download a new i2c device, I need to check the code for crud like:

#ifdef __AVR__
#define WIRE Wire
#else
#define WIRE Wire1
#endif

because of course if it isn't an AVR it must be a DUE. This is why I feel the wire devices needs to be an argument to the data declaration.

Headroom
10-05-2014, 07:31 PM
You really have not provided enough info to answer the question wether it will be possible to run all the Teesny 3.1s at 2.4 MHz.
Annyway, there is no reason to run the two libraries in parallel on the Teesny 3.1. The i2c_t3 library will communicate fine with the ATTinys at 100KHz.
You'll use the wire.h library only when programming the ATTinys.
In Order to run the I2C bus at such high frequencies you need to keep the bus length very short. The issue is bus capacitance.

JohnDH
10-23-2014, 07:18 PM
The i2c_t3 Example will not compile for teensy 3.1
Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_2400);
does not produce a valid F_BUS value?

nox771
10-23-2014, 08:17 PM
The i2c_t3 Example will not compile for teensy 3.1
Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_2400);
does not produce a valid F_BUS value?

What bus frequency are you trying to use? At the moment the lib only supports 24MHz and 48MHz. It is on my to-do list to support more, but I haven't gotten there yet.

Edit: To be clear, for a 2400kHz I2C rate you would need to be compiling for either 96MHz or 48MHz frequencies. Are you set to one of those?

JohnDH
10-23-2014, 08:58 PM
There does not seem to be an I2C_RATE_XXXX value for either 24MHz, or 48MHz. Am I missing something?

nox771
10-24-2014, 03:15 AM
There does not seem to be an I2C_RATE_XXXX value for either 24MHz, or 48MHz. Am I missing something?

The bus freq is the internal freq of the ARM data buses. It is a setting which depends on the freq at which the T3 is run. For the Arduino IDE it is controlled by the menus:

2830

Both the 96MHz and 48MHz rates use an F_BUS setting of 48MHz. If you are building via some other IDE, eg. Eclipse, then it is set in the project properties (indirectly by setting F_CPU):

2831

JohnDH
10-24-2014, 01:34 PM
Hi, I running a copy of the example code
// Teensy3 I2C Master Test
// 08Mar13 Brian (nox771 at gmail.com)
I'm using the Arduino GUI, and test shows Board "Teensy 3.1", and CPU Speed "72MHz"
In setup I have Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_400);
When I compile I get this error message
C:\Users\John Harris\Documents\Arduino\libraries\i2c_t3\i2c_t3.c pp:220:10: error: #error "F_BUS must be 48 MHz or 24 MHz"
So where in the code is F_BUS defined? I had this same problem in my own code, so I went to the Example code to find a fix.

Headroom
10-24-2014, 02:55 PM
I'm using the Arduino GUI, and test shows Board "Teensy 3.1", and CPU Speed "72MHz"

What speaks against setting the clock frequency in the ArduinoIDE to 96MHz or 48MHz as nox771 suggested in his last post ?

Also, if you are using the Arduino Eclipse IDE (or plugin) you can also set the Teensy Clock frequency in the Project properties and will end up with the correct F_BUS.

nox771
10-24-2014, 03:25 PM
Sorry, but as I mentioned I don't have the 72MHz support in just yet (it is using F_BUS 36MHz). F_BUS is defined in the mk20dx128.h file, as follows:

#if (F_CPU == 168000000)
#define F_BUS 56000000
#define F_MEM 33600000
#elif (F_CPU == 144000000)
#define F_BUS 48000000
#define F_MEM 28800000
#elif (F_CPU == 120000000)
#define F_BUS 60000000
#define F_MEM 24000000
#elif (F_CPU == 96000000)
#define F_BUS 48000000
#define F_MEM 24000000
#elif (F_CPU == 72000000)
#define F_BUS 36000000
#define F_MEM 24000000
#elif (F_CPU == 48000000)
#define F_BUS 48000000
#define F_MEM 24000000
#elif (F_CPU == 24000000)
#define F_BUS 24000000
#define F_MEM 24000000
#elif (F_CPU == 16000000)
#define F_BUS 16000000
#define F_MEM 16000000
#elif (F_CPU == 8000000)
#define F_BUS 8000000
#define F_MEM 8000000
#elif (F_CPU == 4000000)
#define F_BUS 4000000
#define F_MEM 4000000
#elif (F_CPU == 2000000)
#define F_BUS 2000000
#define F_MEM 1000000
#endif


The blue choices above are what is supported at the moment. I'll see if I can rework to support all these. For right now you will need to choose one of the blue choices from the menu.

JohnDH
10-24-2014, 05:50 PM
So I find changing the CPU clock speed to either 96MHz, or 48MHz, and the compile error goes away.
Great, now I can move on with my own code Thanks

Swap_File
11-16-2014, 04:26 AM
I'm going to be getting data from i2c slave devices that can be hot plugged in and out (bad idea, I know), so I've been playing around with a way to make sendTransmission and sendRequest recover from bus lockups (I was running into them during testing).

In the library commented out the two instances of:


while(*(i2c->S) & I2C_S_BUSY);

Then, in my program, I accessed my i2c device with code similar to this (proof of concept):


if(Wire.done()){
if (millis() - last_sample > 50){
Wire.sendRequest(target,256,I2C_STOP);
last_sample = millis();
count = 0;
}
}
else
{
//set timeout here
if (millis() - last_sample > 1000){
//change i2c pins back to digital IO
pinMode(19,OUTPUT);
pinMode(18,INPUT);

//watch for sda to go high
//try pulsing the clock a max of 9 times
while (digitalRead(18) == 0 && count < 10){
//pulse clock once
digitalWrite(19,HIGH);
digitalWrite(19,LOW);
digitalWrite(19,HIGH);
count++;
}
//try to restart wire using initial settings
Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_2400);
}
}

It seems to work, but I need to clean it up so everything isn't hard-coded, maybe factor out some bits and put it in a copy of the library. And I'm pretty sure this breaks all multi-master support.

I'm thinking that there really should be a delay between the digitalWrite commands proportional to the I2C rate, especially for slower devices, or maybe this delay could be handled by banging out the clock slowly over repeat calls so as not to block.... I'll have to think about it.

Any thoughts?

Headroom
11-16-2014, 11:18 AM
IIRC the library already has a timeout-&-restart feature to deal with bus lock- ups.
Some I2C hardware is designed to be hot pluggable e.g. Some I2C bus buffers allow hot plugging devices on the bus.

Swap_File
11-16-2014, 01:02 PM
The timeouts in the library seem to only deal with slaves that go silent and disappear, I want to handle slaves that disappear and come back thinking they still need to send some data (and are trying to hold SDA low).

I Suppose I could also detect if something is holding SCL low, but there isn't anything that could be done about it (other than generate an error).

I probably shouldn't be commenting out "while(*(i2c->S) & I2C_S_BUSY)", I could add an adjustable timeout like everything else.

Swap_File
11-17-2014, 01:06 AM
I realized that modifying the library would be a bit out of the scope of my quick little project, so I did what needed externally.

Here is an quick and dirty example that is non-blocking (As long as you stick to the non-blocking functions) and can recover from SDA being left down by a slave trying to send data.

2951

The only modification I did to the library (6b) was to comment out "while(*(i2c->S) & I2C_S_BUSY)".

You may need to add in a mechanism to slow down the bit-banging of the recovery clock depending on the devices you are using.

Swap_File
11-19-2014, 11:13 AM
Initially I based my reset off of this: 3.1.16 Bus clear (http://www.nxp.com/documents/user_manual/UM10204.pdf)

Page 20: "If the data line (SDA) is stuck LOW, the master should send nine clock pulses. The device that held the bus LOW should release it sometime within those nine clocks. If not, then use the HW reset or cycle power to clear the bus."

That seemed to catch a lot of the problems, but occasionally it would still lock up afterwards with both SDA and SCL high.

Then I found this: Solution 1: Clocking Through the Problem (http://www.analog.com/static/imported-files/application_notes/54305147357414AN686_0.pdf)

Page 2: "It is the masterís job to recover the bus and restore control to the main program. When the master detects the SDA line stuck in the low state, it merely needs to send some additional clocks and generate a STOP condition. How many clocks will be needed? The number will vary with the number of bits that remain to be sent by the slave. The maximum would be 9."

OK, so I need to bit bang a STOP condition... K20 Sub-Family Reference Manual (https://www.pjrc.com/teensy/K20P64M72SF1RM.pdf)

Page 1185: "The master can terminate the communication by generating a STOP signal to free the bus. A STOP signal is defined as a low-to-high transition of SDA while SCL is asserted. The master can generate a STOP signal even if the slave has generated an acknowledgement, at which point the slave must release the bus."

My final code looks like this:


void unlockthebus(void){

Serial.println("Resetting i2c Bus...");

//change i2c pins back to digital IO
pinMode(19,OUTPUT);
pinMode(18,INPUT);

int count = 0;

//watch for sda to go high
//try pulsing the clock a max of 9 times
while (digitalRead(18) == 0 && count < 10 ){
//pulse clock once
digitalWrite(19,HIGH);
digitalWrite(19,LOW);
digitalWrite(19,HIGH);
count++;
Serial.println(count);
}

//bit bang a stop
pinMode(18,OUTPUT);
digitalWrite(18,LOW);
digitalWrite(19,HIGH);
digitalWrite(19,LOW);
digitalWrite(18,LOW);
digitalWrite(19,HIGH);

//try to restart wire using initial settings

Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_2400);

Serial.println("Fixed.");
}

I didn't need any delays between writing bits, but this will likely depend on your device. It would be best to bit bang at a speed similar to your i2c bus.

Then I altered sendTransmission and sendRequest to return true or false depending on I2C_S_BUSY. This way I can't get stuck in the while loop with a hung bus.

I replaced:

while(*(i2c->S) & I2C_S_BUSY)

with:

if(*(i2c->S) & I2C_S_BUSY){
return false;
}

(This could have an timeout before returning false if desired.)

I'll upload some full example code once I get it cleaned up...

nox771
11-19-2014, 06:49 PM
This is an interesting idea. The overall idea seems reasonable, although pulsing single clocks instead of blocks of nine might be better. I don't know if it makes sense to roll this into the main library calls or if it is better served as an example. I could add this technique as a bundled example.

The trouble with incorporating this as a part of the library is that it is specific to a particular setup (single master, immediate call). By making the library call immediately exit if busy, it requires the user code to expect this situation. I'm wondering in particular about background calls where it is used in more of a launch and forget manner. I'll need to think about this some more.

Regarding the STOP I don't know if a bit-bang solution can work. That would require muxing the I2C peripheral off the pins, do the bit-bang, and then muxing it back in (however it's internal state might still think it is in a busy state). I'll try and study this and see if there is a clean way to exit this stuck condition.

Swap_File
11-21-2014, 01:41 AM
While I seem to have it working, my solution doesn't seem to agree with the datasheets... I ended up doing a lot of trial and error until I could get the bus to reliably recover. After I got it working well enough, I set up a little machine with relays to continually connect and disconnect SDA and SCL quickly for a few hours as a stress test. Not very scientific, but it survived, which is good enough for me.

I also had to add a status reset to the start of begin() to prevent some other lockups.

Attached is the scope outputs between two Teensy 3.1s. I was able to get about 700kbit/s half duplex (with no CRC8 errors), with a bit of bus time unused. The master uses about ~8% of the CPU time on a 96mhz Teensy 3.1, but it's difficult to measure with only micros().
2970

Slightly modified library
2967
2968

Master and Slave example
2969
2972

David Goodenough
11-24-2014, 10:26 AM
I need to do non-blocking write then read requests to an ic2 client (I am the master) in order to read the value of a register on the client.
I have looked at the various methods and the code, and I see that the low level code is capable of doing a transmit followed by a receive, but I can only see how to do a general request (i.e. address the client's address with the read bit set) not how to set up a write of the address (with read bit) and the register number followed by a receive. Is this possible, if so how, if not what is the simplest way to do a register read in a non-blocking fashion?

Swap_File
11-24-2014, 01:23 PM
You could do something like my Non-Blocking master example from one post above. You'd likely adjust the mode 0 section to write whatever the command byte is instead of a whole array like I was doing.

You don't need to use my modified library unless you have problems with the i2c bus locking up.

First I'd set the address you want to read:

Wire.beginTransmission(slave_address);
Wire.write(slave_registery_address);
Wire.sendTransmission(I2C_NOSTOP); //Nostop because we are reading the data we requested next

Wait for sendTransmission to finish then...

Wire.sendRequest(target,MEM_LEN,I2C_STOP) //request and start reading in the data, with a i2c_stop at the end

Wait for sendRequest to finish then....

while(Wire.available()) {
recbuffer[index++]= Wire.readByte(); //read the data sitting in the i2c buffer into an array (check bounds if this is real code)
}


I split the various i2c writes and reads into modes that increment after completion, that way I can keep looping through my main loop without blocking.

astroyam
11-24-2014, 03:53 PM
I have been compiling codes with i2c_t3 with no problem for Teensy 3.0 board type, but if I switch to Teensy 3.1 board type I get errors of the type:
'C:\Users\chris\Documents\Teensy_6_12_13\arduino-1.0.5/master.ino:326: undefined reference to `i2c_t3::beginTransmission(unsigned char)'

For example, I try to compile the example Master.ino in the i2c_t3 library. It compiles fine with Teensy 3.0, but I get the errors using Teensy 3.1 type. (No changes to the code itself at all.)
I am using the V6b library and Arduino 1.0.5.

Is there something else that needs to be switched for use with Teensy 3.1?

nox771
11-24-2014, 05:48 PM
I need to do non-blocking write then read requests to an ic2 client (I am the master) in order to read the value of a register on the client.
I have looked at the various methods and the code, and I see that the low level code is capable of doing a transmit followed by a receive, but I can only see how to do a general request (i.e. address the client's address with the read bit set) not how to set up a write of the address (with read bit) and the register number followed by a receive. Is this possible, if so how, if not what is the simplest way to do a register read in a non-blocking fashion?

The way I2C works, is that in receive operation it sends the target address with the READ bit set, then on the next byte the direction reverses and the master can receive data from the slave. There is no such "extended" READ operation which combines a multi-byte write, and then the READ. However you can achieve this using a two-step process as follows.

If the device requires setting up a register address or such, then you do that by first sending a WRITE command with the settings (register address, subcommands, etc), using the I2C_NOSTOP flag, then the READ command to capture the response. The preceding I2C_NOSTOP flag will cause the subsequent READ command to send a Repeated-START (without an intervening STOP), and the device will recognize the WRITE + READ operation as a single continuous operation.

The method Swap_File shows above is correct for doing this using entirely non-blocking operations (non-blocking WRITE (with I2C_NOSTOP), wait for done, then non-blocking READ, wait for done, then read Rx buffer).

Try something like that first. If you still have problems, send what device type you are trying to talk to along with a link to the datasheet. We can probably code up a routine for it in short order.

nox771
11-24-2014, 05:57 PM
I have been compiling codes with i2c_t3 with no problem for Teensy 3.0 board type, but if I switch to Teensy 3.1 board type I get errors of the type:
'C:\Users\chris\Documents\Teensy_6_12_13\arduino-1.0.5/master.ino:326: undefined reference to `i2c_t3::beginTransmission(unsigned char)'

For example, I try to compile the example Master.ino in the i2c_t3 library. It compiles fine with Teensy 3.0, but I get the errors using Teensy 3.1 type. (No changes to the code itself at all.)
I am using the V6b library and Arduino 1.0.5.

Is there something else that needs to be switched for use with Teensy 3.1?

You need to list what kind of build environment you are using, it is Teenyduino (what version?) and Arduino? Is it Eclipse, or some Makefile or such. For Teensyduino+Arduino, I don't think I've seen build errors as long as the pulldown menu options are set correctly. For other build environments it is more complicated and you need to have the linker and symbols setup correctly.

Nantonos
01-10-2015, 05:37 PM
I ended up accidentally using this library. I had breadboarded a circuit with the MCP23017 I2C expander, using the Adafruit library (https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library); this worked. I then laid out a PCB and it was more convenient to use the alternate I2C pins. I had somehow imagined that setSDA and setSCL were already implemented, oops. Its now working with i2c_t3 instead of Wire. I had to make a hacked copy of the Adafruit library (just a rename, different include, and changed
Wire.begin(); to
Wire.begin(I2C_MASTER, 0x00, I2C_PINS_16_17, I2C_PULLUP_EXT, I2C_RATE_400);.

Nantonos
01-10-2015, 05:48 PM
Incidentally the MCP23017 is supposed to work at 100kHz, 400kHz, and 1.7Mhz. I briefly tried

Wire.begin(I2C_MASTER, 0x00, I2C_PINS_16_17, I2C_PULLUP_EXT, I2C_RATE_1500);
without success. I'm using external 2k2 pullups, maybe that speed needs smaller ones.

nox771
01-10-2015, 06:22 PM
Incidentally the MCP23017 is supposed to work at 100kHz, 400kHz, and 1.7Mhz. I briefly tried

Wire.begin(I2C_MASTER, 0x00, I2C_PINS_16_17, I2C_PULLUP_EXT, I2C_RATE_1500);
without success. I'm using external 2k2 pullups, maybe that speed needs smaller ones.

Yes the speeds are very dependent on wiring and pullups. If your devices have strong pulldown capability you can get aggressive and remove the external pullups and switch it to internal pullups (very low resistance ~200 ohms). I've run back-to-back T3s using internal pullups only, and I can get the 2.4MHz freq to run.

Also there are many freq settings between 400k and 1.5M, so you can iterate through the settings to see if an intermediate freq will work.

nox771
01-13-2015, 12:05 AM
All,

I'm preparing an update for this library. It isn't done yet, I'm working on it now, but one of the recurring themes in the thread is that of renaming the library (for those that don't like names to be a jumble of letters/numbers/underscores). I'm ok with that. I'm looking for input as to the preferred name. The following are previous suggestions:


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.

FastWire has a negative in that there are already some Arduino libs using that name (for AVR I think). WireFast seems unused, although I didn't look hard. Could also sub thesaurus words for "fast", something like hyperWire.

Anyway open to suggestions (if nothing else maybe I'll just add some more letters/numbers/underscores to i2c_t3).

onehorse
01-13-2015, 01:48 AM
Since it is currently limited to use with the Teensy 3.1 I would suggest TeensyWire.h to make it clear that this is a Teensy-only Wire.h-like library. It could also be Teensy3.1Wire.h but that is a bit cumbersome!

nox771
01-15-2015, 05:29 PM
Here is a teaser on the upcoming lib, these begin() lines have a couple new elements which some might spot. Analyzer plots go with the respective lines. Of interest is the bottom wave in each plot. That indicates I2C ISR activity. Also of interest is how similar the SDA/SCL waves are. Regardless of control method the I2C peripheral appears to be bottlenecking the throughput. This is likely the highest I2C performance that these interfaces can manage.

I have other improvements also, but I'm still testing it all, so no release just yet.


Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_3000, I2C_OP_MODE_ISR);

3326


Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_3000, I2C_OP_MODE_DMA);

3327

Headroom
01-15-2015, 10:23 PM
I would actually not change the name. This library has already made a name for itself ;-)
Looking forward to new version!

nox771
01-18-2015, 09:19 PM
All, I've uploaded the new v7 library, and modified the top post accordingly. I'll try to summarize the changes here.

Clocking

The library now supports all Teensyduino F_BUS frequencies: 60MHz, 56MHz, 48MHz, 36MHz, 24MHz, 16MHz, 8MHz, 4MHz, 2MHz
In addition new rates have been added: I2C_RATE_1800, I2C_RATE_2800, I2C_RATE_3000

The supported rates depend on the F_BUS setting which in turn depends on the F_CPU setting. The current F_CPU -> F_BUS mapping (Teensyduino 1.21), is as follows. For a given F_BUS, if an unsupported rate is given, then the highest freq available is used (since unsupported rates fall off from the high end).


I2C_RATE (MHz)
F_CPU F_BUS 3000 2800 2400 2000 1800 1500 1200 1000 800 600 400 300 200 100
----- ----- ---------------------------------------------------------------------
168M 56M y y y y y y y y y y y y y
144M 48M y y y y y y y y y y y y
120M 60M y y y y y y y y y y y y y y
96M 48M y y y y y y y y y y y y
72M 36M y y y y y y y y y y
48M 48M y y y y y y y y y y y y
24M 24M y y y y y y y y
16M 16M y y y y y y
8M 8M y y y y
4M 4M y y
2M 2M y

The rates are not directly equivalent to SCL clock speeds. I've measured the SCL rates for the 48/56/60MHz F_BUS speeds on a logic analyzer (refer to notes in .cpp file, setRate function), and the results are inconsistent. The peripheral limits the actual SCL speeds to well below the theoretical speeds. To get a better idea of throughput I've measured the transfer time for a 128 byte transfer across different F_CPU / F_BUS / I2C_RATE combinations (specifically the interesting overclock speeds). This is shown below.

3345

A few takeaways here, are:
1) To the first order, it doesn't really matter what F_CPU / F_BUS combination you use, for a given I2C_RATE the variation is only slight.
2) The 48MHz bus speed seems to work slightly better than the irregular 56MHz / 60MHz speeds.
3) The F_CPU appears to be more important than F_BUS, as the peak performance is at the 144MHz / 48MHz corner.
4) The difference from standard 100kHz Arduino rate to the peak rate is about 10x. Likewise from standard 400kHz rate to peak rate is about 2.5x.


New Operating Modes

Old versions of the library were exclusively interrupt based. There are now two new modes of operation: DMA and Immediate. These modes are only for Master operation.

The library supports DMA mode transfers for Master send and receive. DMA mode has little to no effect on I2C operating speed, however it does greatly reduce the number of ISR calls needed to service the I2C. You can observe this in the following captures, whereby the bottom plot in each indicates the ISR activity.

Interrupt Mode:
3346

DMA Mode:
3347

The gain that can be expected from this depends on a particular applications traffic. For many applications it may be marginal. On a 256 byte transfer at 400kHz, I've measured about a 10% increase in available CPU time when using DMA. So for a large background task such as driving a display, or using I2C as a networking channel, it may be beneficial. For small message, intermittent traffic such as sensors, there may be little benefit.

Unfortunately there is no DMA mode for Slave operation. I'll elaborate a bit on this and other DMA .. issues .. below.

Also, in cases of unavailable DMA channels, the library is configured to automatically fall back to standard interrupt mode.

The other new mode is Immediate. In this mode the ISR is not used, the call will loop and wait for the I2C operations to complete. As such this mode is always blocking regardless of call. Configuring for Immediate mode essentially makes the library operate the same as the standard Wire library. However this mode is also utilized in special cases involving priority (see next).


Priority Escalation

Since the library is generally configured to be interrupt based, there is always the problem of someone launching I2C calls from inside a higher-priority interrupt, thereby blocking the I2C ISR. To fix this, prior to engaging the ISR, the library will first try to determine the priority of the calling function, and if necessary it will adjust the I2C priority to a high enough level to exceed that of the calling function. If this is not possible (eg. calling function has a priority of zero), then it will revert to Immediate mode. As such it should not be possible to block the I2C from running.


New Functions

The following functions have been added to support the above features:
1) Added new begin() functions to allow setting the initial operating mode. This just adds an operating mode argument to the existing functions. If the mode argument is not specified then begin() calls will default to ISR mode (same as previous behavior):

begin(i2c_mode mode, uint8_t address, i2c_pins pins, i2c_pullup pullup, i2c_rate rate, i2c_op_mode opMode) - Master or Single-address Slave
begin(i2c_mode mode, uint8_t address1, uint8_t address2, i2c_pins pins, i2c_pullup pullup, i2c_rate rate, i2c_op_mode opMode) - Address-range Slave
whereby i2c_op_mode can take the following values: I2C_OP_MODE_ISR, I2C_OP_MODE_DMA, I2C_OP_MODE_IMM

2) Added new functions:

uint8_t setOpMode(i2c_op_mode opMode) - used to change operating mode on the fly (only allowed when bus is idle)
void sendTransmission() - non-blocking Tx with implicit I2C_STOP, added for symmetry with endTransmission()
uint8_t setRate(uint32_t busFreq, i2c_rate rate) - used to set I2C clock dividers to get desired rate. busFreq allows devices which alter their running frequency to recalibrate the I2C rates to the new freq. This form uses an i2c_rate enum argument.
uint8_t setRate(uint32_t busFreq, uint32_t i2cFreq) - used to set I2C clock dividers to get desired SCL freq. busFreq allows devices which alter their running frequency to recalibrate the I2C rates to the new freq. This form uses a uint32_t frequency argument (quantized to nearest i2c_rate based on empirical measurements)

3) Added new Wire compatibility functions (mostly uint8_t type casts):

void setClock(uint32_t i2cFreq) - (note: this is actually a degenerate form of setRate() with busFreq == F_BUS)
uint8_t endTransmission(uint8_t sendStop)
uint8_t requestFrom(uint8_t addr, uint8_t len)
uint8_t requestFrom(uint8_t addr, uint8_t len, uint8_t sendStop)


Fixes and Cleanup

Some bug fixes were done in Slave range code and in arbitration (note: arbitration has never been vetted on this library, it is on my to-do list).
Removed the I2C1 defines as they are redundant now that kinetis.h has them
Completely removed all debug code and the rbuf dependency
Cleaned and reworked the examples to simplify the code, test new things, and eliminate debug. In the examples I tried to group the Wire calls into a tight block so it is obvious which commands are being used.
Added an interrupt example to test running I2C from inside an ISR.


To-Do
1) When I get some time I'm going to investigate the interesting anti-lockup technique that Swap_File posted in #173: https://forum.pjrc.com/threads/21680-New-I2C-library-for-Teensy3?p=58368&viewfull=1#post58368
2) Verify arbitration


Other DMA Stuff

Ok, so now on the topic of DMA I have to rant a bit:

Unless I am missing a magic bit setting somewhere (which really should not exist because it would have to be labelled "Breaks I2C Protocol Bit"), the DMA implementation with this I2C peripheral is absolute ****. You see, one of the fundamental elements of the I2C protocol is the notion of an Acknowledge bit. When you talk to a Slave you will either get a ACK or a NACK. When Slaves have a problem or if they don't exist you get a NACK. When you get a NACK you STOP what you're doing. Now DMA sets up an automated transfer from point A to point B. You would think if you were in the business of moving things from point A to point B and you got a NACK you would STOP. Perhaps something that would result in a DMA error yes? NO!! this DMA system will happily blast out whatever you told it to regardless of the ACK/NACK responses.

Is this a problem? Technically (ideally) NO. For well constructed Slaves if they don't recognize their address at the beginning of the message they should ignore the entire message - even if it involves a string of 1000 bytes and 1000 NACKs. More importantly though, for the Master device it tosses the entire notion of verified transmission out the window - the Master in this case can only know if the message was sent, not if it was received.

To circumvent this pile, I've implemented the following workaround - when in DMA mode, the first and last bytes of the messages are sent via ISR-based routines. The bulk of the message in the middle is sent via DMA. The reason for this is that if the first byte (address) NACKs then you know the Slave doesn't exist, and it will STOP immediately. The last byte can also generate a NACK, which would tell you if the Slave died or had some kind of error somewhere between the address and the end of the message. There might be corner cases which are not caught, but for most cases it should work well enough. However, to mitigate the overhead of this nonsense I've set the minimum overall message length to 5 bytes when in DMA mode, eg. Address (ISR) - 3 bytes data (DMA) - last byte data (ISR). Messages below that size will automatically transfer in ISR mode, even if DMA mode is configured. That's for Master Tx mode, and for Master Rx mode a similar approach is used.

Slave mode is a different story. I am unable to figure out a way to make Slave mode work with DMA. Slave Tx/Rx transmissions are indeterminate in size, and DMA blocks normal ISR code from running, so there is no apparent way to terminate a transfer. Stupidly, even major bus events like a STOP do not trigger an ISR interrupt when DMA is active. In theory a similar fix to the rising-SDA hack used by the normal ISR Slave Rx code could be used to terminate a transfer, but at that point you are running an ISR on every byte, so where is the benefit of DMA versus the normal ISR code? Because of this, Slave mode will only work in ISR mode regardless of requested mode in begin() or setOpMode().

Another caveat - Timeouts may behave erratically when using DMA. This is because the DMA bulk transfer doesn't trigger the ISR, so it never gets a chance to detect the timeout condition and exit early. It may detect the timeout on the last byte when the ISR re-engages, but depending on bulk transfer length it could be any amount of time.

Note that I'm not trying to dissuade anyone from using DMA mode (I use it myself), and when it works it could be of some benefit, and really it is essentially free so why not. However just be aware there are workarounds in play (similar to Slave mode hack). So report any problems you have.

zminjie
01-29-2015, 04:04 AM
Brian, first of all, thanks for this awesome library.

I have a couple questions regarding using only the internal pullups of the Teesny. In several of your posts, you mentioned it's okay to use the internal pullups as long as the slaves are "strong" enough to drive the line low. How do you determine if the slave is capable of pulling the line low with the internal pullup?

The reason I'm asking is that I have a couple of IMU breakouts that all share the same slave address. I'm using an A/D multiplexer to connect them all to the same I2C bus. The problem is that all the breakouts were designed to be directly connected to the bus without a mux inbetween (they all have on board pull-ups). When I use the bus in external pullup mode (since the boards have pull ups), it doesn't work when they are connected through the mux. Wire.endTransmission hangs, and looking at the code, I assume it's because the line is not being pulled up so it thinks there is a bus contention. However, if I use the bus in internal pullup mode, everything works just fine. I'm just wondering if the internal pull-ups are sufficient or should I get some 2.2k resistors to pull the lines up even with the on-board pull-ups on the IMUs.

Nantonos
01-29-2015, 06:42 AM
All, I've uploaded the new v7 library, and modified the top post accordingly. I'll try to summarize the changes here.

Clocking

The library now supports all Teensyduino F_BUS frequencies: 60MHz, 56MHz, 48MHz, 36MHz, 24MHz, 16MHz, 8MHz, 4MHz, 2MHz
In addition new rates have been added: I2C_RATE_1800, I2C_RATE_2800, I2C_RATE_3000

The supported rates depend on the F_BUS setting which in turn depends on the F_CPU setting. The current F_CPU -> F_BUS mapping (Teensyduino 1.21), is as follows. For a given F_BUS, if an unsupported rate is given, then the highest freq available is used (since unsupported rates fall off from the high end).


I2C_RATE (MHz)
F_CPU F_BUS 3000 2800 2400 2000 1800 1500 1200 1000 800 600 400 300 200 100
----- ----- ---------------------------------------------------------------------
168M 56M y y y y y y y y y y y y y
144M 48M y y y y y y y y y y y y
120M 60M y y y y y y y y y y y y y y
96M 48M y y y y y y y y y y y y
72M 36M y y y y y y y y y y
48M 48M y y y y y y y y y y y y
24M 24M y y y y y y y y
16M 16M y y y y y y
8M 8M y y y y
4M 4M y y
2M 2M y



Does your library assume that F_BUS and F_CPU always have the relations listed in your table? Or is that just what Teensy 3.1 uses?

Asking because Teensy3-LC will apparently have F_CPU 48M and F_BUS 24M (where Teensy 3.1 would have 48M). I'm hoping that since F_BUS 24M is already supported, things will just work with the new board.

nox771
01-30-2015, 02:56 AM
In several of your posts, you mentioned it's okay to use the internal pullups as long as the slaves are "strong" enough to drive the line low. How do you determine if the slave is capable of pulling the line low with the internal pullup?

Well the short answer is if it works then you are good.

The technical answer has to do with the pulldown strength of the slave device. Slaves generally pull the bus low through an NMOS device. The NMOS device is sized to pull down a particular current at the SDA low voltage. So for instance in a datasheet it might say the SDA can sink 5mA at 0.3V (I'm just pulling numbers out of air here, there are minimum requirements given by the I2C spec, and specific numbers sometimes given in the device datasheet). So Ohm's law gives us R = V/I = 300mV/5mA = 60 ohms for the NMOS on-impedance.

So using that, do a resistor divide with the pullup device (parallel combination them if there are multiple) and pullup voltage, and you get the minimum SDA voltage that a slave can pull. If you know the low switching point of the T3 and the slave then you will know if they will recognize the SDA low signal.

Now for the T3 the internal pullups are something like 200 ohms. This is an unnaturally low impedance for an I2C pullup. The above example slave could only pull SDA down to something like 23% of SDA pullup voltage. However for real slaves many will oversize the pulldown device (because it's easy to do) and the true switching point is generally much higher than the input-low given by the spec. So in practice the T3 pullup device usually works ok.



The reason I'm asking is that I have a couple of IMU breakouts that all share the same slave address. I'm using an A/D multiplexer to connect them all to the same I2C bus. The problem is that all the breakouts were designed to be directly connected to the bus without a mux inbetween (they all have on board pull-ups). When I use the bus in external pullup mode (since the boards have pull ups), it doesn't work when they are connected through the mux. Wire.endTransmission hangs, and looking at the code, I assume it's because the line is not being pulled up so it thinks there is a bus contention. However, if I use the bus in internal pullup mode, everything works just fine. I'm just wondering if the internal pull-ups are sufficient or should I get some 2.2k resistors to pull the lines up even with the on-board pull-ups on the IMUs.

This might be because there is no pullup on the T3 side of the mux. Both the Master and Slaves need to see a pullup device. If each of your Slaves has a pullup, but the mux blocks the T3 from seeing it, then it won't work.

The only penalty of T3 pullups versus 2.2k (if they both work), is that the T3 will have a higher current draw during SDA/SCL low periods. This is normally a transient current, but if the bus gets stuck then you might get a current of 3.3V / 200 ohms = 16.5mA. If your Slave can handle sinking that then it should be ok.

nox771
01-30-2015, 03:03 AM
Does your library assume that F_BUS and F_CPU always have the relations listed in your table? Or is that just what Teensy 3.1 uses?

Asking because Teensy3-LC will apparently have F_CPU 48M and F_BUS 24M (where Teensy 3.1 would have 48M). I'm hoping that since F_BUS 24M is already supported, things will just work with the new board.

At the moment it is hardcoded based on F_BUS only. That is F_CPU is defined which gives F_BUS, but the code only looks at F_BUS. However the F_BUS to SCL ratios are fixed, so if the new part has a different divider scheme then there will need to be additional code to handle that.

A. Williams
01-31-2015, 01:59 AM
sorry i'm still trying to wrap my head around all of this, and I have a question: if i just want to send the analogRead input from one pin attached to the slave teensy to the master teensy, do i need to use a Tx pin in addition to the SCL and SDA pins or can I just send that info over the SCL and SDA pins? sorry I've been trying to understand how this all works and I can't seem to find any entry points.

Does anyone have any simple examples using this lib for 1 slave teensy sending analogread info to a master teensy? I can't seem to figure out how to it

Or, is there anyway to use the old wire library with the SCL1 and SDA1 pins on the teensy 3? it appears that you can only use the default pins with the old library and i need to use those pins for other things atm :P

Headroom
01-31-2015, 02:10 AM
The best thing is if you post a schematic of what you have wired to what. I know what nox771 means but a schematic would help us to provide the reference points to help your understanding.
No you don't need a TX pin. You can send the info directly over I2C. Nick Gammon has written a library called I2C_Anything.h and you'll find that and a whole lot more I2C info on his web page (I2C_Anything.h)

A. Williams
01-31-2015, 02:35 AM
scratch this, sorry. I retooled the original wire master and slave sketches to do what needed. Sorry to be a bother!
but thanks for the tip on that webpage :D

Nantonos
01-31-2015, 08:57 AM
At the moment it is hardcoded based on F_BUS only. That is F_CPU is defined which gives F_BUS, but the code only looks at F_BUS. However the F_BUS to SCL ratios are fixed, so if the new part has a different divider scheme then there will need to be additional code to handle that.

Okay. From what Paul said in the announce thread, Teensy3-LC does not support much/any overclocking. We also don't know how low it will clock.

For now, an ifdef for the Teensy3-LC and a hardcoded F_BUS of 24M rather than one computed from F_CPU would get this lib working with Teensy3-LC. From what you said above, it seems that the current code would deliver an F_BUS that is 2x too large. I will ask on the announce thread what the best symbol would be for that test.

PaulStoffregen
01-31-2015, 09:19 AM
For now, you can assume F_BUS is always 24 MHz.

Teensy-LC can't use faster F_BUS, because the flash memory also runs from F_BUS. There no separate divider for the flash. F_BUS is divided down from F_CPU, not directly from the PLL like on Teensy 3.0 & 3.1. The USB clock is fixed at F_PLL divided by 2, so the PLL must be exactly 96 MHz for USB to work. All of these less flexible clock configuration settings work together to really limit our options for overclocking. F_CPU at 96 MHz with F_BUS at 24 MHz might be possible, maybe, but I don't see any other options that would still allow the USB to work.

This chip does have some really intriguing possibilities for low power. More of the peripherals are designed to be able to work in stop mode or very low power modes. The DMA engine can also work in limited ways with the clock stopped, and generate interrupts for wakeup when a DMA buffer has been fully transferred.

Like with Teensy 3.0 & 3.1, my top priority is going to be getting all the Arduino functionality and libraries working first. Support for low speed modes (without USB) will come later.

The good news is I2C is pretty much identical to Teensy 3.1, except for F_BUS being 24 MHz when the CPU is at 48 MHz.

Nantonos
01-31-2015, 05:10 PM
I see some code in the recently renamed kinetis.h (https://github.com/PaulStoffregen/cores/blob/master/teensy3/kinetis.h#L239)

#elif (F_CPU == 48000000)
#define F_PLL 96000000
#if defined(KINETISK)
#define F_BUS 48000000
#elif defined(KINETISL)
#define F_BUS 24000000
#endif
Maybe something similar would be a good fit for this library?

Paul, point taken about compat with the standard Arduino Wire library being first priority as a the most widely deployed lowest common denominator. But especially with two I2C on the T3LC, having this more powerful I2C library also working soonish seems very desireable.

Also, my point in the post above about limited overclocking was not a criticism. Rather, it was pointing out that handling the F_CPU=48M, F_BUS=24M case would cover most if not all of the options, thus making support more tractable.

nox771
02-02-2015, 06:40 PM
Sorry for the delay. Just moved and was offline for a few days. I'll try to get the dual I2C working as soon as I can get to work with it. From Paul's comments it sounds like the changes will be minimal. Although some of the new modes like DMA might depend on the DMA header/library working. Interrupt based sounds like it should work.

Roland
02-05-2015, 07:25 PM
Hi,
I just wonder if I didn't see it while reading this thread or if I just don't understand it or if it really isn't asked and answered yet...

I need a very simple wire master function that reads from a slave (in my case 6 bytes), but does not get blocked if the slave crashes (or is to slow for any other reason)
Is there a chance to use a wire.read() for a given time?

I actually use the standard wire lib to connect two teensy3.1 chips, one working as an intelligent sensor multiplexer(slave) and one as master.
I simply want the master to ask the slave for new sensor data but don't bother (still saying so) if the connection fails or drops or locks.

Thanks for your help!
Roland

nox771
02-05-2015, 08:37 PM
Hi,
I need a very simple wire master function that reads from a slave (in my case 6 bytes), but does not get blocked if the slave crashes (or is to slow for any other reason)
Is there a chance to use a wire.read() for a given time?
Roland

There are timeout functions which you can try. These require more extended forms of the function calls. Instead of this:

Wire.endTransmission();
you will need to call it like this:

Wire.endTransmission(i2c_stop, timeout);
whereby i2c_stop == I2C_STOP or I2C_NOSTOP and timeout is given in microseconds.

Similarly for the receiving call, instead of this:

Wire.requestFrom(address, length);
you will need to call it like this:

Wire.requestFrom(address, length, i2c_stop, timeout);
where the arguments are the same as above.

On the top post if you scroll down you will see descriptions of these functions if you need more info.

nox771
02-05-2015, 08:39 PM
All - I've uploaded the library to GitHub, you can find it here:
https://github.com/nox771/i2c_t3

Roland
02-05-2015, 08:53 PM
Hi nox771, thanks for your fast answer!
I am not entirely sure if I understand all of it right, but I will make my way through your examples,
especially the timout one.
Thanks again
Roland

doughboy
02-06-2015, 06:37 PM
question to Brian/nox771. can I use this library from an interrupt handler (Interval timer) to send 1 byte of data? I think the interrupt priority of interval timer is 128. I am migrating code from arduino mega that sets port/pin in a timer interrupt handler. Due to the limited number of pins of the T3.1, I have to use an i2c io expander, but want to retain the code design of controlling an 8 bit port from a timer interrupt handler, since controlling the port pin is time critical and cannot be delayed (how much latency is added by sending the 1 byte via i2c? compared to direct port bit setting) like in case main loop blocks. or should I do direct i2c register manipulation from the timer interrupt handler routine to send the 1 byte of data?

nox771
02-06-2015, 08:02 PM
The latest library has automatic priority escalation, so it can run from inside interrupts. The "interrupt" example does exactly what you are trying to do.

Edit: I just saw a typo in that example, it is using internal pullups even though the comments say external. Just be sure to set your begin() call correctly for your setup.

doughboy
02-06-2015, 08:49 PM
thanks. I'll try it out.

manitou
02-23-2015, 07:16 PM
beta testing of teensy LC shows that the LC's I2C pullups are high impedance (44Kohms). some preliminary results here:
https://forum.pjrc.com/threads/27689-Teensy-LC-Beta-Testing?p=65437&viewfull=1#post65437

nox771
02-23-2015, 08:03 PM
beta testing of teensy LC shows that the LC's I2C pullups are high impedance (44Kohms).
That's almost unusably high. I guess it could work for 100kbit rate.

So, 200 ohms on 3.1 or 44k on LC, I don't know what's up with Freescale. You would think they could make something in the range of 2k to 5k.

Frank B
02-23-2015, 08:52 PM
beta testing of teensy LC shows that the LC's I2C pullups are high impedance (44Kohms). some preliminary results here:
https://forum.pjrc.com/threads/27689-Teensy-LC-Beta-Testing?p=65437&viewfull=1#post65437

Yes, according to the datasheet the pullups are between 20K min and 50K max

manitou
02-23-2015, 09:14 PM
That's almost unusably high. I guess it could work for 100kbit rate.

So, 200 ohms on 3.1 or 44k on LC, I don't know what's up with Freescale. You would think they could make something in the range of 2k to 5k.
The teensy 3.* is the outlier. Paul's note in Wire.cpp would suggest Freescale screwed up for the 3.* I2C pullup. The LC pullup value matches other architectures (AVR and ARM DUE/maple) value for the internal pullup (20k to 50K), and for that mattter, the value of the 3.* INPUT_PULLUP is around 32K.

manitou
02-24-2015, 10:34 AM
Since I hadn't played with I2C pullups on 3.1 til now, i'm concerned about the power limits on the I2C pins. if the 3.1 I2C pullup is only 190 ohms (I measured 164 ohms on my 3.1), then such a pin could draw 17ma. Is that a concern? Does the pulsing of I2C signals mitigate power concerns??

Headroom
02-24-2015, 10:38 AM
The I2C specification requires a pin to be able to sink 3mA. In this case I'd use an external pull-up.

manitou
02-24-2015, 11:11 AM
The I2C specification requires a pin to be able to sink 3mA. In this case I'd use an external pull-up.

I was more concerned that several of the i2c_t3 library examples use I2C_PULLUP_INT, and that old arduino users might be in the habit of using "internal pullups" ...

nox771
02-24-2015, 04:40 PM
As far as current, the mitigating factor is that the I2C bus when idle is at a high voltage level, so it is not a static 17mA current (it's probably not 17mA either, the pulldown devices will have some impedance also). The current only flows during periods of traffic. The worst case condition would be SDA line during transmitting a continuous stream of zeroes. I think it's probably fine for burst traffic. If I was driving some kind of display and sending large framebuffer data (potentially with a lot of zeroes), then I would probably use external.

On the next rev with LC support I'll probably change the examples to external pullup (since 44k is just about useless). I usually have it set as internal because that's how I test it (and I'm too lazy to remember to change it back).

defragster
03-06-2015, 09:12 AM
I pulled the i2c_t3 from github and the kriswiner/MPU-9250 was hanging on me, sometimes with no serial.print. Tried a few sketches there and confirmed other sketches work. Opened the i2c_T3\example\scanner and it was hanging with no serial.print. Adjusted the begin() with a while(!serial and delay) then I got a line, and hacked into the debug print with no pins set and it prints starting scan, then prints after wire.begin and then never prints after Wire.endTransmission();

I have kriswiner hardware on 29_30(bottom solder) [with onboard pull-ups], but I also set to scan two other OPEN pins pairs (16_17 && 18_19) on this teensy and one T3.1 with nothing soldered and nothing returns.

Is this an open issue for IDE 1.6 w/TeensyD 1.21b7? It is wrong to assume it would just time out on open i2c pins? It acts the same on the 29_30 pair that should be responding on the one T3.1.

Running: Current beta-7 at 96Mhz.

onehorse
03-06-2015, 04:33 PM
You need to use the Wire1.begin call and Wire1.write etc. on the bottom I2C pins 29/30 when using the i2c_t3.h library. You can't use these pins with the Arduino Wire.h library AFAIK.

defragster
03-06-2015, 04:45 PM
Makes sense - the scanner started working with wire1 - ACK's "everywhere" 1-127 on the soldered board . . . now to go get a corrected sample to work. The scanner does just hang on the unpopulated teensy3.1

nox771
03-06-2015, 09:00 PM
I have kriswiner hardware on 29_30(bottom solder) [with onboard pull-ups], but I also set to scan two other OPEN pins pairs (16_17 && 18_19) on this teensy and one T3.1 with nothing soldered and nothing returns.

Is this an open issue for IDE 1.6 w/TeensyD 1.21b7? It is wrong to assume it would just time out on open i2c pins? It acts the same on the 29_30 pair that should be responding on the one T3.1.

IIRC you want to avoid floating I2C pins, it will hang on floating pins because they eventually get pulled low and then it thinks another bus master is holding the SDA/SCL low. It hangs at this point in sendTransmission():


// we are not currently the bus master, so wait for bus ready
while(*(i2c->S) & I2C_S_BUSY);

I need to augment that with a timeout check, but it hasn't been done yet. Timeout was set to start after acquiring control of the bus, but I suppose it makes more sense to pull it before the above point. I'll try to roll it in with the LC fixes. For now just be sure to use pullups on the pins.

defragster
03-06-2015, 10:00 PM
Sounds like a good fix and that explains it not working on open pins. And with 3 sets shown on the 3.1/LC cards it would be easy to get into this state and nice to have it get resolved. I resorted to scanner having it sit was not helpful - as I had just hand soldered to 10 teensy bottom pads making a $45 unit I wanted to see work. For my first time practice with solder paste I put a Tiny13 onto an SMD converter board, then I did these 10 'cut' through holes.
3795

Nantonos
03-14-2015, 06:28 AM
(Nevermind. I thought there was LC support already in the Github version, but there is not, yet).

nox771
03-15-2015, 08:29 PM
Sorry I don't have LC support yet, I'll try to get it in soon.

Nantonos
03-16-2015, 09:21 AM
That was not intended as a criticism. Just my replacement text for a long "this doesn't seem to work" post followed by several realizations as to why. I was trying to use this library to read Adafruit I2C FRAM on the Wire1 second I2C interface on LC.

nox771
04-10-2015, 10:04 PM
All,

I've uploaded a new v8 version of the library (refer to top post). I'll be updating GitHub shortly also. It has the following changes:

Teensy LC support

Fully supported - Master/Slave modes, IMM/ISR/DMA operation, Wire/Wire1 interfaces
Wire: pins 16/17 or 18/19 (rate limited to I2C_RATE_1200 due to 24MHz F_BUS)
Wire1: pins 22/23 (rate limited to I2C_RATE_2400 due to 48MHz F_CPU)

Improved Timeout behavior:

Added timeout on acquiring the bus. Prior to sending a START the Master will wait for the bus to not be busy. If that does not occur in the specified time, then it will exit with a timeout error (more below). This should prevent lockups involving the I2C waiting for the bus to be released (due to stuck Slave or something else). Of course this requires that a timeout is specified on the calls.

Added setDefaultTimeout(timeout) function:

To assist in specifying timeouts, there is this new function. The default timeout is initially zero (infinite wait), however once it is set to something else then all subsequent commands will use that timeout. This should save some code editing, just throw one of these at the top of the code, and there you go.

Added resetBus() function:

In the above case where the Master can't send a START because a Slave is holding the SDA low, this command will toggle the SCL line (up to 9 clock pulses), to try and get the Slave to release SDA. Once SDA is released the Master should be able to send new commands.

Added I2C_AUTO_RETRY define:

This user define puts the above behavior all together. When this is defined, if the Master cannot send a START due to bus busy, it will call resetBus() automatically, and then if the SDA line releases, it will issue the START and continue on. So instead of a timeout on bus busy it will try to free the bus and then retry the START. If the bus remains busy it will return as a timeout error. Because of what it is doing, in the general sense this is not compatible with multi-master buses. However I am defaulting this to defined because I think the majority of scenarios are single-master. This may be a bad assumption, I don't know, so if anyone thinks this is a problem then leave a comment.

Added setRate(rate) function:

This is just for simplicity, similar to setClock(freq). It does not require specifying the bus freq.


This update was more involved than I originally anticipated. The DMA and Slave modes both gave me trouble for a while. However, this latest code seems to work well. I ran a bunch of tests in different configurations on the LC and it all seemed to work. I also checked 3.0/3.1 for regressions and I didn't find anything. If anyone has problems then let me know.


Misc notes:


Thanks - First thanks to manitou for the LC investigations. That was helpful in getting going on the LC update. Also thanks to Swap_File for the resetBus() idea. Hopefully this new feature will help out with flaky devices.
DMA and timeouts - I still don't have a good fix for timeouts in DMA mode. If they give you trouble switch to ISR mode. DMA is like a freight train without a driver, once you set it on it's way it doesn't want to stop. IMO the current implementation is a bad fit for error-prone communication. It doesn't respond to bus events, and abruptly stopping it causes the I2C hardware to get stuck.
I2C_FLT - there is some odd stuff going on in this register on LC. I've seen bit4 toggling when I read it back, and in the ISR clearing STOPF isn't sufficient to get things working, the read value must be written back to it. It's strange...

jakorten
04-13-2015, 05:36 PM
Whoohoo! Thanks Brian!!!

nox771
04-13-2015, 10:41 PM
Sure, hope it works for you.

PaulStoffregen
04-13-2015, 11:01 PM
Great work!

Please let me know when the github repo is updated, so I can put the latest into the upcoming 1.23 installer.

nox771
04-13-2015, 11:28 PM
It is already updated.

PaulStoffregen
04-14-2015, 12:10 AM
Ok, I've updated here. This version will be in the 1.23 installer.

nox771
04-14-2015, 12:25 AM
Great! Hopefully there are no regressions. It was interesting to find that the LC Wire1 is as fast as the T3.1 buses.

BOONY
04-26-2015, 03:45 PM
Hello Everybody,

I have several issues using the Teensy 3.1. I am working with OS X 10.10.3 , Arduino 1.6.3, Teensyduino 1.22. I can compile and upload simple programs like the blink- or the HelloWorld-example and they are working fine.
The problems are starting when I2C is involved. I try to build a "Network" of Teensies wich are communicating over I2C. One device is the master and is asking the clients with Wire.request for Data. To test the basic communication I have written these little sketches.

Master:

#include <i2c_t3.h>


void setup()
{
Wire.begin(I2C_MASTER, 0, I2C_PINS_18_19, I2C_PULLUP_INT, I2C_RATE_100);
Serial.begin(9600);
}


void loop()
{
Serial.println("send Request");
Wire.requestFrom(1,5);

while(Wire.available()){
Serial.print(Wire.read());
Serial.print(" ");
}

delay(1000);
}


Client:

#include <i2c_t3.h>



void setup()
{

Serial.begin(9600);
Wire.begin(I2C_SLAVE, 1, I2C_PINS_18_19, I2C_PULLUP_INT, I2C_RATE_100);
Wire.onRequest(answer);

}

void loop()
{

}

void answer(){
Wire.write("Hello");
}

The two Teensy 3.1 are on a breadboard connected with Pin18 to Pin18 and Pin19 to Pin19.
The Problem is: the Master is not getting any response and if I open the serial monitor the connection or the Teensy freezes. It must have been running before I opened the monitor because the log shows some printlns:

Serial Monitor:
send Request
send Request
send Request
send Request
send Request

Another Problem is that if several Teensies are plugged in, the IDE is choosing a random device on which the sketch will be uploaded. The choice of the serial port in the IDE is completely ignored. The only way to guarantee that the correct device programmed is to unplug all other Teensies.

I hope somebody can help me.
Thanks,
best regards from germany,
Andi



//EDIT: I have just seen there is a dedicated thread for support. Is it possible to move this post to the support-thread?

defragster
04-26-2015, 04:50 PM
You could move this 'text' to a new Thread and just leave behind a link to it here perhaps.

Did you do pull-up resistors? (https://forum.pjrc.com/threads/28348-Teensy-3-1-LC-I2C-slave-pullups-on-SCL-and-SDA?p=70531&viewfull=1#post70531) I didn't search much, there may be other thread details out there.

Multiple Teensy's 'Does That' - the solution is to 'VERIFY' build only - then button push the device to trigger the desired upload.

With Multiple devices - Serial through IDE is one only - I've found TYQT (https://forum.pjrc.com/threads/27825-Teensy-Qt?highlight=tyqt) to be quite useful as you can monitor multiple devices with it - and having the monitor open typically stops TeensyDuino from doing an upload until you button press.

MichaelMeissner
04-26-2015, 05:56 PM
If you are connecting multiple teensies, make sure the grounds are inter-connected if you are using separate power supplies for each Teensy. One Teensy needs pull-up resistors (usually 4.7K resistors, 2 separate resistors, one between pin 18 and 3.3v, and the other between pin 19 and 3.3v).

nox771
04-26-2015, 06:23 PM
A few things:

1) Your Master and Slave devices are configured to start with zero delay. You might want to put a delay in the setup() of the Master, which will give time for the Slaves to fully configure.

2) Second the T3.1 internal pullups are very low resistance. This is even more so if you have both your Master device and multiple Slave devices all configured using internal pullups. The pullup resistance in that case will be excessively low, and likely won't work at all. Set all the Slave devices to I2C_PULLUP_EXT, instead of I2C_PULLUP_INT.

3) Your choice of Slave address (0x01) is not ideal. You should use addresses in the range of 0x08 to 0x77. IIRC, above and below that are reserved by the I2C protocol. Also, I am assuming you are doing this, your Slaves obviously need to be configured on different addresses.


You might also want to add checking of the return values from the functions. In the Master sketch add this function and call it after the Wire.requestFrom() call:

void print_i2c_status(void)
{
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;
}
}


Then in the Master code:

Wire.requestFrom(...);
print_i2c_status();

BOONY
04-28-2015, 10:26 AM
The pullups did the trick, works like a charm now!

@defragster: Thank you for the tipp with the TYQT-program. This makes a lot of things easier. Also the upload-button makes a lot more sense now^^

@MichaelMeissner: I've done the pullup-wiring as you described and it works perfectly. Thank you!

@nox771: Your print_i2c_status funktion is quite helpful. Thanks for that! I think the addresses do not matter for now because I have only "self defined" I2C devices in this bus, but I keep this in mind for the case I add other devices than the Teensies.

Thanks to everybody for the quick and helpful answers! It's great to see an active and kind community like this.

kito
05-22-2015, 02:12 AM
Hi nox771,

The i2c scanner example hangs. I'm not sure who is responsible for the scanner example, but if I use the i2c_t3 library, the app hangs on two teensy 3.1 devices that I use and another user reported it. I believe the default behavior of the i2c_t3 library is to wait infinitely. I made it work by adding a timeout value. I think the only people using the scanner example would be newbies, and so it really would help to modify the example.


https://forum.pjrc.com/threads/28608-I2C-Lock-up-i2c_t3-questions

jakorten
05-22-2015, 05:11 AM
kito, did you modify if(digitalRead(12) == LOW || digitalRead(11) == LOW) to //if(digitalRead(12) == LOW || digitalRead(11) == LOW)

that is what I do to make sure I do successful scanning without the need to hook pin12 / pin11 to GND.

kito
05-22-2015, 09:14 AM
kito, did you modify if(digitalRead(12) == LOW || digitalRead(11) == LOW) to //if(digitalRead(12) == LOW || digitalRead(11) == LOW)

that is what I do to make sure I do successful scanning without the need to hook pin12 / pin11 to GND.

Yes I did that. I see all my i2c devices when the timeout is set. Using the scanner without it will hang somewhere in the i2c_t3 library. No output and the led stay lit.

Are you saying you can download the latest v8 on the website and run the scanner example without hanging? I have two teensy 3.1 devices which both hang with v8 lib?

nox771
05-22-2015, 04:07 PM
I'll look into the scanner example. It is a little odd that it would hang, it is doing only a simple address ACK/NAK check. Regardless the timeout is a good idea for that example, I'll make a note to modify it.

PaulStoffregen
06-15-2015, 12:25 PM
Is there an updated version of the i2c_t3 library, which should be distributed with Teensyduino?

Arduino is likely to release version 1.6.5 today. If they do, I'll be making a beta release today or early tomorrow, and probably a final 1.24 release within a week.

nox771
06-15-2015, 01:44 PM
I haven't had a chance to do any updates recently. Aside from the scanner example I haven't gotten any bug reports. GitHub is current.

I can run some compile tests against your new beta once it's out though.

Markk
08-15-2015, 04:09 PM
As Headroom mentioned there is no reason to run i2c_t3 and Wire together. The i2c_t3 library is a superset of the Wire API, so it implements all the functions already. Moreover they use the same hardware peripheral, so running both of them at once is likely to cause problems.

Hi

thank you for this great library! I have a project where I use many i2c devices. So libraries by other people are involved. These typically #include <Wire.h>.

Am I correct that there is no way but to modify these libraries to switch them over to i2c_t3?
(Modifying these libraries means losing easy update capability and integratability of my own code, of course.)

Or is there an "Arduino way" to spoof libs?

In other environments I might be creating a stub Wire.h in a priority include directory that just includes i2c_t3.h instead and adds some typedefs for compatibility. But I think this will not work within the Arduino petting zoo, right?

Thanks for all your help!

-Markk

Headroom
08-15-2015, 05:31 PM
Yes, you may want to replace wire.h in these libraries with i2c_t3.h but in the arduino environment you can make this conditional with #defines based on what board is selected.
In essence if done right you can add i23_t3.h features to Teensy 3.1 and Teesy LC and the library will switch back to wire.h automatically if you have selected a non Teensy board. You don't loose any compatibility rather you create a win win situation ;-)

defragster
08-15-2015, 06:12 PM
Just added this to WIKI list - I posted here on the end of this thread - but never read the start - how to swap out WIRE for better i2c_t3.h is something I could have used already! (https://forum.pjrc.com/threads/28866-Wiki-Coming-Please-link-worthy-posts?p=79422&viewfull=1#post79422)

The speed of I2C may not be the best - but two wires and the number of devices makes it so common and easy when done right!

I understood external pull-ups were required for I2C to work when I started with Digistump SPARKS and OLED's - and since internal pull-ups are not sufficient on Teensy - a section on that up front could save lots of the traffic I've seen on that issue. The software 'I2C_PULLUP_INT' could be tagged as on the PJRC.com page-
Teensy 3.0 requires pullup resistors to +3.3V. The on-chip pullups are not used. 4.7K resistors are recommended for most applications. :: "I2C_PULLUP_EXT" required.


You do need two pullup resistors, one from SDA to 3.3V and one from SCL to 3.3V. It sounds like you've already tried that, but perhaps try again (and do not try other ways on SCL and SDA, pullups absolutely are required). (https://forum.pjrc.com/threads/27154-Teensy-3-1-and-I2C?p=58736&viewfull=1#post58736)

Markk
08-15-2015, 09:30 PM
Thanks for the quick reply!

>Yes, you may want to replace wire.h in these libraries with i2c_t3.h

But if I change the 3rd party library I lose the update capability. If a new version is released I have to re-modify it. With many libs this becomes a nightmare.

Also, if I want to distribute my code I need to include all the modified 3rd party libs. Not nice.

Of course I could try to persuade the original author to include the #ifdefs in his/her original code. But I guess this is improbable for companies (i.e. Adafruit) that prefer their libs to be readable and simple.

Yet it seems I have to live with it.

Thanks,
Markk

defragster
08-15-2015, 10:20 PM
If something like this is in your sketch there is no library mod? :

#if defined (TEENSYDUINO)
#include <i2c_t3.h>
#else
#include <Wire.h>
#endif

Markk
08-16-2015, 08:38 AM
If something like this is in your sketch there is no library mod? :

Yes but it's not about my sketch, it's about the 3rd party library.

For instance I use the Adafruit_INA219 lib and it's header Adafruit_INA219.h (https://github.com/adafruit/Adafruit_INA219/blob/master/Adafruit_INA219.h) includes <Wire.h> (see the code there (https://github.com/adafruit/Adafruit_INA219/blob/master/Adafruit_INA219.h)).

So I would need to modify that header.

--Markk

Headroom
08-16-2015, 11:03 AM
Yes, that is a bit of a dilemma when upgrading these libraries. In the case of Adafruit it may be possible to persuade them to make modifications to the library. They have modified a number of their libraries to include Teensy specifics and they also sell Teensy boards. The easiest way is to create a pull request on Adafruits GitHub repository for that library.
It's worth a try.

PaulStoffregen
08-17-2015, 12:01 AM
First, at least get your project working, even if that means editing a file you got from Adafruit.

Later, only after the edits have been proven to work well, can we even begin to talk about asking Adafruit to revise their code.

doughboy
08-17-2015, 05:00 PM
Yes but it's not about my sketch, it's about the 3rd party library.

For instance I use the Adafruit_INA219 lib and it's header Adafruit_INA219.h (https://github.com/adafruit/Adafruit_INA219/blob/master/Adafruit_INA219.h) includes <Wire.h> (see the code there (https://github.com/adafruit/Adafruit_INA219/blob/master/Adafruit_INA219.h)).

So I would need to modify that header.

--Markk

how about if you define TwoWire_h on the very top of your sketch. Anything that #includes Wire.h will then not use the wire library since TwoWire_h is already defined.

Markk
08-17-2015, 05:12 PM
how about if you define TwoWire_h on the very top of your sketch. Anything that #includes Wire.h will then not use the wire library since TwoWire_h is already defined.

I think this will not work, because the library .cpp files are compiled independently without any influence from the sketch.

nox771
08-17-2015, 10:01 PM
Yes but it's not about my sketch, it's about the 3rd party library.

For instance I use the Adafruit_INA219 lib and it's header Adafruit_INA219.h (https://github.com/adafruit/Adafruit_INA219/blob/master/Adafruit_INA219.h) includes <Wire.h> (see the code there (https://github.com/adafruit/Adafruit_INA219/blob/master/Adafruit_INA219.h)).

So I would need to modify that header.


In order to spoof the libraries into using i2c_t3 instead of Wire, without editing anything, you would probably need to either modify the build process, or rely on directory ordering, or simply swap in the i2c_t3 in place of the default Wire library.

There was a pull request on GitHub dealing with this (from April actually, but I was too busy to look into it):
https://github.com/nox771/i2c_t3/pull/1

The suggested method in that request is basically to modify the build process by setting up a "compatibility" directory that would have header files that would in-turn include i2c_t3. This doesn't work for Arduino, it is specific to build processes that you can modify as needed (makefiles, Eclipse, etc...). I'll probably close it for that reason, it is not universally workable.

There is a way to get Arduino to build it without editing your Arduino install (thus keeping upgrade compatibility). It relies on the way Arduino prioritizes sketchbook libraries over default libraries. It does require require renaming and slight editing to i2c_t3.cpp and i2c_t3.h, as follows:

1) I'm going to assume you are working in the following directory structure using the Arduino window to compile and upload. The <sketchbook> directory will refer to that defined in the Arduino preferences in the "Sketchbook location" box:

<sketchbook>/myproj/myproj.ino <-- single-file Arduino project "myproj"
<sketchbook>/libraries/i2c_t3/i2c_t3.* <-- original unpack location for i2c_t3.h and i2c_t3.cpp


2) Create a Wire directory in <sketchbook>/libraries:

<sketchbook>/libraries/Wire/


3) Copy i2c_t3.cpp and i2c_t3.h to that directory as Wire.cpp and Wire.h respectively

<sketchbook>/libraries/Wire/Wire.cpp
<sketchbook>/libraries/Wire/Wire.h


4) Edit the Wire.cpp and in the include just below the large comment block change the line from #include "i2c_t3.h" to:

#include "Wire.h"


5) Load and recompile your project. The Arduino window should say something like:

Multiple libraries were found for "Wire.h"

Used: <sketchbook>/libraries/Wire


Using this I was able to compile the Adafruit library you mentioned without errors, and it used the i2c_t3 library spoofed as Wire (or so it said, I didn't actually verify the linker output).

So if it's easy to setup this way, why not always do this? Well because sometimes you do in fact want the original Wire library. If you do this it will always use i2c_t3 in place of Wire.

nox771
08-17-2015, 10:10 PM
I understood external pull-ups were required for I2C to work when I started with Digistump SPARKS and OLED's - and since internal pull-ups are not sufficient on Teensy - a section on that up front could save lots of the traffic I've seen on that issue. The software 'I2C_PULLUP_INT' could be tagged as on the PJRC.com page-
Teensy 3.0 requires pullup resistors to +3.3V. The on-chip pullups are not used. 4.7K resistors are recommended for most applications. :: "I2C_PULLUP_EXT" required.

You are correct, issues involving pullups are one of the most common questions. I'll see if I can add in a section on the top post regarding pullups and recommended configurations.