Strange behavior between I2C master and slave

Status
Not open for further replies.

alexandros

Well-known member
I have one Teensy 3.6 and one 3.2, where the first is an I2C master (among other things it does) and the second is a slave. The code of both is rather long (about 2000 lines for the master and 3000 lines for the slave), and I'm really not sure which part I should post, but I'd like to ask a generic question about it.
While developing the slave, I've reached a point where if I add the slightest thing (even if this has nothing to do with I2C), it messes with how the master behaves.
To be a bit more precise, the master connects to some input and output shift registers via SPI. When this strange thing happens (loading something extra to the slave), the master can't communicate properly with the shift registers. When I say I add something to the slave code, the following is something that can cause this behaviour:
Code:
String notes[12] = { "C", "C#", "D", "D#", "E", "F", "F#" ,"G", "G#", "A", "Bb", "B" };
Before I start posting chunks of code one after the other I would like to ask if something similar has ever happened to anyone else.
 
Sometimes when I see things like this, I start to look at some generic things like:
a) Are you trying to use more memory than you have. Example has your data gotten large enough where your stack collides with heap or other things loaded high?

b) You corrupt some variables, example write over the beginning or end of an array. Example: like:
Code:
uint8_t my_array[16];
my_array[16] = 0;
As my Array is 16 elements (0-15) so the write to 16 will overwrite whatever followed it. Sometimes this can be fine other times...

c) lots of other things, like timings, or ISRs that screw up, or data that is accessed or set in ISRs which are not volatile ...

But again not really anything to go on here.
 
Sometimes when I see things like this, I start to look at some generic things like:
a) Are you trying to use more memory than you have. Example has your data gotten large enough where your stack collides with heap or other things loaded high?
The output of the Arduino IDE when loading the code with this extra snippet is this:
Code:
Sketch uses 60284 bytes (22%) of program storage space. Maximum is 262144 bytes.
Global variables use 18740 bytes (28%) of dynamic memory, leaving 46796 bytes for local variables. Maximum is 65536 bytes.
It seems like there's plenty more memory available, but I don't know if the stack/heap collision could be possible with the above output, and if so, how can I detect it?

b) You corrupt some variables, example write over the beginning or end of an array. Example: like:
Code:
uint8_t my_array[16];
my_array[16] = 0;
As my Array is 16 elements (0-15) so the write to 16 will overwrite whatever followed it. Sometimes this can be fine other times...
No such indexing is happening
c) lots of other things, like timings, or ISRs that screw up, or data that is accessed or set in ISRs which are not volatile ...

But again not really anything to go on here.
Well, the slave Teensy does have eight rotary encoders read with the Encoder library which uses interrupts, still the very weird thing is that only the code snippet I posted in the OP alone causes the master Teensy to malfunction. And that's just an example, if I add anything else I get the same behaviour. If I comment out this line of code (or whatever other addition), then everything works fine.
 
Last edited:
Just to be sure, do you have pull-up resistors on one of the Teensy's? You need two resistors, one between pin 18 and 3.3v and the other between pin 19 and 3.3v (these pull-up resistors are in parallel to the data connection). Typically 2.2K resistors are appropriate on 3.3v systems (4.7K is more appropriate on an i2c bus that can run either at 3.3v or 5v).

You need to have as short of wires as possible between the two Teensys. If you need longer wires, you might need to reduce the I2C speed. For really long connections, you probably need something like an i2c bus extendter.

You need to have a common ground between the two Teensys, even if you are powering the Teensys separately.

I've seen issues with flaky breadboards or wiring was causing electrical noise that could affect communication. Ditto for poor solder joints.
 
I do have 4.7K pull-up resistors on both Teensies. As I wrote in both posts above, removing the line of code in the OP makes things work fine, so there's definitely nothing wrong with the circuit.
 
Codewise I personally almost never use the string class.
Code:
String notes[12] = { "C", "C#", "D", "D#", "E", "F", "F#" ,"G", "G#", "A", "Bb", "B" };

Also don't know where you are adding these? Are you adding them as global variable or within a funciton.

I could be wrong, but I believe that this code will end up calling malloc for each one of these strings to allocate a buffer for them at run time.

If they are contained within a function, it will malloc each of these every time your function is called. So again it may depend on where things line up in memory. If you have a lot of these string objects, your heap will continue to grow and does not shop up in your static usages...

There are some libraries that can help detect some of this, or sometimes I roll my own...

Example running this on T3.6
Code:
const int led = LED_BUILTIN;  // the pin with a LED
//String notes[12] = { "C", "C#", "D", "D#", "E", "F", "F#" , "G", "G#", "A", "Bb", "B" };
extern "C" {
  extern unsigned long _stext;
  extern unsigned long _etext;
  extern unsigned long _sdata;
  extern unsigned long _edata;
  extern unsigned long _sbss;
  extern unsigned long _ebss;
  extern unsigned long _estack;
  extern void *_sbrk(int incr);
}

void setup() {
  // print heap size...
  // put your setup code here, to run once:
  while (!Serial && millis() < 5000) ;
  Serial.begin(115200);
  Serial.printf("Heap Size: %d\n", (uint32_t)_sbrk(0) - (uint32_t)&_ebss);
  delay(1000);
  pinMode(led, OUTPUT);
}


void loop() {
  // put your main code here, to run repeatedly:
}
Note: the commented out. The code sizes showed:
Code:
Sketch uses 33036 bytes (3%) of program storage space. Maximum is 1048576 bytes.
Global variables use 5312 bytes (2%) of dynamic memory, leaving 256832 bytes for local variables. Maximum is 262144 bytes.
And it printed the heap size was 0.

Now if I uncomment your array of strings we see:
Code:
Sketch uses 33728 bytes (3%) of program storage space. Maximum is 1048576 bytes.
Global variables use 5512 bytes (2%) of dynamic memory, leaving 256632 bytes for local variables. Maximum is 262144 bytes.

So it add 200 bytes to globals plus:
Heap Size: 2328

So again it surprising how much memory can be eaten up by simple things.
 
It's a global variable. I'll try to print the heap size and see, never approached it this way. You suggest though that it is very possible to be a memory issue, right? Can this be affecting the I2C master in any way?
 
I had repeatedly memory and heap issues with strings, more depending on the number of strings than on their length. It looks like the String class allocates an enormous overhead of dynamic memory. Since I discovered that, I modified almost everything to use char arrays.
 
Eventually that was probably it! I changed all String objects to char and the strange behaviour is gone! I thought that posting the OP was a long shot, but your advice helped a lot! Thank you everyone!
 
Status
Not open for further replies.
Back
Top