Inter IC (I2C) bus design and test for embedded systems: Part 1 -

Inter IC (I2C) bus design and test for embedded systems: Part 1

“No man is an island, entire of itself…,” John Donne

Likewise in embedded systems, a simple function rarely exists inisolation. A simple pushbutton and LEDs controller is useful, but ifthat controller cannot report when a user has pressed a button or sharethe status its LEDs indicate, the controller’s worth is lessened.

The key to increased value is communication and increasingly theInter-IC (I2C) protocol, a2-wire master/slave communications bus standard, is the communicationbus of choice for embedded systems of all sizes.

I2C enabled devices are available as off-the-shelf, drop-infunctions, but the true power of the bus is revealed when a customdevice is created using a microcontroller. Customization enhances thefit; communication multiplies the value of the solution.

Before you proceed to incorporate I2C communications in your system,you face two strategic questions:

1) How to use I2Ccommunications in your system, and
2) How to verify your systemis working properly?

In Part 1 I will answerthe first question, concentrating on the design rules, things toconsider for the I2C master, and guideline to a successful slaveimplementation. In Part 2 , Iwill concentrate on test and verification, and you will see that designand test go hand in hand in any successful embedded project. Togetherwe will describe a strategy for designing an embedded system (orsubsystem) that uses I2C communication to increase its value,flexibility and reliability.

Introduction to I2C
I2C is a low- to medium-data-rate master/slave communication bus.Philips, the creator of I2C, describes the bus as a simplebi-directional 2-wire bus for efficient inter-IC control [1] . The bus consists of just twowires or circuit traces, one for clock and the other for data, with apull-up resistor on each wire of the bus. The physical layer is asimple handshaking protocol that relies upon open collector outputs onthe bus devices and the device driving or releasing the bus lines.

The master supplies the clock; it initiates and terminatestransactions and the intended slave (based upon the address provided bythe master) acknowledges the master by driving or releasing the bus.The slave cannot terminate the transaction but can indicate a desire toby a “NAK” or not-acknowledge. Addressing opens the lines ofcommunication between the master and its intended slave device and themaster keeps the connection open until it wishes to terminate theconnection (when the master is finished with the slave).

The simple hardware design and relatively low data rates allow anyengineer to take advantage of I2C as a communication solution. You canlearn more about the protocol by reading through the Philipsspecification and various application notes [2] (almost every I2C slave devicealso describes the specification with sufficient detail to gain aworking understanding of it [3] ).

Nearly every microcontroller vendor has taken care of the physicalimplementation of the bus, so you do not need to understand all thedetails in order to harness the power of I2C (just as you do not needto understand RS232 communications to be able to use devices withHyperTerm).

Off-the-shelf components like serial EEPROM, remote temperaturesensors and I/O port expanders using I2C are available as slavedevices. Since most microcontroller vendors offer I2C master and slavecapabilities on most of their devices, there is nothing to prevent anembedded designer from taking advantage of I2C.

Creating a custom I2C slave device is the point of this article, andit is easier than you think, as long as you keep to a plan and followsome simple rules. 

Simple Rules Lead to Success
Creating a custom I2C slave requires firmware: the application-generalprotocol (how data is interpreted from the physical layer/hardwareregisters), the application-specific interfaces (what data isexchanged, and functional implementation (what a specific piece of datameans, what action does it cause). I have four rules to a successfulI2C firmware implementation:

1) Make the master generic;let the slaves specialize.
2) Use a standardizedregister-based protocol.
3) Use well-defined(unchanging) interfaces.
4) Follow an established setof design practices.

These rules are not hard to understand, nor are they hard to follow.Some are just reminders of what many embedded designers would doanyway. Without rules many small subsystems develop organically, andbefore you know it you're getting ready to ship something that you wishyou could re-design (which the schedule seldom permits). Starting yourdesign with these rules in mind will reduce your regret over theresults.

Rule#1: Make the master genericand let the slaves specialize.
The master in an I2C design is often the master of the entire system,and as such has many things on its mind. Designing specialized slaves,that provide specific functionality shields the complex master in yoursystem from the winds of change as the solution solidifies. Addingfeatures to the slaves (as they inevitably creep in) will have littleimpact on the systems as a whole, when the master’s role is a genericone.

Rule#2: Use a standardizedregister-based protocol.
When you read the datasheet for an off-the-shelf I2C slave[4], you willfind a description of a set of registers, what data they contain orwhat actions writing data to them causes. This set of registers looksto a master like a memory map.

The details of the registers may differ from slave to slave, but allfollow a standard pattern: write the first data byte to set theinternal register-pointer, and then write the second data byte totransfer the data to the register pointed-to by the register-pointer(the first data byte).

When a master wants to read from the slave, it first writes theregister-pointer to the slave, then starts a read transaction and readsout the data pointed-to by the register-pointer. Many devices allowmulti-byte reads and writes, but it is in this way that they differfrom device-to-device.

If you use this same register-based protocol in your design, youbenefit by having everyone intuitively understand the protocol. Thismakes a team more productive solving real problems (rather thandebugging a new protocol). The standardization yields greater dividendsbecause it enables a design (both the master and slave) to be moreeasily reused.

Rule#3: Use well-defined(unchanging) interfaces.
This is a tenet of any project, but it bears reiteration. Define theinterfaces and honor them as contracts. In rule #1 I stated the masterwould be generic, the slave would carry the burden of the task. To beable to pull this off, the interface between the master and slave mustbe set early and set in concrete. Use simple, easy to understandinterfaces. Keep the interfaces to the essentials, let the slave boilthe data down or expound a command. The interface need not reflect theslave implementation details, in fact it is best not to.

Rule#4: Follow an established setof design practices .
This rule is less well defined, but the main point is that designsshould be supported with established, accepted design practices. Thepractices will vary from team to team, but the point of this rule isthat you want the team’s concentration to focus on the value-addedfunction to be accomplished, and not worry about all the methods.

For an I2C slave there are two practices of particular usefulness:first, maintain separate foreground and background threads or tasks;second, partition the data interface into read-only and read/writeblocks, and protect against writing to read-only registers.

Considerations for the I2C Master
It is the master in the system that often has the least flexibility. Somany other requirements and concerns find a home in the master, thatthere cannot be many rules or guidelines imposed upon the master by aslave device. And yet, that is precisely what happens whenoff-the-shelf I2C slave devices are chosen.

Most require the master not only to configure the slave, but alsooften to repeatedly poll and command it. A custom I2C slaveimplementation using a microcontroller can shift the balance of thework into the slave, make life easier for the master, and thereforemake it easier to add higher-value functionality to the system.

There are a few things to keep in mind with the master for a moresuccessful implementation: plan for tolerance to a slave’s timing, usehigh-level commands rather than micromanaging the slave, and implementinterfaces early then resist changing them.

While the master is in control of the timing at the physical layer,often a slave has many things to do at the functional layer. When aslave is designed to act upon a specific command, it may not beinstantaneous, and therefore the master should not expect immediacy. Atolerant timing profile, where the master does not make snap decisionsabout a slave’s response, is the best approach.

Remember masters: be tolerant, and do not make snap decisions (ifinstantaneous action is required, we’ll see in the next section wherethe slave can help out).

Rule #3 above says to define interfaces and then follow them, but what theseinterfaces should be or should look like isn’t addressed. For themaster’s benefit, create an interface of high-level commands; limit theinteractions of the master. With a custom slave, the master need noteven configure the slave (this can be programmed into the start-upsection of the firmware), and it only needs to be contacted when anunusual event occurs (i.e. fault condition).

Lastly, since the main implementation task for the master is theinterfaces, the earlier these interfaces are implemented and tested,the sooner the master can move on to its other demands. If theinterfaces are defined, followed and consist of high level commands,the implementation burden on the master is light, and the actualrequirements of each command can be explored in the context of thegreater system.

Remember, the master will drive functional requirements for theslaves, so allow as much time as possible for the interfaces and theintents of the high-level commands to be hardened in the context of thesystem.

Considerations for the I2C SlaveImplementation
When a custom slave is the goal, implementation of the slave is likelyto be the largest task, and the fundamental reason of pursuing a customslave device is to get a custom fit.

As discussed earlier, this custom fit can entail offloading themaster of lower-level functions, or it can be as simple as multiplyingthe number of I/O pins for a host processor (without the burdens thatoff-the-shelf devices can impose). A custom device can serve as abuffer between several off-the-shelf I2C slave devices, again reducingthe burden on the master or providing address arbitration for slavesthat have none.

For whatever reason (there are so many) that you pursue a custom I2Cslave, keep in mind the following guidelines to increase the value ofyour results: separate the firmware that handles the communicationsprotocol from everything else, maintain coherent data at all times, andprovide an “emergency signal” to the master to limit the master’s needto poll the slave.

The “perceived” main task of the slave is not communicating ormanaging the communications protocol. The truth is, if not handledcorrectly, or if designed incorrectly, the main design and debug taskwill be the communications.

The first step on the path to successfully managing communicationswas to adopt a standard register-based protocol, rule #2 abov e. The second step is to use a tested, debugged firmwareimplementation of that protocol. It may be possible to find thisalready implemented for your microcontroller of choice by searchingthrough their application notes or user forums.

(Cypress Semiconductor offers animplementation of this protocol called EZ I2C for all of their PSoCmicrocontroller devices, built into the development tools (seethe SidebarCreatinga Custom I2C Device in 15 Minutes.”).

However you accomplish this, the key is to resist reinventing foreach project the protocol or communication management firmware.

Make the protocol managment aseparate task
If this is your first time and you want to keep the I2C protocolfirmware separate from everything else, put the protocol managementinto a separate task from the rest of the system. For a microcontrollerthis means attaching it to the I2C interrupt.

Since interrupts need to be handled quickly with minimal systemdisruption, the interrupt handler will not do any data manipulation orexecute any higher-level functions. The interrupt handler will simplytake the data off the bus and transfer it to a register or retrievedata from a register and provide it for transmission on the bus.

When the protocol management is handled in a separate task(interrupt) form the main application, the registers look like adual-port RAM. On one side, the main application is acts upon andupdates the data in the registers. On the other side the I2C protocoltask transfers data received from the master into the registers andfrom the registers to the bus for the master. Implementing the protocolmanagement as described above will allow it to be transferred fromproject to project.

Once the protocol is taken care of, the next concern is the actualdata in the registers. The protocol will be asynchronously transferringdata in and out of the registers. It is the application’s job to keepthe data coherent, so that the data the master sees accurately reflectswhat is going on.

This can be ensured by making all data 8-bits wide, since I2Ctransfers are byte-by-byte. But many data elements will not fit into8-bits and retain the desired resolution. Also, coherency is not justabout keeping multiple bytes of a data element in-sync; it means thatat no point will a data element contain a anything but its true value(registers are not used for temporary values, such as setting aregister to zero and then setting individual status bits true).

Keep all data transfers atomic; if a multi-byte data element is tobe updated, disable interrupts around the transfer. When calculationsare needed before a register can be determined, use a separatetemporary variable or scratchpad and then transfer the final data.

Finally, slaves should never require a master to intervene unlessabsolutely necessary. The master is the one to decide when it isabsolutely necessary, so in your slave design you need to provideinterrupt points, but also provide the master the ability to decide notto be interrupted. Provide a mask bit for each status that can cause aninterrupt.

A slave should be designed to handle everything that comes up. Theemergency interrupt is there to inform the master what has happened,but not to wait for the master to take action. Designing yourinterrupts this way will further reduce the burden on the master whileallowing for a wide range of unusual situations to be handled.

I2C provides a relatively painless approach to creating customfunctions that stand-alone and partition a system into a high-levelmaster and lower-level functioning slaves. When Philips created the busthey envisioned system designers building entire devices like computersand televisions by dropping ready-made slave devices onto the 2-wirebus.

While this did not happen, the I2C bus has become an embedded systemstandard. I2C enabled devices are available as off-the-shelf, drop-infunctions, but not to the degree and in the variety required tobuild-up a complex device.

Microcontroller-implemented custom slave devices can fill this gapand for a company they can provide a portfolio of plug-and-playfunctions that address the repetitive portion of their designs. Followthe rules and you can successfully harness the I2C bus in your system.

In Part 2 I will addressthe test and verification process for I2C in your system. We willrevisit and build upon many of the design considerations and show howthey support and improve the test and verification of an I2C-enabledsystem.

Jon Pearson isthe product manager for PSoC development tools at Cypress Semiconductor, located inLynnwood, WA. He can be reached at

[1] The I2C-Bus Specification,Version 2.1. January 2000
[2] Philips has a pagedescribing the bus and providing links to the official specificationand other resources that can be found at http://www/
[3] Cypress Semiconductoroffers I2C port expanders and has an application note describing them,AN2305 – Using Cypress I2C port Expanders with Flash Storage, which canbe found at by searching on “AN2304”
[4] Cypress CY8C9520 datasheet(found at; Philips PCF8574 datasheet (found at

1 thought on “Inter IC (I2C) bus design and test for embedded systems: Part 1

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.