I2C stands for Inter-Integrated Circuit bus which is used to communicate between different silicon chips. More specifically, a bus is a digital communications channel that can be shared by many devices or peripherals.

I2C has a wide range of application and is usually used to communicate between integrated circuits in the same box, either on the same circuit board or across different boards where high-speed communication speed is not critical.

Philips originally developed I2C for communication between the chips inside a TV set, but with time the system has boomed and from its first release in 1982, it is now used by more than 1000 different integrated circuits.

One reason for its success is that it is a practical and economical way of creating complex circuits by connecting everything on the same bus with just two wires, the data line and the clock line, which significantly reduces the number of electrical signals that must be routed on a circuit board.

Examples of real world applications are connecting device like non volatile memory, Real Time Clock circuits, digital sensors, I/O expanders, digital LEDs, liquid crystal displays and switches.

The I2C specification has been revised several times, always maintaining backward compatibility, from v1.0 in 1992, v2.1 in 2000 and v3.0 in 2007. The Standard mode runs at up to 100 kbit/s, up to 400 kbit/s in Fast mode, up to 1 Mbit/s in Fast-mode Plus, or up to 3.4 Mbit/s in High-speed mode. The higher speed modes were added later in new revisions of the I2C specification, so not all integrated circuits support all the speeds. However, the protocol is backward compatible, so newer, faster devices can still talk to the older, slower devices, and the speed of one device connected to the bus does not affect the other devices on the bus. The hardware of the Mizar32 I2C interface supports Standard mode reaching 100 kbit/s and Fast mode up to 400 kbit/s.

Some manufacturers, like in our case Atmel, use the name TWI (Two Wire Interface) for I2C because it doesn't implement every last detail of full I2C, but they are compatible and are essentially the same thing.

To understand more of I2C, let's take a closer look at how the I2C protocol works.

I2C is an Half Duplex protocol, which means that only one device can talk at a time. The bus has two wires: clock and data.

The Master device supplies the clock to the bus. The most common configuration is one Master device with one or more Slave devices. An alternative to this is Multi master mode, in which more than one Master device can talk on the same bus without causing errors. The Masters have a way of deciding between them who is controlling the bus in each moment. The mechanisms for doing this are called Bus Arbitration and Clocks Synchronization.

In a configuration with a single Master device and many slaves, it is the Master device which supplies the clock signal, but slave devices can make it to slow it down to a lower speed if they need some extra time.

Electrically, the two wires are pulled high with one resistor each, and the devices send signals by pulling these lines low. In Standard mode with a maximum bus speed of 100 Kbit/s, the resistor value is 10K ohm and in Fast mode, up to 400 Kbit/s, each resistor is 2.2K ohm.

Each Slave device has a 7-bit address that it responds to on the bus, and every device on the same I2C bus must be set to respond to a different address.

When two devices are communicating, one of them is Master and the other Slave, and one of them is transmitting data on the bus and the other is receiving it, so in any particular moment, a device can be a Master-Transmitter, a Master-Receiver, a Slave-Receiver or a Slave-Transmitter.

Here is the sequence of signals generated by a Master-Transmitter, the device that initiates communication with a Slave:

The first event is when a Master-transmitter generates a start condition, which is a high-to-low transition of the data (SDA) line while the clock (SCL) line is high. The first thing in all I2C messages is always a Start condition.

The Start condition causes all the Slaves to wake up and listen, so the Master sends the Slave-receiver address, composed of 7 bits, followed by a direction bit (0 to send, 1 to receive), then an acknowledgement (ACK) bit is generated by the Slave-Receiver to say that it has recognised its address and is listening. If the direction bit was 0 to say that the Master wants to send data to the slave, it transmits 8 bits of data, the Slave sends an ACK bit again, the Master sends another 8 bits of data, the Salve an ACK and so on. After every byte of data, the Slave that is receiving the data must send an ACK bit by pulling the Data line low for a moment. If the ACK is missing, this means that nobody received the data and whoever is transmitting stops the transmission. After all the bytes of data are transmitted, the Master closes the communication by generating a Stop condition on the bus, which is a low-to-high transition of the SDA line while the clock (SCL) is high. All I2C messages always end with a Stop condition.

In a more condensed form:

S Start condition
ADDR 7-bit slave address
R/W Data direction bit: 1 to read or 0 to write
DATA 8 bits of data
ACK 1 bit of Acknowledgement
P Stop condition

Sometimes ADDR and R/W are considered as an 8-bit value, with the address in the top 7 bits and the R/W flag in the least significant bit. For example, we might speak of a 7-bit slave address of 42 and a direction bit of 1, while in other moments we might speak of an 8-bit slave address of 85, which means the same thing.

Here is a condensed view of sending two bytes of data from a Master to a Slave:


Hardware view


The AVR32UC3A chip has one I2C controller, and the Mizar32 provides its signals on the Left bus connector BUS2.

The main board of the Mizar32 also has a PCA9540 two-way I2C multiplexer chip which can connect the I2C signals to either one of two more sets of I2C bus pins, the Left and Right I2C buses, one of which is available on the BUS2 and the other on the BUS5 connector. If you use these instead of the main I2C bus pins, you can have twice as many I2C devices in your system. Furthermore, if your hardware design needs two I2C devices that respond the same I2C address, you can put one on the Left I2C bus and one on the Right.

When the Mizar32 is turned on, the multiplexer is not active and the AVR32's I2C signals are only fed to the main I2C bus pins. To have the I2C bus signals appear on the Left I2C bus, you program the value 5 to the multiplexer's control word, to talk with the Right bus you program the value 4 to the control word. To disable the multiplexer, you program 0.

See the code examples below for how to do this using eLua.

The I2C multiplexer and all the main I2C devices on the Mizar32 add-on modules are on the main I2C bus, so you don't have to program the I2C multiplexer to access them. The only devices that the Mizar32 modules place on the Left and Right I2C buses are the EEPROMS on each add-on module.

Bus pins
Signal GPIO Bus pin Notes
SDA PA29 BUS2 pin 10 Main I2C bus data
SCL PA30 BUS2 pin 11 Main I2C bus clock
BUS_SDA_L - BUS2 pin 12 Left I2C bus data
BUS_SCL_L - BUS2 pin 13 Left I2C bus clock
BUS_SDA_R - BUS5 pin 3 Right I2C bus data
BUS_SCL_R - BUS5 pin 4 Right I2C bus clock

I2C address assignment


The following tables show the I2C slave addresses used by the chips on the Mizar32 and its add-on modules.

In the following tables, we give both the 7-bit codes in decimal notation, and the corresponding 8-bit command byte values in hexadecimal.

Addresses used by devices on the Mizar32 main I2C bus
Device 7-bit address
LCD display (commands) 0111110 (62) 7C/7D Reading returns the cursor position
LCD display (data) 0111111 (63) 7E/7F Reading returns the push-buttons
VGA board 24LC512 1010000 (80) A0/A1 (1)
PCF8563 RTC on Ethernet board 1010001 (81) A2/A3
DS1337 RTC on Ethernet board 1101000 (104) D0/D1
I2C multiplexer PCA9540 1110000 (112) E0/E1

(1) The VGA's 24LC512 EEPROM contains the program for the Parallax Propeller chip that runs the VGA board, which is only connected to the Mizar32's main I2C bus if two pairs of contacts, GS4 and GS5, are joined by solder on VGA board, allowing you to program the Propeller chip from the Mizar32 using the Mizar32 Propeller Programmer.

Software view


Alcor6L has an i2c module providing setup, start, address, send/receive byte and stop primitives.

Sending a single command byte to the I2C splitter (eLua)

-- Send a byte to the I2C multiplexer to tell it to enable the left I2C bus

id = 0                    -- which I2C bus to use (there is only one!)
mux_addr = 112            -- the slave address of the I2C multiplexer
mux_disable = 0           -- control word to disable the multiplexer
mux_left = 5              -- control word to enable left bus
mux_right = 4             -- control word to enable right bus

i2c.start( id )
if not i2c.address( id, mux_addr, i2c.TRANSMITTER ) then
  print "The multiplexer did not reply"
  -- enable the multiplexer onto the left bus
  if i2c.write( id, mux_left ) ~= 1 then
    print "The multiplexer did not acknowledge the write"
i2c.stop( id )

Sending a single command byte to the I2C splitter (PicoLisp)

# Send a byte to the i2c multiplexer to tell it to
# enable the left i2c bus

   id 0             # Which i2c bus to use?
   mux-addr 112     # The slave address of the i2c mux
   mux-disable 0    # Control word to disable the mux
   mux-left 4       # Control word to enable left bus
   mux-right 5 )    # Control word to enable right bus

(i2c-start 0)

(if (not (= T (i2c-address id mux-addr *i2c-transmitter*)))
   (prinl "The multiplexer did not reply")
   (if (not (= (i2c-write id mux-left) mux-left))
      (prinl "The multiplexer did not acknowledge the write") ) )

(i2c-stop id)

Please note: You may also download the above code send-data.l from our examples repository on github.

Reading a byte from an I2C device (eLua)

-- Retrieve and print the contents of the I2C splitter's control register

id = 0                    -- which I2C bus to use (there is only one!)
mux_addr = 112            -- the slave address of the I2C multiplexer

i2c.start( id )
if not i2c.address( id, mux_addr, i2c.RECEIVER ) then
  print "The multiplexer did not reply"
  cr = id, 1 ) -- read the control register (one byte)
  -- print the contents of the control register as a decimal number
  print( "Control register = " .. string.byte( cr ) )
i2c.stop( id )

Reading a byte from an I2C device (PicoLisp)

# Retrieve and print the contents of the i2c splitter's
# control register

# misc.l in the Alcor6L codebase contains
# the function PicoLisp function stringToNum.
# misc.l should exist in /mmc
(load "@misc.l")

   id 0           # Which i2c bus to use? (there's only one!)
   mux-addr 112 ) # The slave address of the i2c multiplexer

(i2c-start id)

(if (not (= T (i2c-address id mux-addr *i2c-receiver*)))
   (prinl "The multiplexer did not reply")
   (let (cr (i2c-read id 1)) # Read the control register (one byte)
      (prinl "Control register: " (stringToNum cr)) ) )

(i2c-stop id)

Please note: The above program requires the file misc.l to work correctly. This version of misc.l is different from the one supplied by default with off-the-shelf PicoLisp. You can also download read-data.l from our examples repository on github.

Further reading