Skip to content

ESP32-S2 fails on I2C at 400 kHz. Extension of issue #12045 #12049

@dzalf

Description

@dzalf

Board

ESP32-S2

Device Description

Custom ESP32-S2 based on the Lolin S2 Mini Wemos

Hardware Configuration

GPIO35 and GPIO35 are connected to SDA and SCL, respectively. My test device is a CAT24M01 EEPROM

Version

latest stable Release (if not listed below)

Type

Bug

IDE Name

VSCode with pioarduino

Operating System

Windows 11

Flash frequency

40 MHz

PSRAM enabled

yes

Upload speed

921600

Description

As previously discussed with @me-no-dev on issue #12045, I recently migrated my project to the latest Core under pioarduino using the platformio.ini lines to get the latest Core:

[env_default]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
board = lolin_s2_mini
framework = arduino

After managing to fix several bugs manually (since the new core broke several parts of my project after the migration), I encountered an issue that I cannot find a solution for this topic: making the I2C to operate stably above 100 kHz.

If I initialise the Wire object at 100 kHz, my sketch seems to work fine and the clock looks stable, according to my logic analyser (Kingst LA5032):

Sketch Output:

src/I2C_Fast_Test/I2C_Fast_Test.cpp<CR>
23:13:02<CR>
[S:SETUP] Starting I2C Test<CR>
[I2C] Clock set to 100000 Hz<CR>
[EEPROM] Writing 21 (0x15) to address 0<CR>
[EEPROM] Writing 22 (0x16) to address 1<CR>
[EEPROM] Writing 'A' (0x41) to address 65536 (crossing 64KB boundary)<CR>
[EEPROM] Read addr 0 = 65 (0x41)<CR>
[EEPROM] Read addr 1 = 22 (0x16)<CR>
[EEPROM] Read addr 65536 = 65 (char 'A', 0x41)<CR>

00000000	41	16	08	09	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000010	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000020	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000030	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000040	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000050	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000060	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000070	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000080	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000090	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
000000A0	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
000000B0	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
000000C0	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
000000D0	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
000000E0	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
000000F0	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	<CR>

TIME: 	717366<CR>

[TEST] Sequential 10-byte write/read verification<CR>
[TEST] Start offset: 65530<CR>
[TEST] Page index: 255<CR>
[TEST] Page offset in page: 250<CR>
[WARN] Sequence crosses a 256-byte page boundary (multi-byte page writes would need splitting).<CR>
[TEST] Writing bytes (value = index)<CR>
[WRITE] addr=65530 value=0 (0x0)<CR>
[WRITE] addr=65531 value=1 (0x1)<CR>
[WRITE] addr=65532 value=2 (0x2)<CR>
[WRITE] addr=65533 value=3 (0x3)<CR>
[WRITE] addr=65534 value=4 (0x4)<CR>
[WRITE] addr=65535 value=5 (0x5)<CR>
[WRITE] addr=65536 value=6 (0x6)<CR>
[WRITE] addr=65537 value=7 (0x7)<CR>
[WRITE] addr=65538 value=8 (0x8)<CR>
[WRITE] addr=65539 value=9 (0x9)<CR>

[TEST] Reading back and verifying<CR>
[READ] addr=65530 got=0 (0x0) expected=0 [OK]<CR>
[READ] addr=65531 got=1 (0x1) expected=1 [OK]<CR>
[READ] addr=65532 got=2 (0x2) expected=2 [OK]<CR>
[READ] addr=65533 got=3 (0x3) expected=3 [OK]<CR>
[READ] addr=65534 got=4 (0x4) expected=4 [OK]<CR>
[READ] addr=65535 got=5 (0x5) expected=5 [OK]<CR>
[READ] addr=65536 got=6 (0x6) expected=6 [OK]<CR>
[READ] addr=65537 got=7 (0x7) expected=7 [OK]<CR>
[READ] addr=65538 got=8 (0x8) expected=8 [OK]<CR>
[READ] addr=65539 got=9 (0x9) expected=9 [OK]<CR>
[RESULT] Errors: 0<CR>
[RESULT] All 10 bytes verified successfully.<CR>
<CR>

Done...<CR>

Logic analyser:

Image

Please pay attention to the time markers A1 and A2, which show a difference of 10 us (very stable 100 kHz):

Image

Now, if I change the frequency of the I2C Wire object to 400 kHz the communication seems to work fine, based on the analyser and serial outputs:

Image
src/I2C_Fast_Test/I2C_Fast_Test.cpp<CR>
23:08:56<CR>
[S:SETUP] Starting I2C Test<CR>
[I2C] Clock set to 400000 Hz<CR>
[EEPROM] Writing 21 (0x15) to address 0<CR>
[EEPROM] Writing 22 (0x16) to address 1<CR>
[EEPROM] Writing 'A' (0x41) to address 65536 (crossing 64KB boundary)<CR>
[EEPROM] Read addr 0 = 65 (0x41)<CR>
[EEPROM] Read addr 1 = 22 (0x16)<CR>
[EEPROM] Read addr 65536 = 65 (char 'A', 0x41)<CR>

00000000	41	16	08	09	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000010	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000020	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000030	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000040	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000050	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000060	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000070	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000080	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
00000090	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
000000A0	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
000000B0	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
000000C0	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
000000D0	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
000000E0	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	
000000F0	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	FF	<CR>

TIME: 	624813<CR>

[TEST] Sequential 10-byte write/read verification<CR>
[TEST] Start offset: 65530<CR>
[TEST] Page index: 255<CR>
[TEST] Page offset in page: 250<CR>
[WARN] Sequence crosses a 256-byte page boundary (multi-byte page writes would need splitting).<CR>
[TEST] Writing bytes (value = index)<CR>
[WRITE] addr=65530 value=0 (0x0)<CR>
[WRITE] addr=65531 value=1 (0x1)<CR>
[WRITE] addr=65532 value=2 (0x2)<CR>
[WRITE] addr=65533 value=3 (0x3)<CR>
[WRITE] addr=65534 value=4 (0x4)<CR>
[WRITE] addr=65535 value=5 (0x5)<CR>
[WRITE] addr=65536 value=6 (0x6)<CR>
[WRITE] addr=65537 value=7 (0x7)<CR>
[WRITE] addr=65538 value=8 (0x8)<CR>
[WRITE] addr=65539 value=9 (0x9)<CR>

[TEST] Reading back and verifying<CR>
[READ] addr=65530 got=0 (0x0) expected=0 [OK]<CR>
[READ] addr=65531 got=1 (0x1) expected=1 [OK]<CR>
[READ] addr=65532 got=2 (0x2) expected=2 [OK]<CR>
[READ] addr=65533 got=3 (0x3) expected=3 [OK]<CR>
[READ] addr=65534 got=4 (0x4) expected=4 [OK]<CR>
[READ] addr=65535 got=5 (0x5) expected=5 [OK]<CR>
[READ] addr=65536 got=6 (0x6) expected=6 [OK]<CR>
[READ] addr=65537 got=7 (0x7) expected=7 [OK]<CR>
[READ] addr=65538 got=8 (0x8) expected=8 [OK]<CR>
[READ] addr=65539 got=9 (0x9) expected=9 [OK]<CR>
[RESULT] Errors: 0<CR>
[RESULT] All 10 bytes verified successfully.<CR>
<CR>

Done...<CR>

HOWEVER, zooming into the SCL line shows a different story. Please look at the markers A1-A2 and B1-B2:

Image

There seems to be some sort of clock stretching going on on my system. I am using 10 kOhms pull-ups.

Any ideas would be very much appreciated

Sketch

//* Requirements: I2C_CAT24M01 library from Rob Tillaart (https://github.com/RobTillaart/I2C_CAT24M01) and the EEPROM chip
#include <Arduino.h>
#include <HardwareSerial.h>
#include "I2C_CAT24M01.h"

constexpr int I2C_BMS_SDA = 33;
constexpr int I2C_BMS_SCL = 35;

// ---- Setting default clock speeds for the I2C Bus
static constexpr uint32_t I2C_CLOCK_STANDARD = 100000;  // 100 kHz
static constexpr uint32_t I2C_CLOCK_FAST = 400000;      // 400 kHz
static constexpr uint32_t I2C_CLOCK_FAST_PLUS = 800000; // 800 kHz
static constexpr uint32_t I2C_CLOCK_CRAZY = 1000000;    // 1 MHz

I2C_CAT24M01 ee(0x57);
uint32_t start, stop;

HardwareSerial *dbg = &Serial0;

// I2C link bus (Wire1)
TwoWire *i2c_port = &Wire;

// Initializes the debug serial port

void dump(uint32_t from, uint32_t to);
void test(uint32_t offset);

void setup()
{
    dbg->begin(115200);

    delay(500);

    if (dbg != nullptr)
    {

        dbg->println(__FILE__);
        dbg->println(__TIME__);
        dbg->println(F("[S:SETUP] Starting I2C Test"));
    }

    // I2C bus setup (same pins as showcase)
    i2c_port->setPins(I2C_BMS_SDA, I2C_BMS_SCL);
    i2c_port->begin();
    i2c_port->setClock(I2C_CLOCK_FAST); // adjust if you use FAST/FASTPLUS per pinout.h

    dbg->print(F("[I2C] Clock set to "));
    dbg->print(i2c_port->getClock());
    dbg->println(F(" Hz"));

    if (!ee.begin())
    {
        dbg->println("EEPROM not found...");
        while (1)
            ;
    }

    dbg->println(F("[EEPROM] Writing 21 (0x15) to address 0"));
    ee.writeByte(0, 21);

    dbg->println(F("[EEPROM] Writing 22 (0x16) to address 1"));
    ee.writeByte(1, 22);

    dbg->println(F("[EEPROM] Writing 'A' (0x41) to address 65536 (crossing 64KB boundary)"));
    ee.writeByte(65536, 'A');

    uint8_t v0 = ee.readByte(0);
    dbg->print(F("[EEPROM] Read addr 0 = "));
    dbg->print(v0);
    dbg->print(F(" (0x"));
    dbg->print(v0, HEX);
    dbg->println(')');

    uint8_t v1 = ee.readByte(1);
    dbg->print(F("[EEPROM] Read addr 1 = "));
    dbg->print(v1);
    dbg->print(F(" (0x"));
    dbg->print(v1, HEX);
    dbg->println(')');

    uint8_t vMid = ee.readByte(65536);
    dbg->print(F("[EEPROM] Read addr 65536 = "));
    dbg->print(vMid);
    dbg->print(F(" (char '"));
    dbg->print((char)vMid);
    dbg->print(F("', 0x"));
    dbg->print(vMid, HEX);
    dbg->println(')');

    start = micros();
    dump(0x00, 0x0100);
    stop = micros();

    dbg->print("\nTIME: \t");
    dbg->println(stop - start);

    test(65530);
    dbg->println("\nDone...");
}

// ------------------------ Loop ------------------------
void loop()
{

    // Minimal pacing (optional)
    delay(5);
}

void dump(uint32_t from, uint32_t to)
{
    for (uint32_t i = from; i < to; i++) //  I2C_DEVICESIZE_CAT24M01
    {
        volatile int x = ee.readByte(i);
        char buffer[24];
        if (i % 16 == 0)
        {
            char buffer[24];
            dbg->print('\n');
#if defined(ESP8266) || defined(ESP32)
            sprintf(buffer, "%08X\t", i); //  ESP cast (long unsigned int)
#else
            sprintf(buffer, "%08lX\t", i); //  AVR needs lX
#endif
            dbg->print(buffer);
        }
        sprintf(buffer, "%02X\t", x);
        dbg->print(buffer);
    }
    dbg->println();
}

void test(uint32_t offset)
{
    dbg->println(F("\n[TEST] Sequential 10-byte write/read verification"));
    dbg->print(F("[TEST] Start offset: "));
    dbg->println(offset);
    dbg->print(F("[TEST] Page index: "));
    dbg->println(offset / 256);
    dbg->print(F("[TEST] Page offset in page: "));
    dbg->println(offset % 256);
    if ((offset % 256) + 10 > 256)
    {
        dbg->println(F("[WARN] Sequence crosses a 256-byte page boundary (multi-byte page writes would need splitting)."));
    }

    dbg->println(F("[TEST] Writing bytes (value = index)"));
    for (uint8_t i = 0; i < 10; i++)
    {
        uint32_t addr = offset + i;
        uint8_t value = i;
        dbg->print(F("[WRITE] addr="));
        dbg->print(addr);
        dbg->print(F(" value="));
        dbg->print(value);
        dbg->print(F(" (0x"));
        dbg->print(value, HEX);
        dbg->println(')');
        ee.writeByte(addr, value);
        delay(5); // modest pacing for EEPROM internal write cycle
    }

    dbg->println(F("\n[TEST] Reading back and verifying"));
    uint8_t errors = 0;
    for (uint8_t i = 0; i < 10; i++)
    {
        uint32_t addr = offset + i;
        uint8_t expected = i;
        uint8_t got = ee.readByte(addr);
        dbg->print(F("[READ] addr="));
        dbg->print(addr);
        dbg->print(F(" got="));
        dbg->print(got);
        dbg->print(F(" (0x"));
        dbg->print(got, HEX);
        dbg->print(')');
        dbg->print(F(" expected="));
        dbg->print(expected);
        if (got == expected)
        {
            dbg->println(F(" [OK]"));
        }
        else
        {
            dbg->println(F(" [FAIL]"));
            errors++;
        }
        delay(5);
    }

    dbg->print(F("[RESULT] Errors: "));
    dbg->println(errors);
    if (errors == 0)
    {
        dbg->println(F("[RESULT] All 10 bytes verified successfully."));
    }
    else
    {
        dbg->println(F("[RESULT] Verification failed. Investigate I2C integrity or EEPROM wear/state."));
    }
    dbg->println();
}

Debug Message

Using:


build_flags = 
    -O3
    -DCORE_DEBUG_LEVEL=5


ESP-ROM:esp32s2-rc4-20191025<CR>
Build:Oct 25 2019<CR>
rst:0x1 (POWERON),boot:0xa (SPI_FAST_FLASH_BOOT)<CR>
SPIWP:0xee<CR>
mode:DIO, clock div:1<CR>
load:0x3ffe5110,len:0x1174<CR>
load:0x4004a000,len:0x8d8<CR>
load:0x4004e000,len:0x3324<CR>
entry 0x4004a138<CR>
[     1][D][esp32-hal-tinyusb.c:825] tinyusb_enable_interface2(): Interface CDC enabled<CR>
[    10][D][esp32-hal-tinyusb.c:703] tinyusb_load_enabled_interfaces(): Load Done: if_num: 2, descr_len: 75, if_mask: 0x10<CR>
[    23][I][esp32-hal-psram.c:104] psramAddToHeap(): PSRAM added to the heap.<CR>

Other Steps to Reproduce

No response

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions