SPI is much easier to write correctly, and pretty much only has the extra wire (usually not a problem) and the phase polarity issues as a negative point.
SPI is much easier to write correctly, and pretty much only has the extra wire (usually not a problem) and the phase polarity issues as a negative point.
* No way to safely bring the bus back to idle from mid-transaction. By "safely" I mean not accidentally transmit an extra byte which could e.g overwrite EEPROM memory. There is no combination of transitioning the 2-wire bus from an arbitrary state back to idle which works in the general case. If it's important, you end up adding a dedicated reset wire.
* No safe, universal way to bring the bus from tristate, or low, to pulled-up. There are designs where this ends up being necessary. You end up with a spurious transaction, which may wedge the bus, or having to add a reset wire or buffer.
* The protocol is extremely hostile to devices with non-zero latency response. It's designed as a simple "Address this register and then immediately read out a byte in the next clock cycle". Works great for trivial devices, but for anything more complex it ends up needing a bank of register acting as a "proxy" to access the higher latency side of the chip. At this point I2C is an awesomely bad choice, but people keep doing this, because it's so universal.
These are great points, and I'll add a note about them in the article. Thanks!
> The protocol is extremely hostile to devices with non-zero latency response. [...]
Technically, this is what clock-stretching is for. In practice, you're right that complex devices implemented proxy registers. I've seen it on DP->MIPI bridges for example.
Why would you want to do that? Not having the ability to do this is part of the contract. If you design your device such that it always completes the transaction, then there should be no problem, unless one of the devices on the bus doesn't play fair but then you have a different problem.
For example, you get a kernel panic, or soft-reset for some reason. When you recover, you now have a bus in an unknown state, possibly mid-transaction, and if you pick the wrong order in which to bring the bus back to idle, you might wedge it or accidentally cause a side-effect (e.g overwrite a byte in an EEPROM).
I mitigated the issues by doing I2C resets on every read (see the Analog Devices I2C reset documentation linked in another comment) and reading multiple times to filter out any spurious bit errors.
Other than that I just have my fingers crossed that a bit doesn't accidentally flip and overwrite something in the RTC. Or that the bus somehow gets stuck driven between sleeps and drains the battery early. sigh SPI would have been so much nicer.
(1) I mean, okay, the massive lithium battery exploding is perhaps the most nerve racking component, but I2C is a close second.
You may think of the extra pins as not a problem, but on every product I've worked on we've been pin-limited on the MCU.
[1] https://www.maximintegrated.com/en/design/technical-document...
Obviously that requires more hardware on the board tho'.
In practice, I don't see that many chips offering 7 bits of address configuration. You buy a chip, it has a hardwired address. Maybe a pin or two for selecting another address.
I agree it's still lower pin count than SPI, but realistically you don't get anywhere near 127 devices.
I've also seen JTAG as an alternative to I2C and SPI. JTAG can be part of normal operation.
It's quite annoying because one stuck device can block all other communication on the bus. Let's hope your reset pin (if you have it), isn't on an i2c expander on the same i2c bus.
I'm not sure that I buy that. A daisy chained SPI bus is a rats nest of configuration hassle and basically impossible to debug. You're trading hardware robustness for software complexity, and that's not always a win.
And as far as hardware messes: SPI doesn't synchronize anything beyond the bit level (modulo an out of band chip enable or reset, of course, which would work to "fix" an I2C device too), making shift/offset bugs a real danger.
Board-level digital interfacing is just hard. That's why we do it as little as possible and put everything on the SoC these days.
I try to avoid it as much as possible. If I need a peripheral, I prefer SPI, if I need a bus for connecting devices I control, I use CAN. If I'm connecting two systems together, a UART.
Granted, that's not easy to do if your peripherals have no reset pin or their power source cannot be switched by the bus master.
Would your problems go away if you had I2C plus a dedicated reset pin on all peripherals?
One more advantage of SPI that I see is higher data rates, full-duplex communication etc..