i2c is a protocol to communicate with microchips. It is widely used if the required data rate is not that high (up to few thousand bytes per second or similar). Reading data from some client (which could be some sort of sensor, or an Analog-Digital-Converter for example) with the Raspberry Pi is usually as easy as just reading from a device file (/dev/i2c-0 for example), specifying the device address beforehand, due to the magic the underlying kernel driver does. Some i2c clients, such as this MAG3110 magnetometer I’m using, require you to specify a register address to read from on each data access, tough. For this, technically, you need to send a START (to tell the device you want to communicate), then the register address you want to read from, then a REPEATED START (to tell the device you’re done with sending the address and want to read the data now), then you can read the data, and then you send a STOP (to tell the device you’re done). Sounds easy, right? It is however not quite obvious to me how to do that. Just sending the register address, then reading does not work, since that will send a STOP after the register address has been written, which leads to the device assuming you’re done and forgetting about everything.
Other people seem to be confused about this as well. Some threads in the internet even conclude that the i2c controller used in the Raspberry Pi is not capable of this functionality at all. However, this fortunately does not seem to be the case: using the i2cget program, you can specify a register to read from, and it will work just fine (same goes for writing).
|Communication generated by the code below. After the first data byte
(the register address, 4) has been transferred, you can easily spot the
spike in SDA (yellow) while SCL (teal) is high, meaning STOP START. This
So, since this is the open source part of the world, we can just go look at what they do, and steal it! 😉
The sources are easy to find (here, for example), and you can just modify i2cget.c to do what you need. Here is my code for accessing the magnetometer mentioned above, as an example. I hope this might help other people which are confused about this too.
Oh, and here’s a fancy image of the board with the magnetometer mentioned above:
|Board with a three-axis magnetometer, in a 2mm x 2mm DFN package|
The actual sensor is the small part in the middle. Doesn’t it look nice? 🙂
I should also mention that the magnetometer itself is pretty awesome! It’s a three-axis magnetometer with 16 bits resolution per axis (!), and it costs less than two euros at farnell (actually the whole board shown above is only about two euros in total if you add up the components, PCB, and the solder). The device is well sensitive enough to measure the earth’s magnetic field; thus, you can use it to accurately determine its absolute rotation. I did a test which rotates meshes in blender in real-time when you rotate the device — it worked quite well! It needs a lot of adjustment to the axes tough, they don’t really match yet. I’ll write about that again if I get it to work nicely.
Here’s an example plot when rotating the device around a bit:
|Raw data received from magnetometer when rotating it by hand|
Here’s moving a magnet towards the device, starting from about 1m away:
|Moving a large magnet towards the device. t=0 corresponds to ~1m distance. The peaks in the y and z axes in the end originate from the magnet being rotated a bit while moving it away.|
Edit on 01.01.2013: Hahaha, look at the photo of the board again — do you notice something? Yep, half of the GND isn’t even connected to anything. That’s what happens if you make just a layout without a proper connection diagram + DRC 😉
After fixing that with some zero-ohm resistors across the traces, the noise level of the device has gone way down. The curves are much smoother than in the plot above now.