Clock stretching exists to provide a simple solution to a simple problem. However, the actual results are endless confusion and issues dealing with real world details when trying to implement a reliable I2C interface to actually support clock stretching.
Imagine the general scenario of a controller (Arduino board, CircuitPython board, Raspberry Pi, etc.) talking to some I2C target device, like the BNO055 9-DOF sensor.
The controller, over I2C, requests sensor values and expects the response to be the requested values. Pretty simple. But what if the sensor needs some time to obtain those values before they are available to be sent back out over I2C? Remember, the controller is what is normally driving the clock signal (SCL). So if the controller keeps blindly driving the clock after the initial request, and the sensor is not ready, what is the target device to do when the controller just keeps clocking?
Enter clock stretching.
The basic idea with clock stretching is the target (not the controller) takes over the clock signal and holds it low as way to indicate "please wait...i'm thinking...". As long as the target holds the clock line low, it is in control of the I2C bus. The host controller must accommodate this.
As simple as the concept is, numerous issues arise once you start working on implementing clock stretching and figuring out the details. This is mainly true on the host controller side.
When exactly should the controller give up control of SCL and check for stretching? Each clock cycle? Before ACK/NACKing? After each byte? After a stop?
How long should the controller wait for the stretch to end? 1 millisecond? A couple of days? Five years?
The standard is not specific. It only describes clock stretching in a general way. It doesn't say anything like "and yet shall not exceed 500 us of total clock stretch time".
Here is the entirety of the I2C clock stretch specification:
So target devices can potentially do anything, and controller implementations need to accommodate that. This can be non-trivial.
A very prominent example of this exists on the Raspberry Pi. Raspberry Pi's are all known to not handle I2C clock stretching well. Further, this is a hardware issue with the I2C implementation in the Broadcom BCM28xx chips used on those models of Raspberry Pi. So it essentially cannot be fixed: it's baked into the silicon. At the time of this guide writing, the issue is almost 10 years old. Here are some (potentially dated) discussions on this issue.
- Raspberry Pi I2C clock-stretching bug - Written 2013-08-17, this blog post is the most often cited and linked. It contains lots of technical details.
- RPI forums I2C clock stretching - Discussion circa 2012 about this issue. There are 70 posts to the thread!
- I2C Broadcom bug workaround - An old issue thread from 2013. There are 73 posts to the thread!
- I2C clock-stretching bug - A more recent issue thread specific to the Pi 4. The saga continues!
If you end up in a situation where a target device (sensor breakout, etc.) is clock stretching and the host controller (Raspberry Pi, etc.) is having issues coping, this can be very annoying. The target device creators will say the host is not handling clock stretching properly. The writers of the host I2C implementation will say the target device is clock stretching in a non-standard way. And you are left with a host/target combo that just won't work. So solutions tend to be somehwhat hack-ish.
Here are some commonly used approaches.
Slow Down The Clock
This is the simplest and easiest approach. The basic idea is to make the I2C clock speed slow enough that any potential clock stretching gets buried within the clock pulses. So, from the point of view of the host controller, it never sees the actual clock stretch. Or at least not all of it.
For example, on a Raspberry Pi, this is done by manually setting the I2C clock speed in /boot/config.txt.
The initial recommended slow down is a factor of 10! From 100kHz to 10kHz. However, unless you need to read from the target device blazingly fast, this should be fine.
Use Software I2C
Ideally, the majority of dealing with I2C can be left to a dedicated I2C hardware peripheral. For example, the peripheral will watch for an address match - without any code being executed. This is the generally preferred approach. But, by their nature, I2C hardware peripherals are fairly fixed entities. If they have quirks - those are baked in (see the Raspberry Pi example above).
By contrast, a purely software I2C implementation (using two regular GPIO pins and code that emulates an I2C transaction) can be modified and updated - since it's just code.
On a Raspberry Pi, software I2C can be enabled by using the i2c-gpio device tree overlay.
This is more low level and only an option if you're actually writing I2C firmware on a controller. If the hardware I2C peripheral on the controller has such an ability, then try increasing it. This is easier for software implementations, since a timeout will likely be part of the base code.
Here's an example related to CircuitPython:
As another example, on the BCM2711 chip used on the Pi 4, the Broadcom Serial Control (BSC) peripheral has a
CLKT register with a
TOUT setting. From the datasheet:
Source A Different Chip
If all else fails, consider simply using an entirely different chip. Luckily there is not just one I2C accelerometer, temperature, humidity, etc. sensor option. If there is no reason to focus on a specific device (do you really need a BNO055?), and that device is causing grief with its clock stretching, consider using a different chip.
Here's a list of chips that are known to be problematic: