Inter-Integrated Circuit (I2C) is a synchronous, multi-master, multi-slave communication protocol widely used for short-distance communication between microcontrollers and peripherals like sensors, EEPROMs, RTCs, and displays.
Master pulls SDA LOW while SCL is HIGH
7-bit or 10-bit address + R/W
Receiver pulls SDA LOW
8-bit packets
SDA HIGH while SCL HIGH
| START | ADDRESS (7/10 bit) | R/W | ACK | DATA (8-bit) | ACK | STOP |
7-bit: Most common
10-bit: Rare
Devices may use internal registers (8-bit or 16-bit)
Start ā Address ā Register ā Data ā Stop
Start ā Address ā Register ā Restart ā Read ā Stop
#include "driver/i2c.h"
#include "esp_err.h"
#include <string.h>
#define I2C_MASTER_SCL_IO 22
#define I2C_MASTER_SDA_IO 21
#define I2C_MASTER_PORT I2C_NUM_0
#define I2C_MASTER_FREQ_HZ 100000
#define I2C_MASTER_TIMEOUT_MS 1000
static esp_err_t i2c_master_init(void)
{
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
ESP_ERROR_CHECK(i2c_param_config(I2C_MASTER_PORT, &conf));
return i2c_driver_install(I2C_MASTER_PORT, conf.mode, 0, 0, 0);
}
esp_err_t i2c_write_reg8_data8(uint8_t dev_addr, uint8_t reg_addr, uint8_t data)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
i2c_master_write_byte(cmd, data, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
return ret;
}
esp_err_t i2c_read_reg8_data8(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data)
{
if (!data) return ESP_ERR_INVALID_ARG;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(cmd, data, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
return ret;
}
esp_err_t i2c_write_reg8_data16(uint8_t dev_addr, uint8_t reg_addr, uint16_t data)
{
uint8_t tx[2] = { data >> 8, data & 0xFF };
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
i2c_master_write(cmd, tx, 2, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
return ret;
}
esp_err_t i2c_read_reg8_data16(uint8_t dev_addr, uint8_t reg_addr, uint16_t *data)
{
if (!data) return ESP_ERR_INVALID_ARG;
uint8_t rx[2];
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_READ, true);
i2c_master_read(cmd, rx, 1, I2C_MASTER_ACK);
i2c_master_read_byte(cmd, &rx[1], I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
if (ret == ESP_OK) *data = (rx[0] << 8) | rx[1];
return ret;
}
esp_err_t i2c_write_reg16_data8(uint8_t dev_addr, uint16_t reg_addr, uint8_t data)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr >> 8, true);
i2c_master_write_byte(cmd, reg_addr & 0xFF, true);
i2c_master_write_byte(cmd, data, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
return ret;
}
esp_err_t i2c_read_reg16_data8(uint8_t dev_addr, uint16_t reg_addr, uint8_t *data)
{
if (!data) return ESP_ERR_INVALID_ARG;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr >> 8, true);
i2c_master_write_byte(cmd, reg_addr & 0xFF, true);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(cmd, data, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
return ret;
}
esp_err_t i2c_write_reg16_data16(uint8_t dev_addr, uint16_t reg_addr, uint16_t data)
{
uint8_t tx[2] = { data >> 8, data & 0xFF };
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr >> 8, true);
i2c_master_write_byte(cmd, reg_addr & 0xFF, true);
i2c_master_write(cmd, tx, 2, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
return ret;
}
esp_err_t i2c_read_reg16_data16(uint8_t dev_addr, uint16_t reg_addr, uint16_t *data)
{
if (!data) return ESP_ERR_INVALID_ARG;
uint8_t rx[2];
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr >> 8, true);
i2c_master_write_byte(cmd, reg_addr & 0xFF, true);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_READ, true);
i2c_master_read(cmd, rx, 1, I2C_MASTER_ACK);
i2c_master_read_byte(cmd, &rx[1], I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
if (ret == ESP_OK) *data = (rx[0] << 8) | rx[1];
return ret;
}
esp_err_t i2c_read_reg8_buffer(uint8_t dev_addr, uint8_t reg_addr, uint8_t *buf, size_t len)
{
if (!buf || !len) return ESP_ERR_INVALID_ARG;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_READ, true);
if (len > 1) i2c_master_read(cmd, buf, len - 1, I2C_MASTER_ACK);
i2c_master_read_byte(cmd, &buf[len - 1], I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
return ret;
}
HAL_StatusTypeDef stm32_write_mem8_data8(uint16_t dev_addr, uint16_t mem_addr, uint8_t data)
{
return HAL_I2C_Mem_Write(&hi2c1, dev_addr<<1, mem_addr, I2C_MEMADD_SIZE_8BIT, &data, 1, 100);
}
HAL_StatusTypeDef stm32_read_mem8_data8(uint16_t dev_addr, uint16_t mem_addr, uint8_t *data)
{
return HAL_I2C_Mem_Read(&hi2c1, dev_addr<<1, mem_addr, I2C_MEMADD_SIZE_8BIT, data, 1, 100);
}
HAL_StatusTypeDef stm32_write_mem8_data16(uint16_t dev_addr, uint16_t mem_addr, uint16_t data)
{
uint8_t tx[2]={data>>8,data&0xFF};
return HAL_I2C_Mem_Write(&hi2c1, dev_addr<<1, mem_addr, I2C_MEMADD_SIZE_8BIT, tx, 2, 100);
}
HAL_StatusTypeDef stm32_read_mem8_data16(uint16_t dev_addr, uint16_t mem_addr, uint16_t *data)
{
uint8_t rx[2];
HAL_I2C_Mem_Read(&hi2c1, dev_addr<<1, mem_addr, I2C_MEMADD_SIZE_8BIT, rx, 2, 100);
*data=(rx[0]<<8)|rx[1];
return HAL_OK;
}
HAL_StatusTypeDef stm32_write_mem16_data8(uint16_t dev_addr, uint16_t mem_addr, uint8_t data)
{
return HAL_I2C_Mem_Write(&hi2c1, dev_addr<<1, mem_addr, I2C_MEMADD_SIZE_16BIT, &data, 1, 100);
}
HAL_StatusTypeDef stm32_read_mem16_data8(uint16_t dev_addr, uint16_t mem_addr, uint8_t *data)
{
return HAL_I2C_Mem_Read(&hi2c1, dev_addr<<1, mem_addr, I2C_MEMADD_SIZE_16BIT, data, 1, 100);
}
HAL_StatusTypeDef stm32_write_mem16_data16(uint16_t dev_addr, uint16_t mem_addr, uint16_t data)
{
uint8_t tx[2]={data>>8,data&0xFF};
return HAL_I2C_Mem_Write(&hi2c1, dev_addr<<1, mem_addr, I2C_MEMADD_SIZE_16BIT, tx, 2, 100);
}
HAL_StatusTypeDef stm32_read_mem16_data16(uint16_t dev_addr, uint16_t mem_addr, uint16_t *data)
{
uint8_t rx[2];
HAL_I2C_Mem_Read(&hi2c1, dev_addr<<1, mem_addr, I2C_MEMADD_SIZE_16BIT, rx, 2, 100);
*data=(rx[0]<<8)|rx[1];
return HAL_OK;
}
8-bit address ā 1 byte
16-bit address ā 2 bytes (MSB first)
16-bit data ā MSB first