I'm wondering how the I2C bus works with internal pullup resistors. In Wire library (or twi), the internal pullup resistors are enabled in begin(). See digitalWrite()
void twi_init(void)
{
// initialize state
twi_state = TWI_READY;
twi_sendStop = true; // default value
twi_inRepStart = false;
// activate internal pullups for twi.
digitalWrite(SDA, 1);
digitalWrite(SCL, 1);
// initialize twi prescaler and bit rate
cbi(TWSR, TWPS0);
cbi(TWSR, TWPS1);
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;
/* twi bit rate formula from atmega128 manual pg 204
SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
note: TWBR should be 10 or higher for master mode
It is 72 for a 16mhz Wiring board with 100kHz TWI */
// enable twi module, acks, and twi interrupt
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
}
So when the I2C hardware is working, does it not alter internal pullup state? This blurb from the data sheet seems to support my assumption:
Note that the internal pull-ups in the AVR pads can be enabled by setting the PORT bits corresponding
to the SCL and SDA pins, as explained in the I/O Port section. The internal pull-ups can in some systems
eliminate the need for external ones.
I have external pullups on the bus and several I2C devices that I want to toggle their powers, but was wondering if I need to turn off the internal pullup so I don't power some I2C devices through the bus when I power them down.
Do you want to remove the power from all the I2C devices on the bus ?
A low on the SDA or SCL means something, it is active low. So you better stop the Wire library with: Wire.end.
I don't know if Wire.end will remove the pullups as well, but you can add that yourself.
The digitalWrite function does not activate "physically" a pull-up resistor on the ports. It sends a 0 or a 1, no more.
Pull-up activation is done during port configuration (when you say if it's an input, an output or open drain with or without pullup)
For I2C, you must have open drain with 2k2 pullup. I am not sure at all that the pull-ups within the AVR are compliant with I2C specifications (I think they are around 10k or 20k, I have to check in the datasheet)
As far as I can understand, I read the comment as "send a 1, so the line is pulled up" (a 0 would activate the low transistor and perform a strong current sink)
BenKissBox, welcome in the wonderful world of AVR chips and Arduino functions
The internal pullup resistor is about 50k.
It is turned on by writing to the output port while the port is set as input.
The pinMode(..., INPUT_PULLUP) set it as input and writes a '1' to the output port.
When the chip is set into I2C mode, those pins follow the I2C specifications for current, speed and so on. However, the normal input and output logic is still there. So writing to the output port (while the pin is set as input) enables the internal pullup.
The I2C does not set it as 100% open-drain
Because there are protection clamping diodes to VCC and GND. If an Arduino is Slave, and it has the power turned off, that will keep the SDA and SCL low via the diodes.
There is even an extra function added, toggle a output pin by writing to the input register
Internal pullup is between 30k-60k on RESET pin and 20k-50k on all other I/O pins, according 328P's datasheet. It is far higher than I2C requirement is. I tested I2C with the internal pullups a time ago. My experience is that it doesn't work. I2C works with the pullup ~10k on short distance. Recommended value is ~5k on most devices so internal pullup is unusable.
What matters is the amount R * C.
R = pull-up value
C = capacity on the bus
If RC value is too large, signals (clock and data) will not have the time to get on and off .
Either you decrease clock frequency below 100 kHz or you decrease the RC product.
As the capacity C is imposed by the modules and is proportional to the number of this module on I2C bus the only way to reduce RC is to reduce the value of the pull up resistor.
So effectively input pull-up, which are automatically set in twi.c (file part of Wire library), are totally ineffective (too large) so totally useless.
Sorry about disappearing from this thread for a few days.
I think I will call wire.end before I disable the internal pull-ups.
I usually use 100kHz and 10K pullups for slow ADCs and RTC on custom PCBs so short distances and slow bus speed has been a successful combination. I've got a logic analyzer with analog input recently. I'll do some poking around the bus to see what the signals look like with 4 ADCs and 1 RTC and 10K pullups.
Thank you 68tjs for a great explanation. I didn't really think about why one has to reduce R when more devices get connected to the bus, just kind of took it for granted.
Internal Pullups only work while the pin is in input mode. When I2C wants to transmit... it needs to PULL the bus pins down (for active signalling) and once in output mode... with the internal pullups effectively gone now... there is no way for the bus to be pulled low... as it is no longer being pulled up by anything.
Am I making sense?
I thought that external pullups were always needed with TWI(I2C), and 1-wire for this reason.
I was thinking about that too until I read the documentation. There are separate open-drain drivers for the I2C hardware so the I/O port functions stay as input while the open-drain drivers toggle the output. I didn't do enough research but I though that this way the hardware can detect bus contention among multiple masters by reading the I/O ports while attempting to pull I2C bus pins to HIGH and see if they stay HIGH.
I think the I2C does not use the normal digital output stage. It is parallel to it. So when the digital output is set a I2C, the internal pullup will still work. When the SDA or SCL pulls the signal low, it is with 3mA, as specified for I2C. The normal digital output stage can push and pull 40mA. Even the analog input is parallel to it. It might be possible to read an analog value, while the I2C is busy (not tested it though).
The following diagram is the TWI hardware module. The two lines SCL and SDA are driven by open drain drivers with slew rate control and read with spike filter. I guess the GPIO pins aren't used to determine bus arbitration, since there is a module called arbitration detection. So I think Koepel is right about reading even analog values while I2C hardware is outputting on these two pins.
steveh2112- Any resolution to this? I want to send commands from a NodeMCU (master) to an Arduino UNO via I2C. It appears that the Uno would see 3v3 as a logic high, so I am wondering if I could just use the 3v3 on the master Nodemcu for the SDA/SCL pullup resistors.