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

I2C clock stretching not implemented at all occurrences #5340

Closed
maarten-pennings opened this issue Nov 14, 2018 · 17 comments
Closed

I2C clock stretching not implemented at all occurrences #5340

maarten-pennings opened this issue Nov 14, 2018 · 17 comments
Labels
Milestone

Comments

@maarten-pennings
Copy link

maarten-pennings commented Nov 14, 2018

When using as I2C slave the CCS811 gas sensor from ams, e.g. using this board, there are communication errors. These are due to the CCS811 doing clock stretching.

The driver file core_esp8266_si2c.c "Software I2C library for esp8266" implements clock stretching. However, at two places clock stretching code is missing. Functions twi_writeTo() and twi_readFrom() set the clock line high using SCL_HIGH(), without checking whether SCL really goes high. All other places in the driver that use SCL_HIGH() do have the clock stretch check.

In both functions, one line should be added (here and here):

  while(SDA_READ() == 0 && (i++) < 10){
    SCL_LOW();
    twi_delay(twi_dcount);
    SCL_HIGH();

    unsigned int t=0; while(SCL_READ()==0 && (t++)<twi_clockStretchLimit); // Clock stretching

    twi_delay(twi_dcount);
  }

See this screenshot for a diff.

@devyte
Copy link
Collaborator

devyte commented Nov 15, 2018

@maarten-pennings I don't have a way to test this. Of note, there are currently no I2C examples or tests. 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, and one sends a hello world to the other. Something like that could then be used as a starting point to test functionality like the issue you describe here.

@maarten-pennings
Copy link
Author

Thanks for the reaction.

I understand what you are asking, but if course this is real work, so I don't know how fast I can deliver this. I need to think about it.

I was hoping that a code review would suffice: most CLK_HIGH() calls are followed by a wait till CLK is really high, except two. That does not look right.

@devyte
Copy link
Collaborator

devyte commented Nov 19, 2018

@suculent @mrwgx3 @bjoham You've contributed to our I2C. Could one of you please help out with assessing the changes proposed here, and with my request for a Wire master/slave example with 2 ESPs?

@devyte devyte added help wanted Help needed from the community waiting for feedback Waiting on additional info. If it's not received, the issue may be closed. labels Nov 19, 2018
@suculent
Copy link
Contributor

suculent commented Nov 19, 2018 via email

@devyte
Copy link
Collaborator

devyte commented Nov 20, 2018

@suculent that would make a great example.
Out of curiosity, why the crc and retry? Did you experience communication errors?

@suculent
Copy link
Contributor

suculent commented Nov 20, 2018 via email

@suculent
Copy link
Contributor

suculent commented Nov 21, 2018

Here is working version of the I2C master/slave example with two ESPs. Please review create issues in that repository, so I can clean it, style it and get ready for real PR (in case it will work to someone else than me :o).

https://github.com/suculent/esp8266-I2C-slave

Unfortunately I don't have CCS811 or anything else I'd now does clock-stretching at all to test the suggestion above. I can just confirm that those changes above did not break my generic communication and it's indeed pure copy from 8 another places in the same source file.

@devyte
Copy link
Collaborator

devyte commented Nov 21, 2018

@suculent it looks very good! There's a lot of code duplicity between the master and slave sketches, but it's an example, so I'm ok with that.
My other comments are specific to code style and conventions. I think we can discuss that directly in the PR.
The next step after this would be to figure out how to mock clock stretching in the slave to test the code change proposed by @maarten-pennings here.

@suculent
Copy link
Contributor

suculent commented Nov 21, 2018 via email

@suculent
Copy link
Contributor

Clock-stretch fix as proposed by @maarten-pennings: #5362
My humble master-slave I2C test converted to example: #5360

@suculent
Copy link
Contributor

suculent commented Nov 21, 2018

The clock-stretching can be simulated/performed from slave side by holding the clock LOW. What the fix does is that it reads the clock and waits until it is low in its two cases as highlighted in copy-paste below. It serves the purpose where returning value (e.g. measurement) takes more time than I2C clock allows.

Source

In an I2C communication, the master device determines the clock speed. The I2C bus provides an explicit clock signal which relieves master and slave from synchronizing exactly to a predefined baud rate.
However, there are situations where an I2C slave is not able to co-operate with the clock speed given by the master and needs to slow down a little. This is done by a mechanism referred to as clock stretching.
An I2C slave is allowed to hold down the clock if it needs to reduce the bus speed. The master, on the other hand, is required to read back the clock signal after releasing it to the high state and wait until the line has actually gone high.

@maarten-pennings
Copy link
Author

This is the official specification of i2c:

https://www.nxp.com/docs/en/user-guide/UM10204.pdf

"Generation of clock signals on the I2C-bus is always the responsibility of master devices; each master generates its own clock signals when transferring data on the bus. Bus clock signals from a master can only be altered when they are stretched by a slow slave device holding down the clock" ... and several other quotes

@suculent
Copy link
Contributor

suculent commented Nov 22, 2018

Maarten, your input has been already merged to the master branch and will be present in next release. We're following up with the I2C peer-to-peer communication example, which still needs some cleanup after being thoroughly reviewed by @devyte, which must have been hell of a job.

Your input about clock stretching points me the way that there's no sofware-only way to test this. It's peripheral-dependent and can be tested only by observation, hardly by simple automation.

@devyte devyte added type: bug staged-for-release and removed waiting for feedback Waiting on additional info. If it's not received, the issue may be closed. labels Nov 23, 2018
@devyte
Copy link
Collaborator

devyte commented Nov 23, 2018

Closing since #5362 is already merged.
The master-slave example PR covers #5288 , and will be handled in that context.

@maarten-pennings
Copy link
Author

Is it allowed to add comments when an issue is closed?
I'm getting a bit more familiar with the code, and now start to have doubts.

Yes, a SCL high by master could be forced down by the slave (clock stretch).
And yes that check is missing in the final while loop in both twi_writeTo and twi_readFrom.
So on first sight my fix makes sense.

However, I now think this while loop in itself is already an exception handler. It checks if SDA is low at the end of an "I2C segment" (I use the word "segment" for a part of an I2C transaction between START and either STOP or a repeated START). If SDA is low, the SCL is pulsed up to 10 times. This looks like the bus clear in the I2C spec https://www.nxp.com/docs/en/user-guide/UM10204.pdf (although that one specs 9 pulses, not 10).

The fact that my "fix" does help is dubious. I'm pretty sure that the I2C bus is not blocked (that is a very rare situation), so the fact that the master runs the bus clear loop is a bad sign. That the bus clear loop needs clock stretch wait code is even more dubious.

What is also dubious is that just before this exception handler loop is the generation of a STOP condition.

  if(sendStop) twi_write_stop();

However, this generation is conditional. So, in the case the STOP is not generated, the I2C transaction is not yet over, so the slave is allowed to pull SDA low. This would not constitute a bus block, so the loop should not run.

@devyte
Copy link
Collaborator

devyte commented Dec 20, 2018

Yes, comments are allowed after closing!
I can't answer your comment, because I'm not really familiar with the protocol specifics. However, someone else might. If you find a specific problem, feel free to open a new issue for discussion.
In the meantime, please continue investigating!
Also, there are other I2C libs out there, you could compare behavior.

@maarten-pennings
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants