Pages: [1]   Go Down
Author Topic: Increase 32 byte I2C buffer size in Wire library  (Read 3420 times)
0 Members and 1 Guest are viewing this topic.
London, UK
Offline Offline
Newbie
*
Karma: 1
Posts: 41
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello,

I am sending display data from one Arduino to another via I2C. The display is an 8x8 RGB LED matrix having an 8 bit colour depth. A full frame of data is 3 bytes for each RGB pixel x 64 pixels = 192 bytes. I then add a start and end data marker byte which takes the total data size to 194 bytes. Ideally I want this all to fit in the I2C buffer so I can send it in one go.

I have tried upping the buffer size values in the following files in the Arduino IDE to 64 bytes as a test, and it worked.

Java/libraries/Wire/utility/twi.h
define TWI_BUFFER_LENGTH 64

and

Java/libraries/Wire/Wire.h
#define BUFFER_LENGTH 64

However if I up them to my ideal of 194 I get strange results - e.g. output to the serial console is truncated, as if I am writing into some other part of memory I should't be.

Is there a way to up these values to 194, or do I need to split the data into multiple I2C transmissions, e.g. for Red Green and Blue data?

Cheers
Nick


« Last Edit: March 05, 2011, 01:40:15 pm by mrnick1234567 » Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I can't reproduce your problem. I made a copy of the SPI library and changed the two lines you mentioned. Then connected two Unos together, using Gnd, +5v, SCL, SDA.

Master:

Code:
#include <Wire.h>

#define ADDRESS 42

void setup ()
{
  Wire.begin ();
}  // end of setup

void loop ()
{

  Wire.beginTransmission (ADDRESS);
  for (int x = 0; x < 194; x++)
    Wire.send (x);
  Wire.endTransmission ();
   
   delay (2000);
}  // end of loop

Slave:

Code:
#include <Wire.h>

#define ADDRESS 42

void setup ()
{
  Serial.begin (9600);
  Wire.begin (ADDRESS);
  Wire.onReceive (receiveEvent);
}  // end of setup

volatile byte buf [200];
volatile byte pos = 0;

void loop()
{
  // display when filled up
  if (pos >= 194)
   {
   for (int i = 0; i < 194; i++)
     Serial.println (buf [i], DEC);
   pos = 0;
   }  // end if
 }  // end loop


void receiveEvent (int howMany)
 {
  while (Wire.available () > 0)
  {
   byte c = Wire.receive ();
   if (pos < sizeof buf)
     buf [pos++] = c; 
  }  // end while available
 
}  // end of receiveEvent

Slave correctly displays the numbers 0 to 193. The whole I2C transaction took 18.6 ms.
Logged

London, UK
Offline Offline
Newbie
*
Karma: 1
Posts: 41
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey Nick,

Thanks so much for taking the time to look at this, it's interesting you saw no issue. After some more googling I found this post: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1294917906

This guy has a similar problems and thinks it's because he is running out of SRAM - he mentions the I2C buffers are defined multiple times in the Wire library. I think this may explain why I see problems, as I have a few other large arrays for display data and such like defined in my sketch.

At the moment I've compromised with 3 sends of data R, G and B. I hope the overhead of the extra sends isn't too much.

Cheers
Nick
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Funny you should mention the SRAM. When I first saw your thread I thought "aha! - it works with 64 bytes but not more, that rings a bell.".

So I looked at length through the Atmel documentation for the magic number "64" because I was sure I had seen it as an I2C limit. But no. Then I looked at the Microchip documentation for their SRAM/EEPROM and indeed for those chips there is a limit of 64 in the I2C buffer.

So then I did a test bed to prove that it does work (I wasn't sure in advance what it would prove), but for my test program it did. So there is no hardware limit.

But of course your point of the multiple buffers is valid. For a chip that has maybe 1Kb SRAM (and you didn't mention your model of chip) then having multiple 194 byte buffers may be a problem for you. It certainly seems to have two buffers in Wire.cpp and another three buffers in twi.c. So it looks like 5 buffers is right. So 5 * 194 is 970 bytes, which on an Atmega138 is going to be almost all available RAM.

That's probably why they stuck to 32-byte buffers rather than 64-bye ones.

You know, even a small programming change reduces the need for big buffers? Change:

 
Code:
Wire.beginTransmission (ADDRESS);
  for (int x = 0; x < 194; x++)
    Wire.send (x);
  Wire.endTransmission ();

to:

 
Code:

  for (int x = 0; x < 194; x++)
    {
    Wire.beginTransmission (ADDRESS);
    Wire.send (x);
    Wire.endTransmission ();
    }

It runs slightly slower because it sends the address in front of every byte, but perhaps in your case that doesn't matter too much.

In fact, as I had the test still set up, I changed the code to what I have above and re-timed it. It went from 18.6 ms to 45.0 ms. Over twice as long, as I would expect, since it sends the device address every time, but still, 45 ms isn't too bad.

If you leave it as how you describe (3 batches rather than 1) then the extra overhead would be neglible.
Logged

London, UK
Offline Offline
Newbie
*
Karma: 1
Posts: 41
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ah cool - that's an interesting way of doing it, I hadn't thought of sending each byte separately.

Out of interest how are you timing the I2C transactions? Speed may become important as I might want to send frames of video to a number of matrices with different addresses.

I have an ATmega 328 (so 2K SRAM) but I don't really want to use a whole kilobyte for buffers!

Nick
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

With my trusty Saleae Logic analyzer. It's great for situations like that. Not only can you time stuff but you see what is there. For example, when I first did it I had it as in my most recent example, and I looked at the logic output and thought "there are too many address bytes there" so I moved the beginTransmission out of the loop. That's the sort of thing you miss otherwise, because it seems to work anyway.

By the way, it's about 30 times as fast to use SPI, and then you don't have buffer issues because each byte goes individually. That's only two more wires.
Logged

London, UK
Offline Offline
Newbie
*
Karma: 1
Posts: 41
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That's interesting to know about SPI, I didn't realise it was quicker. I'll see how I do with the I2C stuff to start with.

Thanks for all your help,

Nick
Logged

Pages: [1]   Go Up
Jump to: