0
\$\begingroup\$

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?

Thanks in advance.

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

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:

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

enter image description here

enter image description here

enter image description here

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

Without resistor on the BUS:

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

enter image description here

enter image description here

enter image description here

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

I2C debugger logs compare:

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

enter image description here

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

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.
\$\endgroup\$
6
  • 1
    \$\begingroup\$ Have you factored-in that the I2C pull-up resistor will be several hundred ohms? \$\endgroup\$
    – Andy aka
    Aug 2 '21 at 13:57
  • 1
    \$\begingroup\$ Not several hundred, at 5V the total pull-up should be more than 1.67k under standard conditions which all chips can use. \$\endgroup\$
    – Justme
    Aug 2 '21 at 14:02
  • \$\begingroup\$ 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. \$\endgroup\$
    – The Photon
    Aug 2 '21 at 15:18
  • \$\begingroup\$ Updated post with details \$\endgroup\$
    – Dmitry
    Aug 3 '21 at 10:34
  • \$\begingroup\$ 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. \$\endgroup\$
    – Justme
    Aug 3 '21 at 10:55
6
\$\begingroup\$

That will not be a problem with connector of 1 ohms.

You will be in specs even with several tens of ohms of resistance in series, but obviously the actual value depends on supply voltage and pull-up resistance value.

If you are seeing that a chip can't pull low during ACK, the problem is not the series resistance, it is something else that is supposed to not happen, most likely MCU IO pins are not used in open drain mode or something similar.

Edit: The problem is that Proteus is wrong and fails to simulate I2C peripheral usage. In real life the AVR would make the pins go into open drain mode as soon as TWEN is set.

\$\endgroup\$
1
  • \$\begingroup\$ This is how it should be, but something goes wrong ... \$\endgroup\$
    – Dmitry
    Aug 3 '21 at 10:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.