A few weeks back, we talked about the no-nos of running I²C over long wires. For prototyping? Yes! But for a bulletproof production environment, this practice just won’t make the cut. This month I plucked my favorite solution from the bunch and gave it a spin. Specifically, I have put together a differential I²C (DI²C) setup with the PCA9615 to talk to a string of Bosch IMUs. Behold: an IMU Noodle is born! Grab yourself a cup of coffee and join me as I arm you with the nuts and bolts of DI²C so that you too can run I²C over long cables like a boss.
What’s so Schnazzy about Differential Signals?
There’s a host of ways to make I²C’s communication lines more noise resistant. From all of the choices we covered, I picked differential signals. They’re simple, fairly standardized, and just too elegant to ignore. Let’s take a moment for a brief “differential-signals-101” lecture. Hopefully, you’re already caffeinated!
Suppose we have two devices that want to communicate over a single wire. To get the conversation started, we run a single-ended signal over a long cable from one device to another. Just imagine a lengthy hookup wire between breadboards. Let’s assume the sender sends a digital output (logic 0 or logic 1), and the receiver expects to receive a digital input (logic 0 or logic 1).
Now, suppose we add some electromagnetic interference (EMI) by introducing a nearby motor and turning it on. From Faraday’s laws, a changing magnetic field from the motor will induce a voltage along these wires, which we’ll pick up as a voltage spike.
If these spikes are strong enough, the receiver will treat these signals as digital inputs, even though they’re not supposed to be there in the first place! What happens? Since we’re not prepared to receive these signals, it’s undefined, and anything goes! Our encoders will read random numbers, steppers will take bonus steps, buttons will read: pressed, rockets will blast off, satellites will fall out of orbit, and we are so fired.
Mayhem aside, let’s now hookup our sender and receiver along a differential signal.
In this setup, the receiver reads the difference between the top and bottom channels. The sender passes a logic 1 by applying equal-and-opposite voltages on both lines at the same time. Here lies the beauty of the differential signals: if we assume that both of our signals are being affected by the noise source equally, then they will both pick up an equal amount of noise. Since there’s no difference between the noise signal across the two channels, the receiver reads nothing. The noise is invisible!
Remember: we’re assuming both signals are being affected by the noise equally. To make that assumption, we need to space our differential pair wires as closely as possible together, which means either ribbon cable (like LVDS) or, even better: a twisted pair (like USB or Ethernet).
The Beginnings of an IMU Noodle
To get everyone jump-started on the DI²C bandwagon, I started tackling a project to showcase DI²C at its finest. I’ve been dying to build an IMU Noodle for almost three years now. It’s only now with both DI²C and the BNO055 that most of the groundwork has been put down; all I need to do is put together the pieces in one clean package, plus a bit of math. My take on this project will be a cluster of uniquely addressable nodes tied together over DI²C. I’ll save the IMU Noodle details for a later post. For now, let’s focus of the nuts and bolts of DI²C that runs along each node and makes this project possible.
Getting Cozy with DI²C
In a nutshell, DI²C isn’t too complicated. We’re all pretty familiar with conventional I²C’s SDA and SCL signals. The PCA9615 simply splits each signal into two differential pairs for a grand total of four communication wires. Any chip that wants to jump onto the DI²C bus first needs to convert it’s SDA and SCL lines into the corresponding Plus and Minus differential pair signals: DSDAP, DSDAM, and DSCLP, DSCLM. After that, communication can begin as normal with no software changes.
One last hiccup: the start and finish of the actual cable that carries the DI²C signal needs to be populated with termination resistors. These resistors are specced to match the characteristic differential impedance of the cable itself. (Find this value in your cable datasheet.) Before you all go home and try building systems without them, hear me out: these resistors are necessary. Without them, the signals that run down the wire will reflect back up and down repeatedly until they dissipate. Of course, we certainly can’t have bogus copies of old data interfering with new data. Simply adding the proper value termination resistors eliminates this problem.
Setting up a DI²C Configuration
The PCA9615 has a few options for setting up a DI²C bus. I chose the multi-drop bus configuration.
In the setup above, each “card” has a local I²C bus which hooks up to a cable that forms a shared DI²C bus. A PCA9615 on each card does the back-and-forth conversion between the open-drain side and the differential side. Together, these cards form one large bus, which means that all addresses must be unique. I opted for this solution for it’s simplicity; each IMU lives on a dedicated board and relays its data back to one master at the start of the cable. At the end of the cable, the final IMU node also terminates the DI²C signal with the proper termination resistors.
This configuration has one main benefit. Except for the last node, each node has the exact same topology. And since PCBAs usually come cheaper by-the-panel, having one design made ten to twenty times is far cheaper than fabbing multiple designs. Lastly, to handle that edge-case where the final board needs termination resistors, I put optional footprints for them on each IMU Node, but only the last board has them populated.
When it comes down to the actual discrete parts for setting up a DI²C node, it’s pretty simple.
This schematic is just the jumble of datasheet recommendations on one page, with some bypass caps for good measure. Keep in mind that the termination resistors should only be populated on the first and last node of the DI²C chain. For all other nodes, they should be omitted.
Side note: this chip does have the option of running the DI²C signal and the local I²C circuitry at two different voltages, allowing small voltage offsets across boards, but mine are joined together for simplicity of wiring.
As for what value termination resistors to choose–don’t sweat it! There’s an app-note for that! Specifically, given (a) the differential impedance of the cable we’re using and (b) the supply voltage of the differential lines, AN-847 will take us through the calculations for proper bus termination resistors.
Finally, to route the data out of the DI²C cable and back into a conventional microcontroller, I spun a vanilla PCA9615 breakout. This board is a duplicate of the DI²C circuitry in the IMU Node schematic, and it’s the first node on the DI²C chain, hence: it gets termination resistors.
Pro-ing up our Cables:
OK, so that multi-drop bus looks nice on paper, but how do we translate that concept into a cleanly executed network of boards? By far, my best answer has been teeny ribbon cable and (ridiculously cute) ribbon-cable connectors and sockets. Ribbon cable has the benefit that we can simply crimp as many intermediate connectors anywhere along its length and the pinout will be identical across each connector.
That’s exactly what I did here, where this ten-pin ribbon cable runs all four differential pairs (interleaved with GND signals), with the remaining wires for the supply voltage. As for those connectors, for their size, they’re ridiculously easy to attach to the mating cable. One squeeze in a vise, and we’re connected! (Ha! Take that, nefarious JST SH Connector! Never again will I crimp you into one of my side projects!)
While ribbon cables aren’t twisted pairs, which would be the ideal way to run these differential signals, the 0.025″ pitch on this ribbon cable is sufficiently closely-spaced that I’ll call it “good enough.”
Take DI²C Out for a Spin
That about sums-it-up for a DI²C implementation. Not too tricky, right? Now go forth, and may all your long-distance I²C projects shine with a savvy communication layer and some pro wiring!
If anyone’s dying to get started with some example files, I’ve dropped the PCB design files (KiCAD) and firmware on Github. May they give you the after-hours kick to jump-start your next long-distance DI²C project! And, of course, if you build anything fun, write back to us. We like that :)