New I2C library for Teensy3

just out of curiosity of watching the chip registers to see if their intact to make sure i2c bus is operational, does the library have it's own check built in to detect operational bus without writing a request to the device on the bus?

Internally the library maintains its state through several variables in the i2cStruct (refer to i2c_t3.h file, and look for i2cStruct, then "Current" variables). There are functions to access some of those, but i2cStruct is public, so any setting can be read from it at any time (eg. Wire.i2c->currentRate for example).

If it needs to determine bus state it will check C1 register (I2C_C1_MST) and S register (I2C_S_BUSY). Refer to the acquireBus_() function to see how it checks those. It does not require talking to Slaves on the bus.
 
just out of curiosity of watching the chip registers to see if their intact to make sure i2c bus is operational, does the library have it's own check built in to detect operational bus without writing a request to the device on the bus?

If by seeing if any of the bus lines are stuck, we have added a simple pin-wiggle high and low test to the startup code in our project which depends on I2C for a lot of the system I/O. Then we report SDA or SCL stuck low or high as errors. Not sure if that's what you are asking here.

If a clock gets "lost" or missed by a slave, it could fail to release the bus, and the library has the ability to send extra clocks to recover from that, if you enable it. I added the ability to see if that recovery has happened, it's in a pull request which you can view if curious about what was added and want to duplicate that.
 
Hi,
is there any option to send data to multiple slaves in one DMA-mode session? I mean write buffer with 1st slave address and data, 2nd slave addr and data (3th and so on) and then use sendTransmission() function?

See @nox771s answer which relates to i2c_t3. But in the bigger world if you have intelligent I2C slaves (microcontrollers) then yes, you can have a multi-master system on I2C or perhaps more correctly, it is a multi-peer system. You can send a message modeled on UDP and all slaves can receive it at the same time. Those that want to pay attention to it can. It's like a broadcast vs sending to one slave. We implemented such a system for a robotic platform and it worked well. The trick is arbitration resolves in the "UDP like" header so there is never a clash of multiple slaves responding to one message. There's nothing preventing this in the I2c spec, though a peer I2C net is unusual.
 
Just FYI - I've uploaded a v9.3 release to the top post and GitHub. It fixes a Slave-mode bug in the ISR for LC/3.5/3.6 devices (STOP bit not properly triggering callback). Try the update if you have any problem running that configuration.

Edit: Just re-uploaded to merge Paul's Teensy 2.0 patch. First post and GitHub are updated.
 
Last edited:
Trouble compiling i2c_t3 under Linux

So... I'm attempting to get a Linux (Ubuntu 17.04) dev system working... because I am hoping it is "better" than Windows. For one thing it is 10X faster to program a new Teensy module: no drivers to install every. single. time. But when I try to build a program under LInux I'm getting errors not present when building on Windows:

This happens with Arduino 1.8.2/TD 1.36 and Arduino 1.8.4/TD1.39
I'm pulling in the identical libraries from my github repo where appropriate so those are the same on Windows and Linux.

The problem appears to be in

Code:
/tmp/arduino_build_440080/libraries/i2c_t3/i2c_t3.cpp.o:/home/bruce/Documents/code/Arduino/libraries/i2c_t3/i2c_t3.cpp:1405: first defined here
/home/bruce/Documents/arduino-1.8.2/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: Disabling relaxation: it will not work with multiple definitions
/tmp/arduino_build_440080/libraries/Wire/WireKinetis.cpp.o: In function `Print::flush()':
/home/bruce/Documents/code/Arduino/libraries/Wire/WireKinetis.h:132: multiple definition of `i2c1_isr'
/tmp/arduino_build_440080/libraries/i2c_t3/i2c_t3.cpp.o:/home/bruce/Documents/code/Arduino/libraries/i2c_t3/i2c_t3.h:926: first defined here
/tmp/arduino_build_440080/libraries/Wire/WireKinetis.cpp.o: In function `Print::flush()':
/home/bruce/Documents/code/Arduino/libraries/Wire/WireKinetis.h:132: multiple definition of `Wire1'
/tmp/arduino_build_440080/libraries/i2c_t3/i2c_t3.cpp.o:/home/bruce/Documents/code/Arduino/libraries/i2c_t3/i2c_t3.h:926: first defined here
/home/bruce/Documents/arduino-1.8.2/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: Warning: size of symbol `Wire1' changed from 20 in /tmp/arduino_build_440080/libraries/i2c_t3/i2c_t3.cpp.o to 108 in /tmp/arduino_build_440080/libraries/Wire/WireKinetis.cpp.o
/tmp/arduino_build_440080/libraries/Wire/WireKinetis.cpp.o: In function `Print::flush()':
/home/bruce/Documents/code/Arduino/libraries/Wire/WireKinetis.h:132: multiple definition of `Wire'
/tmp/arduino_build_440080/libraries/i2c_t3/i2c_t3.cpp.o:/home/bruce/Documents/code/Arduino/libraries/i2c_t3/i2c_t3.h:926: first defined here
/home/bruce/Documents/arduino-1.8.2/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: Warning: size of symbol `Wire' changed from 20 in /tmp/arduino_build_440080/libraries/i2c_t3/i2c_t3.cpp.o to 108 in /tmp/arduino_build_440080/libraries/Wire/WireKinetis.cpp.o
collect2: error: ld returned 1 exit status

I'm not understanding how this error can happen just on Linux... assuming all the code and library files are the same on both platforms... and not sure how to proceed. Thanks.
 
So... I'm attempting to get a Linux (Ubuntu 17.04) dev system working... because I am hoping it is "better" than Windows. For one thing it is 10X faster to program a new Teensy module: no drivers to install every. single. time. But when I try to build a program under LInux I'm getting errors not present when building on Windows:

This happens with Arduino 1.8.2/TD 1.36 and Arduino 1.8.4/TD1.39
I'm pulling in the identical libraries from my github repo where appropriate so those are the same on Windows and Linux.

The problem appears to be in

Code:
/tmp/arduino_build_440080/libraries/i2c_t3/i2c_t3.cpp.o:/home/bruce/Documents/code/Arduino/libraries/i2c_t3/i2c_t3.cpp:1405: first defined here
/home/bruce/Documents/arduino-1.8.2/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: Disabling relaxation: it will not work with multiple definitions
/tmp/arduino_build_440080/libraries/Wire/WireKinetis.cpp.o: In function `Print::flush()':
/home/bruce/Documents/code/Arduino/libraries/Wire/WireKinetis.h:132: multiple definition of `i2c1_isr'
/tmp/arduino_build_440080/libraries/i2c_t3/i2c_t3.cpp.o:/home/bruce/Documents/code/Arduino/libraries/i2c_t3/i2c_t3.h:926: first defined here
/tmp/arduino_build_440080/libraries/Wire/WireKinetis.cpp.o: In function `Print::flush()':
/home/bruce/Documents/code/Arduino/libraries/Wire/WireKinetis.h:132: multiple definition of `Wire1'
/tmp/arduino_build_440080/libraries/i2c_t3/i2c_t3.cpp.o:/home/bruce/Documents/code/Arduino/libraries/i2c_t3/i2c_t3.h:926: first defined here
/home/bruce/Documents/arduino-1.8.2/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: Warning: size of symbol `Wire1' changed from 20 in /tmp/arduino_build_440080/libraries/i2c_t3/i2c_t3.cpp.o to 108 in /tmp/arduino_build_440080/libraries/Wire/WireKinetis.cpp.o
/tmp/arduino_build_440080/libraries/Wire/WireKinetis.cpp.o: In function `Print::flush()':
/home/bruce/Documents/code/Arduino/libraries/Wire/WireKinetis.h:132: multiple definition of `Wire'
/tmp/arduino_build_440080/libraries/i2c_t3/i2c_t3.cpp.o:/home/bruce/Documents/code/Arduino/libraries/i2c_t3/i2c_t3.h:926: first defined here
/home/bruce/Documents/arduino-1.8.2/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: Warning: size of symbol `Wire' changed from 20 in /tmp/arduino_build_440080/libraries/i2c_t3/i2c_t3.cpp.o to 108 in /tmp/arduino_build_440080/libraries/Wire/WireKinetis.cpp.o
collect2: error: ld returned 1 exit status

I'm not understanding how this error can happen just on Linux... assuming all the code and library files are the same on both platforms... and not sure how to proceed. Thanks.

This looks like a problem in the build system, not in the source code. It seems to be trying to link in default Wire lib (WireKinetis.cpp.o) at the same time as i2c_t3, which won't work because they define the same things. I don't have experience trying to use Arduino build system in linux. If the source is not using Wire lib then it shouldn't be trying to link it in, so I don't know why that is happening. If you can get the attention of Paul you might get a better answer.
 
All - I've uploaded a v9.4 release to the top post and GitHub. A couple other bugs showed up involving RepSTART into Slave ISR on LC/3.5/3.6. Changes are:

  • 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
 
I'd like to read data from a slave asynchronously. Your API explanation seems to indicate that this is possible using sendRequest(address, length, i2c_stop), but I can't find an example. There seems to be no callback when a request has been handled, so I'd have to use done() to determine if data has been read, or finish() to wait until it has been read.

What happens when a second independent part of an application calls sendRequest() while another request is already being handled?

Regards

Christoph
 
I'd like to read data from a slave asynchronously. Your API explanation seems to indicate that this is possible using sendRequest(address, length, i2c_stop), but I can't find an example. There seems to be no callback when a request has been handled, so I'd have to use done() to determine if data has been read, or finish() to wait until it has been read.

sendRequest() will background a transfer, but it is correct you will need to poll or periodically check done() or finish() to know when the transfer is complete. The expectation was this is something that would be done at the end of a main loop iteration or similar. I suppose in the future I could set it up with a callback similar to Slave operation, but at the moment it does not do this. The advanced_master example has a tiny test for sendRequest() where it just runs a counter while the transfer is backgrounded, but there is no complex example of this.

What happens when a second independent part of an application calls sendRequest() while another request is already being handled?

That would not be supported. Starting a new Master operation while one is ongoing will negate the ongoing operation. The way to deal with this is to have the code block or poll for done() or finish() until the bus is clear, then start the new operation (or perhaps utilize a second dedicated bus). The basic_echo example does something like this in the receiveEvent() callback where the Slave will echo data incoming on Wire1 out on Wire (as a backgrounded transmit), but it blocks at Wire.finish() until the bus is clear to send.
 
All, I've uploaded a new v10.0 library to the top post and GitHub. Documentation has been updated.

This has passed all my testing but it is a pretty involved change in some parts, so post here if you have any problems with it. There are three major changes (first three below), and some extra cleanup and fixes. The changes are:

1. Unbound SCL, SDA pin assignment. The library now supports setSCL() and setSDA() functions, and in general any place that used to take a pins enum value will now accept a SCL,SDA pin pair (in that order). This includes the begin() function. The enum values will remain supported so there is no risk of breaking existing code. This is primarily to add compatibility to any code written against standard Teensy Wire. Refer to the top-post or GitHub for the full array of valid pin settings.

New functions are (where '^' indicates optional parameters):
  • begin(mode, address, ^pinSCL, ^pinSDA, ^i2c_pullup, ^rate, ^i2c_op_mode);
  • begin(mode, address1, address2, ^pinSCL, ^pinSDA, ^i2c_pullup, ^rate, ^i2c_op_mode);
  • pinConfigure(pinSCL, pinSDA, ^pullup);
  • setSCL(pin);
  • setSDA(pin);
  • getSCL();
  • getSDA();
Examples:
Code:
[FONT=courier new]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[/FONT]
Note that this change impacted the Wire.i2c->currentPins internal variable, which is now replaced by Wire.i2c->currentSCL and Wire.i2c->currentSDA. This is not normally something someone would use, but be aware if you run into a 'currentPins not defined' error (possibly on old example code).


2. Added Master callback functions. These callbacks were initially added to trigger when background transfers complete (sendTransmission() and sendRequest()), however they will also trigger on foreground transfers. There is also a error function callback which will trigger on any error that terminates a Master transfer. These can be used to do exit status and error checking in common functions instead of explicitly coding everywhere. Additions are:
  • 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 error which terminates the Master bus operation (eg. NAK, timeout, acquire fail, etc)
  • basic_master_callback - an added example which demonstrates the above functions

3. Error counters. An error counting system was added which can track bus errors over long durations. It tracks seven different types of errors. This functionality is gated by a define I2C_ERROR_COUNTERS, which by default is enabled, but it can be disabled if there is any performance concern (refer to top of i2c_t3.h file). There are two supporting functions, one to return error count, and the other to zero the count. The functions are:
  • getErrorCount(counter)
  • zeroErrorCount(counter)
  • where counter is an enum value with one of the following settings:
    • 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
4. Misc cleanup and fixes.
  • Added default settings on many function arguments and did some cleanup. With the exception of old style Arduino calls (Wire.begin() and Wire.begin(addr) which are 100kHz), more complex calls will use default pins, external pullups, 400kHz, and ISR mode if those settings are not explicitly set.
  • Also fixed some blocking conditions that could occur in immediate mode
  • Updated all docs

 
Hi nox771

thanks for all the improvements. Will try them. I had serious problems with a data-collecting master and a slave writing data to an SD-card, communicating over i2c. This was last spring and I could not spot where the problem was. Happened sometimes after 5 minutes, sometimes it took 1 hour. Well - end of last spring I had not the time to investigate further and I let it be as it was.

best regards
 
General Call

Hi Brian,

Great library, I use it with all of my Teensy specific code. Is there a plan to implement the ability for slaves to respond to a general call (i.e. respond to a master transmitting to address 0)?

Thanks!
Brian
 
Great library, I use it with all of my Teensy specific code. Is there a plan to implement the ability for slaves to respond to a general call (i.e. respond to a master transmitting to address 0)?

GC is one of several things that are lacking, but unfortunately at the moment I'm out of time, and it might be a while before I can look into it. I can add this as a future to-do.
 
Forgive me if I'm missing something obvious, but wire.readByte and wire.peekByte seem like they'll have a problem if the data byte to be returned is a zero. How do you tell the difference between a zero data byte and the buffer being empty?
 
Forgive me if I'm missing something obvious, but wire.readByte and wire.peekByte seem like they'll have a problem if the data byte to be returned is a zero. How do you tell the difference between a zero data byte and the buffer being empty?

You don't use those functions to determine if the buffer is empty, instead use Wire.available(). Those functions exist because at a hardware level the I2C moves data in bytes, so it is natural to manipulate data in bytes.

The original Wire lib tried to mash two functions together by returning a signed int, with -1 being buffer empty. This almost certainly requires casting to use the data (it is actually cast twice, once to convert to signed int, and again to convert to something else).

Both sets of functions exist in the lib so you can use either, eg. Wire.read()/Wire.peek() for signed int, or Wire.readByte()/Wire.peekByte() with Wire.available() for working with uint8_t. It's up to the end user to decide which they like.
 
Hi nox771

just a short question : is it possible that an I2C-slave responds to more than one I2C-adress ? Would be handy but I am afraid this is not possible. Right ?
 
Hi nox771

just a short question : is it possible that an I2C-slave responds to more than one I2C-adress ? Would be handy but I am afraid this is not possible. Right ?
Hi, I am not nox... But I have noticed in the code that you can define the Slave with a range of addresses. Not sure if that is what you are wanting or not.

But from the header file you have:
Code:
inline void begin(i2c_mode mode, uint8_t address1, uint8_t address2, uint8_t pinSCL, uint8_t pinSDA,
                      i2c_pullup pullup=I2C_PULLUP_EXT, uint32_t rate=400000, i2c_op_mode opMode=I2C_OP_MODE_ISR)
 
Hi, I am not nox... But I have noticed in the code that you can define the Slave with a range of addresses. Not sure if that is what you are wanting or not.

But from the header file you have:
Code:
inline void begin(i2c_mode mode, uint8_t address1, uint8_t address2, uint8_t pinSCL, uint8_t pinSDA,
                      i2c_pullup pullup=I2C_PULLUP_EXT, uint32_t rate=400000, i2c_op_mode opMode=I2C_OP_MODE_ISR)

Hi

thanks for reply. No - you misunderstood. The slave should respond to different adresses.

Short description of the problem : I have a flight controller which scans for different sensors. Of course I can hook the sensors directly to the flight controller. In my application I do some computations with various sensors on my own because the flight controller does not provide the same computations. But I would like to forward the raw sensor readout to the flight controller because I don,t want two sets of sensors.

My wishlist : something like three addresses on the same bus with three call back functions. Hope it doesn't sound too weird.
 
Last edited:
Hi

thanks for reply. No - you misunderstood. The slave should respond to different adresses.

Short description of the problem : I have a flight controller which scans for different sensors. Of course I can hook the sensors directly to the flight controller. In my application I do some computations with various sensors on my own because the flight controller does not provide the same computations. But I would like to forward the raw sensor readout to the flight controller because I don,t want two sets of sensors.

My wishlist : something like three addresses on the same bus with three call back functions. Hope it doesn't sound too weird.

You can make a Slave respond to a range of addresses, and then determine which one was addressed using the Wire.getRxAddr() function. If you look at the basic_slave_range example it shows something like this.

So for instance, to make slave respond to addresses 0x35, 0x38, 0x3F, you could do something like:

Code:
volatile uint8_t target;
void setup()
{
    // ...
    Wire.begin(I2C_SLAVE, 0x35, 0x3F, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
    // Wire.begin(I2C_SLAVE, 0x35, 0x3F, 19, 18, I2C_PULLUP_EXT, 400000);  <-- alternate begin(), explicit SCL,SDA pin list is also allowed now
    target = 0;
    // register events
    Wire.onReceive(receiveEvent);
    Wire.onRequest(requestEvent);
}

//
// handle Rx Event (incoming I2C data)
//
void receiveEvent(size_t count)
{
    target = Wire.getRxAddr();  // getRxAddr() is used to obtain Slave address
    switch(target)
    {
    case 0x35: do something; break;
    case 0x38: do something else; break;
    case 0x3F: do other else; break;
    }
}

//
// handle Tx Event (outgoing I2C data)
//
void requestEvent(void)
{
    target = Wire.getRxAddr();  // getRxAddr() is used to obtain Slave address
    switch(target)
    {
    case 0x35: do something; break;
    case 0x38: do something else; break;
    case 0x3F: do other else; break;
    }
}

This is as close as you get with this micro, but there is a subtle difference. Because the micro only responds to a range and not a list of addresses, it will ACK everything between the address endpoints, even if it only really responds properly to a few as above.

A different way is to use something like a T3.6 and then bind Wire1/2/3 in parallel and give each of them a different slave address. That will provide individual targets without the blanket ACK.
 
Sorry, hopefully @nox771 knows more magic about this. My quick look through the T3.6 document, I see there is a register: I2Cx_A1
Which I believe holds the slave address.

Don't know if there is any fudging you can do with your setup. Like if you know your device talking to the Teensy is going to cycle through each of the I2C devices, if you can outguess it and keep changing the slave address. I have no idea if something like that can work or not.
 
Thanks for all the replies - great.

@nox771 - I will give your proposal a try. At the moment one adress between Teensy and FC works fine. The adresses presented from the FC are well defined and not a big number - depends on configuration. If I provide a handling for all messgages sent by the FC - do you think I am out of the woods ?

@KurtE - the slave Teensy talking with the FC is also master for another Teensy which actually reads the sensors and also does data processing. I came to this solution because the sensors are quite far away in the tail of the glider. I just want to provide data passthrough from the sensors to the FC. Somewhat weird doing this with two Teensies in between - I admit but there are many other tasks involved.
 
i worked on a multi spi/i2c bus controller for teensy, where you could add as many teensies you want and have the i2c/spi/uart busses controlled from a single teensy, might be more efficient than having several sketches between mcus
 
@nox771 - I will give your proposal a try. At the moment one adress between Teensy and FC works fine. The adresses presented from the FC are well defined and not a big number - depends on configuration. If I provide a handling for all messgages sent by the FC - do you think I am out of the woods ?

You can provide handling or not for unneeded addresses. Usually it is not a problem (since nothing talks to those addresses anyway). But something like that should work for emulating multiple slaves. At one point I had made a composite slave device like this, but I can't find the sketch.
 
Back
Top