4

I want to attach an I2C device; it has 4 lines (SCA, SDL, +, -). I understand I can connect the first two to pins 3 and 5, and the last two to various places. I also understand I can multiplex the bus and attach multiple devices to those pins.

But how do I go about programming an application? I know there is a userspace interface that can be activated using device tree in config.txt:

dtparam=i2c=on

Then loaded after booting the kernel with:

modprobe i2c-dev

Which provides a /dev/i2c-1 node, but what do I do with this? I have i2cdetect installed (apt install i2c-tools) and it correctly shows the address of the device.

I am using Raspbian. The device comes with a lengthy datasheet, but it seems very abstract and I am not sure where to start with corresponding that to code.

2 Answers 2

4

I am using Raspbian.

First, I'll clarify that the answer is not distro specific; the methodology is exactly the same on any GNU/Linux distro. It is also the same for any model of pi (in fact, it isn't even specific to the pi and would be the same on any linux system with a user I2C bus).

The kernel interface is documented in the official kernel docs. These are included in the source tree in the same path (Documentation/i2c). Most of the stuff there isn't relevant to using the userland interface in /dev. The two that are essential are:

  • "dev_interface": Explains how to open a handle to a specific device according to bus (on the Pi, generally /dev/i2c-1) and individual address.

  • "smbus_protocol": Explains various commands that can be used with the device handle. Although, as noted there, the SMBus protocol is actually a subset of I2C, unless you find a specific reason to believe otherwise, assume your device fits into this.

Note that since the device handle is a normal file descriptor, you can use normal read(), write(), and even poll() (multiplex!) with it; anecdotely this sometimes seems to produce a different result than an smbus command that ostensibly does the same thing. Anyway, you are free to fool around, nothing is going to get damaged. Probably.

Beware...

There is no library you need to link to; all the code is in the relevant header, <linux/i2c-dev.h>. However, there are two versions of this floating around, and they will overwrite one another in /usr/include. The first one is a useless stub that might be installed with "kernel headers" packages, and you'll get obvious errors regarding undefined functions if you try to use it. The other is much bigger (10k vs 1k), and can be installed on Raspbian with the libi2c-dev package. This is literally just that header, as (again) there is no pre-compiled userspace library. The core implementation is in the kernel.

The smbus functions defined in that header are mostly wrappers on ioctl(), so if you can find it on any linux system, regardless of architecture, you can copy it onto a Pi and use it.

Moving on...

This base API is in C, but there are wrappers for it in other languages. Since it isn't actually pi specific, you don't need (or want) to look for stuff that is "for the raspberry pi". You want stuff that is for the linux "dev" based I2C/SMBus interface (although in some cases these were developed on the Pi, the developers themselves are certainly aware of their generic nature1). I have not tried these (I've been using C++), but I did check out their docs, etc., a bit:

  • Perl has Dev::SMBus; the best way to install that will be via CPAN.
  • Python has smbus; on Raspbian and other Debian derived systems this is in the python-smbus package.
  • I could not find much for Ruby beyond this thing on github. You may want to use some pi specific GPIO library that includes I2C functions. Note that the smbus library is drop dead simple (have a look at the docs above and the C header), so if you can write Ruby modules which include compiled native code, this would be a pretty easy task.
  • Ditto PHP. The github stuff in this case seems to be all "for the raspberry pi"; I didn't look at it.
  • For Java, Oracle seems to have a cross-platform deviceaccess.i2cbus class.

Finally...

The device comes with a lengthy datasheet, but it seems very abstract and I am not sure where to start with corresponding that to code.

This is the hard part, although once you get the hang of it, not necessarily that hard; some devices are more complex than others, obviously. Also, some datasheets are clearer than others. Note that you can ignore very low level stuff about timing of the data line (SDL) in relation to the clock (SCL) since that's taken care of in the driver and reads/writes will be appropriately buffered. All that may or may not be mixed in with references to the data and control register addresses (which might be significant). In general, keep in mind you aren't directly accessing either line. You're using the high level SMBus protocol in userspace, which abstracts away these details.

For example, if the data sheet refers to an (8-bit) read register at 0xF6, you can get the contents with:

int data = i2c_smbus_read_byte_data(device_fd, 0xf6);

For a 16-bit register:

int data = i2c_smbus_read_word_data(device_fs, 0xf6);

But watch out -- some devices are big endian. Some devices also have write only registers that are controlled via a read on the address OR'd with a value.


1. This also means software using them will work on devices such as the Banana Pi, PandaBoard, BeagleBoard, etc. etc.

0

You can also use https://docs.rs/linux-embedded-hal/latest/linux_embedded_hal/ for Rust if you'd like to use these peripherals on boards like the Raspberry Pi.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.