New I2C library for Teensy3

Thanks for the suggestion but it makes no difference. ...

The fact that the len is wrong - but covers the message - and then recovers for the next message - might show nox771 something

Can you show the output produced using the "for(...<len...)" code - in the case of your latter 'count' test - assuming that isn't what gave that.
 
Well I got good news and bad news. Good news is I was able to duplicate the Slave receive error on the LC whereby it re-triggers the callback. So this does look like a library error.

Bad news is I don't know exactly what it is. Most likely I forgot to clear a flag or something in LC Slave mode. It might take a bunch of trial and error to find it (this is where HW debug would really be nice).
 
nox771 - I wouldn't know where in the code to look for what - other than my pass through the code to look at K66. Interesting the len grows by one - a finite # of times - maybe until the next message arrives? I'm not sure if there is room for a race condition? From something Frank got me to try the other day - the LC I/O pins do rise/write/read faster than T_3.1 - at least in the case he had trying to control parallel SPI like I/O the T_3.1 would be one bit behind and needed a NOP for settling time, and the LC was ready next instruction.

BTW - hope your updated I2C going well - I ported a SSD1306 for an At-tiny once so was thinking of that and not until I looked after deciding to do it did I see it was your code - I paused another day before starting ... Not sure when you started - hopefully my code was of some value - if not I learned some things. Anyhow looking for more on the K66 thread.
 
Ok not too bad. I got a patch for you to try out. You will need to edit a few lines in i2c_t3.cpp file. Change as indicated in red below (this is around line ~1620 or so).

At the top a line is added, and at the bottom two lines are commented out. Try this and let me know if it fixes the problem. If so I will roll this into the next update.

Code:
        #if defined(__MKL26Z64__) // LC
        else if(flt & I2C_FLT_STOPF) // STOP detected (LC only)
        {
            // There is some weird undocumented stuff going on with the I2C_FLT register on LC.
            // If you read it back, bit4 is toggling, but it is supposed to be part of FLT setting.
            // Writing just the STOPF bit causes things to get stuck, but writing back the read value
            // seems to work
            [COLOR=#ff0000]*(i2c->FLT) = flt;               // clear STOP intr
            *(i2c->FLT) &= ~I2C_FLT_STOPIE;  // disable STOP intr[/COLOR]
            i2c->currentStatus = I2C_WAITING;
            if(i2c->user_onReceive != nullptr)
            {
                i2c->rxBufferIndex = 0;
                i2c->user_onReceive(i2c->rxBufferLength);
            }
        }
        #endif
        else
        {
            // Continue Slave Receive
            //
            // setup SDA-rising ISR - required for STOP detection in Slave Rx mode for 3.0/3.1
            #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) // 3.0/3.1
                i2c->irqCount = 0;
                if(i2c->currentPins == I2C_PINS_18_19)
                    attachInterrupt(18, i2c_t3::sda0_rising_isr, RISING);
                else if(i2c->currentPins == I2C_PINS_16_17)
                    attachInterrupt(17, i2c_t3::sda0_rising_isr, RISING);
                #if I2C_BUS_NUM >= 2
                else if(i2c->currentPins == I2C_PINS_29_30)
                    attachInterrupt(30, i2c_t3::sda1_rising_isr, RISING);
                else if(i2c->currentPins == I2C_PINS_26_31)
                    attachInterrupt(31, i2c_t3::sda1_rising_isr, RISING);
                #endif
[COLOR=#ff0000]//            #elif defined(__MKL26Z64__)
//                *(i2c->FLT) |= I2C_FLT_STOPIE; // enable STOP intr[/COLOR]
            #endif
            data = *(i2c->D);
            if(i2c->rxBufferLength < I2C_RX_BUFFER_LENGTH)
                i2c->rxBuffer[i2c->rxBufferLength++] = data;
        }
        *(i2c->S) = I2C_S_IICIF; // clear intr
    }
}

#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) // 3.0/3.1
// ------------------------------------------------------------------------------------------------------
// SDA-Rising Interrupt Service Routine - 3.0/3.1 only
//
// Detects the stop condition that terminates a slave receive transfer.
// If anyone from Freescale ever reads this code, please email me at
// paul@pjrc.com and explain how I can respond to the I2C stop without
// inefficient polling or a horrible pin change interrupt hack?!
//
 
BTW - hope your updated I2C going well - I ported a SSD1306 for an At-tiny once so was thinking of that and not until I looked after deciding to do it did I see it was your code - I paused another day before starting ... Not sure when you started - hopefully my code was of some value - if not I learned some things. Anyhow looking for more on the K66 thread.

I'm mostly using the code that others have posted as sort of markers for where the broken code is. It's not a case of NIH, I'm just doing a more extensive overhaul than just patching it into a working state for K66. For instance given the increasing myriad of overclock and bus freq variants, the old method of using a set of divider rate tables (one for each bus freq) was just not cutting it. So to fix that I've entirely replaced the setRate() function with a more algorithmic method whereby freq can be set arbitrarily (of course to maintain compatibility the I2C_RATE_xxx enums will still work, but they will not be needed in the future). This will be transparent to anyone who sets rate initially in the begin() function, but it may mean some slight tweaks for anyone using the function directly. In additon to being cleaner and more scalable (and actually shorter code-wise), this also provides a fix for those who want to run custom freqs, as was indicated earlier in the thread (eg. sub 100kHz, I've actually run the bus down to 12.5kHz).

Another thing I'm doing is coding in all 4 buses on the K66 (it's not clear if they will all pinout), but in doing so I'm really trying to code it to scale to "N" buses (across a multitude of parts), which means I have to remove all the little shortcuts and assumptions I use when the choice was only 1 or 2 buses. It's enough of a style change that the submitted patches don't quite work directly anymore.

And then there are fixes like this one just now. K66 and other new parts behave like LC in terms of STOP functionality, so getting it right is important.
 
Good answer - With the larger number of 'bus' and the increased potential for 'RATE' control - makes sense to keep the design that worked before from getting bogged down. Will watch the K66 thread for progress ... I didn't learn much - but more than I knew before.
 
Ok not too bad. I got a patch for you to try out. You will need to edit a few lines in i2c_t3.cpp file. Change as indicated in red below (this is around line ~1620 or so).

At the top a line is added, and at the bottom two lines are commented out. Try this and let me know if it fixes the problem. If so I will roll this into the next update.

Code:
        #if defined(__MKL26Z64__) // LC
        else if(flt & I2C_FLT_STOPF) // STOP detected (LC only)
        {
            // There is some weird undocumented stuff going on with the I2C_FLT register on LC.
            // If you read it back, bit4 is toggling, but it is supposed to be part of FLT setting.
            // Writing just the STOPF bit causes things to get stuck, but writing back the read value
            // seems to work
            [COLOR=#ff0000]*(i2c->FLT) = flt;               // clear STOP intr
            *(i2c->FLT) &= ~I2C_FLT_STOPIE;  // disable STOP intr[/COLOR]
            i2c->currentStatus = I2C_WAITING;
            if(i2c->user_onReceive != nullptr)
            {
                i2c->rxBufferIndex = 0;
                i2c->user_onReceive(i2c->rxBufferLength);
            }
        }
        #endif
        else
        {
            // Continue Slave Receive
            //
            // setup SDA-rising ISR - required for STOP detection in Slave Rx mode for 3.0/3.1
            #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) // 3.0/3.1
                i2c->irqCount = 0;
                if(i2c->currentPins == I2C_PINS_18_19)
                    attachInterrupt(18, i2c_t3::sda0_rising_isr, RISING);
                else if(i2c->currentPins == I2C_PINS_16_17)
                    attachInterrupt(17, i2c_t3::sda0_rising_isr, RISING);
                #if I2C_BUS_NUM >= 2
                else if(i2c->currentPins == I2C_PINS_29_30)
                    attachInterrupt(30, i2c_t3::sda1_rising_isr, RISING);
                else if(i2c->currentPins == I2C_PINS_26_31)
                    attachInterrupt(31, i2c_t3::sda1_rising_isr, RISING);
                #endif
[COLOR=#ff0000]//            #elif defined(__MKL26Z64__)
//                *(i2c->FLT) |= I2C_FLT_STOPIE; // enable STOP intr[/COLOR]
            #endif
            data = *(i2c->D);
            if(i2c->rxBufferLength < I2C_RX_BUFFER_LENGTH)
                i2c->rxBuffer[i2c->rxBufferLength++] = data;
        }
        *(i2c->S) = I2C_S_IICIF; // clear intr
    }
}

#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) // 3.0/3.1
// ------------------------------------------------------------------------------------------------------
// SDA-Rising Interrupt Service Routine - 3.0/3.1 only
//
// Detects the stop condition that terminates a slave receive transfer.
// If anyone from Freescale ever reads this code, please email me at
// paul@pjrc.com and explain how I can respond to the I2C stop without
// inefficient polling or a horrible pin change interrupt hack?!
//

Yep, that has fixed it, nice work!

I don't want to sound mean, but I am relieved it's nothing my end. (You are never quite sure when you try some new hardware/software, whether you have just made a stupid mistake).

I guess now, I just need to decide a protocol to send the commands from the PI to the Teensy.

Thanks again for fixing this so quickly.

Regards,

Les
 
This is all really strange. Slow-degredation could be caused by things like memory leaks on the software side, or temperature on the hardware side. I'm not sure about something like temp, but given the historical use of the library I think something like a memory leak would have shown up by now.

I think the common factor here is the heavy loading on the I2C bus. I'm wondering if the peripheral has trouble with slow-rise signals. I've worked on the hardware side of these interfaces before and there can be all manner of circuits used from very simple to excessively complex. Sometimes designers redesign everything and sometimes they use "off-the-shelf" blocks (either way could be potentially error prone). Even though I2C is only a 2 wire bus, there is an implicit conversion from analog to digital, and various methods of setting A/D switch points (eg. fixed ground referenced, mid-level based on input swing, mid-level based on supply, etc...). It's possible slow-rise signals are messing with the hardware side peripheral logic (given the rather poor state of the internal pullup design for I2C on these parts, this would not be surprising to me at all).

A suggestion I have would be to speedup the rise-time on the bus. Cut the pullup resistance way down to get the edge rates up and see if there is any effect. I'm really not sure what else might be going on, I'll think about it some more.

Thanks for the thoughts and feedback. I tried turning on the internal pullup resistors on in the Teensy to greatly decrease the total pullup resistance. One of the I2C devices dropped off (Si7020), which I assume means it can't sink enough current to properly communicate, but the other sensors remained online. Bus dropouts still occurred as described before at 100kHz. Not sure if this would point towards the Teensy overheating, in which case the internal pullups might possibly increase the problem. Will try dropping the external pullups to 1K from 2K2 and test with the internal pullups off and running at 100kHz. Have been having good success running at 400kHz with 2K2 external pullups (except when using the system in very wet conditions that it wasn't designed for, aka thunderstorms).
 
i ported the RTC RV3029-C2 produced by micro crystal swizerland http://www.microcrystal.com/images/_PDF/5_Real-Time-Clock_RV/rv-3029-c2.pdf
base library was the ds1307 https://github.com/PaulStoffregen/DS1307RTC
and it use this i2c library. thanks to nox.
to setup the chip you have to edit the cpp file. also supports the rtc temperatur reading.
for shure this lib is not optimized but perhaps a firm user will tell me mistakes/options and i will change it.
so far working...
Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_1200);

don´t own github so hope this will be ok
https://mega.nz/#!NRsGBbwT!ImKEaTJhMQPQYlgDswXHYw7203Hw25h0yY6zY7MStmc
 
i ported the RTC RV3029-C2 produced by micro crystal swizerland http://www.microcrystal.com/images/_PDF/5_Real-Time-Clock_RV/rv-3029-c2.pdf
base library was the ds1307 https://github.com/PaulStoffregen/DS1307RTC
and it use this i2c library. thanks to nox.
to setup the chip you have to edit the cpp file. also supports the rtc temperatur reading.
for shure this lib is not optimized but perhaps a firm user will tell me mistakes/options and i will change it.
so far working...
Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_1200);

don´t own github so hope this will be ok
https://mega.nz/#!NRsGBbwT!ImKEaTJhMQPQYlgDswXHYw7203Hw25h0yY6zY7MStmc


Thanks. I've added a link for it under "Compatible Libraries" on the first post. I'll add it also on GitHub when I do the next library update (should be soon).
 
thanks for your offer. github would be the best platform for this. so next will be fram 25C04 i will give a try.

Sorry, I meant I would add the link to it on i2c_t3 GitHub page when I do the next library update. As far as storing the library itself I would recommend you get a GitHub account and upload it there (it is free to setup a public repository).
 
All - a new v9 library has been uploaded, to both GitHub and here. Top-post has been modified accordingly. Changes are as follows:

  • Added support for upcoming Teensy 3.5/3.6 devices (aka K64/K66 devices):
  • - 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

This was actually a major rewrite on some routines (particularly in regard to setting frequency rates, pins, and STOP interrupt detection). I've done my best to test it thoroughly, however if anyone sees regressions in their I2C circuit behavior please let me know. Thanks.
 
Is this i2c library to be considered as "non-blocking"? As opposed to the Arduino wire.h that uses while-loops that freeze the uC if there is a problem with communication with the slave.
 
Easy enough to look at the code, no? ;)

FWIW, I haven't found the whole loops during communication to be as much a problem as the sensor library implementations themselves. For example, for the popular 1-wire sensors there are ways to initiate a poll of the sensor and then to come back later to read out the registers. For simplicities sake, most libraries don't support this sort of reading because it put the burden on the programmer to remember to come back later. Ditto for humidity sensors like the sht21/htu21/si702x series.

The data sheets give some pretty good ideas on what codes to send to the sensor, so extending currrnt libraries is pretty easy as long as you follow the syntax set out by other commands in the library and remember to modify both the Cpp file and the .h file.
 
Last edited:
That's a different issue than the one I thought you were wrestling with, my apologies! Assumptions....

The easiest way is to simply implement the thing instead of the standard wire library and see what happens. This is where a good debugger implementation would be very helpful.... And some day we may get one. Everything arduino progresses at glacial pace while luminaries like Paul are egging on the ice berg with whips and carrots.

It's pretty easy btw to go through the library code and start enabling debugging messages to signal to you where the cpu likely got stuck. Make a zipped backup of the library directory and then add a lot of serial prints to the Cpp file to signal what the last thing was that worked.
 
Last edited:
My current project locks-up/freezes the uC several times a day, with very random intervals. I suspect that its the i2c that is the problem. I started to research i2c freeze problems on the web, and found other people having the same problem, originating in that the wire library uses while-loops that has no timeout. A glitch in the communication over i2c was enough to get stuck in the while-loop and virtually freeze the uC. I hope this new library has sorted that out. Going to try it this week.
 
Is this i2c library to be considered as "non-blocking"? As opposed to the Arduino wire.h that uses while-loops that freeze the uC if there is a problem with communication with the slave.

The library can run in a number of different configurations including non-blocking (background) transfers.

You may or may not be referring to "non-blocking" when you talk about timeouts. The library supports timeouts, on both blocking and non-blocking transfers, which are easiest to use via the setDefaultTimeout() function right after the begin() call in the setup() function (refer to some of the basic examples, which IIRC use default timeouts).

More specifically:
  • Non-blocking refers to initiating a background transfer - specifically that means code flow continues immediately after starting a transfer, and the transfer completes at a later time.
  • Blocking commands refer to commands which stop code flow until after the I2C transfer is complete. This is traditionally the way Arduino operates. It will wait for I2C to complete before proceeding with code execution.
  • Timeouts can apply to both types of commands, and are used to handle error conditions. That is, after a set amount of time the transfer will terminate if it has not completed already, and code will continue execution. Its effect is obvious in the case of blocking transfers. In the case of a non-blocking background transfer, this timeout error would not be apparent until you checked the status of the I2C at some later time.

Again the basic examples should demonstrate setting timeouts with blocking commands. IIRC, the only example which deals with non-blocking, background transfers is the advanced_master example. You'll have to clarify exactly what you're after.
 
My current project locks-up/freezes the uC several times a day, with very random intervals. I suspect that its the i2c that is the problem. I started to research i2c freeze problems on the web, and found other people having the same problem, originating in that the wire library uses while-loops that has no timeout. A glitch in the communication over i2c was enough to get stuck in the while-loop and virtually freeze the uC. I hope this new library has sorted that out. Going to try it this week.

This sounds like you want timeouts, not non-blocking. Try this:

  1. Change Wire.h to i2c_t3.h (make sure to download/install latest i2c_t3 lib)
  2. Add Wire.setDefaultTimeout(100000); after your begin() call (this would be for 100ms timeout, adjust as needed)
  3. After Wire.endTransmission() calls you can call a function that does something similar to this:
Code:
if(Wire.getError())
{
    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;
    }
}

That should give you some idea of what's going on if the I2C transfer ends with an error.
 
Thank you for that. I have added "Wire.setDefaultTimeout(100000);" as you suggest. I dont have any Wire.endTransmission() in my code, that I can see. One part of my code that is doing i2c is this:
Code:
unsigned long Readi2cWindVane() {
  byte hb;
  byte lb;
  Wire.requestFrom(WIND_VANE_I2C_SLAVE_ADDR, 1);
  if (Wire.available()) {
    lb = Wire.read();
  }
  Wire.requestFrom(WIND_VANE_I2C_SLAVE_ADDR, 1);
  if (Wire.available()) {
    hb = Wire.read();
  }
  unsigned long value = ((hb << 8) + lb);

  return value;
}

Where do you think I can put in the error code handling that you suggest?
 
Thank you for that. I have added "Wire.setDefaultTimeout(100000);" as you suggest. I dont have any Wire.endTransmission() in my code, that I can see. One part of my code that is doing i2c is this:
Code:
unsigned long Readi2cWindVane() {
  byte hb;
  byte lb;
  Wire.requestFrom(WIND_VANE_I2C_SLAVE_ADDR, 1);
  if (Wire.available()) {
    lb = Wire.read();
  }
  Wire.requestFrom(WIND_VANE_I2C_SLAVE_ADDR, 1);
  if (Wire.available()) {
    hb = Wire.read();
  }
  unsigned long value = ((hb << 8) + lb);

  return value;
}

Where do you think I can put in the error code handling that you suggest?

You can put the error check after the Wire.requestFrom() and before the if(Wire.available()) statements. Maybe something like:
Code:
if(Wire.getError())
    do error stuff
else if(Wire.available())
    do normal stuff
 
Thanks, I'll try that!

Yesterday I updated my code to use i2c_t3.h instead of Wire.h. I also had to alter the compass library LSM303.h (https://github.com/pololu/lsm303-arduino/tree/master/LSM303) to use i2c_t3.h instead of Wire.h. Also I added Wire.setDefaultTimeout(100000); in setup(). Everthing worked just as before, but didnt have the chance to give it long enough runtime to determine if it still freezes or not. Will try to do that this weekend.
 
To me attempting to use a timeout to address what likely are electrical/signal problems seems not a good approach. Have you checked signals with an oscilloscope and verified that that's all OK ?
Would you have some more detail at what frequency you are running the I2C bus, wire length, type of wire, number of I2C devices on the bus etc. ?
 
Back
Top