Watch the Video Tutorial: Introduction to I2C

I²C (pronounced I-squared-C) created by Philips Semiconductors for use in communication of peripheral devices within a TV-set and commonly written as ‘I2C’ stands for Inter-Integrated Circuit and allows communication of data between I2C devices over two wires. I²C is sometimes called Two Wire Interface. 

I2C has become one of the most common serial communication protocols in electronics. I2C enables communication between electronic components or IC to IC. Figure 1 below demonstrates how many devices can be connected with this simple two wire systems using only two pins of the microcontroller.  

The devices don’t have to be identical as long as they support I²C protocol. In our illustration, the first device with address 1 is a digital temperature sensor, the second one is a real time clock and the third one is a serial LCD display and the bus could carry on even more devices. 
Communication takes place from the master (PIC) to the individual selected slave only as shown in this illustration, the master sends data to the slave address 2 only.

Figure 1: Slave devices connected to PIC with I²C bus  

Both SCL and SDA lines are “open drain” drivers. This basically means that the chip can drive its output low, but it cannot drive it high. For the line to be able to go high you must provide pull-up resistors to the 5V supply. 
One resistor from the SCL line to the 5V line and another from the SDA line to the 5V line. You only need one set of pull-up resistors for the whole I2C bus, not for each device, as illustrated above.
The value of the resistors is not really critical anything from 1k8 to 47k can be used, their values depend on the length of the bus line. If the resistors are missing, the SCL and SDA lines will always be low nearly 0 volts and the I2C bus will not work. Figure 2 below shows how the two pull up resistors (Rp) are connected.

Pull-up resistors connected to I²C bus

Figure 2: Pull-up resistors connected to I²C bus

I²C bus is suitable when the microcontroller and a device which the microcontroller should exchange data with are in close proximity like preferably on the same PCB. these devices (usually smart devices) can be memories modules like 24AA01 1Kb serial EEPROM, temperature sensors like the TC74, real-time clocks like the DS1307, DACs, ADCs, Input/Output expanders etc.
Similar to serial communication in SPI mode, data transfer in I2C mode is synchronous and bidirectional. 
This time only two pins are used for data transmission, One for DATA: the SDA (Serial Data) and the other for CLOCK: the SCL (Serial Clock) pins. 
This communication can support a maximum of 112 devices on the bus (The specification declares that 128 devices can be connected to the I2C bus, but it also defines 16 reserved addresses) connected in a simple way by using only two valuable I/O pins. 

Advantages of I²C:

  • It’s faster than asynchronous serial allowing large quantities of data to be transferred quickly but it slower than the SPI. The bus can operate at various speeds which are generally dictated by the slowest device on the bus. Common bus speeds are 100Kbs (kilobits per second) in Standard Mode, 400Kbs in fast mode and 1Mbs in High speed mode but a new revision of the specification allows for a maximum throughput of 3.4Mbps in High-speed Mode.
  • Another advantage of using I2C over SPI is the number of pins required. Connecting a single master to a single slave with an SPI bus requires four lines. Each additional slave requires one additional chip select I/O pin on the master. This can become very cumbersome in situations where lots of devices must be slaved to one master.
    I2C requires only two wires, like asynchronous serial, but those two wires can support up to 112 slave devices.
  • Also, unlike SPI, I2C can support a multi-master system, allowing more than one master to communicate with all devices on the bus (although the master devices can’t talk to each other over the bus and must take turns using the bus lines).
  • I2C supports multiple slaves up to 112 slave devices. I²C devices may have external address pins which allows you to assign a unique address to each device and therefore allow multiple of the same device to operate on a single bus.
  • I2C supports slave acknowledgment which means that you can be absolutely sure that you’re actually communicating with something. With SPI, a master can be sending data to nothing at all and have no way to find out.

Disadvantages of I²C:

  • Communication via I2C is more complex than with a USART or SPI communication. The signalling must adhere to a certain protocol for the devices on the bus to recognize it as valid I2C communications.
  • I2C draws more power than other serial communication busses due to the open-drain topology of the communication lines.
  • Since devices can set their communication speed, slower operational devices can delay the operation of faster speed devices.
  • he shared nature of the I2C bus can result in the entire bus hanging when a single device on the bus stops operating. Cycling the power to the bus can be used to restart the bus and restore proper operation.
  • I2C is designed for fairly short range i.e. on the same PCB as the Master or via a relatively short cable compared to RS-232, RS-485, or CAN-bus


Figure 3: I²C bus operation

As this is a synchronous serial  communication, a clock signal is necessary to synchronize the operation of both devices, is always generated by a master device (a microcontroller) and its frequency directly affects the baud rate. When master and slave components are synchronized by the clock, every data exchange is always initiated by the master. Once the MSSP module has been enabled, it waits for a Start condition to occur. The master device first sends the START bit (logic zero) through the SDA pin, then a 7 or 10-bit address of the selected slave device, and finally, the bit which requires data write (0) or read (1) to the device. 
At this point, if the slave address exists on the bus the slave will send an acknowledgment bit to the master. The data is than transferred on the SDA line in the direction that was specified by the master. An acknowledgment bit is sent at the end of each transfered byte by the receiving end of the transmission. The only exception is that when the master is in receive mode and the slave in transmit mode the master will not send an acknowledge bit after the very last bit received.

Lastly the communication is stopped with the master sending a stop command. The start and stop commands are simply a transition from high to low (Start) on the SDA line with SCL high or low to high (Stop) on the SDA line with SCL high. Transitions for the data bits are always performed while SCL is low. The high state is only for the start/stop commands.

I2C Configuration with MPLAB Code Configurator

The MPLAB® Code Configurator (MCC) is a user friendly Graphical User Interface (GUI) plug-in tool for MPLAB® X IDE which generates easy to understand C code that is inserted into an MPLAB® X project, based on the settings peripherals configurations and selections made in the Graphical User Interface (GUI).  The advantage of  MCC , it can generate codes not only for PIC18F but for a wide range of PICs including PIC16F and PIC24 series.

To learn more, read the article: MPLAB® Code Configurator

Watch the Video Tutorial: I²C Configuration 

Below is the configuration of I²C Master. The I²C is under the MSSP in the Device Resources of MPLAB Code Configurator.

  • I²C clock frequency: A clock frequency  of 100KHz is selected. By changing the value in the Baud rate generator, the clock frequency will change accordingly to your desired value.
  • Slew rate control: Slew rate is basically how fast the signal changes from low to high, or vice versa. By limiting this sudden transition, you can reduce ringing from signal reflections, and limit crosstalk between signal lines. At 100kHz, the signal rates are so slow that the slew rate doesn’t really matter but at 400kHz you may experience problems, you’ll have to enable it. As we are setting our clock frequency to 100KHz, we will set the Slew rate option to Standard Speed.
  • Slave Addresses: Here you can specify the format of the slave addresses whether you’re gonna use a 7-bit or a 10-bit address number for your slave devices.
  • Registers tab: In this tab, you have access to the I²C registers for advanced configuration.

Figure 4: Master I²C Configuration

MPLAB Code Configurator generates the functions that you can use to access the I²C Peripheral. Here is the description of some few functions. The full description can be read from the i2c.h file generated by the MPLAB Code Configurator

  • I2C_Initialize(): This routine initializes the i2c driver instance for: index, making it ready for clients to open and use it. This routine must be called before any other I2C routine is called. This routine should only be called once during system initialization.
  • I2C_MasterWrite(): This function handles one i2c master write transaction with the supplied parameters. It will send data to i2c queue, waits for the transaction to complete and returns the result. The parameters are:
    • address: The address of the i2c slave peripheral to be accessed
    • length: The length of the data block to be sent
    • *pdata: A pointer to the block of data to be sent
    • *pstatus: A pointer to the status variable
  • I2C_MasterRead(): This function handles one i2c master read transaction with the supplied parameters. It will send data to i2c queue, waits for the transaction to complete and returns the result. The parameters are:
    • address: The address of the i2c slave peripheral to be accessed
    • length: The length of the data block to be sent
    • *pdata: A pointer to the block of data to be sent
    • *pstatus: A pointer to the status variable
  • I2C_MasterQueueIsEmpty(): This function returns the empty status of the Master queue. You can use this function to check if the queue is empty. This can verify if the Master is currently idle. This function will return True if the queue is empty and false if the queue is not empty.

In the Interfacing Real Time Clock DS1307 with PIC Microcontroller article, an example of how to read and write data to or from an I2C Real Time Clock with MPLAB XC8 Compiler and MPLAB Code Configurator is explained.

I²C Functions with PIC18F Peripheral Library

Programming I²C is not difficult, most of the compilers provide some library routines to use all the I²C functions. From the PIC18F Peripheral Library Help Document found inside your compiler installation directory in: ..Program Files (x86) Microchip xc8 v1.34 docs MPLAB_XC8_Peripheral_Libraries.pdf (assuming you installed your compiler in the Program Files (x86) directory. v1.34 is the version of your compiler, it might be different if you are using a different compiler).
Search for the PIC you are going to use, click on: “CLICK HERE for the Peripheral Library Support Details for this Device” For a 18F2620 family, the I²C functions are from page 1152 in the pdf ).


  • MPLAB is phasing out the PIC18F Peripheral Library which is no longer included in XC8 compiler from XC8 v1.35. You have to download and install them separately into your compiler and they are now called Legacy Peripheral Libraries. It’s now advisable to use MPLAB® Code Configurator 
  • For devices with multiple I²C peripherals, you will have to specify which I²C module you are going to use:
  • For PIC Microcontrollers which don’t have an I²C module or if any input/output other than the default       hardware pins are required, the Software I²C functions can be used. 

Below is a quick descriptions of functions and macros:


OpenI2C: This is the first function to configure the I²C bus. Here you can choose the device to be either the master or the slave: 
OpenI2C(MASTER, SLEW_OFF);       //Initialize I²C module, select device as master and Slew rate Off.
WriteI2C: This function is used to write out a single data byte to the I²C device. 
WriteI2C( unsigned char data );    // Write the content of data variable to the I2C device
ReadI2C: This function is used to read a single byte from I²C bus 
 unsigned char data = ReadI2C( );   //Read from the I2C bus and assign that read value to the data variable
putsI2C: This function is used to write out a data string to the I²C bus. 
getsI2C: This function reads predetermined data string length from the I²C bus.
IdleI2C: This function generates Wait condition until I²C bus is Idle. It is always good to use this function between two commands to make sure one command is finished before starting the other.
IdleI2C( );
CloseI2C: This function turns off the I²C module when you are done.
CloseI2C( );


EnableIntI2C: enables I²C Interrupt
DisableIntI2C: disables I²C Interrupt
SetPriorityIntI2C: sets the priority level for I2C interrupt. 

I2C_Clear_Intr_Status_Bit: Clear I2C Interrupt Status bit
I2C_Intr_Status StopI2C: Macro to return I2C Interrupt Status 
StopI2C: Macro to initiate stop condition
StartI2C: Macro to initiate start condition
RestartI2C: Macro to initiate Restart condition
NotAckI2C: This macro initiates negative acknowledgement condition and waits till the acknowledgement sequence is terminated. 
This macro is applicable only to master.

AckI2C: This macro initiates positive acknowledgement condition and waits till the acknowledgement sequence is terminated. 
This macro is applicable only to master.

DataRdyI2C: This Macro provides status back to user if SSPxBUF register contain data. 
putcI2C: This macro is identical to WriteI2C
getcI2C: This macro is identical to ReadI2C
In the Interfacing Real Time Clock DS1307 with PIC Microcontroller article, an example of how to read and write data to or from an I2C Real Time Clock with MPLAB XC8 Compiler is explained.