I2C Protocol Fundamentals
Basic Characteristics
- Developed by Philips in the 1980s
- Serial communication protocol
- Uses two wires: SDA (Serial Data) and SCL (Serial Clock)
- Supports multiple master and slave devices
- Low-speed communication (standard modes: 100 kbps, 400 kbps, high-speed modes up to 5 Mbps)
Protocol Structure
- Physical Layer
- Two-wire communication
- Open-drain/open-collector architecture
- Requires pull-up resistors
- Devices can be masters or slaves
- Supports multiple devices on the same bus
- Communication Mechanism
- Master initiates all communications
- Uses START and STOP conditions to control bus
- 7-bit or 10-bit addressing
- Data transferred in 8-bit packets
- Includes acknowledgment (ACK) bit after each byte
Communication Sequence
Copy1. START Condition
2. Slave Address (7/10 bit)
3. R/W Bit (Read/Write)
4. Acknowledgment
5. Data Transfer
6. STOP Condition
Detailed Communication Steps
- START Condition
- SDA transitions from HIGH to LOW while SCL is HIGH
- Signals the beginning of communication
- Address Phase
- Master sends slave address
- 7-bit address + 1 bit R/W
- Slaves compare address with their own
- Acknowledgment
- Receiver pulls SDA LOW to acknowledge receipt
- Indicates successful data transfer
- Data Transfer
- 8 bits transferred per packet
- Most significant bit sent first
- Alternates between master transmitting and receiving
Basic I2C Protocol Structure
Copy// Typical I2C communication structure
typedef struct {
uint8_t device_address; // 7-bit slave address
uint8_t* data_buffer; // Data buffer for transmission/reception
uint16_t data_length; // Length of data
} I2C_Transaction;
Core I2C Communication Sequence
// Simplified I2C communication flow
bool i2c_write(I2C_Transaction* transaction) {
// Start Condition
i2c_send_start();
// Send Slave Address (Write Mode)
i2c_send_address(transaction->device_address, WRITE_MODE);
// Send Data Bytes
for (int i = 0; i < transaction->data_length; i++) {
if (!i2c_send_byte(transaction->data_buffer[i])) {
// Handle transmission error
i2c_send_stop();
return false;
}
// Wait for Acknowledgement
if (!i2c_wait_ack()) {
i2c_send_stop();
return false;
}
}
// Stop Condition
i2c_send_stop();
return true;
}
Detailed Protocol Implementation
cCopy// Low-level I2C communication functions
void i2c_send_start() {
// Pull SDA low while SCL is high
// Indicates start of communication
SET_SDA_LOW();
SET_SCL_LOW();
}
void i2c_send_stop() {
// Pull SDA high after pulling SCL high
// Indicates end of communication
SET_SDA_LOW();
SET_SCL_HIGH();
SET_SDA_HIGH();
}
bool i2c_send_byte(uint8_t data) {
// Send 8 bits, most significant bit first
for (int i = 7; i >= 0; i--) {
// Set data bit
if (data & (1 << i)) {
SET_SDA_HIGH();
} else {
SET_SDA_LOW();
}
// Clock pulse
SET_SCL_HIGH();
SET_SCL_LOW();
}
return true;
}
bool i2c_wait_ack() {
// Slave pulls SDA low to acknowledge
SET_SDA_HIGH(); // Release SDA
SET_SCL_HIGH(); // Clock high for acknowledgement
// Check if slave pulled SDA low
if (READ_SDA() == LOW) {
SET_SCL_LOW();
return true;
}
return false;
}
- Advanced I2C Read Operation
Copybool i2c_read(I2C_Transaction* transaction) {
// Start Condition
i2c_send_start();
// Send Slave Address (Read Mode)
i2c_send_address(transaction->device_address, READ_MODE);
// Receive Data Bytes
for (int i = 0; i < transaction->data_length; i++) {
transaction->data_buffer[i] = i2c_read_byte();
// Send Acknowledgement for all bytes except last
if (i < transaction->data_length - 1) {
i2c_send_ack();
} else {
// Send NACK for last byte
i2c_send_nack();
}
}
// Stop Condition
i2c_send_stop();
return true;
}
- Addressing Modes
// I2C Addressing
#define GENERAL_CALL_ADDRESS 0x00
#define WRITE_MODE 0
#define READ_MODE 1
void i2c_send_address(uint8_t address, bool read_mode) {
// Combine 7-bit address with R/W bit
uint8_t full_address = (address << 1) | read_mode;
i2c_send_byte(full_address);
}
- Error Handling and Arbitration
typedef enum {
I2C_NO_ERROR,
I2C_TIMEOUT,
I2C_NACK_ERROR,
I2C_BUS_BUSY
} I2C_Error;
I2C_Error i2c_master_transfer(I2C_Transaction* transaction) {
// Check bus availability
if (i2c_is_bus_busy()) {
return I2C_BUS_BUSY;
}
// Perform transaction with error checking
if (!i2c_write(transaction)) {
return I2C_NACK_ERROR;
}
return I2C_NO_ERROR;
}
Key Protocol Characteristics:
- Serial Communication
- Uses two lines: SDA (data) and SCL (clock)
- Synchronous communication
- Half-duplex mode
- Addressing Mechanism
- 7-bit or 10-bit addressing
- Supports multiple masters and slaves
- Each slave has a unique address
- Communication Phases
- Start Condition
- Address Transmission
- Data Transfer
- Acknowledgement
- Stop Condition
- Timing Considerations
- Standard Mode: 100 Kbps
- Fast Mode: 400 Kbps
- High-Speed Mode: 3.4 Mbps
Practical Considerations:
- Use pull-up resistors on SDA and SCL lines
- Implement timeout mechanisms
- Check bus availability before transmission
- Handle potential bus contention
Common Use Cases:
- Sensor interfaces
- EEPROM communication
- Real-time clock modules
- Low-speed peripheral communication
Limitations:
- Limited bandwidth compared to SPI
- More complex protocol overhead
- Potential for bus contention with multiple masters
Sample Device Interaction:
// Example: Reading temperature from I2C sensor
I2C_Transaction temp_sensor = {
.device_address = 0x48, // Example sensor address
.data_buffer = temperature_data,
.data_length = 2
};
if (i2c_read(&temp_sensor) == I2C_NO_ERROR) {
// Process temperature data
process_temperature(temperature_data);
}
Debugging Tips:
- Use logic analyzers to view I2C communication
- Implement detailed error logging
- Verify timings and acknowledgement sequences
This comprehensive overview covers the programmatic implementation of the I2C protocol, highlighting its core mechanisms, communication flow, and practical considerations.