1 I2C 背景介绍
ESP32 的 I²C(Inter-Integrated Circuit)总线是一个非常常用的串行通信接口,广泛用于与各种外围设备(如传感器、显示屏、EEPROM 等)进行通信。
1.1 I²C 基本原理
I²C 是一种 双线通信协议,使用两根线进行主从式通信:
-
SDA(Serial Data):数据线
-
SCL(Serial Clock):时钟线
-
特点包括:
支持多个主机和多个从设备
每个设备通过地址区分(7 位或 10 位地址)
半双工通信(主机控制通信过程)
1.2 ESP32 上的 I²C 特点
1.2.1 控制器
支持多达两个硬件 I²C 控制器:I2C_NUM_0 和 I2C_NUM_1。 可以使用任意 GPIO 引脚作为 SDA 和 SCL(自由配置)。
| I²C 通道 | SDA 默认引脚 | SCL 默认引脚 |
| ------ | -------- | -------- |
| I2C0 | GPIO21 | GPIO22 |
1.2.2 速率
ESP32 支持以下 I²C 速度:
标准模式:100 kHz
快速模式:400 kHz
高速模式:ESP32 不原生支持 3.4 MHz(需扩展)
1.3 应用范围
- OLED 显示屏(如 SSD1306)
- 温湿度传感器(如 SHT30、BME280)
- EEPROM(如 AT24C32)
- IO 扩展芯片(如 PCF8574)
2 配置流程
包含常用的头文件
#include "driver/i2c.h"
2.1 初始化配置结构体
2.1.1 配置函数
esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf);
作用:配置 I²C 控制器的参数,比如 SDA/SCL 引脚、通信速率、主从模式等。
参数:
i2c_num: 控制器编号(如 I2C_NUM_0)
i2c_conf: 配置结构体 i2c_config_t
返回值:成功返回 ESP_OK
2.1.2 例子
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = GPIO_NUM_21,
.scl_io_num = GPIO_NUM_22,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 100000, // 100kHz
};
i2c_param_config(I2C_NUM_0, &conf);
ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &conf));
2.2 安装驱动
2.2.1 i2c_driver_install
esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode,
size_t slv_rx_buf_len, size_t slv_tx_buf_len, int intr_alloc_flags);
作用:安装 I²C 驱动程序,使配置生效。
参数:
i2c_num: I²C 控制器编号
mode: 通信模式(I2C_MODE_MASTER 或 I2C_MODE_SLAVE)
slv_rx_buf_len / slv_tx_buf_len: 从机接收/发送缓冲区大小(主机模式下设为 0)
intr_alloc_flags: 中断分配标志(通常设为 0)
返回值:成功返回 ESP_OK
2.2.2 案例说明
i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0);
2.3 通信操作
2.3.1 i2c_cmd_link_create
i2c_cmd_handle_t i2c_cmd_link_create();
作用:创建一个 I²C 命令链接句柄,用于构建一次传输过程。
返回值:返回句柄,用于后续写入、读、发送起始信号等操作。
2.3.2 i2c_master_start()
esp_err_t i2c_master_start(i2c_cmd_handle_t cmd_handle);
作用:添加 I²C 起始条件(Start condition),开启通信。
参数:命令句柄 cmd_handle
必须是第一个操作
2.3.3 i2c_master_write_byte()
esp_err_t i2c_master_write_byte(i2c_cmd_handle_t cmd_handle, uint8_t data, bool ack_en);
作用:写入一个字节(通常是地址 + 读写位,或数据)
参数:
data: 要写的字节
ack_en: 是否期望 ACK 应答(设为 true)
2.3.4 i2c_master_write()
esp_err_t i2c_master_write(i2c_cmd_handle_t cmd_handle, const uint8_t *data,
size_t data_len, bool ack_en);
作用:连续写多个字节数据
参数:
data: 数据指针
data_len: 长度
ack_en: 每个字节是否检查 ACK
2.3.5 i2c_master_read()
esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t *data,
size_t data_len, i2c_ack_type_t ack);
作用:读取多个字节的数据
参数:
data: 接收缓冲区
ack: 最后一个字节是否发送 ACK(通常最后一个字节发送 NACK)
2.3.6 i2c_master_stop()
esp_err_t i2c_master_stop(i2c_cmd_handle_t cmd_handle);
作用:添加停止条件(Stop condition),终止通信
2.3.7 i2c_master_cmd_begin()
esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle,
TickType_t ticks_to_wait);
作用:执行上述构建好的命令链
参数:
ticks_to_wait: 超时时间(如 pdMS_TO_TICKS(1000) 表示等待 1000 ms)
2.3.8 i2c_cmd_link_delete()
esp_err_t i2c_cmd_link_delete(i2c_cmd_handle_t cmd_handle);
作用:释放命令链占用的内存资源
2.4 写数据
esp_err_t i2c_master_write_slave(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, size_t len) {
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, data, len, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
2.5 读数据
esp_err_t i2c_master_read_slave(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, size_t len) {
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, data, len - 1, I2C_MASTER_ACK);
}
i2c_master_read_byte(cmd, data + len - 1, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
3 其他
3.1 SP_ERROR_CHECK 宏详解
ESP_ERROR_CHECK 是 ESP-IDF 中一个非常有用的错误处理宏,它用于检查 ESP-IDF 函数的返回值并自动处理错误。
基本功能
ESP_ERROR_CHECK 宏会检查传入的表达式(通常是函数调用)的返回值.
如果返回值不是 ESP_OK(即发生了错误),打印错误信息(包括错误代码和错误位置),调用 abort() 终止程序执行
// 普通方式
esp_err_t err = i2c_param_config(I2C_NUM_0, &conf);
if (err != ESP_OK) {
ESP_LOGE(TAG, "i2c_param_config failed: %s", esp_err_to_name(err));
return;
}
// 使用 ESP_ERROR_CHECK 简化
ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &conf));