User avatar
moscardo
Posts: 2
Joined: Tue Dec 26, 2017 11:22 am
Location: Milan, Italy
Contact: Website Twitter

I2C pullup configurations

Tue Dec 26, 2017 11:50 am

Hi everybody and Merry Christmas to you all.
I've a question regarding the RaspberryPi 3 Model B.

In the installation I'm working on, I need to connect 8 of them and I must do this using the I2C protocol.
I'm using the I2C port 0 actually (pin 27 and 28, called ID_SD and ID_SC respectively) since I cannot use the other one.

I've tried to read as much documentation as I could about the topic and I know that these pins are not ment to be used this way:

i2c_zero_pins.jpg
i2c_zero_pins.jpg (72.62 KiB) Viewed 1259 times

Nevertheless it (almost) seems to work and now I have the PIs acting as masters, pulling information from a single Arduino UNO board, which is their slave (poor guy :lol: )!

config1.jpg
config1.jpg (33.96 KiB) Viewed 1259 times

My question is about the pullup resistors.

I know that the I2C bus drivers are “open drain”, so I must take care for the signal to return high when not in use.
The problem here is that Arduino and the PIs are operating at two different voltages (isn't it?).

As suggested by the same sparkfun tutorial, in cases like this I can use the "trick" of putting the pullups on the lower voltage device (the RaspberryPi in my case).

The question is:

1. Do these (I2C 0) RaspberryPi pins already have their own internal pullup resistors? Just in case, can these resistors be enabled/disabled?

---

On the Arduino side, I know pins A4 (SDA) and A5 (SCL) already have their own internal pullups, or at least that's what I can say after reading the source code of the Wire library I'm using for the Arduino code.
See the begin and the end functions (inside the Wire.cpp file) which in turns call the twi_init() and twi_disable() functions (see utility/twi.c file).

Here the interested lines of code for twi_init():

Code: Select all

  // activate internal pullups for twi.
  digitalWrite(SDA, 1);
  digitalWrite(SCL, 1);
and for twi_disable():

Code: Select all

  // deactivate internal pullups for twi.
  digitalWrite(SDA, 0);
  digitalWrite(SCL, 0);
---

If I will explicitly disabled the Arduino internal pullups via code (using the digitalWrite(SDA, 0) and digitalWrite(SCL, 0)), is this hardware configuration going to work (pullpus to 3.3V the Arduino side)?

pullups.jpg
pullups.jpg (12.56 KiB) Viewed 1259 times

I'm mean, I've already tried it and it works but I'm not perfectly sure it is the right way to approach the problem. I don't know if I'm damaging my I2C Pi pins and consequently I don't know if this configuration will be sufficiently robust to last for hours/days/months;

The second question which naturally came out is:

2. Am I damaging my pins? Are these pins protected in some way? Do I risk to permanently damage them, maybe with some incorrect voltage on the I2C bus? ( I hope not but actually I think I've already damaged one of my pi using external pullups to 5V the Arduino side).

Sorry for my wall of text. I will highly appreciate any support!
Thank you so much and again
...Merry Xmas.

Nick

User avatar
joan
Posts: 13404
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: I2C pullup configurations

Tue Dec 26, 2017 12:39 pm

You should only have one set of pull-ups. It doesn't matter if they are at the Pi or the Arduino end but they must be to 3V3.

The Pi does not have external pull-ups connected to GPIO 0/1.

User avatar
rpdom
Posts: 12578
Joined: Sun May 06, 2012 5:17 am
Location: Essex, UK

Re: I2C pullup configurations

Tue Dec 26, 2017 12:43 pm

The HAT Specification says that in order to use those pins for i2c you should fix 3K9 resistors between the pins and 3V3, as they don't have any on the board. The internal ones won't be enabled by default, and they are a bit weak anyway.

Disable or remove any resistors that may pull up to 5V and you should be ok.

User avatar
mahjongg
Forum Moderator
Forum Moderator
Posts: 10643
Joined: Sun Mar 11, 2012 12:19 am
Location: South Holland, The Netherlands

Re: I2C pullup configurations

Tue Dec 26, 2017 12:55 pm

moscardo wrote:
Tue Dec 26, 2017 11:50 am

I'm using the I2C port 0 actually (pin 27 and 28, called ID_SD and ID_SC respectively) since I cannot use the other one.
why can't you use the normal I2C bus?
The problem here is that Arduino and the PIs are operating at two different voltages (isn't it?).
Yes, that what is why you need a level converter between the two sections
use the "trick" of putting the pullups on the lower voltage device (the RaspberryPi in my case).
True, but only if the 5V device can cope with the lower high voltage, I don't think Arduino's can, look it up in the datasheet of the chip used in an Arduino, (normally an ATMEGA328P) look at what the minimum voltage for ViH is, if its just 2V, its okay, of its something like 70% of Vcc then its not as 70% of 5V is 3.5V, which is too much (more than 3.3V)
Do these (I2C 0) RaspberryPi pins already have their own internal pullup resistors? Just in case, can these resistors be enabled/disabled?[/i]
The official I2C bus has 1K8 external pullups to 3V3, arduino's have no external pullups for I2C but rely on weak internal 50K software switchable soft pullups, the unofficial I2C bus of the PI, dedicated to HAT's has no pullups, they should be on the HAT board
config1.jpg (picture showing one slave connected to multiple masters)
That cannot work, I2C can only have one master (and multiple slaves) that is why the designers of I2C chose this naming convention
Am I damaging my pins? Are these pins protected in some way?
YES! putting 5V onto *any* RPI GPIO pin risks getting a latchup effect (see wikipedia) which will destroy the chip. PI's GPIO's are NOT 5V tolerant, its why you need level shifters. But as said you cannot have multiple masters on a bus!

6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 5198
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: I2C pullup configurations

Tue Dec 26, 2017 4:16 pm

mahjongg wrote:
Tue Dec 26, 2017 12:55 pm
moscardo wrote:
Tue Dec 26, 2017 11:50 am
]
config1.jpg (picture showing one slave connected to multiple masters)
That cannot work, I2C can only have one master (and multiple slaves) that is why the designers of I2C chose this naming convention
Actually that is supported by the i2c spec, but admittedly not on the Pi implementation.
If a master supports a multiple master configuration then it has to read back SDA whenever it is transmitting, and support clock stretching. Any difference between the line state compared to the expected state should be treated as a collision and the master immediately back off and retry later, which should leave the other talking master to complete the transaction without error.

i2c is actually "inter integrated circuit". No indication of how many masters or slaves in that. https://en.m.wikipedia.org/wiki/I²C
Software Engineer at Raspberry Pi Trading. Views expressed are still personal views.
Please don't send PMs asking for support - use the forum.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

User avatar
moscardo
Posts: 2
Joined: Tue Dec 26, 2017 11:22 am
Location: Milan, Italy
Contact: Website Twitter

Re: I2C pullup configurations

Tue Jan 02, 2018 11:45 am

Thank you all for your support!
joan wrote:
Tue Dec 26, 2017 12:39 pm
You should only have one set of pull-ups. It doesn't matter if they are at the Pi or the Arduino end but they must be to 3V3.
The Pi does not have external pull-ups connected to GPIO 0/1.
Thank you joan for your quick reply.
rpdom wrote:
Tue Dec 26, 2017 12:43 pm
The HAT Specification says that in order to use those pins for i2c you should fix 3K9 resistors between the pins and 3V3, as they don't have any on the board. The internal ones won't be enabled by default, and they are a bit weak anyway.
Disable or remove any resistors that may pull up to 5V and you should be ok.
Thank you too rpdom for your advice: very helpful.

--
mahjongg wrote:
Tue Dec 26, 2017 12:55 pm
why can't you use the normal I2C bus?
Thank you mahjongg for your reply.

I can't use the official I2C port because it is physically covered by the socket of this device I'm using: a 4inch HDMI LCD by waveshare (here another interesting link) which is mounted directly "on" the Pi; with its socket it is entirely covering the first 13 pairs of pins.

Image

---
mahjongg wrote:
Tue Dec 26, 2017 12:55 pm
moscardo wrote:
Tue Dec 26, 2017 11:50 am
The problem here is that Arduino and the PIs are operating at two different voltages (isn't it?).
Yes, that what is why you need a level converter between the two sections
use the "trick" of putting the pullups on the lower voltage device (the RaspberryPi in my case).
True, but only if the 5V device can cope with the lower high voltage, I don't think Arduino's can, look it up in the datasheet of the chip used in an Arduino, (normally an ATMEGA328P) look at what the minimum voltage for ViH is, if its just 2V, its okay, of its something like 70% of Vcc then its not as 70% of 5V is 3.5V, which is too much (more than 3.3V)
I've looked for the information on the ATmega328P microcontroller Datasheet and, as you suspected, the ViH seems to be 70% of Vcc. Since I'm using 5V as the supply voltage for my Arduino UNO the "perceveid" high voltage from the Arduino will be 3.5V (!= 3.3V :( ).

How can I solve this issue?
Is it the exact same problem you talked about where the voltage level converter could come in handy?

---

Thank you 6by9 for your advice.
6by9 wrote:
Tue Dec 26, 2017 4:16 pm
Actually that is supported by the i2c spec, but admittedly not on the Pi implementation.
If a master supports a multiple master configuration then it has to read back SDA whenever it is transmitting, and support clock stretching. Any difference between the line state compared to the expected state should be treated as a collision and the master immediately back off and retry later, which should leave the other talking master to complete the transaction without error.
Actually, even if my hardware/electrical configuration is quite technically incorrect, I've been able to make it continuously work (the most I could keep it runnign was 4/5 hours approx).

I had the single Arduino continuously asked for information on the I2C bus from 6 RaspberryPis. It worked well: all the machines seems to be pretty much syncronized.

On the RaspberryPis I've used the smbus subset (as shown here).

I like to share here my code, I think it can be helful to understand what I'm doing. This is the python script I'm running on all the RaspberryPis.
As you see, I've used an exception to catch possible conflicts on the bus in this multimaster setup.

Code: Select all

import smbus, time, random

if __name__ == '__main__':
	# local startup variables
	arduino_address = 0x05
	data = 1

	# Open the i2c bus 0 ( /dev/i2c-0 )
	bus = smbus.SMBus( 0 )

	# Open omxplayer
	#args = shlex.split( args )
	#process = Popen( args, stdin=PIPE, stdout=PIPE )
	#print("This is the omxplayer process id: ",  process.pid )

	# Main loop
	while True:
		try:
			data = bus.read_byte( arduino_address )
		except:
			# If the i2c bus is unavailable/busy at the moment,
			# wait before trying to use it again.
			timeToWait = float( random.randint(15, 20) )
			time.sleep( timeToWait / 1000.0 )
			continue

		# If we are here it means we got some data from the bus.
		# Time to use it.
        #[...]

#End of the script
Here is the Arduino code instead:

Code: Select all

#include <Wire.h>
#define SLAVE_ADDRESS 0x05

byte data = 0;

/******************************************************** SETUP */
void setup() {
  Serial.begin(9600);
  openI2C();
}

/********************************************************* LOOP */
void loop() {
  // do nothing but wait
  delay(10);
}

/**************************************** SERIAL EVENT CALLBACK */
void serialEvent() {
    //update 'data' byte value
    [...]
}

/************************************ CALLBACK for SENDING DATA */
void sendData()
{
    Wire.write( data );
}

/**************************************** I2C UTILITY FUNCTIONS */
void openI2C()
{
  //Serial.println("enabling TWI interface");
  
  // initialize the Arduino as a SLAVE on the i2c bus
  Wire.begin( SLAVE_ADDRESS );
  
  // deactivate the internal pullup resistors.
  // These two lines are taken directly from the 
  // 'twi.c' source code - see 'twi_disable' function.
  digitalWrite( SDA, 0 );
  digitalWrite( SCL, 0 );

  // define callback for i2c communication
  Wire.onRequest( sendData );

  Wire.write( 'a' );
}


void closeI2C()
{
  //Serial.println("disabling TWI interface");
  Wire.end();
}

//End of the program
Thank you all for your kind support!

User avatar
mahjongg
Forum Moderator
Forum Moderator
Posts: 10643
Joined: Sun Mar 11, 2012 12:19 am
Location: South Holland, The Netherlands

Re: I2C pullup configurations

Thu Jan 04, 2018 11:38 pm

I've looked for the information on the ATmega328P microcontroller Datasheet and, as you suspected, the ViH seems to be 70% of Vcc. Since I'm using 5V as the supply voltage for my Arduino UNO the "perceveid" high voltage from the Arduino will be 3.5V (!= 3.3V :( ).

How can I solve this issue?
Is it the exact same problem you talked about where the voltage level converter could come in handy?
yes, and there are two ways to solve this:
either lower the 5V voltage, so that 70% of the new voltage is below 3.3V, you could use a series diode to drop 0.7V to the ATmega328P so it runs at 4.3 volt.
70% of 4.3V is 3.01V, so that should work. With a lower VCC the ATmega328P might run a bit slower though, but you could try this.

OR

Use the common I2C levels converter (twice an n-FET and an extra pullup) .

by the way, if the display HAT doesn't actually use I2C, you could just attach (solder) I2C wires to the HAT. and even when the HAT does use I2C, chances are good that it will still work, as the HAT i2C function almost certainly will use a different I2C address. Soldering is an *essential* skill. :mrgreen:

User avatar
bitbank
Posts: 236
Joined: Sat Nov 07, 2015 8:01 am
Location: Sarasota, Florida
Contact: Website

Re: I2C pullup configurations

Fri Jan 05, 2018 4:23 am

Is it necessary to use the Arduino from a 5V source? As others have stated, it will not register a high signal from the 3.3v logic coming from the RPIs. You can run the Arduino at 3.3v and this will solve the I2C signal issue, but you'll have to run it at a slower speed (usually 8Mhz). You can buy Arduino's pre-configured for 3.3v and 8Mhz or you can modify one that was sold to be run at 5V.
The fastest code is none at all :)

Return to “Advanced users”

Who is online

Users browsing this forum: No registered users and 13 guests