Back
Loading views...I2C
I2C Protocol
Overview
I2C (Inter-Integrated Circuit) is a two-wire synchronous serial protocol invented by Philips. It supports multiple masters and multiple slaves on the same bus using 7-bit or 10-bit addressing. It is widely used for sensors, EEPROMs, RTCs, and display controllers in embedded systems.
1. Theory & Fundamentals
- Problem solved: Connect multiple ICs with just 2 wires using addressing
- Physical layer: Open-drain, requires pull-up resistors (typically 4.7kΩ)
- Wires: SDA (data), SCL (clock)
- Addressing: 7-bit (112 devices) or 10-bit (extended)
- Speeds: Standard (100kHz), Fast (400kHz), Fast+ (1MHz), High-speed (3.4MHz)
- Multi-master: Supported with arbitration
2. Frame / Packet Structure
START ADDR(7-bit) R/W ACK DATA(8-bit) ACK ... STOP
S A6..A0 0/1 A D7..D0 A P
- START: SDA falls while SCL is HIGH
- STOP: SDA rises while SCL is HIGH
- ACK: Receiver pulls SDA LOW on 9th clock
- NACK: Receiver releases SDA (HIGH) — signals error or end
3. Protocol Mechanics
- Clock stretching: Slave can hold SCL LOW to pause master
- Arbitration: Multi-master — if two masters drive simultaneously, loser detects conflict and backs off
- Repeated START: Change direction without releasing bus
- ACK polling: Used with EEPROM write cycle completion check
4. Hardware Implementation
- Pull-up resistors: 4.7kΩ for 100kHz, 2.2kΩ for 400kHz, 1kΩ for 1MHz
- Level shifting: PCA9306 for mixed 3.3V/5V systems
- Bus capacitance: Max 400pF limits cable length (~1–2m)
- Common I2C ICs: 24LC256 (EEPROM), DS3231 (RTC), BMP280 (pressure), SSD1306 (OLED)
- I2C mux: PCA9548A for multiple identical-address devices
5. Register-Level Programming (STM32)
// Init I2C1 at 100kHz, 8MHz APB1 clock
I2C1->CR2 = 8; // APB1 freq in MHz
I2C1->CCR = 40; // 100kHz: CCR = fAPB/(2*fSCL) = 8M/(2*100k)
I2C1->TRISE = 9; // Max rise time: (1000ns / 125ns) + 1
I2C1->CR1 |= I2C_CR1_PE; // Enable I2C
// Write byte to slave
void I2C_Write(uint8_t addr, uint8_t reg, uint8_t data) {
I2C1->CR1 |= I2C_CR1_START;
while (!(I2C1->SR1 & I2C_SR1_SB));
I2C1->DR = addr << 1; // Write mode
while (!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2; // Clear ADDR
I2C1->DR = reg;
while (!(I2C1->SR1 & I2C_SR1_TXE));
I2C1->DR = data;
while (!(I2C1->SR1 & I2C_SR1_BTF));
I2C1->CR1 |= I2C_CR1_STOP;
}
6. Driver Development
uint8_t I2C_ReadReg(uint8_t addr, uint8_t reg) {
// Write register address
I2C_Start(); I2C_SendAddr(addr, WRITE); I2C_SendByte(reg);
// Repeated START then read
I2C_RepeatedStart(); I2C_SendAddr(addr, READ);
uint8_t data = I2C_ReadByte(NACK);
I2C_Stop();
return data;
}
void I2C_ReadBurst(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t len) {
I2C_Start(); I2C_SendAddr(addr, WRITE); I2C_SendByte(reg);
I2C_RepeatedStart(); I2C_SendAddr(addr, READ);
for (int i = 0; i < len - 1; i++) buf[i] = I2C_ReadByte(ACK);
buf[len-1] = I2C_ReadByte(NACK);
I2C_Stop();
}
7. Debugging & Testing
- Logic analyzer: Decode with correct I2C protocol, check address
- Common issues:
- Missing pull-ups → no signal
- Wrong pull-up value → slow edges or bus lockup
- Address conflict → unexpected NACKs
- Clock stretching ignored → data corruption
- Bus lockup → SDA stuck LOW (clock 9 pulses to recover)
- I2C scanner: Probe all 128 addresses to discover devices
8. Real-World Applications
- BMP280: Read temperature and pressure via I2C
- SSD1306 OLED: Send pixel commands over I2C
- DS3231 RTC: Read/write time registers
- 24LC256 EEPROM: Store configuration data with ACK polling
- MPU-6050 IMU: Stream 6-axis data via I2C with interrupt
9. Advanced Topics & Edge Cases
- 10-bit addressing: Extended addressing for larger systems
- SMBus: I2C superset with defined timeouts and protocols
- I2C multiplexer: PCA9548A — expand to 8 sub-buses
- Software I2C: Bit-bang on any GPIO when hardware I2C unavailable
- I2C over long distance: Use active buffers (P82B96)
- ACK polling for EEPROM: Retry after write until device responds
10. Standards & Variants
| Mode | Speed | Use case |
|---|---|---|
| Standard | 100kHz | Sensors, EEPROMs |
| Fast | 400kHz | Most modern sensors |
| Fast+ | 1MHz | High-speed sensors |
| High-speed | 3.4MHz | Display drivers |
| Ultra-fast | 5MHz | Unidirectional only |
💡 Practical Examples
Example 1: I2C Scanner
for (uint8_t addr = 1; addr < 127; addr++) {
if (I2C_Probe(addr) == ACK)
printf("Device at 0x%02X\n", addr);
}
Example 2: Read BMP280 temperature
uint8_t msb = I2C_ReadReg(0x76, 0xFA);
uint8_t lsb = I2C_ReadReg(0x76, 0xFB);
int32_t raw = (msb << 12) | (lsb << 4);
// Apply compensation formula...
Example 3: Bus recovery (stuck SDA)
void I2C_BusRecover(void) {
// Clock 9 times to release any stuck slave
for (int i = 0; i < 9; i++) {
SCL_HIGH(); delay_us(5);
SCL_LOW(); delay_us(5);
}
I2C_Stop(); // Generate STOP condition
}
🧪 Practice Questions
Beginner
- Why does I2C need pull-up resistors?
- What is the purpose of the ACK bit?
- How many devices can share one I2C bus?
- What is a repeated START condition used for?
- What is the difference between 7-bit and 10-bit addressing?
Intermediate
- How do you calculate the correct pull-up resistor value for 400kHz I2C?
- What causes I2C bus lockup and how do you recover?
- Write a burst read function for reading 6 bytes from MPU-6050.
- How does I2C multi-master arbitration work?
- Explain clock stretching and when a slave uses it.
Advanced
- Implement a complete EEPROM driver with page write and ACK polling.
- How would you use a PCA9548A to talk to 8 sensors with the same address?
- Design a software I2C implementation on bit-bang GPIO.
- What limits I2C bus speed and how do you push to 1MHz (Fast+)?
- How would you debug intermittent I2C NACKs in a noisy industrial environment?
Hands-on Projects
- Environmental Monitor: Read temp/humidity (SHT31) and pressure (BMP280) over I2C, display on SSD1306 OLED.
- I2C EEPROM Logger: Store timestamped data in 24LC256 with wear leveling.
- Multi-sensor Hub: Use PCA9548A to read 8 identical sensors on one bus.
Checklist
- [ ] Draw I2C START, data, ACK, and STOP waveforms
- [ ] Calculate pull-up resistor values for different speeds
- [ ] Write bare-metal I2C master driver
- [ ] Implement I2C scanner
- [ ] Read multi-byte sensor data with burst read
- [ ] Handle NACK and implement retries
- [ ] Implement I2C bus recovery sequence
- [ ] Use I2C with EEPROM including ACK polling
- [ ] Debug I2C with logic analyzer
- [ ] Implement software (bit-bang) I2C
- [ ] Use I2C multiplexer (PCA9548A)
- [ ] Configure I2C DMA for high-throughput sensors