🚀 Practice real-world tasks & problems for I2C to build pro-level skills — Click here

I2C Basics

Two-wire communication protocol(Serial-synchronous)

  • SDA (Serial Data Line) – Bidirectional data line.
  • SCL (Serial Clock Line) – Clock signal generated by the master.

The master initiates communication always.

It is a Short-distance communication protocol with a distance of <10cm.

With  I²C, multi-master and multi-slave or both at the same time are possible.

 I²C communication is commonly used to interface such as LCDs, OLED displays, RTC modules, and various sensors.

 

I²C Communication

 I²C Frame

 

 

I²C Frame Description

FrameDescription
Star Condition SCL = HIGH, SDA = HIGH → LOW
7-Bit AddressSlave address sent by master (7 bits).
Read/ Write bit

0 = Write to slave

1 = Read from slave

 ACK/NACK bit

0 = ACK (address received)

1 = NACK (Not received)

Data Byte8 bits of data (read/write).
ACK/NACK bit

0 = ACK (Data received)

1 = NACK (Data not Received)

Stop Condition SCL = HIGH, SDA = LOW → HIGH (End of Communication)

Note: SDA never changes when SCL is HIGH. If it changes, it will be either a start or stop condition.

 

Example frames

    1.I²C master writing data (0x65) to slave (address = 0x35)

     2. I²C master reading data (0x66) from slave (address = 0x08)

Note: 1) Data direction →  MSB to LSB (MSB 1st)
           2) The ideal state of SCL and SDA is HIGH.

     3. DSO output of I²C master sending 0x7B to slave at address 0x33.

     Frame 

     DSO Output

 

 

Pull-up in  I²C Communication

  • SDA and SCL use open-drain connections.
  • Open drain Output→ I²C devices can only pull the bus LOW. They cannot drive HIGH actively.
  • To pull the line High actively → An Internal / External Pull-up needs to be used. Without it, the bus floats and gives garbage data.
  • Pull-up resistor can be from 1kΩ  to 10kΩ. For long distances or high speeds, use a lower value resistor.

 

 

I²C Modes

 I²C modes are defined based on communication speed. Most of the sensors come with only the standard mode.

ModesMaximum Bit Rate
Standard Mode100 kbps
Fast Mode400 kbps
Fast Mode Plus1 Mbps
High-Speed Mode3.4 Mbps
Ultra-Fast-Mode5 Mbps

 

 

I²C Addressing (7-bit Address)

Generally, addresses from 0x00 to 0x07 are reserved for special purposes. 

Address (0x00): It is used to broadcast a message sent to all slave devices, known as a General Call.

 

 

I²C Clock Stretching 

A slave device can pause communication by holding the SCL clock line LOW to force the master to wait.

Gives slaves time to:

  • Process received data
  • Prepare data to send

 

 

I²C Voltage Level Mismatch

Mixed voltages (e.g., 3.3V MCU ↔ 5V sensor) can result in invalid logic levels and can damage devices (overvoltage on I/O pins).

To avoid this, a bidirectional level shifter or I²C buffer with voltage translation can be used.

 

 

I²C Arbitration

In multi-master I²C, when both masters try to initiate communication at the same time instance, then the master that transmits a ZERO (LOW ) bit first gets control of the I²C bus.

Why: Since the Master transmitting ZERO (LOW) will force the SDA line to LOW, although the other master tries to send ONE (HIGH).

Example

Master 1: 0xA5 → 10110010
Master 2: 0xAD → 10110100

Bus matches for 5 bits, at bit-6, master-1 wins and continues.

(Note : In I2C on data line, transmission starts with MSB first.)

 

 

I²C in Micro-Controllers

As an important on-board communication protocol,  I²C is always present in most of the controllers, i.e., AVR, PIC, ARM, and RISC.

Generally following registers are present in controllers. (with reference to AVR ATmega 328P- also known as TWI (Two-Wire Interface)).

Registers Description
TWBR I²C Bit Rate Register: sets the  I²C clock frequency 
TWCR I²C Control Register:  manages interrupts, enables the interface, and controls acknowledgment and start/stop conditions.
TWSR I²C Status Register: Stores the status code of the  I²C (TWI) bus after each operation, sets the clock divider for  I²C.
TWDR I²C Data buffer: holds the data to be transmitted or the data received via  I²C (TWI) 
TWAR I²C Address Register: stores the slave address, enables response to general call address.

Writing  I²C Driver (Master- Writing Data to slave)

  • Set Bitrate - Set the prescaler and desired  I²C bitrate.
  • Send Start Condition - Set the bits in the control register to initiate the start condition.
  • Wait for TWI0NT- wait for action to perform.
  • Send Slave Address + Write Bit .
  • Wait for TWINT - wait for action to perform
  • Send a Data Byte to the slave.
  • Wait for TWINT -wait for action to perform.
  • Send Stop Condition - Stop  I²C & bus release.

 

 

 I²C in Arduino Uno (ATmega328P)

 I²C communication pins in Arduino UNO

 

Arduino I²C (Wire Library) Functions

In Arduino UNO, the Wire.h library is used for  I²C.
The default speed is 100 kHz, but it can be changed to 400 kHz.

FunctionDescription 
Wire.begin()Initialize I²C as master
Wire.begin(address)Initialize I²C as slave with address
Wire.setClock(frequency)Set I²C speed (Hz)
Wire.beginTransmission(address)Start communication with a slave device
Wire.write(data)Send data to buffer.
Wire.endTransmission()Ends transmission started by beginTransmission() and sends all queued bytes to the device.
Wire.requestFrom(address, len)Request bytes from a slave
Wire.available()Check if data is received
Wire.read()Read received byte
Wire.onReceive(handler)Set callback for received data
Wire.onRequest(handler)Set callback for data requests

 

 

Example: Send character ‘A’  from master( Arduino UNO) to slave 0x08 (Arduino UNO) after every 1 second.

Master CodeSlave Code

#include <Wire.h>

void setup() {

  Wire.begin(); // Initialize  I²C as master

}

void loop() {

  Wire.beginTransmission(0x08);  // Slave address 0x08

  Wire.write('A'); // Send character

  Wire.endTransmission();

  delay(1000);

}  

 

 

#include <Wire.h>

void setup() {

  Serial.begin(9600); // initialize the serial communication

  Wire.begin(0x08);  // Initialize I2C in slave mode

  Wire.onReceive(receiveEvent);  // Register callback

}

void loop() { 

}

// Callback function definition

void receiveEvent(int bytesReceived) {

  if (Wire.available()) {

    char receiveData = Wire.read(); // read the received data from master

    // display data received from master

    Serial.print(receiveData);

  }

}