arduino / ArduinoCore-mbed

318 stars 189 forks source link

Raspberry Pi Pico (RP2040) - I2C holds SCL low #448

Open marmil opened 2 years ago

marmil commented 2 years ago

Hi everybody,

i am currently trying to implement an I2C connection between an ESP32-WROVER and a Raspberry Pi Pico (RP2040). The ESP is configured as master and shall request data from the Pico which is configured as slave. Besides requesting data the master also sends data to the slave. After encountering some issues that the I2C communication is not working properly i created a minimal example for master and slave where the problem still occurs. Regularly the I2C times out (diagnosed by ESP via Debug messages and Logic analyzer - see below) and no data can be transmitted anymore. Pullups of SDA/SCL should be fine with 3K3.

First the program-code of the slave, then some measures&measurements i already took/did and in the end the despaired beg for help finding the problem which i assume in the I2C implementation.

Slave (Pico) Code:

#include <Arduino.h>
#include <Wire.h>

#define DATABYTES   6

unsigned long lastLEDToggle = 0;
#define LED_INTERVAL 750
bool LEDstatus = HIGH;

#define I2C_SLAVE_ADDRESS 0x18

MbedI2C myI2C(14, 15);
uint8_t tmpRcvdData[DATABYTES];
uint8_t tmpRcvdDataInc;

uint8_t writeBuffer[DATABYTES] = {0x12};

void initCommunication();
void setupCommunication();

void onReceive(int numBytes);
void onRequest();

void setup() {

  pinMode(LED_BUILTIN, OUTPUT);

  setupCommunication();

}

void loop() {

  if (millis() - lastLEDToggle > LED_INTERVAL) {

    digitalWrite(LED_BUILTIN, LEDstatus);
    LEDstatus = !LEDstatus;

    lastLEDToggle = millis();

  }

}

void setupCommunication() {

  initCommunication();

  myI2C.onReceive(onReceive);
  myI2C.onRequest(onRequest);

}

void initCommunication() {

  myI2C.begin(I2C_SLAVE_ADDRESS);
  myI2C.setClock(800000);

}

void onReceive(int numBytes) {

  tmpRcvdDataInc = 0;

  while (myI2C.available() && tmpRcvdDataInc < DATABYTES) {
    tmpRcvdData[tmpRcvdDataInc] = myI2C.read();
    tmpRcvdDataInc++;
  }

  while (myI2C.available() ) {
    myI2C.read(); // clear buffer
  }

}

void onRequest() {

  myI2C.write(writeBuffer, DATABYTES);

}

What did i try already?

Measurements & Logs

ESP Debug message incl. some serial output i had on master side:

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:10124
load:0x40080400,len:5828
entry 0x400806a8
Requesting Slave
Slave answered 12 0 0 0 0 0
Requesting Slave
Slave answered 12 0 0 0 0 0
Requesting Slave
Slave answered 12 0 0 0 0 0
Requesting Slave
Slave answered 12 0 0 0 0 0
Writing Slave
Requesting Slave
[D][esp32-hal-i2c.c:1353] i2cProcQueue():  Gross Timeout Dead start=0x4e59, end=0x4e8b, =50, max=50 error=1
[E][esp32-hal-i2c.c:318] i2cDumpI2c(): i2c=0x3ffbdbb4
[I][esp32-hal-i2c.c:319] i2cDumpI2c(): dev=0x60013000 date=0x16042000
[I][esp32-hal-i2c.c:321] i2cDumpI2c(): lock=0x3ffb844c
[I][esp32-hal-i2c.c:323] i2cDumpI2c(): num=0
[I][esp32-hal-i2c.c:324] i2cDumpI2c(): mode=1
[I][esp32-hal-i2c.c:325] i2cDumpI2c(): stage=3
[I][esp32-hal-i2c.c:326] i2cDumpI2c(): error=1
[I][esp32-hal-i2c.c:327] i2cDumpI2c(): event=0x3ffb84d4 bits=0
[I][esp32-hal-i2c.c:328] i2cDumpI2c(): intr_handle=0x3ffb8508
[I][esp32-hal-i2c.c:329] i2cDumpI2c(): dq=0x3ffb84b0
[I][esp32-hal-i2c.c:330] i2cDumpI2c(): queueCount=1
[I][esp32-hal-i2c.c:331] i2cDumpI2c(): queuePos=0
[I][esp32-hal-i2c.c:332] i2cDumpI2c(): errorByteCnt=-1
[I][esp32-hal-i2c.c:333] i2cDumpI2c(): errorQueue=0
[I][esp32-hal-i2c.c:334] i2cDumpI2c(): debugFlags=0x00000000
[I][esp32-hal-i2c.c:311] i2cDumpDqData(): Debug Buffer not Enabled
[I][esp32-hal-i2c.c:354] i2cDumpInts(): Debug Buffer not Enabled
 0
Requesting Slave
[I][esp32-hal-i2c.c:1138] i2cProcQueue(): Bus busy, reinit
[W][esp32-hal-i2c.c:1419] i2cCheckLineState(): invalid state sda(21)=0, scl(22)=1
[D][esp32-hal-i2c.c:1427] i2cCheckLineState(): Recovered after 3 Cycles
[D][esp32-hal-i2c.c:1353] i2cProcQueue():  Gross Timeout Dead start=0x52cf, end=0x5301, =50, max=50 error=1
[E][esp32-hal-i2c.c:318] i2cDumpI2c(): i2c=0x3ffbdbb4
[I][esp32-hal-i2c.c:319] i2cDumpI2c(): dev=0x60013000 date=0x16042000
[I][esp32-hal-i2c.c:321] i2cDumpI2c(): lock=0x3ffb844c
[I][esp32-hal-i2c.c:323] i2cDumpI2c(): num=0
[I][esp32-hal-i2c.c:324] i2cDumpI2c(): mode=1
[I][esp32-hal-i2c.c:325] i2cDumpI2c(): stage=3
[I][esp32-hal-i2c.c:326] i2cDumpI2c(): error=1
[I][esp32-hal-i2c.c:327] i2cDumpI2c(): event=0x3ffb84d4 bits=0
[I][esp32-hal-i2c.c:328] i2cDumpI2c(): intr_handle=0x3ffb8508
[I][esp32-hal-i2c.c:329] i2cDumpI2c(): dq=0x3ffb84b0
[I][esp32-hal-i2c.c:330] i2cDumpI2c(): queueCount=1
[I][esp32-hal-i2c.c:331] i2cDumpI2c(): queuePos=0
[I][esp32-hal-i2c.c:332] i2cDumpI2c(): errorByteCnt=-1
[I][esp32-hal-i2c.c:333] i2cDumpI2c(): errorQueue=0
[I][esp32-hal-i2c.c:334] i2cDumpI2c(): debugFlags=0x00000000
[I][esp32-hal-i2c.c:311] i2cDumpDqData(): Debug Buffer not Enabled
[I][esp32-hal-i2c.c:354] i2cDumpInts(): Debug Buffer not Enabled
0
Requesting Slave
[I][esp32-hal-i2c.c:1138] i2cProcQueue(): Bus busy, reinit
[W][esp32-hal-i2c.c:1419] i2cCheckLineState(): invalid state sda(21)=1, scl(22)=0
[D][esp32-hal-i2c.c:1427] i2cCheckLineState(): Recovered after 1 Cycles
[E][esp32-hal-i2c.c:1434] i2cCheckLineState(): Bus Invalid State, TwoWire() Can't init sda=1, scl=0
0
Requesting Slave
[D][esp32-hal-i2c.c:1344] i2cProcQueue():  Busy Timeout start=0x5b36, end=0x5b68, =50, max=50 error=1
[E][esp32-hal-i2c.c:318] i2cDumpI2c(): i2c=0x3ffbdbb4
[I][esp32-hal-i2c.c:319] i2cDumpI2c(): dev=0x60013000 date=0x16042000
[I][esp32-hal-i2c.c:321] i2cDumpI2c(): lock=0x3ffb844c
[I][esp32-hal-i2c.c:323] i2cDumpI2c(): num=0
[I][esp32-hal-i2c.c:324] i2cDumpI2c(): mode=1
[I][esp32-hal-i2c.c:325] i2cDumpI2c(): stage=3
[I][esp32-hal-i2c.c:326] i2cDumpI2c(): error=1
[I][esp32-hal-i2c.c:327] i2cDumpI2c(): event=0x3ffb84d4 bits=0
[I][esp32-hal-i2c.c:328] i2cDumpI2c(): intr_handle=0x3ffb8508
[I][esp32-hal-i2c.c:329] i2cDumpI2c(): dq=0x3ffb84b0
[I][esp32-hal-i2c.c:330] i2cDumpI2c(): queueCount=1
[I][esp32-hal-i2c.c:331] i2cDumpI2c(): queuePos=0
[I][esp32-hal-i2c.c:332] i2cDumpI2c(): errorByteCnt=0
[I][esp32-hal-i2c.c:333] i2cDumpI2c(): errorQueue=0
[I][esp32-hal-i2c.c:334] i2cDumpI2c(): debugFlags=0x00000000
[I][esp32-hal-i2c.c:311] i2cDumpDqData(): Debug Buffer not Enabled
[I][esp32-hal-i2c.c:354] i2cDumpInts(): Debug Buffer not Enabled
[W][esp32-hal-i2c.c:1419] i2cCheckLineState(): invalid state sda(21)=1, scl(22)=0
[D][esp32-hal-i2c.c:1427] i2cCheckLineState(): Recovered after 1 Cycles
[E][esp32-hal-i2c.c:1434] i2cCheckLineState(): Bus Invalid State, TwoWire() Can't init sda=1, scl=0

The I2C can only be reset correctly after first restarting the Pico and then the ESP32.

I also checked the bus with a Logic analyzer:

Annotation 2022-04-07 171810 Annotation 2022-04-07 172026

Blue: SDA / Red: SCL

As promised here: Please help me :) - for me it looks like the I2C implementation has some issue with the I2C drivers of the Pico.

TareObjects commented 1 year ago

Hi.

I am having the same trouble.

My platform is Mr.Earle Philhower version and I am using XIAO PR2040. Both SDA and SCL are pulled up at 4.7K.

The moment master send something, SCL is low. And it freezes without giving any information, so there is no clue to the solution.

waynepiekarski commented 1 year ago

In the past, I've use ATmega328p devices and had problems with the I2C bus getting stuck randomly. However, at some point the Wire library added a setWireTimeout() method, where it will not get stuck in an I2C transaction any more. https://www.arduino.cc/reference/en/language/functions/communication/wire/setwiretimeout/ This isn't perfect because if one of the I2C peripherals gets stuck, you need to power cycle those peripherals to do a full bus recovery. But I found the setWireTImeout() method very useful in ensuring the software doesn't get stuck and you can then do something about it.

However, the RP2040 mbed implementation does not support setWireTimeout yet, and this would be helpful to have. It is an optional Wire feature (see URL above). I've only just started working with RP2040 and noticed this missing when I tried to port my code from ATmega328p.

zmechu commented 6 months ago

Hello @waynepiekarski - how about your porting setWireTimeout() into RP2040. I'm also very interested in that function - it is very useful for handling problems over I2C communication.

waynepiekarski commented 6 months ago

Hello @waynepiekarski - how about your porting setWireTimeout() into RP2040. I'm also very interested in that function - it is very useful for handling problems over I2C communication.

I didn't do any implementation work for setWireTimeout(). It looks like the underlying mbed library used on the RP2040 doesn't support timeouts either last time I checked.

thinksilicon commented 3 months ago

Hey, I opened an isssue with arduino-esp32 thinking that it was a problem over there. After some debugging it seems like it is a problem with ArduinoCore-mbed.

Any chance this could be looked into?