Skip to content

I2C fix READ of zero bytes hardware hang #2294

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from

Conversation

stickbreaker
Copy link
Contributor

The i2c peripheral will hang if a READ request is issued with a zero data length. The peripheral
drops into a continuous timeout interrupt response. The STOP command can not be set out to the I2C
bus. The SLAVE device correctly ACK'd the address byte, with READ bit set, it has control of the SDA
pin. The ESP32 send out the next SCL HIGH pulse but, since the SLAVE is in READ Mode, and the First
bit it is sending happened to be a ZERO, the ESP32 cannot send the STOP. When it releases SDA during
the SCL HIGH, the pin does not change state. The pin stays low because the SLAVE is outputting a LOW!
The ESP32 drops into a permanent WAIT state waiting for SDA to go HIGH (the STOP).

esp32-hal-i2c.c

  • add databuff length checks to i2cRead() and i2cWrite()

waiting for the ESP32 peripheral to send clock pulses on the SCL pin.

me-no-dev and others added 11 commits December 29, 2018 00:15
**esp32-hal-i2c.c**
* add callback for cpu frequency changes
* adjust fifo thresholds based on cpu frequency and i2c bus frequency
* reduce i2c bus frequency if differential is too small
**Wire.h**
* version to 1.1.0
The i2c peripheral will hang if a READ request is issued with a zero data length.  The peripheral
drops into a continuous timeout interrupt response.  The STOP command can not be set out to the I2C
bus. The SLAVE device correctly ACK'd the address byte, with READ bit set, it has control of the SDA
 pin.  The ESP32 send out the next SCL HIGH pulse but, since the SLAVE is in READ Mode, and the First
bit it is sending happened to be a ZERO, the ESP32 cannot send the STOP.  When it releases SDA during
the SCL HIGH, the pin does not change state.  The pin stays low because the SLAVE is outputing a LOW!
The ESP32 drops into a perminent WAIT state waiting for SDA to go HIGH (the STOP).

**esp32-hal-i2c.c**
* add databuff length checks to `i2cRead()` and `i2cWrite()`

waiting for the ESP32 peripheral to send clock pulses on the SCL pin.
@me-no-dev
Copy link
Member

too many changes in this PR?

@stickbreaker
Copy link
Contributor Author

I just fetched it against main i saw you merge cpu-clock-integration in, why are all your changes showing as new?

I'll delete and manually copy.

@@ -1609,6 +1609,9 @@ i2c_err_t i2cFlush(i2c_t * i2c)
}

i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis){
if((i2c==NULL)||((size>0)&&(buff==NULL))) { // need to have location to store requested data
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can possibly be simplified to:

if(!i2c||(size && !buff))

NULL is defined as zero..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@atanisoft yes it could be obfuscated down to your concise example. I like to write code such that assumptions are minimized. I have found that I make more mistakes when I try to rely on assumptions.
The !i2c starts off wrong in my mind. !(not) starts right off describing a boolean operation, then up comes a pointer? So, my mind says "if not a pointer" What? What's NOT a pointer? Oh!, this construct means "if the value of i2c is not zero" well why don't you just write it that way, a few extra characters in a source file don't mean squat when the hard drive is measured in terabytes.

Chuck.

@johannruch
Copy link

hi,
my project is an ESP32 + MPU-9250.
with the version from 2018-12-02, the I2C connection was hanging. now with the newest from yesterday, no hangs was detected.
but with the new version i noticed the following issue. readings of registers from the MPU-9250 over I2C are slower!
2018-12-02: reading 15 bytes ~7ms
2019-01-13: reading 16 bytes ~17ms
the issue can be reproduced with the two versions.

regards, johann

@stickbreaker
Copy link
Contributor Author

@johannruch Don't know of anything that would cause this slowdown, What clock speed are you setting the I2c bus too?

Chuck.

@johannruch
Copy link

@chuck: i define 400kHz:
#define I2C_BUS_FREQUENCY 400 // [kHz]
bool i2cReady = Wire.begin(SDA, SCL, I2C_BUS_FREQUENCY);

i know there are other changes since dec 2018, but the nearest item was I2C :-)
johann

@stickbreaker
Copy link
Contributor Author

@johannruch i just uploaded a readme on i2c debugging #2325 try displaying the interrupts, it will show you exact timing.

@johannruch
Copy link

@chuck: i'm impressed! your hint about the i2c bus frequency let me thinking about 400 or 40000L for 400kHz. a short look in esp32-hal-i2c.c gaves the answer, it has to be 40000L!
now the i2c speed is back again (or better than before) also with the new libraries.
but why has it worked with 400 and the old library?

thanks for your (and others) work and support!
johann

@stickbreaker
Copy link
Contributor Author

The esp32 @ 240mhz has a minimum i2c freq of 10khz. New code sets freq at nearest edge when out of bounds.

  • if below low bound then freq equal low bound.

  • If above high bound then freq equal high bound.

  • old code set freq at 100khz if out of bound.

Chuck.

@stickbreaker
Copy link
Contributor Author

Make sure you use enough zeros.
40000 != 400000.

@stickbreaker stickbreaker deleted the clock1 branch January 16, 2019 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants