# I2C (TWI): Hold SDA-line low by Slave when line has resistance between Master and Slave

Greetings to everyone!

The schema in brief:

Located at device 1                                   Located at device 2
Master SDA --------------- <physical connector> --------------- SDA Slave


According to I2C specifications: The Slave device must provide ACK signal holding SDA line on logical zero on success or do nothing on error, leaving I2C line high (NACK, logical one). This works fine when the resistance between Master is Slave is very low, for example, 5 milli-Ohm. But when the resistance rises to 100 or even 1000 milli-Ohm, the Slave can't hold line at appropriate logical zero. Such resistance can appear when a physical connector exists between Master and Slave.

Schematically, the SDA-signal at line with 100-1000 milli-Ohm resistance behaves like this:

Master sends some control sequence (7 bits + 1 R/W bit) to the I2C bus and the Slave responses with 1 bit ACK (logical zero). Response with logical zero means that Slave holds line low, when Master holds line high.

                   Dev address            W     ACK
1    0    1    0    1     1    0     0      ?
5V -----     -----     ----- -----
|   |     |   |     |         |             ----- ~2.5V
0V |   |_____|   |_____|         |_____ _____


Here the ACK signal is at level between 2-3 volts, so it is obviously not a logical zero.

How to "help" Slave keep SDA-line on zero (below 0.3V)? Are there are any common practices for solving this?

==========================================================

UPDATED POST WITH DETAILS:

Double checked the I2C manual: https://www.nxp.com/docs/en/user-guide/UM10204.pdf Page 10, section 3.1.6

The Acknowledge signal is defined as follows: the transmitter releases the SDA line during the acknowledge clock pulse so the receiver can pull the SDA line LOW and it remains stable LOW during the HIGH period of this clock pulse

Thus, Master must release the line by switching to high impedance mode. But according to the details posted below Master stays at high level, preventing the Slave to hold SDA line low.

Tested at Proteus 8.10
Master: MPU ATmega328P
Slave: EEPROM 24C01C


======================================

With resistor on the BUS:

======================================

======================================

Without resistor on the BUS:

======================================

======================================

I2C debugger logs compare:

======================================

======================================

C-code:

======================================

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

int main(void)
{
// Set bit rate to 400 KHz
TWBR = (8000000LU / 400000LU - 16) / 2;

// Send START
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);

// Wait till operation is complete: Interrupt Flag is set
while ( !(TWCR & (1 << TWINT)) );

// Exit if status not: START has been transmitted
if (TWSR != 0x08) {
return 1;
}

// Load data register with: 0b_1010_000_0 (EEPROM addres + write)
TWDR = 0xA0;

// Transmit data
TWCR = (1 << TWINT) | (1 << TWEN);

// Wait till operation is complete: Interrupt Flag is set
while ( !(TWCR & (1 << TWINT)) );

// Exit if status not: SLA+W has been transmitted and ACK received
if (TWSR != 0x18) {
return 1;
}

// ... rest of code skipped

// Send STOP
TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN);

return 0;
}


Compile log:

avr-gcc.exe -Wall -gdwarf-2 -fsigned-char -MD -MP -DF_CPU=1000000 -O1 -mmcu=atmega328p  -o "main.o" -c "../main.c"
avr-gcc.exe -mmcu=atmega328p  -o "./Debug.elf" "main.o"
avr-objcopy -O ihex -R .eeprom "./Debug.elf" "./Debug.hex"
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O ihex "./Debug.elf" "./Debug.eep" || exit 0
Compiled successfully.

• Have you factored-in that the I2C pull-up resistor will be several hundred ohms? Aug 2 '21 at 13:57
• Not several hundred, at 5V the total pull-up should be more than 1.67k under standard conditions which all chips can use. Aug 2 '21 at 14:02
• Your schematic should show roughly where the pull-up resistor is located and what its value is. Please edit your post to include this information. If you don't have a pull-up resistor, you should edit to clarify that point. Aug 2 '21 at 15:18
• Updated post with details Aug 3 '21 at 10:34
• If you are not doing this in real life but in Proteus, then it's the Proteus that's at fault. You should have mentioned that as it was very important part of what is wrong. Aug 3 '21 at 10:55