Go Down

Topic: I2C transfer rates using Wire library. (Read 12841 times) previous topic - next topic

wyojustin

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

graynomad

#1
Apr 10, 2011, 03:41 am Last Edit: Apr 10, 2011, 12:17 pm by Graynomad Reason: 1
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
Rob Gray aka the GRAYnomad www.robgray.com

frank26080115

Just change TWBR manually, see the ATmega328P datasheet

wyojustin

Thank you for your responses.

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 /

frank26080115

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

nickgammon


... or about a second for 10000 bytes. 


I would agree with that. I did timings here:

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

I found that by adding

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

Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

graynomad

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

What's the holdup?

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

frank26080115


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

nickgammon

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.

Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

graynomad

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
Rob Gray aka the GRAYnomad www.robgray.com

frank26080115

#10
Apr 11, 2011, 04:04 am Last Edit: Apr 11, 2011, 04:09 am by frank26080115 Reason: 1

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 and the encryption initialization takes quite a long time.

nickgammon

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.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

wayneft

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.

WanaGo

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

nickgammon

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.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Go Up