Page 1 of 1

Bit-banged I²C kernel driver

Posted: Tue Apr 16, 2013 10:10 am
by kadamski
I've made a handy kernel module for everybody wanting to use bit-banged host instead (or in addition to) hardware one. It's like a wrapper for i2c-gpio kernel built-in module that let you dynamically create and remove i2c hosts on different gpio pins. It can be handy if you experience problems with hardware i2c clock stretching or you need more i2c hosts for some reason.

The code and some instructions can be found on my github. You need a custom kernel (with CONFIG_I2C_GPIO enabled) in order to use it. It got some limited testing so it should work but of course I don't guarantee anything.

Anybody interested in testing this?

Re: Bit-banged I²C kernel driver

Posted: Mon Apr 29, 2013 11:47 am
by hmitman
Hey there,

I am quite new to working with the RPi, with compiling and Linux. Last week I started testing your module and it seems to work so far. Though, adding new busses is only possible for some of the GPIOS and I could not figure out, according to which rules this happens. Do you know anything about restrictions here? (Message in case of failure: bash: echo: write error: File exists - the GPIOS seem to have other functions, but the pin explanation does not clearify the problem.)
In addition, is there a way to load the module right after booting? And maybe also to add a new bus automatically after booting?

Thanks for your help and also thanks for your work - this was exactly what I was looking for!

Best regards


Re: Bit-banged I²C kernel driver

Posted: Tue Apr 30, 2013 6:11 pm
by kadamski
First the easiest question - in order to create the bus on system startup you can add insmod command to /etc/rc.local file. If you need some more busses, add apriopriate echo lines too.

As for the restrictions - the only restrictions that I coded in the module is that you can't use pin 5 or 6. Using any of them on my board resulted in disconnecting ethernet module (if you find any other pin numbers that crash anything and you think that they should be blacklisted too, please tell me about it). Any other pin, providing it is not used by other modules and is not used by any other busses, should be working ok. If you wan't to get more information about the problem you can use dmesg command and look for the messages about i2c-gpio and i2c-gpio-param modules.

Also note that you should use broadcom pin numbering, not header numbers. This means that only some of the pins will be usable at all. Look for GPIO X (where X is the number you should use) pins on this image for reference.

Re: Bit-banged I²C kernel driver

Posted: Thu May 02, 2013 2:27 pm
by hmitman
Thanks again for your help, this got me a lot further.

The autoload works partly. The module is loaded at boot and it is possible to remove the default bus 7. (This bus is not needed.) Unfortunately it is not possible to add busses. The line to add a bus simply causes the script to exit, so all lines after the add_bus line are skipped. Do you have an idea what might be the cause? (Didn't work with any bus number/pin number combination I tried. Adding busses manually works without problems.)

The problem, that some pins did not seem to work was caused by myself, so no further crashes.

The hint about the broadcom pin numbers was very valuable!

Re: Bit-banged I²C kernel driver

Posted: Thu May 02, 2013 7:39 pm
by kadamski
Could you please show me the script you are using? And could you check dmesg to see if there are any error messages there?

Re: Bit-banged I²C kernel driver

Posted: Fri May 03, 2013 7:52 am
by hmitman
Ok, here is the script. The beginning is the standard comment and a part, where the ip address is printed. This existed from the beginning. Here is what I added:

Code: Select all

/sbin/insmod /home/pi/kernel_compile/i2c-gpio-param/i2c-gpio-param.ko
/bin/echo 7 > /sys/class/i2c-gpio/remove_bus
/bin/echo 5 17 25 > /sys/class/i2c-gpio/add_bus

exit 0
The first two lines work, for the last one dmesg tells me

Code: Select all

[   27.992237] id=5, sda=17, scl=25, udelay=0, timeout=0, sda_od=0, scl_od=0, scl_oo=0
[   27.995140] i2c-gpio-param: Got error when registering the bus.

Re: Bit-banged I²C kernel driver

Posted: Fri May 03, 2013 9:57 am
by kadamski
There is a little hack in the code to check for errors when adding the bus. It is not working correctly, I have to investigate this. As a workaround, please add "sleep 0.5" just before "add_bus" line.

Re: Bit-banged I²C kernel driver

Posted: Fri May 03, 2013 1:07 pm
by hmitman
This works perfectly well! I am really close to finishing this project, just need to use a specific pin for SDA now and I think I know how to do this.

Thanks for your help!

Re: Bit-banged I²C kernel driver

Posted: Wed May 08, 2013 1:41 pm
by hmitman
Hi again,
this time I did not only run into a problem. ;) Today I measured the clock speed of an added I2C bus and the result was different from what I expected following (500 / udelay). I used an arbitrary "ground" pin and the scl pin, I specified when adding the bus, to measure the frequencies. In this case this was gpio pin 4. Maybe I understood the equation's meaning wrong in any way, but here are my results:
udelay = 5 => clock frequency = 76,5 kHz
udelay = 4 => clock frequency = 90 kHz
udelay = 3 => clock frequency = 111 kHz
After that I stopped, as I need a speed of about 100 kHz. Is there simply a number different from 500 used or did I get something wrong?

The problem now is passing 1 instead of 0 to sda_od and scl_od. Doing so causes the bus only to recognise one address in a chain of I2C devices. Passing 0, the bus recognises all of te devices. Any idea, what's happening here?

Thanks a lot and greets

Re: Bit-banged I²C kernel driver

Posted: Wed May 08, 2013 3:57 pm
by kadamski
You did everything right. 500/udelay comes from kernel documentation (include/linux/i2c-gpio.h). It does not work well for RaspberryPi, though. udelay value is a time in microseconds that scl should be low or high. This value is passed to udelay() kernel function that should sleep that long.

So in order to create clock signal, kernel basicaly sets pin high, call udelay and then sets pin low. For some reason, the delay is alsmost always about 2us longer than it should. I suspect it's because it takes as much as 2us to actually set value low or high on the pin (i don't know why, though). This is why you see wrong frequency when udelay has low value.

Re: Bit-banged I²C kernel driver

Posted: Mon Jun 05, 2017 9:06 am
by audio-badger
Can I just say a huge thankyou to kadamski for this. It works beautifully (having it look and feel exactly like the hardware I2C driver is perfect) and has complete solved my issues with clock stretching.

Re: Bit-banged I²C kernel driver

Posted: Wed Dec 27, 2017 8:46 am
by Rooney
This is awesome!!!!

I'm using your code in combination with the stm32flash to flash a STM32F091 microcontroller via I2C using clock stretching.
Saved me days of development time.


Re: Bit-banged I²C kernel driver

Posted: Sun Jan 21, 2018 8:54 pm
by SlowBro
Wanted to mention that this driver didn't work for me. Nothing was detected. Not sure why. I could see the new device and got messages in dmesg, but could not detect anything. But when I used the i2c-gpio overlay that comes with Raspbian Stretch software it worked well. So this functionality is now built-in and it works for me whereas the code above did not. I'm sure it was a terrific effort but it did not work for me.

To use the built-in bit-bang, simply add this to /boot/config.txt and reboot. There are some other features as well; use the source, Luke.

Code: Select all

# Software I2C
Only wanted to mention it on this old thread because this thread is the first hit on Google when you search pi bitbang i2c.