New I2C library for Teensy3

Also to anyone following this thread - in regards to Teensy4:

I was offered a T4 beta board back when it started, but I could not accept as I did not have the requisite time to work on a library port.

I have now obtained a couple of the retail boards, and although I still lack time, I will probably attempt a port as I can get to it. I have not reviewed Wire code on T4 to know how different it is, but I did read something about it using a FIFO, so my guess is the ISR code would be different enough to warrant a new lib (eg. i2c_t4). I am uncertain of interest level, if any, so any comments would be helpful.
 
Hi nox771,

Thanks for getting back on this.

Before I waste any of your time trying to help me - as I could be trying to achieve the impossible here.
Would you expect your library to help increase the speed/performance of sending & displaying text to 8 x i2c displays?

I have now managed to get the "spoof (wire/ic2_t3) " method to compile, but I'm not really seeing much difference performance wise.

I know there are limitations with this protocol - but not sure what your library is offering versus the std Wire lib.
As per a post on another thread - I'm seeing a cascading/domino effect when sending the text updates.

I'm not a software person, so don't really understand most of this stuff.
Just someone who manages to fumble his way through with the help of forums like this.

Thanks
 
Hi nox771,

Thanks for getting back on this.

Before I waste any of your time trying to help me - as I could be trying to achieve the impossible here.
Would you expect your library to help increase the speed/performance of sending & displaying text to 8 x i2c displays?

I have now managed to get the "spoof (wire/ic2_t3) " method to compile, but I'm not really seeing much difference performance wise.

I know there are limitations with this protocol - but not sure what your library is offering versus the std Wire lib.
As per a post on another thread - I'm seeing a cascading/domino effect when sending the text updates.

I'm not a software person, so don't really understand most of this stuff.
Just someone who manages to fumble his way through with the help of forums like this.

Thanks

It depends. Mostly any speedup will depend on the slave device and if it will respond well to faster than normal I2C speeds. I've gotten faster than normal communication working on something like a SSD1306 I2C display (like the cheap ones on ebay: https://www.ebay.com/sch/i.html?_nkw=ssd1306+i2c&_sop=12 ). In that case IIRC it can help with refresh speeds.

If you are using standard default settings (100kHz/400kHz), then you will likely see the same performance as Wire, as that is what it uses, so it does require specifying something above normal rates.

Also there are other indirect ways to improve performance. Typical Wire is a blocking interface. Such that the program halts and waits for communication to complete. For i2c_t3 you can background transfers. So in that case you can load the buffer, start the transfer, then continue to a foreground task while it transfers in the background. Most recent releases also support a Master callback that can call a given function when the background transfer completes, so you do not need to monitor it. These are capabilities above and beyond what the standard Wire library will offer. You can check the "basic_master_callback" example for further information on that.

Note the library linked in post #1 in this thread is obsolete. You should grab the latest one from GitHub (currently v11.0). Due to updated forum rules I can no longer edit the top post to maintain it, so GitHub is the main location now.
 
Note the library linked in post #1 in this thread is obsolete. You should grab the latest one from GitHub (currently v11.0). Due to updated forum rules I can no longer edit the top post to maintain it, so GitHub is the main location now.

Thanks.

I picked up v11.0 already. It's the only 1 I've tried.
 
The Digole displays come with their own library for sending data (text, shapes etc) to them.
So apart from the include for wire.h - all commands for sending the data are Digole specific commands.
I'm don't directly interact with the Wire libray & commands - I guess that is done indirectly via the Digole Library.

I just add these statements for each Display & then create an array listing each instance :
Code:
DigoleSerialDisp mydisp1(&Wire, '\x27'); 
DigoleSerialDisp mydisp2(&Wire, '\x28'); 
...
DigoleSerialDisp mydisp8(&Wire, '\x34'); 

mydisp[8] = {mydisp1, mydisp2, ... mydisp8};

This lets me easily loop each Display & send the data as required.

Apart from what this statement is actually doing
Code:
DigoleSerialDisp mydisp1(&Wire, '\x27');
I'm able to mostly understand the above.
But I think I'm just not smart enough to figure out how to make changes to use Master Callbacks etc. :(
 
The Digole displays come with their own library for sending data (text, shapes etc) to them.
So apart from the include for wire.h - all commands for sending the data are Digole specific commands.
I'm don't directly interact with the Wire libray & commands - I guess that is done indirectly via the Digole Library.
...

You'll have to supply a link to your library you are using. Other than that it is guessing. Using google I can see a DigoleSerial.h lib at:
https://github.com/phalpern/Thermostat/blob/master/DigoleSerial.h

That one has a very generic init for I2C config:
Code:
_myWire->begin();
That may be giving you a 100kHz clock, which is standard for Arduino.

A simple option is to use your init code as-is, then insert a Wire command to adjust the clock:
Code:
DigoleSerialDisp mydisp1(&Wire, '\x27'); 
DigoleSerialDisp mydisp2(&Wire, '\x28'); 
...
DigoleSerialDisp mydisp8(&Wire, '\x34'); 
[COLOR=#ff0000]Wire.setClock(200000);  // 200kHz[/COLOR]
...
...

Add that, test it, and see if it works. Then increase it and try again: 400000 = 400kHz, 600000 == 600kHz, 800000 = 800kHz, and so on.

i2c_t3 lib will quantize the number to the nearest legal value, so you can use any number you like. Most T3 parts can run up to 3000000 = 3MHz. Recent T3.5/3.6 can do higher.

At some point it will fail and the screen will get garbage or something, then you can back it off.

Edit: Forgot to mention, high speeds are also limited by the pullup resistor value. If the R-value on the bus is too high the pullups will be slow and it will limit your speeds. Refer to this link:
http://dsscircuits.com/articles/86-articles/47-effects-of-varying-i2c-pull-up-resistors
 
Hello nox771,

is it possible to modify/add an modified version of void i2c_t3::resetBus_(struct i2cStruct* i2c, uint8_t bus) to create an clock stretching for slave mode?
 
Hello nox771,
is it possible to modify/add an modified version of void i2c_t3::resetBus_(struct i2cStruct* i2c, uint8_t bus) to create an clock stretching for slave mode?

I don't quite follow your question. On Master-side the resetBus function will take I2C off the pins for a moment, manually send some clocks down SCL to release any stuck slaves, and then reconnect I2C. In this case you are talking about having a slave swap I2C off the pin and then holding SCL low for some time?

I'm not sure if it would work. It would only be possible between bytes, after bit9 and before bit1. But SCL might go high before the slave could switch to pulling it low (not seamless). If it were embedded in the Slave ISR it might be fast enough. But trying to change I2C pin inside the ISR in order to enable manual control would be quite odd. Hard to say, it would require experimentation.
 
In this case you are talking about having a slave swap I2C off the pin and then holding SCL low for some time?

I'm not sure if it would work. It would only be possible between bytes, after bit9 and before bit1. But SCL might go high before the slave could switch to pulling it low (not seamless). If it were embedded in the Slave ISR it might be fast enough. But trying to change I2C pin inside the ISR in order to enable manual control would be quite odd. Hard to say, it would require experimentation.

i2c_clock_stretching.png

Yes this was what I have in mind
 
Looks like an can't edit my post.

Technically the picture is wrong, after I²C-bus specification and user manual Rev. 6

3.1.9 Clock stretching
Clock stretching pauses a transaction by holding the SCL line LOW. The transaction
cannot continue until the line is released HIGH again. Clock stretching is optional and in
fact, most slave devices do not include an SCL driver so they are unable to stretch the
clock.
On the byte level, a device may be able to receive bytes of data at a fast rate, but needs
more time to store a received byte or prepare another byte to be transmitted. Slaves can
then hold the SCL line LOW after reception and acknowledgment of a byte
to force the
master into a wait state until the slave is ready for the next byte transfer in a type of
handshake procedure.
On the bit level, a device such as a microcontroller with or without limited hardware for the
I2C-bus, can slow down the bus clock by extending each clock LOW period. The speed of
any master is adapted to the internal operating rate of this device.
In Hs-mode, this handshake feature can only be used on byte level.​

So after the ACK the Slave can hold the SCL line LOW, I was thinking about this:

Code:
void i2c_t3::stretchClock_(struct i2cStruct* i2c, uint8_t bus)
{
    uint8_t scl = i2c->currentSCL;
    uint8_t sda = i2c->currentSDA;

    // change pin mux to digital I/O
    pinMode(sda,((i2c->currentPullup == I2C_PULLUP_EXT) ? INPUT : INPUT_PULLUP));
    [B]digitalWrite(scl,LOW);
    pinMode(scl,OUTPUT);[/B]

    [B]//Do what ever I want inside the sketch and after it go back in I²C Mode[/B]

    // reconfigure pins for I2C
    pinConfigure_(i2c, bus, scl, sda, i2c->currentPullup, 0, 0);

    // reset config and status
    if(*(i2c->S) & 0x7F) // reset config if any residual status bits are set
    {
        *(i2c->C1) = 0x00; // disable I2C, intr disabled
        delayMicroseconds(5);
        *(i2c->C1) = I2C_C1_IICEN; // enable I2C, intr disabled, Rx mode
        delayMicroseconds(5);
    }
    i2c->currentStatus = I2C_WAITING;
}
 
You'll have to supply a link to your library you are using. Other than that it is guessing. Using google I can see a DigoleSerial.h lib at:
"https://github.com/phalpern/Thermostat/blob/master/DigoleSerial.h"

Hi sorry for slow response.
Believe I got the library from here:
https://www.digole.com/forum.php?topicID=1

I had already tried various values for Wire.setClock - nothing seemed to make a difference - to the naked eye at least.

Edit: Forgot to mention, high speeds are also limited by the pullup resistor value. If the R-value on the bus is too high the pullups will be slow and it will limit your speeds. Refer to this link:
http://dsscircuits.com/articles/86-a...l-up-resistors

Tried both 4k7 & 2k7 so far - again no noticeable difference.

The only thing I have seen make a slight difference was changing the Font.
The displays come with 4 "built-in" fonts but I been using a custom Font (Bold) which can be written direct to the display.
Flipping back one the std fonts seemed to improve things a touch
 
So after the ACK the Slave can hold the SCL line LOW, I was thinking about this:

Code:
void i2c_t3::stretchClock_(struct i2cStruct* i2c, uint8_t bus)
{
    uint8_t scl = i2c->currentSCL;
    uint8_t sda = i2c->currentSDA;

    // change pin mux to digital I/O
    pinMode(sda,((i2c->currentPullup == I2C_PULLUP_EXT) ? INPUT : INPUT_PULLUP));
    [B]digitalWrite(scl,LOW);
    pinMode(scl,OUTPUT);[/B]

    [B]//Do what ever I want inside the sketch and after it go back in I²C Mode[/B]

    // reconfigure pins for I2C
    pinConfigure_(i2c, bus, scl, sda, i2c->currentPullup, 0, 0);

    // reset config and status
    if(*(i2c->S) & 0x7F) // reset config if any residual status bits are set
    {
        *(i2c->C1) = 0x00; // disable I2C, intr disabled
        delayMicroseconds(5);
        *(i2c->C1) = I2C_C1_IICEN; // enable I2C, intr disabled, Rx mode
        delayMicroseconds(5);
    }
    i2c->currentStatus = I2C_WAITING;
}

It's an interesting idea, but I suspect it is a bit more complex than it seems. On the Slave end, the code would run as part of the ISR. The code you show has elements that seem right, but overall it seems off. It would need to work similar to the onReceive() callback.

In a generic sense it would be more like a per-byte callback, as opposed to onReceive(), which is an end-of-message callback. But the experimental/weird part is that IIRC there is no mechanism to force pin state on the I2C peripheral (I might be wrong, its been a long time since I read the docs). So that only leaves doing the pinConfigure swap as you show, which might work, as long as the I2C bus doesn't change its state while the peripheral is disconnected, and it otherwise remains oblivious to the pin change.

I'll think about it, but it will be some time before I could really get into it. If you manage to get something to work then let me know, I would be interested.
 
Hi sorry for slow response.
Believe I got the library from here:
https://www.digole.com/forum.php?topicID=1

I had already tried various values for Wire.setClock - nothing seemed to make a difference - to the naked eye at least.

Tried both 4k7 & 2k7 so far - again no noticeable difference.

The only thing I have seen make a slight difference was changing the Font.
The displays come with 4 "built-in" fonts but I been using a custom Font (Bold) which can be written direct to the display.
Flipping back one the std fonts seemed to improve things a touch

I'm not sure what else to suggest. Other displays I've used generally scale with transmit speed (but they were graphical, so I2C was the bottleneck for moving frame data). Perhaps that display has a processor that controls/limits the speed.

Another possibility is that the host program is sending at a constant rate - in other words, the data itself is communicated faster, but the frame-to-frame speed is constant. I've seen this situation before, where frames were sent at a fixed rate, and even if the data was faster, the host would just idle until the next frame interval.
 
Sorry if this has been covered elsewhere in this thread. (I did search for it!)

I'm seeing delays on the I2C bus between each part of a command. I'll explain with an example. First, the code snippet (from the master):

Code:
Wire.beginTransmission(addr);
Wire.write(cmd_byte);
Wire.endTransmission(I2C_NOSTOP);
Wire.requestFrom(addr, 2, I2C_STOP, 500000);
Wire.read();
Wire.read();

What I'm seeing on the bus is:

1) Send address with R/W bit low
2) 0.3 ms delay with SCL low
3) Send cmd_byte
4) 1 ms delay with SCL low
5) Send address with R/W bit high
6) 1 ms delay with SCL low
7) Receive first byte
8) 0.9 ms delay with SCL low
9) Receive second byte

(I can't seem to upload an image of the bus capture, for whatever reason.)

Is the master responsible for some (or all) of those delays? I wonder if that could confuse the slave? I know clock stretching is a possibility, but it seems like that wouldn't account for every delay in this sequence.

Assuming these delays are caused by the master, is there anything I can do about them?
 
It's an interesting idea, but I suspect it is a bit more complex than it seems. On the Slave end, the code would run as part of the ISR. The code you show has elements that seem right, but overall it seems off. It would need to work similar to the onReceive() callback.

In a generic sense it would be more like a per-byte callback, as opposed to onReceive(), which is an end-of-message callback. But the experimental/weird part is that IIRC there is no mechanism to force pin state on the I2C peripheral (I might be wrong, its been a long time since I read the docs). So that only leaves doing the pinConfigure swap as you show, which might work, as long as the I2C bus doesn't change its state while the peripheral is disconnected, and it otherwise remains oblivious to the pin change.

I'll think about it, but it will be some time before I could really get into it. If you manage to get something to work then let me know, I would be interested.

I will make some Tests soon, I have an SHT31-D who supports SCL-Stretching, the does support i2c_t3 SCL-Streching?

See some Interesting about it there https://forum.pjrc.com/threads/25768-I%C2%B2C-Clock-Stretching-Implementation-for-Teensy-3
 
Sorry if this has been covered elsewhere in this thread. (I did search for it!)

I'm seeing delays on the I2C bus between each part of a command. I'll explain with an example. First, the code snippet (from the master):

Code:
Wire.beginTransmission(addr);
Wire.write(cmd_byte);
Wire.endTransmission(I2C_NOSTOP);
Wire.requestFrom(addr, 2, I2C_STOP, 500000);
Wire.read();
Wire.read();

What I'm seeing on the bus is:

1) Send address with R/W bit low
2) 0.3 ms delay with SCL low
3) Send cmd_byte
4) 1 ms delay with SCL low
5) Send address with R/W bit high
6) 1 ms delay with SCL low
7) Receive first byte
8) 0.9 ms delay with SCL low
9) Receive second byte

(I can't seem to upload an image of the bus capture, for whatever reason.)

Is the master responsible for some (or all) of those delays? I wonder if that could confuse the slave? I know clock stretching is a possibility, but it seems like that wouldn't account for every delay in this sequence.

Assuming these delays are caused by the master, is there anything I can do about them?

Those delays should not be caused by the Master. Ignoring the SCL delay, when the bytes transmit, what is the SCL toggle speed? That would be the Master-side clock.

I'm not sure why the hold times with SCL low would occur. Perhaps try getting the Master to talk to a different Slave device. Also make sure the pullups are correct (although if they were not you probably would not get any data).

Also list what is the Wire.begin() statement.
 
Those delays should not be caused by the Master. Ignoring the SCL delay, when the bytes transmit, what is the SCL toggle speed? That would be the Master-side clock.

I'm not sure why the hold times with SCL low would occur. Perhaps try getting the Master to talk to a different Slave device. Also make sure the pullups are correct (although if they were not you probably would not get any data).

Also list what is the Wire.begin() statement.

The begin statement is this:

Code:
Wire.begin(I2C_MASTER, 0x01, I2C_PINS_18_19, I2C_PULLUP_EXT, 100000);

I measured 5 us high and 5us low on SCL, when it's not delayed. That matches the clock speed I setup in the begin statement. I used 4.7k pullup resistors.

The slave responds to some commands, and not others. I was thinking the delay might be responsible for that, but at this point, I'm pretty sure the delay isn't interfering with anything. Still, it seems like more delays than there should be.
 
The begin statement is this:

Code:
Wire.begin(I2C_MASTER, 0x01, I2C_PINS_18_19, I2C_PULLUP_EXT, 100000);

I measured 5 us high and 5us low on SCL, when it's not delayed. That matches the clock speed I setup in the begin statement. I used 4.7k pullup resistors.

The slave responds to some commands, and not others. I was thinking the delay might be responsible for that, but at this point, I'm pretty sure the delay isn't interfering with anything. Still, it seems like more delays than there should be.

The 5us high/low is correct for 100kHz rate. 4.7k is a good value. It really looks like Slave clock stretching, but that is quite a long duration. I'm not sure why it would not respond to some commands. If you have a different Slave, particularly a different device type, I would suggest trying that to see if the bus behaves better, or at least differently. That would isolate the problem to that particular Slave device. What is it exactly, does it have a datasheet?
 
The 5us high/low is correct for 100kHz rate. 4.7k is a good value. It really looks like Slave clock stretching, but that is quite a long duration. I'm not sure why it would not respond to some commands. If you have a different Slave, particularly a different device type, I would suggest trying that to see if the bus behaves better, or at least differently. That would isolate the problem to that particular Slave device. What is it exactly, does it have a datasheet?

It's a power supply that speaks PMBus. I bought it used, and expected it to follow the datasheet, but the manufacturer tells me it was customized and probably had those commands disabled in firmware. So I'm pretty sure at this point the delay I see on the bus has nothing to do with the lack of response from those commands.

When I get a chance, I'll test it out with another Teensy as slave, and see if there are still delays or not.
 
We have a library to talk PMBus to an Artesyn LCM300 supply. It works well. I'm on the road for a few days but will see if I can post a link to that here. You should monitor the slave response... Is there an ACK to your request? Check all the possible exceptions. I2c_t3 is awesome for that. We added a library to keep a strict of exception counters and we can run many millions of messages, even billions, with no errors. I'll see if I can also post a link to that code. We also have PMBus test code. Good luck
 
Related issue that I've just dealt with. With a system that is running a Teensy LC as a slave (main loop runs background work, I2C handled by ISRs), Teensy 3.6 as a master (100 kHz) - that we experienced a SCL-low hang by the slave, occurring in the middle of a byte transfer to master rather than the typical timing issue at the end of one. All the signals are picture perfect for I2C. Very intermittent issue, perhaps once every 30 min to 2-3 hours.

We tried a number of the suggested mechanisms to recover from this, to no effect. In the end, we resorted to using a spare GPIO to monitor the SCL line; when the main loop sees it hung for longer than expected, a new library function is called to disable the I2C hardware module and effectively reinitialize the I2C. This clears the hang; the application on the master side is forgiving enough to just ignore the momentary loss of the slave.

I'm working to extricate this into a minimum code / library set that I can share and anybody with an LC and 3.6 can reproduce. I'll be working to determine if this is something that be fixed in a cleaner way or if its a fundamental issue with the hardware.
 
Link to LCM300 PMBus library

Here is the code we wrote and which is heavily tested and in use in multiple field installations: GitHub Systronix LCM300 library with the usual disclaimers that it might not work for your needs etc. We have some extensive error tracking and logging and there are notes about that in the linked libraries. Also some simple examples which print basic info from the supply and actually led to some surprises: the supply actually differs from its spec in regard to many message details. For one, the vendor in the supply is "Emerson"; they never changed that when Artesyn purchased them. And sadly PMBus got mired in patent disputes - see the links in readme. So it never reached its potential acceptance in the market and is unlikely to be adopted more widely than it is now. Still, for the price (about $125 is what we last paid at 50-ish units) for 300 watts it is IMO a good deal.
 
Last edited:
I am uncertain of interest level, if any, so any comments would be helpful.
I'm interested in the topic, my new Teensy 4.0 cannot access I2C buses to control devices I already tested with 3.2 and 3.6.
Any news about development?
Any suggestion about how to add some modification to the last library version to make it work (even modification not very efficient) for T4.0?

Thank you anyway
 
Back
Top