Design an efficient programmable I2C slave

The latest MCUs make it feasible to include programmable I2 C slaves in low-cost systems.

Most low-speed serial devices communicate with each other using the I2 C bus. Although there's a wide variety of I2 C slave ICs, programmable I2 C slaves are rare. One such programmable slave is the microcontroller (MCU). Forcing a microcontroller to emulate an I2 C slave device isn't a difficult task, but until recently, serious problems hampered this approach. New low-cost, low-power microcontrollers now make it feasible to include programmable I2 C slave devices in inexpensive systems.

Typically an I2 C system is constructed from one master and many slaves. Such systems can be broken down into a larger number of subsystems, each controlled by a dedicated inexpensive MCU. Each subsystem connects to a faster (and typically more expensive) master. This approach lets engineers develop several subsystems in parallel, all of which link to the master controller with little additional design effort, as shown in Figure 1.

A hypothetical subsystem might include a linear-position sensor, a linear slide driven by a stepper motor, and code that controls this motor based on sensor feedback. Integrating the code into the main firmware of a single-processor system is no easy feat. It's easier to write the firmware for a dedicated microcontroller found within a subsystem. The main processor repositions the linear slide by writing an 8- or 16-bit position value to an I2 C register. As an added benefit, the subsystem designer needn't worry about other real-time system processes hoarding MCU resources, which could interfere with the digital control system.

Data transmission on the I2 C bus is accomplished using two wires (plus ground) and a regimented protocol. Transmissions are always initiated by the master regardless of the data flow direction. Each transmission is framed by an I2 C-Start and an I2 C-Stop condition.

Data are transmitted serially in byte-wide increments. Typically, the first byte is the slave's 7-bit address followed by a read/write (R/W) bit (possible exceptions are the 10-bit addressing and HS-mode prefix). Subsequent data are transmitted from or to the slave depending on the logic level of the R/W bit. All data bytes are followed by an acknowledge bit that's used, not only as an indicator of data integrity, but also as part of the flow-control scheme.To avoid confusion with the Start and Stop conditions, all data transitions on the serial data (SDA) line must occur during the serial clock (SCL) line's low phase, as shown in Figure 2. A typical I2 C data transmission (Figure 3) is composed of a Start condition, a 7-bit address with the associated R/W bit (8 bits total), some byte-wide data, and a Stop condition.

Bus interface circuitry
The I2 C slave device must always monitor the bus lines for Start and Stop conditions and react accordingly when its corresponding slave address is written to the bus. Monitoring the I2 C bus' SDA/SCL lines with MCU GPIO pins would require constant polling of the lines for activity. A better method is to attach both lines to interrupt pins on the MCU. Both methods are resource-intensive, especially during the times when monitoring the lines isn't necessary (in other words, when the I2 C master is talking to an I2 C device with a different slave address). To increase the amount of processing resources available for other tasks, thus obtaining maximum efficiency, it's necessary to detect the I2 C Start and Stop conditions by using a hardware interface.

The hardware interface, illustrated by the circuit in Figure 4, is composed of two flip-flops, two buffers, two MOSFETs, and an inverter. This vital circuit offloads I2 C Start and Stop detection (Figure 5) and lets the microcontroller use its full resources on the end application rather than on monitoring bus lines. This circuit is compatible with the I2 C specification because it doesn't load each bus line with more than 10 pF of capacitance, and it's compatible with standard (100 kHz) and fast (400 kHz) switching speeds. The circuit maintains the low-power theme by consuming less than 4 μA of supply current.

View the full-size image

I2 C slave framework
The firmware framework is written in C, using CrossStudio for MAXQ (Rowley Associates). Although the source code presented here is specific to a Maxim MCU, the principles underlying the code can be applied to any MCU featuring edge-triggered interrupt inputs that can be dynamically reconfigured at run time (in other words, rising- or falling-edge triggered).

The framework mimics a register-based I2 C slave that features an 8-bit register pointer and a configurable number of 8-bit read/write registers. Writing data to the registers requires that the register pointer be initialized to the desired register address. Register data is written sequentially, and the register pointer is incremented after each byte of written data.

Figure 6 outlines a typical I2 C write transaction. Note that the register pointer and register data are written within the same I2 C transaction. Reading the register contents (Figure 7) requires a little more effort because both a write transaction (required to initialize the register pointer) and a read transaction (required to read the register contents) must occur.

View the full-size image

View the full-size image

Although these transactions can be separated by a Stop and subsequent Start condition, programmers typically reduce transmission time by substituting the Stop/Start with a repeated Start. The repeated Start is the same as a regular Start condition except that it occurs between regular Start/Stop conditions.Turn the key
The framework buries the I2 C slave implementation details and adds several hooks that make program development quick and painless. Available hooks include an initialization routine (Listing 1), a polling routine (Listing 2), a pass-through interrupt handler routine, which is required because the MCU only provides one interrupt vector for all interrupt conditions (Listing 3), and the all-important I2 C interrupt service routine (Listing 4).

View the full-size image

View the full-size image

View the full-size image

View the full-size image

The I2 C interrupt service routine (ISR) is called after each valid I2 C write transfer and serves as the connection point that ties the I2 C slave registers to the user firmware. The framework automatically logs register changes by using memory-economizing bit-flags. The flags relieve the firmware designer from the resource-intensive burden of tracking register changes. Built-in helper functions access the bit-flags and let the firmware designer check and set one or all of the flags. These functions can be used to implement a simple register-check algorithm like the one detailed in Listing 4.

State diagram
The I2 C communications interface consumes little of the MCU's resources. Processor overhead is minimized by using interrupts to create a state machine. Interrupts on the Start, Stop, and SCL inputs are catalysts that instigate a state change. A Start interrupt always forces the state machine into the address detection state, while a Stop always forces the state machine to abandon what it's doing, clear the input buffer, release the I2 C-data line (SDA), and return to the Idle state.The SCL is connected to an interrupt and is masked (disabled) when the I2 C master is talking to a slave with a different address. Because the slave must respond after both rising and falling edges of the SCL, the SCL interrupt edge must be dynamically configurable at run-time.

Because the firmware framework is interrupt driven, it consumes few of the MCU's resources. The code outlined in the listings is used to calculate efficiency. They configure the MCU's general-purpose I/O (GPIO) pin as an output in the FWInit() hook. A delay loop in the FWPoll() hook evenly spaces clock edges on the GPIO output. When no I2 C activity is occurring, the microcontroller has full use of its resources, and the GPIO output toggles at a fixed maximum frequency. I2 C activity will sporadically increase the amount of time between edges because of the added overhead that the I2 C state machine imposes. Counting the number of pulses during a fixed 100-μs window and calculating the ratio between the I2 C idle and I2 C active states reveals that the I2 C state machine requires about 30% of the microcontroller's total resources during the switching phase of an I2 C transaction. Although the average resource consumption of the I2 C state machine depends on the length and frequency of I2 C transactions, the maximum resource drain should never exceed the aforementioned 30% figure.

Figuring the cost
The firmware framework imposes minimal drains on code/data space and allows for maximum efficiency of the attached MCU. The framework requires 824 bytes of program code, and six plus NumberOfRegisters bytes of data memory. The bus-interface circuitry budgetary pricing is in the 70-cent range and draws minimal current (less than 4 μA) from the power supply. The solution presented here costs around $4 to $5, which is comparable to some fixed nonprogrammable I2 C slave devices. Board real estate is minimized by choosing an MCU that's packaged in a space-saving QFN package, with bus-interface circuitry composed of SOT-23 and SC-70 packages.

Although I chose to implement only an I2 C slave interface with a 7-bit slave address, the code could easily be modified to respond to a 10-bit slave address. Depending on the chosen MCU, the interface could be optimized to work at fast- or high-speed switching modes of 400 kHz or 1.7/3.4 MHz, respectively. In addition, you could add an SMBus-compatible interrupt line (SMBALERT# ), which would require the device to respond to address 0x18 (the SMBus alert response address). An I2 C slave is also allowed to slow the data transmission by holding the clock line low. Add this useful feature if the MCU must respond to some real-time process that supersedes the I2 C communications' priority.

Donald Schelle was a member of the technical staff at Maxim Integrated Products Inc. He received a bachelor's of engineering and a diploma in electrical engineering from Lakehead University, Thunder Bay, Ontario, Canada, and a diploma in electrical engineering-computer control from Confederation College, Thunder Bay.

Eric Schlaepfer is a strategic applications engineer at Maxim Integrated Products. He graduated from Cal Poly in 2005 with a bachelor of science in electrical engineering. Schlaepfer can be reached at .

“I2 C bus specification,” version 2.1, Phillips Semiconductors, January 2000.

“System Management Bus (SMBus) specification,” version 2.0, SBS Implementers Forum, August 2000.

Leave a Reply

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