I2C between arduino and STM32

I'm having problems with an I2C connection between an arduino uno and an STM32 microcontroller, where my arduino is the slave and the STM32 will request data from it. The STM32 board is a Flip32 flight controller which is already using I2C to get sensor information from accelerometer, gyro, compass and barometer, so I'm using the same I2C functions that it uses to access those sensors.

The problem is that as soon as my arduino is set as a slave and given the address to use, the other I2C sensors start reading garbage values. The values that my slave is sending are correctly received by the STM32 but only about 50% of the time. The other 50% of the time the i2cRead() function that handles the I2C interaction returns failure.

I found that I can reproduce the problem with a sketch that does nothing more than call Wire.begin(...)

I have spent a whole day on this trying everything I can think of to figure it out, and more details can be found here:

Are there any other things I might be missing?
Is the Wire library even appropriate to use for connection with STM32 chips?

iforce2d:
I'm having problems with an I2C connection between an arduino uno and an STM32 microcontroller, where my arduino is the slave and the STM32 will request data from it.

Ok,
The standard Wire library has a few limitations.

1: 32byte transmit and receive buffers. So, you have to work around these if you have longer data.
2: In the OnRequestEvent() callback, only the LAST Wire.write() is actually sent.
3: OnRequestEvent() is only called ONCE per Slave Transmit transaction.
4: if the bus is ever held low (shorted to ground) every call, requestFrom(),endTransmission() will hang forever! until the bus 'clears'.

I modified the Wire library (1.6.5) to addresses #2,#3,#4. I'm thinking about fixing #1, Changing #1 would have compatibility issues. The other problems did not created issues when I fixed them.

I added timeouts for bus errors, added a direct access to twi_error, to return the actual errors.
changed Slave Transmit processing to allow infinite transmits (actually the transmits are controlled by the Master device). Multiple Wire.write() calls in the onRequesEvent() work as expected, up to the 32byte buffer limit. If more than 32 bytes are needed, the Slave Transmit process repeatedly calls onRequestEvent() until either the Master stops clocking, or onRequestEvent() does not return anything.

I can attach my changes, and a couple double Arduino test Sketch is anyone is interested. To implement these changes Wire.h, Wire.cpp, utill\twi.h, util\twi.c were affected.

Chuck.

Hi Chuck

I'm only trying to transfer a single byte at the moment, and the real data will be between 5-13 bytes, so the buffer size should not be an issue.

I'm only using a single Wire.write() call in the callback that writes to the bus, and only expecting one slave transmit per transaction.

About 4. you mean a physical wiring problem right? The garbage data read by the sensors does not occur unless I call Wire.begin(...) from the arduino side, so I think the physical connections are ok.

I'm suspecting that it might be an issue of clock timing, since the STM32 is running at 72MHz and I assume capable of faster throughput than the 16MHz arduino. Then again, if I understand it correctly I2C conforms to a standard 'high' speed of 400kHz to allow compatibility across many devices. In past projects I have had two arduinos communicating via I2C without any trouble at all.

Sure, I'd be glad to try your changes and see if it helps at all.

btw here is my test sketch in full, if it helps:

#include <Wire.h>

void i2c_slave_write_handler(int numBytes)
{
  for (int i = 0; i < numBytes; i++)
    Wire.read();
}

void i2c_slave_read_handler()
{
  uint8_t val = 123;
  Wire.write(&val, 1);
}

void setup()
{
  Wire.begin(0x42);
  Wire.onReceive(i2c_slave_write_handler);
  Wire.onRequest(i2c_slave_read_handler);
}

void loop()
{
}

iforce2d:
Hi Chuck

About 4. you mean a physical wiring problem right? The garbage data read by the sensors does not occur unless I call Wire.begin(...) from the arduino side, so I think the physical connections are ok.

Yep,
The Wire library does not handle problems.

The Arduino wire Library configures the I2C clock (only used while a master to 100k) Are you sure the STM32 only runs it a 400k? If found this I2C reference the mentions I2C speeds up to 4.3mhz

iforce2d:
Sure, I'd be glad to try your changes and see if it helps at all.

btw here is my test sketch in full, if it helps:

I saw nothing wrong with your code. You might verify the STM32 I2C clock speed.

attached is my updated Wire library. You can just 'add library' and it will be added to your custom library folder. Arduino will use it instead of the default. If you don't want to use it, you must Delete my library from your custom Library folder. Usually under the Sketches folder \libraries\Wire

I added a sketch to the Example folder, a sketch called Master that works both as a Master and Slave using my 'fixes' for multiple onRequestEvents(), and timeout, and error messages.

Chuck.


Check out my Kickstarter Project Memory Panes an expansion RAM Shield for Mega2560's. It adds 1MB of RAM for those projects where 8KB is not enough.

Wire.zip (19.3 KB)

Hmm... unfortunately the library does not change anything. I found where the I2C bus speed was being set by the STM32, and changed it from 400kHz to 100kHz, also 75kHz etc without much difference.

iforce2d:
Hmm... unfortunately the library does not change anything. I found where the I2C bus speed was being set by the STM32, and changed it from 400kHz to 100kHz, also 75kHz etc without much difference.

One other thing, What is the size of your pullup resistors, for 100k you should have about 4.7k ohm to VCC, 2.2k for 400k. You never mentioned it?

I have not had comm errors unless you consider multiple master collisions/arbitration losses as errors.

Chuck.


Check out my Kickstarter Project Memory Panes an expansion RAM Shield for Mega2560's. It adds 1MB of RAM for those projects where 8KB is not enough.

I'm using 4.7k (sorry, I mentioned in the page I linked to).

The Flip32+ has 1k pullups onboard already, if I'm reading this correctly:
http://www.readytoflyquads.com/media/catalog/product/cache/1/image/1200x/040ec09b1e35df139433887a97daa66f/6/-/6-12-2013_3-22-56_am_1_1.jpg

iforce2d:
I'm using 4.7k (sorry, I mentioned in the page I linked to).

The Flip32+ has 1k pullups onboard already, if I'm reading this correctly:
http://www.readytoflyquads.com/media/catalog/product/cache/1/image/1200x/040ec09b1e35df139433887a97daa66f/6/-/6-12-2013_3-22-56_am_1_1.jpg

Ok,
Next question what voltage are you running the Arduino at? It looks like the Flip32+ runs 3.3V Vcc? It has 1k to Vcc but I cannot verify the actual voltage. If you are expecting the 5V Arduino to reliably detect High levels on the I2C (normally 70% of VCC for the Arduino, which calculates to 3.5V) with a max of 3.3V this is where your errors may be originating. If you are using a 5v Arduino you needs some type of level shifter AdaFruit or Sparkfun make small modules, or you could roll your own with mosfets and resistors.

Chuck.


Check out my Kickstarter Project Memory Panes an expansion RAM Shield for Mega2560's. It adds 1MB of RAM for those projects where 8KB is not enough.

I was originally using a 3.3v arduino, then moved to a 5v just for the ease of connecting things. After reading your last post I moved back to the 3.3v arduino.

With the 3.3v arduino the success rate of the i2cRead() call is about 70% versus 50% or so with the 5v arduino. Setting the clock speed of both devices to 100kHz gives markedly better results than 400kHz.

By the way, if there are already 1K pullups built into the Flip32+, there is no way I can increase the pullup resistance right? I mean, I can only add resistance in parallel which would decrease the pullup value....

I put together a poor mans logic analyzer (GitHub - gillham/logic_analyzer: Implementation of a SUMP compatible logic analyzer for the Arduino) and tried grabbing some samples at 1MHz.
You can see there are occasional very short blips on the SDA line that I don't think are supposed to be there, and I'm not sure that the clock line is altogether correct either...
http://www.iforce2d.net/blips-low.png
http://www.iforce2d.net/blips-high.png
Then again, perhaps this cheapass 'analyzer' should not be trusted much in the first place.

iforce2d:
With the 3.3v arduino the success rate of the i2cRead() call is about 70% versus 50% or so with the 5v arduino. Setting the clock speed of both devices to 100kHz gives markedly better results than 400kHz.

By the way, if there are already 1K pullups built into the Flip32+, there is no way I can increase the pullup resistance right? I mean, I can only add resistance in parallel which would decrease the pullup value....

Which device is the master for these traces? The master device controls the clock high. A slave may extend a low (clock stretching) to allow for extra processing time. Basically on a Slave Transmit (onRequestEvent()) after the slave (arduino) has accepted the I2C address the slave holds the clock low, pausing the master (flipp32+) until the onRequestEvent() returns, and the arduino starts sending the data out.
The resistor 1k pulls the clock high after both the slave, and the master have to stop pulling the clock low. So for a 'High' glitch the 'Master' is usually responsible. Is the I2C interface on the flip+ hardware, or is it a bit banger?

Chuck

iforce2d:
I put together a poor mans logic analyzer (GitHub - gillham/logic_analyzer: Implementation of a SUMP compatible logic analyzer for the Arduino) and tried grabbing some samples at 1MHz.
You can see there are occasional very short blips on the SDA line that I don't think are supposed to be there, and I'm not sure that the clock line is altogether correct either...
http://www.iforce2d.net/blips-low.png
http://www.iforce2d.net/blips-high.png
Then again, perhaps this cheapass 'analyzer' should not be trusted much in the first place.

I just spent the afternoon playing with this 'poor mans' analyzer. I used a Mega2560 as the probe, It works neat!

I identified those little blips, that is the handoff between the Master driving the SDA line and the Slave Driving it for an Acknowledge, On my test that little blip only happened when the SLAVE ID was being acknowledged. My Master was an UNO talking to a DS1307 RTCC, So the RTCC took a little time before it drove the SDA LOW, It is not a 'glitch' because the buss changes are allowed during the SCL LOW period. Changes During SCL HIGH are only allowed for START or STOP conditions.

In the Attached screen grab I have annotated the 'blip' in RED as ACK SDA handoff Orange highlights are Master out, Green are Slave Responses.

I Did not see anything that would account for your comm failures.

Chuck.


Check out my Kickstarter Project Memory Panes an expansion RAM Shield for Mega2560's. It adds 1MB of RAM for those projects where 8KB is not enough.

Hi Chuck

Thanks for sticking with me :slight_smile:

I'll put this aside for a while, since I've attached the flight controller to the quadcopter it's really awkward to get in and attach probes, plug things in and out, re-flash it etc, and I don't really want to risk messing up a nicely working quad.

I have another Flip32+ arriving soon, so I'll get back to this in a few days when I can deal with just the two controller boards on their own.

iforce2d:
Hi Chuck

Thanks for sticking with me :slight_smile:

Drop me a note if I can help you.

Chuck.

Hi Chuck

I never did figure this out. Once I got the other flip32 (STM32 flight controller) I tried implementing software SPI in the Cleanflight source code which fortunately worked without any trouble. This is a nicer outcome actually because I don't need any arduino at all :slight_smile:

More details: