Mizar32/I2C

= Introduction =

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:

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.

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.

(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  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" else -- enable the multiplexer onto the left bus if i2c.write( id, mux_left ) ~= 1 then print "The multiplexer did not acknowledge the write" end end i2c.stop( id )

Sending a single command byte to the I2C splitter (PicoLisp)
(setq   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)
 * 1) Send a byte to the i2c multiplexer to tell it to
 * 2) enable the left i2c bus

Please note: You may also download the above code 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" else cr = i2c.read( 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 ) ) end i2c.stop( id )

Reading a byte from an I2C device (PicoLisp)
(load "@misc.l") (setq   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)
 * 1) Retrieve and print the contents of the i2c splitter's
 * 2) control register
 * 1) misc.l in the Alcor6L codebase contains
 * 2) the function PicoLisp function stringToNum.
 * 3) misc.l should exist in /mmc

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

= Further reading =
 * Wikipedia's I2C article
 * The Atmel AT32UC3A datasheet Chapter 24: Two-Wire Interface (TWI).
 * Philips Semiconductors PCA9540 2-channel I2C multiplexer's datasheet
 * Philips Semiconductors I2C address allocation table
 * The I2C module in the eLua reference manual.