Raspberry Pi I2C Bus Timing vs. BNO055 Clock Stretching

An Adafruit BNO055 connected to a Raspberry Pi 3 I2C bus, despite knowing it won’t work:

I2C 100kHz - BNO055 SCL 1 mA-div - B7 Rd error
I2C 100kHz – BNO055 SCL 1 mA-div – B7 Rd error

The I2C bus ticks along at 100 kHz (nominal) = 62.5 kHz (actual), as described a while ago.

The three digital traces along the bottom are D0 = SCL, D1 = SDA, and D2 = trigger raised when the Python program detects an error.

The upper trace shows the SCL current (1 mA/div) between the Pi and the BNO055, as described yesterday, with the latter stretching the clock whenever the current goes negative.

The fourth burst is the BNO055 sending the chip’s temperature in response to a simple request:


temp_c = bno.read_temp()

A closer look at the last transaction:

I2C 100kHz - BNO055 SCL 1 mA-div - B7 Rd error - SCL glitch
I2C 100kHz – BNO055 SCL 1 mA-div – B7 Rd error – SCL glitch

I commend to your attention a useful tutorial on I2C bus protocols / transactions / signalling.

Over on the left, the BNO055 has SCL held low (-2 mA) during the ACK phase of the previous byte. The first upward SCL edge marks the ACK, with the BNO055 holding SDA low during the edge until the Pi drops SCL.

The BNO releases SDA when SCL goes low again, whereupon SDA goes high, exactly as it should.

Now, things gets ugly.

The -1 mA current on SCL shows both the Pi (+1 mA) and the BNO (-2 mA) are pulling SCL low.  The BNO is clock-stretching after the ACK, which the Pi can’t handle.

The Pi seems to think the rising edge of SCL occurs when it stops pulling SCL down, at the point where the SCL current goes from -1 mA to -2 mA, halfway though the high SDA pulse. It reads SDA at that point and receives an incorrect binary 1 bit from SDA, because the BNO hasn’t yet seen a rising SCL edge.

The SCL rising edge occurs when the BNO055 releases SCL and produces the SCL sliver.

Here’s a very close look at the sliver:

I2C 100kHz - BNO055 SCL 1 mA-div - B7 Rd error - Pi SCL mistiming
I2C 100kHz – BNO055 SCL 1 mA-div – B7 Rd error – Pi SCL mistiming

The two vertical cursors bracketing the sliver mark the 16 µs SCL timing produced by the Pi’s SCL clock, which is not the 10 µs you’d expect at 100 kHz.

The right cursor sits on the next rising edge, so the left cursor marks where SCL should rise: exactly where the Pi releases its hold on SCL and expects it to pop up.

That error happens about 6% of the time, producing a chip temperature 128 °C higher than reality. The other 94% of the reads either work correctly or, perhaps, encounter a bogus SDA state coincidentally delivering a binary zero that looks good.

Here’s a sample of a “good” read:

I2C 100kHz - BNO055 SCL 1 mA-div - B7 Rd OK - long SCL high
I2C 100kHz – BNO055 SCL 1 mA-div – B7 Rd OK – long SCL high

The stop bit is now off to the right, so the last rising SCL edge is the ACK. Counting leftward eight edges from the ACK, the left cursor marks where the SCL edge should be for bit B7. Instead it occurs instantly after the BNO releases its hold on SCL, shown by the transition from -2 mA to 0 mA. I don’t know the setup and hold times for the Pi’s I2C port, but 250-ish ns seem aggressive; I think the data transitions should happen close to the down-going SCL edges.

Running the I2C bus at 200 kHz seems to work fine, but it still has the same aggressive SDA-to-SCL setup time. Here’s a close look at the same situation as in the previous photo, with SCL set for 200 kHz:

I2C 200kHz - BNO055 SCL 1 mA-div - Rd OK - 250 ns SDA-SCL setup
I2C 200kHz – BNO055 SCL 1 mA-div – Rd OK – 250 ns SDA-SCL setup

This being a read, the BNO sets SDA to whatever it should be, then releases its clock-stretching hold on SCL about 200 ns later. The Pi raises SCL shortly thereafter and it apparently Just Works. Maybe it’s Good Enough to be consistent, but I’d like to run more tests before trusting it.

Anyhow, that’s how the Pi’s I2C hardware doesn’t handle a chip using clock stretching.

One thought on “Raspberry Pi I2C Bus Timing vs. BNO055 Clock Stretching

Comments are closed.