1. Basics of the I2C Protocol
I2C (Inter-Integrated Circuit) is a two-wire serial communication protocol with the following core features:
- Two-wire Communication: Only requires two signal lines, SCL (clock line) and SDA (data line)
- Multi-master Multi-slave Architecture: Supports multiple master and slave devices
- Addressing: 7-bit or 10-bit slave addresses (7-bit can address up to 112 devices)
- Speed Grades:
- Standard Mode: 100kHz
- Fast Mode: 400kHz
- High-speed Mode: 3.4MHz
Typical Application Scenarios
- Temperature and Humidity Sensors (SHT30, BME280)
- OLED Displays (SSD1306)
- Real-time Clock Modules (DS1307, DS3231)
- EEPROM Memory (AT24C Series)
2. ESP32 I2C Hardware Features
The ESP32 integrates two I2C controllers:
| Controller | Operating Mode | Maximum Frequency | Features |
|---|---|---|---|
| I2C0 | Master/Slave Mode | 1MHz | Pin Remapping |
| I2C1 | Master/Slave Mode | 1MHz | Pin Remapping |
Key Advantages:
- Hardware FIFO buffer improves communication efficiency
- Supports Clock Stretching
- Programmable digital noise filtering
- Supports 10-bit addressing mode
3. ESP-IDF I2C Programming Interface
1. Configuration Structure
typedef struct {
i2c_mode_t mode; // Operating mode
gpio_num_t sda_io_num; // SDA pin
gpio_pullup_t sda_pullup_en; // SDA pull-up enable
gpio_num_t scl_io_num; // SCL pin
gpio_pullup_t scl_pullup_en; // SCL pull-up enable
uint32_t clk_speed; // Clock frequency (Hz)
uint32_t clk_flags; // Special clock flags
} i2c_config_t;
2. Common API Functions
| Function | Description |
|---|---|
<span>i2c_param_config()</span> |
Configure I2C parameters |
<span>i2c_driver_install()</span> |
Install I2C driver |
<span>i2c_master_write_to_device()</span> |
Master writes data |
<span>i2c_master_read_from_device()</span> |
Master reads data |
<span>i2c_cmd_link_create()</span> |
Create command link |
<span>i2c_cmd_link_delete()</span> |
Delete command link |
4. I2C Communication Examples
1. Master Mode Initialization
#include "driver/i2c.h"
#include "esp_log.h"
#define I2C_PORT I2C_NUM_0
#define SDA_PIN GPIO_NUM_21
#define SCL_PIN GPIO_NUM_22
#define I2C_FREQ_HZ 400000 // 400kHz
void i2c_master_init() {
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = SDA_PIN,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = SCL_PIN,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.clk_speed = I2C_FREQ_HZ,
};
ESP_ERROR_CHECK(i2c_param_config(I2C_PORT, &conf));
ESP_ERROR_CHECK(i2c_driver_install(I2C_PORT, conf.mode, 0, 0, 0));
}
2. Data Write Example (OLED Control)
#define OLED_ADDR 0x3C
void oled_write_command(uint8_t cmd) {
uint8_t buf[2] = {0x00, cmd}; // 0x00 is the command identifier
ESP_ERROR_CHECK(i2c_master_write_to_device(
I2C_PORT,
OLED_ADDR,
buf,
sizeof(buf),
pdMS_TO_TICKS(1000)
));
}
3. Data Read Example (SHT30 Sensor)
#define SHT30_ADDR 0x44
esp_err_t read_sht30(float *temp, float *humidity) {
uint8_t cmd[2] = {0x2C, 0x06}; // Trigger measurement command
uint8_t data[6];
// Send measurement command
ESP_ERROR_CHECK(i2c_master_write_to_device(
I2C_PORT, SHT30_ADDR, cmd, sizeof(cmd), pdMS_TO_TICKS(100)
));
vTaskDelay(pdMS_TO_TICKS(20)); // Wait for measurement to complete
// Read data
ESP_ERROR_CHECK(i2c_master_read_from_device(
I2C_PORT, SHT30_ADDR, data, sizeof(data), pdMS_TO_TICKS(100)
));
// Data conversion
uint16_t raw_temp = (data[0] << 8) | data[1];
uint16_t raw_hum = (data[3] << 8) | data[4];
*temp = -45 + 175 * (float)raw_temp / 65535;
*humidity = 100 * (float)raw_hum / 65535;
return ESP_OK;
}
5. Advanced Application Techniques
1. I2C Device Scanning
void i2c_scanner() {
printf("Scanning I2C bus...\n");
for (int addr = 1; addr < 127; addr++) {
esp_err_t ret = i2c_master_write_to_device(
I2C_PORT, addr, NULL, 0, pdMS_TO_TICKS(50)
);
if (ret == ESP_OK) {
printf("Found device at 0x%02X\n", addr);
}
}
printf("Scan completed\n");
}
2. Composite Operation (Write + Read)
esp_err_t i2c_write_then_read(uint8_t addr,
uint8_t *write_buf, size_t write_len,
uint8_t *read_buf, size_t read_len) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
// Write phase
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true);
if (write_len > 0) {
i2c_master_write(cmd, write_buf, write_len, true);
}
// Read phase
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_READ, true);
if (read_len > 0) {
i2c_master_read(cmd, read_buf, read_len, I2C_MASTER_LAST_NACK);
}
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_PORT, cmd, pdMS_TO_TICKS(1000));
i2c_cmd_link_delete(cmd);
return ret;
}
6. Common Troubleshooting
-
No Response from Device:
- Check physical connections (Are SCL/SDA reversed?)
- Confirm the slave address is correct (Use a scanning tool to verify)
- Check pull-up resistors (Usually 4.7kΩ)
Data Errors:
- Test by lowering the clock frequency
- Check power stability
- Shorten communication cable length
Unstable Communication:
- Increase pull-up resistor value
- Enable built-in I2C noise filtering
- Avoid running parallel to other high-frequency signal lines
7. Performance Optimization Suggestions
- High-speed Mode Configuration:
<span><span>i2c_config_t</span></span><span><span> conf = {</span></span><span><span> .clk_speed = </span></span><span><span>1000000</span></span><span><span>, </span></span><span><span>// 1MHz</span></span><span><span> .clk_flags = I2C_SCLK_SRC_FLAG_FOR_NORMAL </span></span><span><span>// Use main clock source</span></span><span> };</span> - DMA Transfer:
<span><span> i2c_driver_install(I2C_PORT, I2C_MODE_MASTER, </span></span><span><span>8</span></span><span><span> * </span></span><span><span>1024</span></span><span><span>, </span></span><span><span>8</span></span><span><span> * </span></span><span><span>1024</span></span><span><span>, </span></span><span><span>0</span></span><span><span>); </span></span><span><span>// 8KB RX/TX buffer</span></span> - Low Power Optimization:
- Enter sleep mode after communication is complete
- Reduce operating voltage (if the device supports it)