Many readers will be familiar with interfacing I2C peripherals. A serial line joins a string of individual I2C devices, and each of the devices has its own address on that line. In most cases when connecting a single device or multiple different ones there is no problem in ensuring that they have different addresses.
What happens though when multiple identical devices share an I2C bus? This was the problem facing [Sam Evans] at Mindtribe, and his solution is both elegant and simple. The temperature sensors he was using across multiple identical boards have three pins upon which can be set a binary address, and his challenge was to differentiate between them without the manufacturing overhead of a set of DIP switches, jumpers, or individual pull-up resistors. Through a clever combination of sense lines between the boards he was able to create a system in which the address would be set depending upon whether the board had a neighbour on one side, the other, or both. A particularly clever hack allows two side-by-side boards that have two neighbours to alternate their least significant bit, allowing four identical boards each with two sensors to be daisy-chained for a total of eight sensors with automatic address allocation.
We aren’t told what the product was in this case, however it’s irrelevant. This is a hardware hack in its purest sense, one of those which readers will take note of and remember when it is their turn to deal with a well-populated I2C bus. Of course, if this method doesn’t appeal, you can always try an LTC4316.
The LTC4316 is something special. It’s an I²C address translator that changes the address of a device that would otherwise conflict with another on the same I²C bus. Not a hack? Not so fast. Exactly how this chip does this trick is clever enough that I couldn’t resist giving it the post it rightfully deserves.
What’s so special? This chip translates the address on-the-fly, making it transparent to the I²C protocol. Up until this point, our best bet for resolving address collisions was to put the clashing chip on a separate I²C bus that could be selectively enabled or disabled. In that department, there’s the PCA9543 and PCA9547 demultiplexers which we’ve seen before. Both of these devices essentially act like one-way check valves. To address any devices downstream, we must first address the multiplexer and select the corresponding bus. While these chips resolve our address collision problems, and while there’s technically a way to address a very large number of devicesif we’re not time-constrained, the control logic needed to address various bus depths can get clunky for nested demultiplexers.
What’s so classy about the LTC4316 is that is preservers simplicity by keeping all devices on the same bus. It prevents us from having to write a complicated software routine to address various sections of a demultiplexed I²C bus. In a nutshell, by being protocol-transparent, the LTC4316 keeps our I²C master’s control logic simple.
If you’re reading these pages, odds are good that you’ve worked with I²C devices before. You might even be the proud owner of a couple dozen sensors pre-loaded on breakout boards, ready for breadboarding with their pins exposed. With vendors like Sparkfun and Adafruit popping I²C devices onto cute breakout boards, it’s tempting to finish off a project with the same hookup wires we started it with. It’s also easy to start thinking we could even make those wires longer — long enough to wire down my forearm, my robot chassis, or some other container for remote sensing. (Guilty!) In fact, with all the build logs publishing marvelous sensor “Christmas-trees” sprawling out of a breadboard, it’s easy to forget that I²C signals were never meant to run down any length of cable to begin with!
As I learned quickly at my first job, for industry-grade (and pretty much any other rugged) projects out there, running unprotected SPI or I²C signals down any form of lengthy cable introduces the chance for all sorts of glitches along the way.
I thought I’d take this week to break down that misconception of running I²C over cables, and then give a couple examples on “how to do it right.”
You will probably be familiar with I²C, a serial bus typically used for not-very-fast communication with microcontroller peripherals. It’s likely though that unless you are an I²C wizard you won’t be intimately familiar with the intricacies of its operation, and each new device will bring a lengthy spell of studying data sheets and head-scratching.
If the previous paragraph describes you, read on. [Clint Stevenson] wrote a library for interfacing I²C EEPROMs to Arduino platforms, and when a user found a bug when using it on an ATtiny85, he wrote up his solution. The resulting piece is a clear explanation of how I²C EEPROMs talk to the bus, the various operations you can perform on them, and the overhead each places on the bus. He then goes on to explain EEPROM timing, and how since it takes the device a while to perform each task, the microcontroller must be sure it has completed before moving to the next one.
In the case of [Clint]’s library, the problem turned out to be a minor incompatibility with the Arduino Wire library over handling I²C start conditions. I²C has a clock and a data line, both of which are high when no tasks are being performed. A start condition indicates to the devices on the bus that something is about to happen, and is indicated by the data line going low while the clock line stays high for a while before the clock line starts up and the data line carries the I²C command. He’s posted samples of code on the page linked above, and you can find his library in his GitHub repository.
We love a good multitool. There’s something seductive about knowing that if, for some reason, you need to saw down a tree on a moment’s notice, you have a tiny saw in your pocket. We also like electronic versions of the multitool: gadgets that serve a lot of purposes as you develop and debug hardware. One of the most polished-looking ones we’ve seen is [Phillip Schuster’s] Little Helper.
The open source gadget looks like an iPod (if an iPod had header pins sticking out of it). It has basic analog I/O capability, can generate PWM pulses, sniff I2C traffic, and do lots of other features. It is open source, so you can always add more capabilities if you need them.
There comes a time when you need to wire up three, four, or more identical i2c devices to a common microcontroller. Maybe you’re thinking about driving 128 seven-segment displays with eight of those MAX6955 16-way digit drivers, or maybe you have a robot full of joints–each of which needs a BNO055 inertial sensor for angle estimation. (See above.) Crikey! In both of those cases, you’re best bet might be a schnazzy I²C device that can do most of the work for you. The problem? With a single I²C bus, there’s no standard way defined in the protocol for connecting two or more devices with the same address. Shoot! It would’ve been handy to wire up three BNO055 IMUs or eight MAX6955s and call it a day. Luckily, there’s a workaround.
We’ve seen some clever tricks in the past for solving this problem. [Marv G‘s] method involves toggling between a device’s default and alternate address with an external pin. This method, while clever, assumes that the device (a) has an alternate I²C address and (b) features an external pin for toggling that address.
I’ll introduce two additional methods for getting the conversation started between your micro’ and your suite of identical sensors. The first is “a neat trick,” but somewhat impractical for widespread use. The second is far more production-worthy–something you could gloat over and show off to your boss! Without further ado, let’s get started with Method 1.
Lastly, if you’d like to follow along, feel free to check out the source code on Github.
The Test Setup:
In both methods, I’m using the same sensor setup to check that each circuit behaves correctly. I happened to have a bunch of extra BMA180s on the bench, so I rolled out an example based on these chips. Back in the day, the BMA180 was a pretty common three-axis digital accelerometer. It has an I²C interface with two optional addresses. For the purpose of this example, I’m fixing them all with the same address. I’ve mounted three of these guys on mutually perpendicular axes of my acrylic “test cube,” and I’m reading each chip’s Z-axis. In this configuration I can easily pick out the gravity vector from the corresponding sensor as the data goes flying by my serial port window. If I can uniquely address each sensor and read the data, I’ve got a working circuit.
Method 1: Splicing Clocks into Chip-Selects
This method tips its hat towards SPI in that it behaves in an oddly similar fashion. If you’re feeling rusty on SPI, here’s a quick recap.
A Quick SPI Refreshment:
SPI, like I²C, is another protocol that shares both its clock and data lines with multiple slave devices. The difference, though, lies in the addressing scheme to talk to these devices that share the same bus. With SPI, while clock and data lines are shared, devices are addressed with separate chip-select (CS) lines.
The master microcontroller dedicates a unique output pin to each device (~SS1, ~SS2, and ~SS3 in this illustration). When the master micro’ wants to talk to a device, it asserts that device’s chip-select input pin by pulling it to logic LOW, and the conversation begins over the data bus. With the chip select LOW, the corresponding slave listens to the data on the bus. Meanwhile, all other devices ignore the conversation between the master and it’s chosen slave by keeping their bus pins in a high impedance state.
Giving I²C Its Own Chip-Selects:
With I²C, Clock (SCL) and Data (SDA) lines are still shared between all I2C slave devices, but the addressing scheme happens by sending a message heard by all devices on the bus. To single out one device on the shared bus, the master first passes down the address of the slave device it wants to talk to, after which that slave replies with an ACKnowledge, and all other slaves ignore the data that follows until both data transmission is complete and the bus is “released.”
Because we have the problem of multiple devices with shared addresses, in theory, all of these devices would reply when the master passes down their shared address, and there’s no way for the master to single out a single device. In reality, this behavior is undefined on the I²C protocol.
Yikes! Anything goes when we wander away from defined behavior, so we try to avoid these things in practice.
According to the I²C spec, It just so happens that an I²C slave device will ignore changes on the data line (SDA) provided that the clock line (SCL) is held high. In this method, I’ll “split” the SCL line into multiple SCL lines such that each shared I²C device gets its own SCL. By selectively rerouting the clock to each I²C device one-at-a-time, I’ve essentially turned the SCL line into a “chip select.”
To chop up that clock line, I’ll need a demultiplexer. A demultiplexer (or decoder) takes a logical input and reroutes it to one of several outputs based on the binary select lines.
I’ve dropped in the 74AC11138 eight-way demultiplexer for this task. It’s fast, capable of switching at megahertz rates, and its outputs default to logic HIGH. That second note is handy since idle SCL lines also default to logic HIGH.
The setup is shown in a simplified schematic above. In it, I’m using a Teensy 3.0 posing as the I²C bus master. To the right of the Teensy is the collection of identical chips, BMA180 accelerometers in this case. In the middle is the 74AC11138 eight-way demultiplexer.
Cons of this Method:
There’s a minor drawback with this technique, though, in that it doesn’t support I²C’s clock-stretching feature. Taking a step back, this method assumes that the SCL line is inherently unidirectional, controlled by only the I²C bus master. In other words, we’re making the assumption that data on the SCL line is only sent from master to slave and never the other way around. If your I²C slave devices implement clock-stretching, however, this assumption breaks down.
What is Clock Stretching?
Clock stretching is a method defined by the I²C protocol where the chip needs to “buy itself more time” and holds the SCL line low, hence, signalling to the master that it’s not ready for the upcoming data. In this scenario, the slave actively controls the SCL line, and it happens to be the only case where data moves up the SCL line from slave to master. In a setup with our demultiplexer between the master and our set of identical slaves, these slaves won’t be able to send back the clock-stretching signal to the master to indicate that they aren’t ready for data, if they happen to implement clock stretching. That said, clock stretching is a pretty rare feature among I²C-compatible devices, so this method is likely to work among a number of chips out now.
More Next Week
That’s all for Method 1. Thanks for tuning in, and check back next week for a slightly-more-professional method of tackling this same problem.