This week, I figured it’d be best to layout a slightly more practical method for solving the same problem of talking to I²C devices that each have the same address.
I actually had a great collection of comments mention the same family of chips I’m using to tackle this issue, and I’m glad that we’re jumping off the same lead as we explore the design space.
Recalling the Work of Our Predecessors
Before figuring out a clever way of hacking together our own solution, it’s best to see if someone before us has already gone through all of the trouble to solve that problem. In this case–we’re in luck–so much that the exact bus-splitting behavior we want is embedded into a discrete IC, known as the PCA9547.
It’s worth remembering that our predecessors have labored tirelessly to create such a commodity piece of silicon.
The PCA9547 (PDF) is an octal, I²C bus multiplexer, and I daresay, it’s probably the most practical solution for this scenario. Not only does the chip provide 8 separate buses, up to seven more additional PCA9547s can be connected to enable communication with up to 64 identical devices! What’s more, the PCA9547 comes with the additional benefit of being compatible with both 3.3V and 5V logic-level devices on separate buses. Finally, as opposed to last week’s “hack,” each bus is bidirectional, which means the PCA9547 is fully compliant with the I²C spec.
Selecting one of the eight I²C buses is done via a transfer on the I²C bus itself. It’s worth mentioning that this method does introduce a small amount of latency compared to the previous clock-splitter solution from last week. Nevertheless, if you’re planning to read multiple devices sequentially from a single bus anyway, then getting as close-as-possible to a simultaneous read/write from each device isn’t likely a constraint on your system.
With a breakout board to expose the pads, I mocked up a quick-n-dirty Arduino Library to get the conversation started and duplicated last week’s demo.
Happily enough, with a single function to change the bus address, the PCA9547 is pretty much a drop-in solution that “just works.” It’s definitely reassuring that we can stand on the shoulders of our chip designers to get the job done quickly. (They’ve also likely done quite a bit more testing to ensure their device performs as promised.) Just like last week, feel free to check out the demo source code up on Github.
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.
A lot of great ICs use I2C to communicate, but debugging a non-working I2C setup can be opaque, especially if you’re just getting started with the protocol/bus. An I2C bus scanner can be a helpful first step in debugging an I2C system. Are all the devices that I think should be present actually there and responding? Do they all work at the bus speed that I’m trying to run? If you’ve got an Arduino or Bus Pirate sitting around, you’re only seconds away from scanning your I2C bus, and answering these questions.
Heat up that iron, you’re going to want to try this one: [Hugatry] is adding hardware to his laptop by tapping into the i2c lines on the memory module. We love this because the penalty for borking memory during the soldering process is much lower than when soldering directly to a motherboard!
Until we watched the video after the break we hadn’t realized that memory modules usually have an i2c EEPROM on them. This is actually a standard called Serial Presence Detect which allows the BIOS to poll the memory and configure automatically. It seems ironic that we knew the Raspberry Pi HAT standard uses this same trick but didn’t know it was on computer memory as well.
Hardware-wise this provides an easy method of soldering your own equipment to the bus. From there it becomes a software hack. Linux, of course, makes this quite easy and that is demonstrated by [Hugatry] with an LM75 temperature sensors. We would like to hear from our Windows and OSX using readers on how the i2c bus can be accessed within those OS’s.
“Instead of an Arduino, he could have done that with a 555 timer.” “Instead of a 555 he could have done that with two transistors.” “Instead of a few transistors, he could have done that with butterflies.” These are quotes from various Hackaday comment threads throughout the years. It seems simplicity is the name of the game here, and if you need a timer chip, how about an 8-pin DIP? This, of course, means an I2C programmable oscillator in the form of an LPC810.
[kodera2t] built this circuit after reaching for a 555 timer a few too many times. It’s a one-chip solution with an ARM core that’s able to generate square waves with 1Hz resolution up to 65536Hz.
The source for this chip is a lot of C, but once it’s in the Flash of the LPC810, this chip becomes a programmable oscillator with an I2C interface. Yes, it’s a one-component solution, no, it’s not a twenty cent chip, but try programming a 555 over I2C.
The videos below show [kodera] playing around with this I2C oscillator, sweeping the frequency from zero to inaudible teenage angst.
[Lewin] wrote in to tell us about a high speed library for Arduino Due that he helped develop which allows interfacing OLED displays that use the SSD1306 display controller, using DMA routines for faster display refresh time.
Typically, displays such as the Monochrome 1.3″ 128×64 OLED graphic display , are interfaced with an Arduino board via the SPI or I2C bus. The Adafruit_SSD1306 library written by [Limor Fried] makes it simple to use these displays with a variety of Arduinos, using either software or hardware SPI. With standard settings using hardware SPI, calls to display() take about 2ms on the Due.
[Lewin] wanted to make it faster, and the SAM3X8E on the Due seemed like it could deliver. He first did a search to find out if this was already done, but came up blank. He did find [Marek Buriak]’s library for ILI9341-based TFT screens. [Marek] used code from [William Greiman], who developed SD card libraries for the Arduino. [William] had taken advantage of the SAM3X8E’s DMA capabilities to enable faster SD card transfers, and [Marek] then adapted this code to allow faster writes to ILI9341-based screens. All [Lewin] had to do was to find the code that sent a buffer out over SPI using DMA in Marek’s code, and adapt that to the Adafruit library for the SSD1306.
There is a caveat though: using this library will likely cause trouble if you are also using SPI to interface to other hardware, since the regular SPI.h library will no longer work in tandem with [Lewin]’s library. He offers some tips on how to overcome these issues, and would welcome any feedback or testing to help improve the code. The speed improvement is substantial. Up to 4 times quicker using standard SPI clock, or 8 times if you increase SPI clock speed. The code is available on his Github repo.
I2C has a seven-bit address space, and you’re thinking “when do I ever need more than 127 devices on a pair of wires?” So you order up some parts only to find that they have one, two, or three user-configurable address pins for any given device type. And you need a bunch more than four or eight capacitive sensor buttons on your project. What do you do?
If you’re reader [Marv G], you think outside the box and realize that you can change the addresses on the fly by toggling address pins high and low with your microcontroller. That is, you can use a single I2C address pin for each device as a chip select signal just like you would have with SPI.
That’s it, really. [Marv G] goes through all of the other possible options in his writeup, and they’re all unsavory: multiple I2C busses, a multiplexer, buying different sensors, or changing micros. None of these are as straightforward as just running some more wires and toggling these with your micro.
We’d even go so far as to suggest that you could fan these chip select lines out with a shift register or one of those 1-of-N decoder chips, depending on how many I2C devices you need to chip-selectify. (We’re thinking 74HC595 or 74HC154.)