New I2C library for Teensy3

Well there is some weird stuff going on. The particular code in question is this part from acquireBus_():
Code:
        while(timeout == 0 || deltaT < timeout)
        {
            // we are not currently the bus master, so check if bus ready
            if(!(*(i2c->S) & I2C_S_BUSY))   [COLOR=#ff0000]<-- without timeout this loops until bus is not busy[/COLOR]
            {
                // become the bus master in transmit mode (send start)
                i2c->currentMode = I2C_MASTER;
                *(i2c->C1) = I2C_C1_IICEN | I2C_C1_MST | I2C_C1_TX;  [COLOR=#ff0000]<-- once not busy this sets Master mode[/COLOR]
                break;
            }
        }
        #if defined(I2C_AUTO_RETRY)  [COLOR=#ff0000]<-- #if/#endif block exists only if auto retry is defined[/COLOR]
            // if not master and auto-retry set, then reset bus and try one last time
            if(!(*(i2c->C1) & I2C_C1_MST))  [COLOR=#ff0000]<-- this should not occur since only way to exit above without timeout is to set Master mode[/COLOR]
            {
                resetBus_(i2c,bus);  [COLOR=#ff0000]<-- this next part tries to clear bus and then reset master mode, but again this shouldn't happen in this case[/COLOR]
                if(!(*(i2c->S) & I2C_S_BUSY))
                {
                    // become the bus master in transmit mode (send start)
                    i2c->currentMode = I2C_MASTER;
                    *(i2c->C1) = I2C_C1_IICEN | I2C_C1_MST | I2C_C1_TX;
                }
            }
        #endif
        // check if not master
        if(!(*(i2c->C1) & I2C_C1_MST))
        {
            i2c->currentStatus = I2C_TIMEOUT; // bus not acquired, mark as timeout
            return 0;
        }

The only thing I can guess here is that the delay between setting Master mode in the top part, and checking to see if it is set in the auto retry part, is too small. Not sure, but maybe the block takes a while to register as Master mode (I've never "speed tested" it, but it's possible given I2C is a low speed block). There are other places where I had to use small delays to wait for I2C status to change.

One test might be putting this after the #if defined(I2C_AUTO_RETRY) line, if auto retry is defined:
Code:
    #if defined(I2C_AUTO_RETRY)
        delayMicroseconds(1);
        ...
 
I have two Teensy 3.6's wired together using pins 18 and 19 and external 4.7 kOhm pullups on 3.3V. Both are overclocked to 240 MHz. One is running advanced_slave and the other is running advanced_master. When I ground pin 8 to test the transfer rate, I see that I "only" get up to a 3 MHz I2C rate; presumably because F_Bus is not overclocked. I would like to compare transfer times up to 6 MHz, how would I overclock F_Bus to 120 MHz?

Thanks much!
Brian
 
Swap commented lines here: "I:\arduino-1.6.11\hardware\teensy\avr\cores\teensy3\kinetis.h"
#if (F_CPU == 240000000)
#define F_PLL 240000000
#ifndef F_BUS
#define F_BUS 60000000
//#define F_BUS 80000000 // uncomment these to try peripheral overclocking
//#define F_BUS 120000000 // all the usual overclocking caveats apply...
 
Here are the results from the tests. Again, this is two Teensy 3.6 devices, overclocked to 240 MHz with F_BUS set to 120 MHz. 4.7 kOhm pullup resistors with a 3.3V source. Short jumper wire lengths and both Teensy devices on breadboards. Transferring 256 bytes of data.

Code:
Mode:IMM    Pins:18/19 256 byte transfer at 10000 Hz     (Actual Rate (Hz): 31250) : 74633 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 100000 Hz     (Actual Rate (Hz): 104166) : 22560 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 200000 Hz     (Actual Rate (Hz): 208333) : 11325 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 300000 Hz     (Actual Rate (Hz): 312500) : 7908 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 400000 Hz     (Actual Rate (Hz): 416666) : 6040 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 600000 Hz     (Actual Rate (Hz): 625000) : 4469 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 800000 Hz     (Actual Rate (Hz): 833333) : 3534 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 1000000 Hz     (Actual Rate (Hz): 1000000) : 2921 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 1200000 Hz     (Actual Rate (Hz): 1250000) : 2599 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 1500000 Hz     (Actual Rate (Hz): 1500000) : 2212 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 1800000 Hz     (Actual Rate (Hz): 1764705) : 2013 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 2000000 Hz     (Actual Rate (Hz): 2000000) : 1783 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 2400000 Hz     (Actual Rate (Hz): 2500000) : 1628 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 2800000 Hz     (Actual Rate (Hz): 2727272) : 1540 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 3000000 Hz     (Actual Rate (Hz): 3000000) : 1430 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 4000000 Hz     (Actual Rate (Hz): 4000000) : 1258 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 5000000 Hz     (Actual Rate (Hz): 5000000) : 1138 us : I2C waiting, no errors
Mode:IMM    Pins:18/19 256 byte transfer at 6000000 Hz     (Actual Rate (Hz): 6000000) : 1063 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 10000 Hz     (Actual Rate (Hz): 31250) : 74632 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 100000 Hz     (Actual Rate (Hz): 104166) : 22630 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 200000 Hz     (Actual Rate (Hz): 208333) : 11360 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 300000 Hz     (Actual Rate (Hz): 312500) : 8204 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 400000 Hz     (Actual Rate (Hz): 416666) : 6336 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 600000 Hz     (Actual Rate (Hz): 625000) : 4489 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 800000 Hz     (Actual Rate (Hz): 833333) : 3502 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 1000000 Hz     (Actual Rate (Hz): 1000000) : 2941 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 1200000 Hz     (Actual Rate (Hz): 1250000) : 2620 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 1500000 Hz     (Actual Rate (Hz): 1500000) : 2237 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 1800000 Hz     (Actual Rate (Hz): 1764705) : 2042 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 2000000 Hz     (Actual Rate (Hz): 2000000) : 1835 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 2400000 Hz     (Actual Rate (Hz): 2500000) : 1637 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 2800000 Hz     (Actual Rate (Hz): 2727272) : 1549 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 3000000 Hz     (Actual Rate (Hz): 3000000) : 1483 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 4000000 Hz     (Actual Rate (Hz): 4000000) : 1281 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 5000000 Hz     (Actual Rate (Hz): 5000000) : 1163 us : I2C waiting, no errors
Mode:ISR    Pins:18/19 256 byte transfer at 6000000 Hz     (Actual Rate (Hz): 6000000) : 1086 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 10000 Hz     (Actual Rate (Hz): 31250) : 74634 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 100000 Hz     (Actual Rate (Hz): 104166) : 22492 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 200000 Hz     (Actual Rate (Hz): 208333) : 11359 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 300000 Hz     (Actual Rate (Hz): 312500) : 7892 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 400000 Hz     (Actual Rate (Hz): 416666) : 6025 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 600000 Hz     (Actual Rate (Hz): 625000) : 4469 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 800000 Hz     (Actual Rate (Hz): 833333) : 3534 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 1000000 Hz     (Actual Rate (Hz): 1000000) : 2842 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 1200000 Hz     (Actual Rate (Hz): 1250000) : 2572 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 1500000 Hz     (Actual Rate (Hz): 1500000) : 2175 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 1800000 Hz     (Actual Rate (Hz): 1764705) : 2024 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 2000000 Hz     (Actual Rate (Hz): 2000000) : 1814 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 2400000 Hz     (Actual Rate (Hz): 2500000) : 1619 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 2800000 Hz     (Actual Rate (Hz): 2727272) : 1541 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 3000000 Hz     (Actual Rate (Hz): 3000000) : 1447 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 4000000 Hz     (Actual Rate (Hz): 4000000) : 1245 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 5000000 Hz     (Actual Rate (Hz): 5000000) : 1126 us : I2C waiting, no errors
Mode:DMA[0] Pins:18/19 256 byte transfer at 6000000 Hz     (Actual Rate (Hz): 6000000) : 1044 us : I2C waiting, no errors
 
Those are some numbers! - Nice summary.
256 bytes in DMA 1044 us looks like 245 KB or 1.96 Kbit/sec - just 4% slower to do it as an ISR.

A 256 Byte broadcast to multiple slaves and pull 256 Bytes back from two or three in 5 us? If they were all T_3.6's.
 
Nice benchmarks. This is actually a faster speedup than I would have expected. The 2.4M rate topped out at around 100kB/s (measured on T3.1), and this running at 6M is ~245kB/s, which is nearly a 1:1 throughput scaling vs I2C clock rate (actual scaling ~0.96). ISR overhead is low, which is good to see.
 
I'm assuming the onRequest function is creating an interrupt to call the specified function when the host requests data from the slave device. I've used it a bunch quite successfully. However, I'm wondering what the interrupt priority of onRequest is? I have an application with several interrupts and I'd like to get the priorities set right for the desired behavior.

Thanks much!
Brian
 
I'm assuming the onRequest function is creating an interrupt to call the specified function when the host requests data from the slave device. I've used it a bunch quite successfully. However, I'm wondering what the interrupt priority of onRequest is? I have an application with several interrupts and I'd like to get the priorities set right for the desired behavior.

Thanks much!
Brian
 
I'm assuming the onRequest function is creating an interrupt to call the specified function when the host requests data from the slave device. I've used it a bunch quite successfully. However, I'm wondering what the interrupt priority of onRequest is? I have an application with several interrupts and I'd like to get the priorities set right for the desired behavior.

In Master mode the library will automatically try to set its priority higher than the calling function (dropping to immediate mode if the calling function is priority 0). For Slave mode operation (onReceive/onRequest) you would need to set the priority on the Slave I2C IRQ manually.

If you look at the code in the bottom half of the acquireBus_() function, you can see how it uses NVIC_GET_PRIORITY() and NVIC_SET_PRIORITY() to change the IRQ priority level (lower numbers are higher priority, IIRC they are quantized to steps of 16).

You can use NVIC_SET_PRIORITY() in your setup function to stack the IRQs the way you want it.
 
How nox

during testing i2c-bus recovery from simulated hardware problems (bad sda/scl connections, removing i2c slave ect) I managed to get stuck in an i2c-bus I2C_TIMEOUT (4). From this I could not recover. Recovery from I2C_ADDR_NAK and I2C_DATA_NAK which I also observed went well. I admit that it took some abuse like grounding the pullup-resistor to get into I2C_TIMEOUT but this could easily reproduced. I just wonder why this bus status could not be cleared. The Teensy was happily doing its task and did not crash.

Shorting SDA (through resistor) ->>>I2C_TIMEOUT, recovery possible.
Shorting SCL (through resistor) ->>>I2C_TIMEOUT, recovery NOT possible.
pulling pullup of SCL without grounding - ->>> I2C_ADDR_NAK, recovery possible


Do you have an idea why i2c_t3 might stay in I2C_TIMEOUT ?

regards Andi
 
Last edited:
Shorting SDA (through resistor) ->>>I2C_TIMEOUT, recovery possible.
Shorting SCL (through resistor) ->>>I2C_TIMEOUT, recovery NOT possible.
pulling pullup of SCL without grounding - ->>> I2C_ADDR_NAK, recovery possible

Do you have an idea why i2c_t3 might stay in I2C_TIMEOUT ?

Shorting SCL to ground will render the bus inoperable. I2C cannot communicate at all if SCL is grounded. If SDA is grounded then it will assume it is a stuck Slave device and it will try to toggle SCL to release the bus. This only happens automatically if I2C_AUTO_RETRY is set in i2c_t3.h, which on the latest release it is not set by default. You can also manually call resetBus(). If SDA stays grounded then again the bus will not work. Obviously the SDA and SCL need to be able to toggle to allow the bus to communicate.

The resetBus() call is a "one-shot" operation. If the error lasts longer than the reset procedure then you will have to call the resetBus() again (eg. loop until the bus is working).
 
Hi Brian

thanks for answer. Looks like my problems are not related to i2c_t3. I have two breakout boards, https://cdn.sparkfun.com/datasheets/Sensors/IMU/SparkFun_MPU-9250_Breakout.pdf and the other https://drotek.com/shop/en/home/44-ms5611-pressure-barometric-board.html. Looks to me like both have 10k pullups incorporated. I have the impression that my bus is flawed. I have to work on this.

Good news is that with read/write timeouts ewerywhere the program runs even if it looses the i2c bus, which happens even without shorting. The bus looses SCL (SCL just stays high, no clocking) and i2c_t3 goes in I2C_WAITING status. Have not tried to regain the bus with Wire.pinConfigure but will give it a try..

Just noticed that you are already calling pinConfigure in resetBus(). Is it possible that a timing problem develops if immediately after calling resetBus a read/write on the bus is initiated ?

Is it a good idea to remove or bridge the pullups on the breakouts ?

regards Andi
 
Last edited:
Good news is that with read/write timeouts ewerywhere the program runs even if it looses the i2c bus, which happens even without shorting. The bus looses SCL (SCL just stays high, no clocking) and i2c_t3 goes in I2C_WAITING status. Have not tried to regain the bus with Wire.pinConfigure but will give it a try..

Just noticed that you are already calling pinConfigure in resetBus(). Is it possible that a timing problem develops if immediately after calling resetBus a read/write on the bus is initiated ?

Is it a good idea to remove or bridge the pullups on the breakouts ?

If you only have 2 10k in parallel (5k equiv) then that should not cause a problem. A 5k pullup is a very reasonable value. The I2C_WAITING state is the normal idle state. This might be expected if timeouts are used everywhere since the errors should not block, and subsequent code might be inadvertently resetting the bus status.

It is unclear what might be causing your problems. You might try running with just one slave device and see if you can isolate the timeouts occurring to using one slave or the other. Can also try running at slower bus speeds if there are long wires involved. Of course a logic analyzer or scope shot of a problem helps a lot.

If you detect when a timeout occurs you can log the previous command (eg. which slave was it talking to). I might try something similar to this: 1) log the error, 2) reset the bus, 3) see if the slave device responds to a simple command (eg. read or write a byte or similar), 4) If that works try resending the original command. If you set a pin high when a timeout occurs, you can use that as a trigger for a logic analyzer.

It is possible that something in the resetBus() code is causing a problem in your configuration. I can't say what that is. Adding in a few microseconds of delay between operations does sometimes help.
 
hey i noticed a problem, i dont know if it's the library or not, but, try this with auto retry enabled and see if it works for you

#1 connect your device to sda/scl
#2 make sure it works
#3 short sda and scl with a jumper for about a sec or more

It doesnt seem to recover the failed bus but the teensy is not locked as it still processes other spi/i2c busses and code. pushing the reset button on teensy OR re-uploading the same code so it resets, however, regains the bus, without touching the i2c attached devices

This may be related to people having lockups like I am, when I ran the mega2560 in my setup, I never had this issue, after i converted to teensy just every couple of days I get freezing.

Thanks
Tony

in my sketch i have:
Code:
///////////////////////////////////////////////////////////
#define I2C_AUTO_RETRY
#include <i2c_t3.h>
///////////////////////////////////////////////////////////
and
Code:
  Wire1.begin(I2C_MASTER, 0x00, I2C_PINS_37_38, I2C_PULLUP_EXT, 100000);
  Wire1.setDefaultTimeout(200000); // 200ms

and I also tried to Wire1.resetBus via a GPIO trigger while watching serial monitor, no bus recovery....

I'm currently breadboard testing it with a mcp23017 on SCL/SDA pins 37/38. I've tried 4.7k pullups, 10K pullups, and 2.2K pullups

a soft reset fixes the issue, so i think theres a busrecovery code problem, I tried to Wire1.end() but *.end() doesnt exist in this library
 
Last edited:
Hi Brian

I did some investigation what the locked condition is about and read out the i2c status register. Readout is consistently 0010 0101, which points to a busy state, slave TX and NAK.
This condition stays (even when the slave is removed from the bus) with SCL/SDA in high.

Started reading the manuals.

regards Andi
 
runs SLIGHTLY better with I2C_OP_MODE_IMM, however, still has freezing issues, I2C_OP_MODE_DMA has same hard lockup as I2C_OP_MODE_ISR
 
runs SLIGHTLY better with I2C_OP_MODE_IMM, however, still has freezing issues, I2C_OP_MODE_DMA has same hard lockup as I2C_OP_MODE_ISR

I implemented a small routine in i2c_t3



uint8_t i2c_t3::makeStartStop_(struct i2cStruct* i2c, uint8_t reg){

uint8_t returnvalue=8;

*(i2c->C1) = (uint8_t)0x00;
delayMicroseconds(5);
*(i2c->C1) = I2C_C1_IICEN; // send STOP, change to Rx mode, intr disabled
delayMicroseconds(1);

return returnvalue;

}
It just disables the processors I2C module operation and enables it again. The effect on the slaves is unknown - but I get at least out of the locked condition described above. Whether its the proper way to handle this condition is beyond my knowledge - but it looks like it is working.
I call it once when I detect the locked condition.
 
hey i noticed a problem, i dont know if it's the library or not, but, try this with auto retry enabled and see if it works for you

#1 connect your device to sda/scl
#2 make sure it works
#3 short sda and scl with a jumper for about a sec or more

It doesnt seem to recover the failed bus but the teensy is not locked as it still processes other spi/i2c busses and code. pushing the reset button on teensy OR re-uploading the same code so it resets, however, regains the bus, without touching the i2c attached devices

...

in my sketch i have:
Code:
///////////////////////////////////////////////////////////
#define I2C_AUTO_RETRY
#include <i2c_t3.h>
///////////////////////////////////////////////////////////
and
Code:
  Wire1.begin(I2C_MASTER, 0x00, I2C_PINS_37_38, I2C_PULLUP_EXT, 100000);
  Wire1.setDefaultTimeout(200000); // 200ms

and I also tried to Wire1.resetBus via a GPIO trigger while watching serial monitor, no bus recovery....

I'm currently breadboard testing it with a mcp23017 on SCL/SDA pins 37/38. I've tried 4.7k pullups, 10K pullups, and 2.2K pullups

a soft reset fixes the issue, so i think theres a busrecovery code problem, I tried to Wire1.end() but *.end() doesnt exist in this library

I think there are fragments of information here. Simply shorting the SCL and SDA together by itself doesn't do anything, since they are both pulled up to the same voltage. Shorting the pins together and then attempting to write to the bus would trigger a timeout (given 200ms timeout, and 1s short). This error implies there is additional code which is not shown.

However, in general there is no long-term fault recovery built into the library. This is assumed to be a application function. The library only provides some functions to assist with bus recovery, but it is not something that automatically happens. It sounds like the bus state setting (i2c->currentStatus) is getting out of sync with HW state (i2c->C1 register). resetBus() was originally intended to only fix stuck Slaves, but it sounds like resetting the C1 state might be needed also.

I implemented a small routine in i2c_t3
Code:
uint8_t i2c_t3::makeStartStop_(struct i2cStruct* i2c, uint8_t reg){
     uint8_t returnvalue=8;
     *(i2c->C1) = (uint8_t)0x00; 
     delayMicroseconds(5);
     *(i2c->C1) = I2C_C1_IICEN; // send STOP, change to Rx mode, intr disabled
     delayMicroseconds(1);
     return returnvalue;
}

This will certainly reset the state of the I2C HW. I can see how there might be some utility in having a hard reset function, so I might add something like this into the next release. Here is something to try - augmenting resetBus() with a write to C1 to send a STOP (this stops short of killing the clock on the module, but it will force a START on the next transmit). If someone can test this and let me know if it works that would be helpful, just add the indicated line (near bottom) into resetBus_() function:

Code:
void i2c_t3::resetBus_(struct i2cStruct* i2c, uint8_t bus)
{
    uint8_t scl=0, sda=0, count=0;

    switch(i2c->currentPins)
    {
    case I2C_PINS_16_17: sda = 17; scl = 16; break;
    case I2C_PINS_18_19: sda = 18; scl = 19; break;
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)  // 3.5/3.6
    case I2C_PINS_7_8:   sda =  8; scl =  7; break;
    case I2C_PINS_33_34: sda = 34; scl = 33; break;
    case I2C_PINS_47_48: sda = 48; scl = 47; break;
    #endif
    #if defined(__MKL26Z64__) // LC
    case I2C_PINS_22_23: sda = 23; scl = 22; break;
    #endif
    #if defined(__MK20DX256__) // 3.1/3.2
    case I2C_PINS_29_30: sda = 30; scl = 29; break;
    case I2C_PINS_26_31: sda = 31; scl = 26; break;
    #endif
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)  // 3.5/3.6
    case I2C_PINS_37_38: sda = 38; scl = 37; break;
    case I2C_PINS_3_4:   sda =  4; scl =  3; break;
    #endif
    #if defined(__MK66FX1M0__) // 3.6
    case I2C_PINS_56_57: sda = 56; scl = 57; break;
    #endif
    }
    if(sda && scl)
    {
        // change pin mux to digital I/O
        pinMode(sda,((i2c->currentPullup == I2C_PULLUP_EXT) ? INPUT : INPUT_PULLUP));
        digitalWrite(scl,HIGH);
        pinMode(scl,OUTPUT);

        while(digitalRead(sda) == 0 && count++ < 10)
        {
            digitalWrite(scl,LOW);
            delayMicroseconds(5);       // 10us period == 100kHz
            digitalWrite(scl,HIGH);
            delayMicroseconds(5);
        }

        // reset status
        [COLOR=#ff0000]*(i2c->C1) = I2C_C1_IICEN; // send STOP, change to Rx mode, intr disabled[/COLOR]
        i2c->currentStatus = I2C_WAITING; // reset status

        // reconfigure pins for I2C
        pinConfigure_(i2c, bus, i2c->currentPins, i2c->currentPullup, 0);
    }
}
 
Also in addition to the above change, if that can fix the bus state when called manually, please let me know if having the I2C_AUTO_RETRY define set in the header file makes it work without intervention.

The way that works is a new Master transmit will automatically call resetBus() if it cannot acquire the bus (it only calls it once). So on a long term short (1s), it should fail with a timeout error still, but if the transmit is called on a loop, say every 2s, then once the error clears, it should go through on the next try. I would be interested to know if that works.
 
Hi Brian

It is possible that something in the resetBus() code is causing a problem in your configuration. I can't say what that is. Adding in a few microseconds of delay between operations does sometimes help.

I intentionally set up a totally flawed bus because I wanted to test the robustness of i2c_t3. The current resetBus clears around 80% of the errors.

It sounds like the bus state setting (i2c->currentStatus) is getting out of sync with HW state (i2c->C1 register). resetBus() was originally intended to only fix stuck Slaves, but it sounds like resetting the C1 state might be needed also.

Thats what I observed. The hardest part was the bus busy flag in the status register. I tried different approaches (including the one you proposed) but could not solve the problem. I had the impression it made the problem even worse.

A little routine was quite helpful and showed that i2c->currentStatus was out of sync.



Code:
uint8_t i2c_t3::getI2Cstatus_(struct i2cStruct* i2c, uint8_t reg){

    uint8_t returnvalue=8;

    switch(reg)
    {
    case 0:
      returnvalue = *(i2c->S);
      break;
    case 1:
      returnvalue = *(i2c->C1);
      break;
    case 2:
      returnvalue = *(i2c->C2);
      break;
    default: 
      break;
    }

    return returnvalue;
  
}




I put the pieces together and made a resetBus routine that should be able to clear a locked bus. The added code was tested in a different routine and works with two slaves well so far - even without calling begin again. I did not investigate so far but probably the bus has to be set up again.
I let the stuff now run for a while. It is happily counting errors.

Code:
void i2c_t3::resetBus_(struct i2cStruct* i2c, uint8_t bus)

{
    uint8_t scl=0, sda=0, count=0;

    switch(i2c->currentPins)
    {
    case I2C_PINS_16_17: sda = 17; scl = 16; break;
    case I2C_PINS_18_19: sda = 18; scl = 19; break;
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)  // 3.5/3.6
    case I2C_PINS_7_8:   sda =  8; scl =  7; break;
    case I2C_PINS_33_34: sda = 34; scl = 33; break;
    case I2C_PINS_47_48: sda = 48; scl = 47; break;
    #endif
    #if defined(__MKL26Z64__) // LC
    case I2C_PINS_22_23: sda = 23; scl = 22; break;
    #endif
    #if defined(__MK20DX256__) // 3.1/3.2
    case I2C_PINS_29_30: sda = 30; scl = 29; break;
    case I2C_PINS_26_31: sda = 31; scl = 26; break;
    #endif
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)  // 3.5/3.6
    case I2C_PINS_37_38: sda = 38; scl = 37; break;
    case I2C_PINS_3_4:   sda =  4; scl =  3; break;
    #endif
    #if defined(__MK66FX1M0__) // 3.6
    case I2C_PINS_56_57: sda = 56; scl = 57; break;
    #endif
    }
    if(sda && scl)
    {
        // change pin mux to digital I/O
        pinMode(sda,((i2c->currentPullup == I2C_PULLUP_EXT) ? INPUT : INPUT_PULLUP));
        digitalWrite(scl,HIGH);
        pinMode(scl,OUTPUT);

        while(digitalRead(sda) == 0 && count++ < 10)
        {
            digitalWrite(scl,LOW);
            delayMicroseconds(5);       // 10us period == 100kHz
            digitalWrite(scl,HIGH);
            delayMicroseconds(5);
        }

       pinConfigure_(i2c, bus, i2c->currentPins, i2c->currentPullup, 0);

       uint8_t i2cstatus = *(i2c->S);

       if (i2cstatus & (uint8_t)0x7F){
          *(i2c->C1) = (uint8_t)0x00; 
          delayMicroseconds(5);
          *(i2c->C1) = I2C_C1_IICEN; // send STOP, change to Rx mode, intr disabled
          delayMicroseconds(5);

          // TODO call i2c_t3(_commdata.bus).begin(I2C_MASTER, 0, _commdata.pins, _commdata.pullups, _commdata.i2cRate);
          
        }
       
       i2c->currentStatus = I2C_WAITING; // reset status
        
    }

}
 
Last edited:
Back
Top