Skip to content
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

Double I2C read in one transaction skips a clock pulse #5528

Closed
maarten-pennings opened this issue Dec 19, 2018 · 15 comments · Fixed by #6654
Closed

Double I2C read in one transaction skips a clock pulse #5528

maarten-pennings opened this issue Dec 19, 2018 · 15 comments · Fixed by #6654
Labels
component: core good first issue If you want to help, this is is a good place to start help wanted Help needed from the community type: bug waiting for feedback Waiting on additional info. If it's not received, the issue may be closed.
Milestone

Comments

@maarten-pennings
Copy link

I was writing a clock stretch injector for #5340 and then I stumbled on another error in the driver file core_esp8266_si2c.c "Software I2C library for esp8266" function twi_readFrom.

The problem occurs when issuing two reads in one I2C transaction (i.e. with a repeated START in between). In this scenario, one clock pulse is not generated by the master: the clock pulse for the 9th bit (the ack bit) just before the repeated start. I tested this with the latest release 2_5_0_BETA2 (but it also
fails on 2.4.2).

Unfortunately, I don't know how to write a single ESP8266 program that detects this. Instead, I took an arbitrary I2C slave, and hooked a logic analyzer to the I2C bus

This is the test code running on the ESP8266 (see full sketch attached at the end):

  // From slave, read 2 bytes, then repeated START
  int res1=Wire.requestFrom(SLAVE_ADDR,(size_t)2,false); // No STOP yet!
 
  // From slave, read 2 bytes, then generate STOP condition
  int res2=Wire.requestFrom(SLAVE_ADDR,(size_t)2,true);  

and this logic analyzer capture shows the problem:

missingacknack

I tried to fix the code, the following seems to work (but I have not thoroughly tested it):

suggestedfix-missingacknack

This leads to the following capture of the logic analyser:

fixedacknack

Here is the test sketch:
ESP8266-i2c2xread.zip

@devyte devyte added type: bug help wanted Help needed from the community component: core good first issue If you want to help, this is is a good place to start labels Dec 19, 2018
@devyte
Copy link
Collaborator

devyte commented Dec 19, 2018

@maarten-pennings Thank you for the analysis and for offering a fix!
I believe the current maintainers aren't involved with I2C, so help is needed to test this.

@maarten-pennings
Copy link
Author

I'm not yet familiar enough with the code, so I'm not sure if the fix works in all cases.
This was my attempt to locate the place in the code where something is wrong.

@maarten-pennings
Copy link
Author

By the way, I added a comment to #5340, but that was already closed. Is my comment lost, should I reopen the issue, create a new issue? Sorry - new to this process.

@maarten-pennings
Copy link
Author

@devyte
Copy link
Collaborator

devyte commented Dec 22, 2018

@maarten-pennings ok, let's try to resolve here.
With just reading your description in the above link, and without actually testing (I need an afternoon of peace and quiet to set up the whole thing, which is near impossible these days), what you did is precisely what I envisioned: a master-slave set up, where the slave sketch can do a configurable clock stretch, thereby testing the master sketch.
I would therefore very much like to get the sketches into the core either as examples or as device tests. We could start as examples, then later derive formal device tests from them to run as part of the device test suite.
I would also like to get the docs you wrote into the core.
However, I think this should come after #5464 to get the Wire lib straight first.
Setting milestone for 2.5.0. There's time, but not too much.

@devyte devyte added this to the 2.5.0 milestone Dec 22, 2018
@maarten-pennings
Copy link
Author

@devyte your comment "Is there any chance you can implement something that doesn't depend on specialized hardware? Example: two ESPs where one is master and the other is slave," did make sense to me. However I could not commit to developing that back then. And on hindsight, it was way more work than expected :-)

Please have a look (when you find your afternoon of peace). My I2C-tool suffers from long interrupt latency and from something I don't understand; the first couple of interrupts are missed, but once it is running it is fine (see the for loop in i2c_init).

Let me know if you have question. And let me know if I can be of any assistance.

@maarten-pennings
Copy link
Author

maarten-pennings commented Dec 27, 2018

@devyte
Copy link
Collaborator

devyte commented Feb 4, 2019

The primary issue here has a proposed solution, but testing it depends on having a testbench, i.e.: a robust master-slave example, and that's not yet done. Pushing back.

@maarten-pennings
Copy link
Author

Hi, I'm confused. I wrote a test framework (https://github.com/maarten-pennings/I2C-tool), you seemed positive about this ("what you did is precisely what I envisioned: a master-slave set up, where the slave sketch can do a configurable clock stretch"), but now you "push back".

What do you want me to do?

@devyte
Copy link
Collaborator

devyte commented Feb 4, 2019

@maarten-pennings I was waiting for a PR with the changes proposed in "how to fix". The two sketches should be added as examples in the Wire lib.
I was also expecting to have #5464 to test your clock stretching on, but I couldn't get those to work.
Can you make a PR with your work here?

@maarten-pennings
Copy link
Author

I submitted 5718. Is this what you wanted?

@devyte
Copy link
Collaborator

devyte commented Feb 5, 2019

Ok, as I said above, let's resolve in this issue.
Can you please make a Pull Request with your proposed changes to twi* and Wire.cpp, as well as the two sketches as examples in Wire/examples/ ?

@andrethomas
Copy link

@maarten-pennings Are you going to make a PR for the changes you posted at https://github.com/maarten-pennings/I2C-tool/blob/master/I2Ctest8266/README.md#how-to-fix and the examples as requested by @devyte ?

@d-a-v d-a-v added the waiting for feedback Waiting on additional info. If it's not received, the issue may be closed. label Sep 26, 2019
TD-er added a commit to TD-er/Arduino that referenced this issue Oct 1, 2019
@TD-er
Copy link
Contributor

TD-er commented Oct 1, 2019

Since all the hard work of @maarten-pennings has not resulted in a pull request, I made one. See #6579
Maybe the changes in there can be discussed and/or reviewed by someone with a proper logic analyzer and knowledge of I2C.

@maarten-pennings
Copy link
Author

Thanks @TD-er for picking this up. I already forgot about it...

TD-er added a commit to TD-er/Arduino that referenced this issue Oct 14, 2019
TD-er added a commit to TD-er/Arduino that referenced this issue Oct 19, 2019
See esp8266#5528  and the more elaborate [description](https://github.com/maarten-pennings/I2C-tool/blob/master/I2Ctest8266/README.md#how-to-fix)
where @maarten-pennings did all the hard work, but as far as I could see, no PR was made.

I also noticed some code duplication, which I moved to separate functions.

According to [this documentation on I2C clock stretching](https://www.i2c-bus.org/clock-stretching/) it is not allowed to have some slave keep the clock low during transmission of a byte, only after an ack.
So that's why it is not done in the while loop.
But I wondered if there should be an extra delay between the sequence of pulses and the extra added clock "valley". See my comment in the code.

Fixes esp8266#5528
earlephilhower pushed a commit that referenced this issue Oct 27, 2019
* Double I2C read in one transaction skips a clock pulse (#5528)

See #5528  and the more elaborate [description](https://github.com/maarten-pennings/I2C-tool/blob/master/I2Ctest8266/README.md#how-to-fix)
where @maarten-pennings did all the hard work, but as far as I could see, no PR was made.

I also noticed some code duplication, which I moved to separate functions.

According to [this documentation on I2C clock stretching](https://www.i2c-bus.org/clock-stretching/) it is not allowed to have some slave keep the clock low during transmission of a byte, only after an ack.
So that's why it is not done in the while loop.
But I wondered if there should be an extra delay between the sequence of pulses and the extra added clock "valley". See my comment in the code.

Fixes #5528

* [I2C] Restore clock stretch functionality during transfer

As requested here: https://github.com/esp8266/Arduino/pull/6654/files/8357a8c644244096cce1df30acd660c35d24a0e4#r339310509

* [I2C] Move duplicated clock stretch call to twi_scl_valley function
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: core good first issue If you want to help, this is is a good place to start help wanted Help needed from the community type: bug waiting for feedback Waiting on additional info. If it's not received, the issue may be closed.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants