I2C on RPi
I recently built an access-control-system, which makes it possible to open our local hack(er)space without a physical key. The core of the system is a Raspberry Pi v2 running Debian. Connected to the RPi’s GPIOs is a big status switch, which is used to announce the hackspace status (open / closed).
More recently it got a couple of MCP23017 I²C port expanders with a couple of bell buttons, a number keypad, door openers, reed switches and other door control related stuff. Additionally I added an Atmel AVR ATTiny85 microcontroller on the I²C port, which is used to drive a couple of WS2812b LEDs.
That setup worked quite well for a few weeks apart from a couple of I²C bus
errors in dmesg: i2c-bcm2835 3f804000.i2c: i2c transfer failed: 100
This error appeared more often recently and makes the system unusable, since button pressed can no longer be detected. I had a look at the RPi’s kernel driver and found a note about I²C clock stretching being more or less broken on that piece of hardware:
$ grep -C2 rpi-i2c-bug drivers/i2c/busses/i2c-bcm2835.c
/*
- This HW was reported to have problems with clock stretching:
- http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html
- https://www.raspberrypi.org/forums/viewtopic.php?p=146272 */
Unfortunately using clock stretching is used by my attiny85 code to properly process the I²C information. There are a couple of possible solutions:
- Try to make the attiny85 code avoid using clock stretching
- Try to avoid the Raspberry Pi’s I2C master and use another one
- Replace the attiny85 with something else
The obvious solution would be 1. On the attiny85 USI is responsible for the I2C communication. Once the master pulls the clock line to low, the USI module will keep it low. It will be free’d in the interrupt routine. Atmel provides a paper (Atmel AVR290), which describes how to avoid clock stretching: the interrupt handler must be processed fast enough.
With this being anoying to debug and us planning to add more (atmel) microcontrollers to the I2C bus, this solution is not stable enough for us.
The second variant requires some other I2C master. There are different variants, that could be implemented:
- I recently built an USB to I2C adapter for easy debugging of I2C slave devices. It costs < 10€. Referencing the I2C master from DT is anoying, though.
- NXP has a chip (SC18IS600), which acts as a bus bridge from SPI to I2C. Unfortunately the mainline kernel does not have a driver for it at the moment. Alternatively Cypress provides a similar chip (CP2120), but that one is harder to acquire and only comes in a QFN package.
- We may use a bitbanging I2C master via the Raspberry Pi’s GPIOs and the kernel GPIO I2C driver. That costs quite some processing power, though.
Last but not least we could just replace the attiny85 / avoid using I2C with it. Since we actually plan to add even more Atmel based chips, thats not a good solution either. As a result the planned fix is to acquire NXP’s SC18IS600, write a kernel driver and have a proper I2C interface on the Raspberry Pi.