Arduino Forum

Using Arduino => Networking, Protocols, and Devices => Topic started by: wyojustin on Apr 09, 2011, 04:38 pm

Title: I2C transfer rates using Wire library.
Post by: wyojustin on Apr 09, 2011, 04:38 pm
I've been playing with using the Wire library to connect two Arduinos.  It appears that the transfer rate is a dismal 1 KB/sec.  I see from the I2C spec(http://www.nxp.com/acrobat_download2/literature/9398/39340011.pdf), there is a "high-speed" mode which offers 3.4 Mb/sec ~ 500KB/sec.

Using a standard 16Mhz Arduino, is there a way to access the high-speed I2C mode?

Thanks,
Justin
Title: Re: I2C transfer rates using Wire library.
Post by: graynomad on Apr 10, 2011, 03:41 am
The standard i2c speed is 100k, it's possible the Wire library is really slow but I would think an Arduino could do that while spell checking War and Peace at the same time. How are you verifying the 1kbps?

The higher speeds are various newer standards, I doubt many devices can run at 3.4MHz and you'd have to seriously look at the wiring for that.

______
Rob
Title: Re: I2C transfer rates using Wire library.
Post by: frank26080115 on Apr 10, 2011, 05:45 am
Just change TWBR manually, see the ATmega328P datasheet
Title: Re: I2C transfer rates using Wire library.
Post by: wyojustin on Apr 10, 2011, 06:51 am

First thing is I'm off by at least a factor of 8 as I converted from bits to bytes twice.  So that gets us up to 8KByte/sec.  Now I hope I am off by another factor of 10.

Lets see.
This prints out how many bytes / millisecond.  I am getting 32 bytes from the slave for every count++.  This prints every 1000 receives.  It prints about 9.  Lets call it 10 for math's sake.

10 = 32000 / x ==> x = 3200.  That is 3.2 seconds for 32000 bytes. or about a second for 10000 bytes.  Which is about 80000 bits / sec.  with 20% overhead, that comes out to 100Kbits/sec.  Bingo.

Thanks again!
If I missed something, please let me know.
Justin

Code: [Select]
`    if(count++ % 1000 == 999){    Serial.print(faults);    Serial.print(" ");    Serial.println(32000. / (millis() - last_time));    last_time = millis();  }`

So we have 32000 bytes /
Title: Re: I2C transfer rates using Wire library.
Post by: frank26080115 on Apr 10, 2011, 07:33 am
oh and in case you didn't realize, changing TWBR will change the clock speed but the data speed still completely depends on the device you are talking to since I2C busses allows for clock stretching
Title: Re: I2C transfer rates using Wire library.
Post by: nickgammon on Apr 10, 2011, 09:56 am

... or about a second for 10000 bytes.

I would agree with that. I did timings here:

http://www.gammon.com.au/forum/?id=10896

Code: [Select]
`TWBR = 12;`

after

Code: [Select]
`Wire.begin ();`

that I got about 3.5 times speed increase. That is, 35Kb / second.

For a lot faster speed, use SPI. See here for comparisons:

http://www.gammon.com.au/forum/?id=10918

Title: Re: I2C transfer rates using Wire library.
Post by: graynomad on Apr 10, 2011, 12:24 pm
Wow that's slow, I wouldn't have picked that.

What's the holdup?

______
Rob
Title: Re: I2C transfer rates using Wire library.
Post by: frank26080115 on Apr 10, 2011, 08:02 pm

Wow that's slow, I wouldn't have picked that.

What's the holdup?

______
Rob

Likely clock stretching done by the other device, also code execution overhead of both Wire.cpp and twi.c
Title: Re: I2C transfer rates using Wire library.
Post by: nickgammon on Apr 10, 2011, 11:27 pm
Quote

What's the holdup?

Are you asking why is I2C so slow? I think the answer basically is that I2C has a slower clock rate. But why does it have a slower clock rate?

I'm guessing for a number of reasons:

• Since it uses open-drain and pull-up resistors, the transition from low to high is necessarily electrically fairly slow, as it relies on the pull-up (possibly some distance away) to bring the line back to the high state (compared to SPI which drives the line low/high "manually" all the time).

• It is designed for longer cable runs.

• The data line in particular is two-way, that is, after every 8th bit the sender waits for an ACK from the receiver. This would take time and the receiver needs to be given time to do it.

• The reply to a query comes down the same data line, so both ends are constantly reconfiguring inputs as outputs, and then open-drain.

• SPI does not wait for any sort of acknowledgement - this must make it able to be designed to be faster.

• There are probably more subtle reasons. It is interesting for example that 1-Wire is slower again.

Title: Re: I2C transfer rates using Wire library.
Post by: graynomad on Apr 11, 2011, 03:33 am
Yeah, I2C will always be fundamentally slower than SPI, but not that slow I wouldn't have thought.

Quote
clock stretching done by the other device,

This app has two Arduinos, does Wire implement clock stretching?

______
Rob
Title: Re: I2C transfer rates using Wire library.
Post by: frank26080115 on Apr 11, 2011, 04:04 am

Quote
clock stretching done by the other device,

This app has two Arduinos, does Wire implement clock stretching?

______
Rob

The clock should be stretched automatically until the end of the on request/receive event handlers.

This was pretty significant since I was making a Wiimote attachment (http://frank.circleofcurrent.com/cache/wii_ext_code.htm) and the encryption initialization takes quite a long time.
Title: Re: I2C transfer rates using Wire library.
Post by: nickgammon on Apr 11, 2011, 07:01 am
After extensive reading of the Atmega documentation, and running an experiment, I'm inclined to agree with this. It appears that I2C is designed so that, although the master initiates the transfer of a bit (by bringing the SCL line low), the slave can prolong the period of that clock pulse by also bringing the SCL low (after the master does). Then when the master releases the SCL line (so it floats back high) it actually has to check that the line actually went high, and if not, wait until it does. This lets slower slaves control the speed at which bits are clocked out to them by stretching the low part of the SCL pulse.

In the library, this appears to be achieved by a call to twi_releaseBus which sets the TWINT flag. According to the manual:

Quote
While the TWINT Flag is set, the SCL low period is stretched. The TWINT Flag must be cleared by software by writing a logic one to it. Note that this flag is not automatically cleared by hardware when executing the interrupt routine. Also note that clearing this flag starts the operation of the TWI, so all accesses to the TWI Address Register (TWAR), TWI Status Register (TWSR), and TWI Data Register (TWDR) must be complete before clearing this flag.

So effectively writing 1 to TWINT acknowledges the interrupt, so that the next byte can be processed. Also I agree with frank26080115 that this stretching therefore only applies during the execution of the interrupt handler.

The manual is a bit fuzzy about whether that applies to individual bits, or the whole byte. The interrupt routine would be called after the byte was received, but presumably a slow slave would also want to stretch individual bits too.
Title: Re: I2C transfer rates using Wire library.
Post by: wayneft on Apr 11, 2011, 03:12 pm
Just make sure if you switch to the faster speed I2C you resize the pull up resistors accordingly.  If you're using anything larger than about 4.7 k ohm you won't be taking full advantage of the higher speed.
Title: Re: I2C transfer rates using Wire library.
Post by: WanaGo on Apr 26, 2011, 04:45 am
Bringing this up again.

Just a question on the post below.

Anything larger than 4.7k. By larger, I assume you mean a higher ohm resistor, as in 10k for example?

Just wanted to double check.
I am using I2C extensively in a design I have, using multiple Adum1250 I2C Expander/isolation chips. I currently have the design using 1k resistors on both sides of each of the chips - and I ideally want the highest throughput possible as it is the 'backbone' for comms between about 10+ boards.

The devices connected are other AVR's (328's, 644P's or 1284P's), I2C Expension Chips MCP23017's, an EEPROM and a DS1307 RTC.
I have modified the wire library to be 400000L rather than 100000L (as detailed in another post), and I just spotted the TWBR = 12 comment on here, so will try that too.

I have read Nick's forum and the experiments made with pullup resistors etc, 2.2k giving a nice square wave etc, however I am still not sure what the ideal resistance is in my case, and for highest speed.

I am sure it has been covered, however if someone can spell it out for me that would be great. Is 1K suitable or should I change it.

Thanks
Title: Re: I2C transfer rates using Wire library.
Post by: nickgammon on Apr 26, 2011, 05:59 am
I've amended my post at:

http://www.gammon.com.au/i2c

I've added images for 1K and 470 ohm pull-ups.

The problem with low-value pull-ups is that they start to "pull" the 0V signal away from the ground reference. That is, they are getting "too strong". For example a 470 ohm pull-up raises the 0V to 0.5V. This should still work, but I would be cautious if you are using long cable runs, because the extra resistance might mean the device at the other end can't pull the signal down low enough. This effect is clearer in the image for the 470 ohm pull-up.

Also, the current through the 470 ohm resistor is now 10mA. That means the I2C devices now need to sink 10mA to counter that (if I am not mistaken). I would be checking their specs to see that they can do that.

Probably in your case I would try to leave the exact value resistors to be checked by trial-and-error.
Title: Re: I2C transfer rates using Wire library.
Post by: WanaGo on Apr 26, 2011, 06:14 am
Thanks Nick

So 1K may not be the best choice then...

My boards are 80mm x 80mm, each with a Adum1250 I2C isolator on them (http://www.analog.com/static/imported-files/data_sheets/ADUM1250_1251.pdf (http://www.analog.com/static/imported-files/data_sheets/ADUM1250_1251.pdf)), and the I2C signals are just going board to board - which are all plugged on top of each other. So the longest I2C wire would be say 100mm long before it goes to an Adum1250 isolator. The backbone between the Adum1250's however may be 400mm in length, but there are no devices attached directly to this, only via the Adum1250's.

Whether or not that makes sense, hopefully it does.

So in summary, should I be putting something like 2.2K on both sides of the Adum1250, to achieve the 'best' speed and stability?

Thanks
Title: Re: I2C transfer rates using Wire library.
Post by: nickgammon on Apr 26, 2011, 06:31 am
That sounds reasonable, but bear in mind what I am doing is largely theory, backed up with a few measurements. If possible, try a test rig, and be prepared to tweak the operating frequency. I note this from page 10 of their datasheet:

Quote

However, the input logic low threshold at Side 2 is designed to be at 0.3 VDD2, consistent with I2C requirements. The Side 1 and Side 2 pins have open-collector outputs whose high levels are set via pull-up resistors to their respective supply voltages.

That appears to suggest that pull-up resistors that pulled the low level higher than 300mV would be too much (ie. too low ohms). So that would rule out the 470 ohm idea.

Also I note from what you appear to be describing that you will have multiple I2C circuits, so you would need to consider them separately, to an extent. My tests are done at 5V, I assume you will be using 5V and not 3.3V? Page 1 of the datasheet says:

Quote
30 mA current sink capability

... so it seems we are within spec there.

It sounds to me you are in the ball park, probably with the 2.2K. And as you can see from my pictures, a fairly wide range was tolerated, and indeed I often run I2C (eg. to LCD screens) only using the internal pull-ups, so you probably have a bit of room to move.