Wire (I2C)

RogerClark
Fri May 01, 2015 7:48 am
The Wire (I2C) library is part of the Arduino STM32 files download, as it is almost a core part of the Arduino suite of libraries

The version of Wire that is currently used, is software / “big banged”, and doesn’t use the hardware I2C features of the STM32

The advantage of a software approach is that Wire can be run on any GPIO pins on the board (that are not used for other specific purposes e.g. Hardware serial)

The disadvantage is the speed. The default speed is 250kbps, and the fastest that can be achieved by minor changes to the Wire library somewhere under 400kbps. With direct GPIO access the speed could easily be increased beyond 400kbps, however at the moment, no one has requested this , or looked into speeding up Wire.

Only one change was required to the original Wire version (written by LeafLabs), which was to fix an issue where it would not work with the I2C scanner
example sketch.

PS.
I’m not absolutely sure why LeafLabs (who wrote all the original code, for their Maple products), chose to go with a software solution, (someone may be able to give us a definitive answer), but my understanding was that programming for the Hardware I2C is problematic, and there may even be bugs in the STM32 chips themselves with the hardware I2C features. The hardware errors are documented by STM, along with work arounds in a document that can be downloaded from their site.


madias
Sun May 03, 2015 9:40 pm
A short words about wire.h:
The software mode (standard!) is definitely strange written: All commands use “digitalWrite” instead of direct pin manipulation, so this is for sure one reason, why there is no more speed beyond 250kbps.
I have included into my port of the adafruit OLED library “hardwire” as standard and see no issues with it. (super clean 400khz SCL with osci/logic analyzer, both (I2C1, I2C2) tested )
So keep in mind, using the SW “wire.h” version will slow down your code and I don’t know how it will react within time critical stuff or many used timers.
So my suggestions is (if you can use the I2c HW pins) to give hardwire a try before implementing this cheesy SW version.

RogerClark
Sun May 03, 2015 11:15 pm
@madias

Thanks.

I’ve never tried HardWire

One thing we could do relatively easily is update the software I2C to use direct register access, it will speed it up greatly and also still allow the flexibility of using whichever pins you want.

I recall several people on the Arduino forum saying they prefer an implementation that isn’t tided to specific pins.

But its good to have both options if they both work well.

Coding the GPIO stuff is not hard, but I’m not sure I have time to look at it at the moment


mrburnette
Mon May 04, 2015 2:31 am
RogerClark wrote:@madias
<…>
I’ve never tried HardWire

RogerClark
Mon May 04, 2015 3:09 am
Ray

It Looks like the forum can be modified to open links into new tabs or windows, but it doesnt seem just to be a settings change,

https://www.phpbb.com/support/docs/en/ … w-windows/

PS. The site is using PHPBB. I tried MyBB but it didnt seem to have all the feature that would be needed, so chose PHPBB instead – as its an old stalwart


strawberrymaker
Fri Jun 05, 2015 6:12 pm
Do you know why the I2C-Scanner Sketch doesnt recognize devices?
// --------------------------------------
// i2c_scanner
//
// Version 1
// This program (or code that looks like it)
// can be found in many places.
// For example on the Arduino.cc forum.
// The original author is not know.
// Version 2, Juni 2012, Using Arduino 1.0.1
// Adapted to be as simple as possible by Arduino.cc user Krodal
// Version 3, Feb 26 2013
// V3 by louarnold
// Version 4, March 3, 2013, Using Arduino 1.0.3
// by Arduino.cc user Krodal.
// Changes by louarnold removed.
// Scanning addresses changed from 0...127 to 1...119,
// according to the i2c scanner by Nick Gammon
// http://www.gammon.com.au/forum/?id=10896
// Version 5, March 28, 2013
// As version 4, but address scans now to 127.
// A sensor seems to use address 120.
//
//
// This sketch tests the standard 7-bit addresses
// Devices with higher bit address might not be seen properly.
//

#include <Wire.h>

void setup()
{
Wire.begin();

Serial.begin(9600);
Serial.println("\nI2C Scanner");
}

void loop()
{
byte error, address;
int nDevices;

Serial.println("Scanning...");

nDevices = 0;
for(address = 1; address < 127; address++ )
{
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
error = Wire.endTransmission();

if (error == 0)
{
Serial.print("I2C device found at address 0x");
if (address<16)
Serial.print("0");
Serial.print(address,HEX);
Serial.println(" !");

nDevices++;
}
else if (error==4)
{
Serial.print("Unknow error at address 0x");
if (address<16)
Serial.print("0");
Serial.println(address,HEX);
}
}
if (nDevices == 0)
Serial.println("No I2C devices found\n");
else
Serial.println("done\n");

delay(5000); // wait 5 seconds for next scan
}


Naguissa
Fri Jun 05, 2015 8:16 pm
strawberrymaker wrote:Do you know why the I2C-Scanner Sketch doesnt recognize devices?
// --------------------------------------
// i2c_scanner
//
// Version 1
// This program (or code that looks like it)
// can be found in many places.
// For example on the Arduino.cc forum.
// The original author is not know.
// Version 2, Juni 2012, Using Arduino 1.0.1
// Adapted to be as simple as possible by Arduino.cc user Krodal
// Version 3, Feb 26 2013
// V3 by louarnold
// Version 4, March 3, 2013, Using Arduino 1.0.3
// by Arduino.cc user Krodal.
// Changes by louarnold removed.
// Scanning addresses changed from 0...127 to 1...119,
// according to the i2c scanner by Nick Gammon
// http://www.gammon.com.au/forum/?id=10896
// Version 5, March 28, 2013
// As version 4, but address scans now to 127.
// A sensor seems to use address 120.
//
//
// This sketch tests the standard 7-bit addresses
// Devices with higher bit address might not be seen properly.
//

#include <Wire.h>

void setup()
{
Wire.begin();

Serial.begin(9600);
Serial.println("\nI2C Scanner");
}

void loop()
{
byte error, address;
int nDevices;

Serial.println("Scanning...");

nDevices = 0;
for(address = 1; address < 127; address++ )
{
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
error = Wire.endTransmission();

if (error == 0)
{
Serial.print("I2C device found at address 0x");
if (address<16)
Serial.print("0");
Serial.print(address,HEX);
Serial.println(" !");

nDevices++;
}
else if (error==4)
{
Serial.print("Unknow error at address 0x");
if (address<16)
Serial.print("0");
Serial.println(address,HEX);
}
}
if (nDevices == 0)
Serial.println("No I2C devices found\n");
else
Serial.println("done\n");

delay(5000); // wait 5 seconds for next scan
}


RogerClark
Fri Jun 05, 2015 9:02 pm
The I2C scanner was working the last time I tried it.

It’s possible some recent changes have broken something.

I will test and get back to you


strawberrymaker
Fri Jun 05, 2015 9:37 pm
Ok, well tried now a Accelerometer and it seems like that one is being detected (although i’ve forgot the pullups). May i’ll try it one more time.

~Straw


RogerClark
Fri Jun 05, 2015 9:48 pm
Ok

I tested with MPU9150 imu and BMP085 barometer and also with some I2C memory

The default bus speed may be too high, I think its around 250kbps, but Arduino is normally 100kpbs, perhaps I better change the default to normal Arduino speed.

Note. I could not get I2C to work with an OV7670 camera at 250kpbs I had to instantiate another instance of Wire and run at 100kpbs


Rick Kimball
Fri Jun 05, 2015 10:13 pm
RogerClark wrote:Note. I could not get I2C to work with an OV7670 camera at 250kpbs I had to instantiate another instance of Wire and run at 100kpbs

RogerClark
Fri Jun 05, 2015 10:29 pm
I cant remember what pull-ups I used

But the other issue with the OV7670 is its not technically I2C, its SCCB.

They are basically the same protocol, but in practice the implementation in the OV7670 doesn’t appear to work with a lot of I2C hosts.
Hence a lot of people end up writing GPIO bit banged implementations of I2C variants for use with this device.

Luckily Wire in libmaple is bit banged and works with SCCB but only at low speeds.
And as its only a control bus to setup the camera, its not worth messing around to get the speed any higher


martinayotte
Fri Jun 05, 2015 10:50 pm
If you are asking for I2C Bus pull-ups, they are generally around 4K7, sometimes lower if the signal on long length and speed is high (such 400Khz instead of 100KHz).

RogerClark
Fri Jun 05, 2015 10:57 pm
4.7k normally works for me.

I think the OV7670 module I have, has 4.7 on the board (I didnt check their precise value)

The OV7670 spec is SCCB to 400khz, and I2C is defaulted to 250k in the Wire class, though I should probably reduce this to 100k to be Arduino compatible for peripherals that cant go faster


Rick Kimball
Fri Jun 05, 2015 11:20 pm
People often overlook that you have to use lower value pull-up resistors with lower voltage to maintain sharp waves. TI had a pretty good doc describing how to calculate: http://www.ti.com/lit/an/slva689/slva689.pdf

To run at 400k you might want to reduce the size of your pull-ups, maybe to 1.5k to 2.2k Ohms

-rick


RogerClark
Sat Jun 06, 2015 12:42 am
I think the general solution is to reduce the default speed in the Wire library, back to 100k

I think it was defaulted to as fast as bit bang’ably possibly, to demonstrate the speed or the processor, but in hindsight it would be better to set it back to 100k as the default


teding
Mon Jun 08, 2015 7:13 pm
Finaly I got the wire libary working, Turns out there was a small mis on my mini maple clone.
Shorting Gnd an pin 15.
Now that rtclib and i2c scanner sketch is working,
I wonder how to change pins without changing the libary. and how do I switch over to hardwire ?

luimarma
Tue Jun 09, 2015 3:03 pm
Can you tell which clone are you using?
I am having trouble with wire library and the I2C scanner also.
by the way I am using Olimexino STM32

Regards,

Luis


teding
Tue Jun 09, 2015 6:08 pm
I use the Baite Maple Mini clone. On the topside headerpin 15 connected to gnd.
Resulting in every adress to reported found with the i2c scanner sketch.
With a sharp pin I cleaned the surronding of the headerpin. Every thing working OK now.
Image

Naguissa
Thu Jun 11, 2015 2:22 pm
teding wrote:I use the Baite Maple Mini clone. On the topside headerpin 15 connected to gnd.
Resulting in every adress to reported found with the i2c scanner sketch.
With a sharp pin I cleaned the surronding of the headerpin. Every thing working OK now.
Image

madias
Thu Jun 11, 2015 3:40 pm
I’ve looked with my mirror glasses on my baite mini: The GND “plate” is really damned near to PIN’s 15 headerpin. So good to know that! (I own about 10 of these, 2 are working 8 are unpacked)

mrburnette
Thu Jun 11, 2015 8:18 pm
madias wrote:I’ve looked with my mirror glasses on my baite mini: The GND “plate” is really damned near to PIN’s 15 headerpin. So good to know that! (I own about 10 of these, 2 are working 8 are unpacked)

teding
Fri Jun 12, 2015 5:03 am
That’s a good tip, With the magnifier and the led light of my cell phone as backlight, I can see the fault.
Next time before I solder the pinheader I will use this inpection.
Leaving me with the question. When i use the wire libary can I define the SDA and SCL pins from the sketch or only in the wire.cpp
// Declare the instance that the users of the library can use
//TwoWire Wire(SCL, SDA, SOFT_FAST);
TwoWire Wire(PB6, PB7, SOFT_FAST);

RogerClark
Fri Jun 12, 2015 5:15 am
@teding

Not being able to change the pins is a hang over from the AVR Arduino’s which can only use I2C on 2 specific pins.

This is also a bi-product of the global instantiation of the Wire (and this also applies to SPI)

As soon as you include Wire.h, it creates an instance of TwoWire called Wire

You can create another instance of TwoWire in your sketch and use different pins e.g.

TwoWire Wire2(PB4, PB5, SOFT_FAST);

But you can’t get rid of Wire and its always on PB6 and PB7

we have a similar issue with SPI, because even the Maple mini has 2 SPI ports, but its not possible to have “SPI” on port 2 :-(

The only reasonable solution I can think of is to have (renamed) duplicates of these classes that omits the global instantiation.

e.g. #include <Wire_NOGLOBAL.h>

I think if we completely removed the global instantiation from Wire (or SPI), a lot of the other libraries that use Wire (or SPI) would break :-(

We could add a menu option to every board which you select to stop global instantiation, but this would not work that well, because you may have some sketches you want to use global instantiation and some you don’t, and you should not need to set a menu each time you load a different sketch, the sketch should be self contained.

I think this whole issue, is possibly something for the arduino.cc programming forum, but I’m not sure they’d be much use, because when I’ve asked questions in the past they have said that these are Non Arduino questions (which is rubbish as I’m using the Arduino IDE)


Naguissa
Sat Jun 13, 2015 12:10 pm
RogerClark wrote:@teding

Not being able to change the pins is a hang over from the AVR Arduino’s which can only use I2C on 2 specific pins.

This is also a bi-product of the global instantiation of the Wire (and this also applies to SPI)

As soon as you include Wire.h, it creates an instance of TwoWire called Wire

You can create another instance of TwoWire in your sketch and use different pins e.g.

TwoWire Wire2(PB4, PB5, SOFT_FAST);

But you can’t get rid of Wire and its always on PB6 and PB7

we have a similar issue with SPI, because even the Maple mini has 2 SPI ports, but its not possible to have “SPI” on port 2 :-(

The only reasonable solution I can think of is to have (renamed) duplicates of these classes that omits the global instantiation.

e.g. #include <Wire_NOGLOBAL.h>

I think if we completely removed the global instantiation from Wire (or SPI), a lot of the other libraries that use Wire (or SPI) would break :-(

We could add a menu option to every board which you select to stop global instantiation, but this would not work that well, because you may have some sketches you want to use global instantiation and some you don’t, and you should not need to set a menu each time you load a different sketch, the sketch should be self contained.

I think this whole issue, is possibly something for the arduino.cc programming forum, but I’m not sure they’d be much use, because when I’ve asked questions in the past they have said that these are Non Arduino questions (which is rubbish as I’m using the Arduino IDE)


RogerClark
Sat Jun 13, 2015 9:18 pm
Defines in the sketch do not get seen by the libraries.

Only core defines and those setup in the library header are present in the library

(Note. I did investigate this as an option, but it doesn’t work)


Naguissa
Sun Jun 14, 2015 11:19 am
RogerClark wrote:Defines in the sketch do not get seen by the libraries.

Only core defines and those setup in the library header are present in the library

(Note. I did investigate this as an option, but it doesn’t work)


RogerClark
Mon Jun 15, 2015 2:34 am
ummm

OK.

I’m not sure why this didnt seem to work for me before, or why it seems to work now but I agree, if I have this in the sketch

#define NO_GLOBAL_INSTANTIATION
#include <Wire.h>


martinayotte
Mon Jun 15, 2015 2:51 am
H Roger,

I understand your reluctance to #ifdef, but I think in some cases, it is really helpful ;)


RogerClark
Mon Jun 15, 2015 3:45 am
Martin

I think we need some mechanism to prevent automatic / global instantiation.

This possibly looks like the only way.

I guess we just need to agree on what to call the define.

And I better post this to “General discussion” so it gets a wider audience

Edit.

This doesn’t seem to do what I thought it did. (well I’m sure I looked at this some time ago and eventually came to the conclusion below.

Although in my tests, the Wire global doesn’t seem to be visible to the Sketch if you set that define , this is because its not declaring the extern for it in Wire.h

However, as Wire.h is directly included by Wire.cpp, then Wire.cpp doesnt get the #define.

So Wire.cpp still declares the variable called Wire, its just the sketch cant see it because its not been extern’ed

All the #define does is turn off the visibility of the Wire global variable from inside the sketch.

Hence you can’t declare your own Wire variable in the sketch e.g.

TwoWire Wire(PB8, PB9, SOFT_FAST);


Naguissa
Mon Jun 15, 2015 9:29 pm
RogerClark wrote:Martin

I think we need some mechanism to prevent automatic / global instantiation.

This possibly looks like the only way.

I guess we just need to agree on what to call the define.

And I better post this to “General discussion” so it gets a wider audience

Edit.

This doesn’t seem to do what I thought it did. (well I’m sure I looked at this some time ago and eventually came to the conclusion below.

Although in my tests, the Wire global doesn’t seem to be visible to the Sketch if you set that define , this is because its not declaring the extern for it in Wire.h

However, as Wire.h is directly included by Wire.cpp, then Wire.cpp doesnt get the #define.

So Wire.cpp still declares the variable called Wire, its just the sketch cant see it because its not been extern’ed

All the #define does is turn off the visibility of the Wire global variable from inside the sketch.

Hence you can’t declare your own Wire variable in the sketch e.g.

TwoWire Wire(PB8, PB9, SOFT_FAST);


Naguissa
Mon Jun 15, 2015 9:34 pm
On point 2, extra define is defined on c:

#define mylib__c

And checked on h to don’t compile code depending on defines, as Wire global, for example. If noglobal is not defined global wire variable will be defined when compiling entire sketch but not available for use on c(pp).

Yes, these are very dirty hacks, but needed to archieve that.

Ah! Only for use on ICs, never for a powerfull machine!


RogerClark
Mon Jun 15, 2015 9:54 pm
Can you post some example code of how you avoided the global instantiation ?
I still can’t see how you managed to control the instantiation in Wire.cpp

Naguissa
Mon Jun 15, 2015 10:00 pm
RogerClark wrote:Can you post some example code of how you avoided the global instantiation ?
I still can’t see how you managed to control the instantiation in Wire.cpp

RogerClark
Mon Jun 15, 2015 10:01 pm
Thanks

martinayotte
Tue Jun 16, 2015 12:51 am
RogerClark wrote:And I better post this to “General discussion” so it gets a wider audience

Naguissa
Tue Jun 16, 2015 4:53 am
RogerClark wrote:Can you post some example code of how you avoided the global instantiation ?
I still can’t see how you managed to control the instantiation in Wire.cpp

RogerClark
Tue Jun 16, 2015 5:03 am
Ok. So the main change is that you instantiated the Wire global variable in Wire.h instead of Wire.cpp

That method had crossed my mind as well, but I had not tried it.

And

_WIRE_C_

really means Wire Global Has Been Defined

Naguissa wrote:
Problem? If Wire is included in any other library global Wire will be defined, but this is logical, as library is included to be used, and usually with that variable.


Naguissa
Tue Jun 16, 2015 5:18 am
RogerClark wrote:Ok. So the main change is that you instantiated the Wire global variable in Wire.h instead of Wire.cpp

That method had crossed my mind as well, but I had not tried it.

And

_WIRE_C_

really means Wire Global Has Been Defined

Naguissa wrote:
Problem? If Wire is included in any other library global Wire will be defined, but this is logical, as library is included to be used, and usually with that variable.


mrburnette
Tue Jun 16, 2015 12:05 pm
… a thought…

I used to work in a corporate environment. I remember all too well having those wall-sized charts created by a team of graphic ants showing system dependencies. They were NEVER right because the systems changed quicker than the ants could analyze the change logs, update the charts, and reprint. Worthless.

Going down a path where #includes are globally mutually exclusive seems to be a very slippery slope… I propose a new environment variable,
SLIPPERY_WHEN_WET

Ray


Naguissa
Tue Jun 16, 2015 7:38 pm
mrburnette wrote:… a thought…

I used to work in a corporate environment. I remember all too well having those wall-sized charts created by a team of graphic ants showing system dependencies. They were NEVER right because the systems changed quicker than the ants could analyze the change logs, update the charts, and reprint. Worthless.

Going down a path where #includes are globally mutually exclusive seems to be a very slippery slope… I propose a new environment variable,
SLIPPERY_WHEN_WET

Ray


mrburnette
Tue Jun 16, 2015 8:50 pm
The alternative is one library per config or including it in each program.

The other alternative is to write more functions and less libraries. Unless there is a true need to inherit from a library, many prigrams are easier to understand as libraries.

Classe libraries have their place, (in Arduino, easy code reuse) but they can be easiiy overrated. Classes can easily constructed right in the editor.

Ray


Naguissa
Tue Jun 16, 2015 9:36 pm
mrburnette wrote: The alternative is one library per config or including it in each program.

The other alternative is to write more functions and less libraries. Unless there is a true need to inherit from a library, many prigrams are easier to understand as libraries.

Classe libraries have their place, (in Arduino, easy code reuse) but they can be easiiy overrated. Classes can easily constructed right in the editor.

Ray


mrburnette
Tue Jun 16, 2015 10:42 pm
Naguissa wrote:
<…>
My case is several sensors wifi-available to read status and logs, so rtc, wifi, sd and stm32 are the base, and sensor handling and actuators are the differences, so I try to envelope same codebase in few libraries and do a sketch for each device.

Leave a Reply

Your email address will not be published. Required fields are marked *