ESP32-S3的IIC
引言
ESP32-S3是一款集成了Wi-Fi和蓝牙功能的低成本、多功能微控制器。在这篇博客中,我们将详细介绍ESP32-S3的IIC(Inter-Integrated Circuit)接口,也被称为I2C。
IIC简介
IIC是一种串行、同步、多设备、半双工通信协议,允许同一总线上存在多个主设备和从设备。IIC使用两个双向开漏线:串行数据线(SDA)和串行时钟线(SCL),由电阻上拉。ESP32-S3有2个IIC控制器(也称为端口),负责处理I2C总线上的通信。每个控制器都可以设置为主机或从机。
IIC 总线有如下特点:
- 总线由数据线 SDA 和时钟线 SCL 构成的串行总线,数据线用来传输数据,时钟线用来同步数据收发。
- 总线上每一个器件都有一个唯一的地址识别,所以我们只需要知道器件的地址,根据时
序就可以实现微控制器与器件之间的通信。 - 数据线 SDA 和时钟线 SCL 都是双向线路,都通过一个电流源或上拉电阻连接到正的电
压,所以当总线空闲的时候,这两条线路都是高电平。 - 总线上数据的传输速率在标准模式下可达 100kbit/s,在快速模式下可达 400kbit/s,在高
速模式下可达 3.4Mbit/s。 - 总线支持设备连接。在使用 IIC 通信总线时,可以有多个具备 IIC 通信能力的设备挂载
在上面,同时支持多个主机和多个从机,连接到总线的接口数量只由总线电容 400pF 的限制决
定。IIC 总线挂载多个器件的示意图如下👇
IIC 控制器介绍
ESP32-S3 有两个 IIC 总线接口,根据用户的配置,总线接口可以用作 IIC 主机或从机模式。
IIC 接口特点:
- 可支持标准模式(100Kbit/s)、快速模式(400Kbit/s),速度最高可达 800Kbit/s,但受限于SCL 和 SDA 上拉强度。
- 可支持 7 位寻址模式和 10 位寻址模式
- 可支持双地址(从机地址和从机寄存器地址)寻址模式
驱动程序的功能
I2C 驱动程序管理在 I2C 总线上设备的通信,该驱动程序具备以下功能:
- 在主机模式下读写字节
- 支持从机模式
- 读取并写入寄存器,然后由主机读取/写入
IIC 的基本的读写通讯过程
✨写操作通讯过程图
主机首先在IIC 总线上发送起始信号,那么这时总线上的从机都会等待接收由主机发出的数据。主机接着发送从机地址+0(写操作)组成的8bit 数据,所有从机接收到该 8bit 数据后,自行检验是否是自己的设备的地址,假如是自己的设备地址,那么从机就会发出应答信号。主机在总线上接收到有应答信号后,才能继续向从机发送数据。注意:IIC 总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。
✨读操作通讯过程图
主机向从机读取数据的操作,一开始的操作与写操作有点相似,观察两个图也可以发现,都是由主机发出起始信号,接着发送从机地址+1(读操作)组成的8bit 数据,从机接收到数据验证是否是自身的地址。 那么在验证是自己的设备地址后,从机就会发出应答信号,并向主机返回8bit 数据,发送完之后从机就会等待主机的应答信号。假如主机一直返回应答信号,那么从机可以一直发送数据,也就是图中的(n byte + 应答信号)情况,直到主机发出非应答信号,从机才会停止发送数据。
使用IIC驱动程序
1. 配置驱动程序 -设置初始化参数(如主机模式或从机模式,SDA 和 SCL 使用的 GPIO 管脚,时钟速度等)
使用i2c_config_t
结构体,用于配置 I2C 参数
- 设置 I2C 工作模式 - 从
i2c_mode_t
中选择主机模式或从机模式 - 设置 通信管脚
1️⃣指定 SDA 和 SCL 信号使用的 GPIO 管脚
2️⃣是否启用 ESP32-S3 的内部上拉电阻 - (仅限主机模式)设置 I2C 时钟速度
- (仅限从机模式)设置以下内容:
1️⃣是否应启用 10 位寻址模式
2️⃣定义 从机地址
然后,初始化给定 I2C 端口的配置,使用端口号和 i2c_config_t
作为函数调用参数来调用 i2c_param_config()
函数。
🔮配置示例(主机):
int i2c_master_port = 0; // 定义 I2C 主设备的端口号i2c_config_t conf = {.mode = I2C_MODE_MASTER, // 设置 I2C 模式为主设备模式.sda_io_num = I2C_MASTER_SDA_IO, // 设置 I2C 主设备的数据线(SDA)的 GPIO 号码.sda_pullup_en = GPIO_PULLUP_ENABLE, // 启用 I2C 主设备的数据线(SDA)的上拉电阻.scl_io_num = I2C_MASTER_SCL_IO, // 设置 I2C 主设备的时钟线(SCL)的 GPIO 号码.scl_pullup_en = GPIO_PULLUP_ENABLE, // 启用 I2C 主设备的时钟线(SCL)的上拉电阻.master.clk_speed = I2C_MASTER_FREQ_HZ, // 设置 I2C 主设备的时钟频率.clk_flags = 0, // 可选项,可以使用 I2C_SCLK_SRC_FLAG_* 标志来选择 I2C 源时钟
};
🔮配置示例(从机):
int i2c_slave_port = I2C_SLAVE_NUM; // 定义 I2C 从设备的端口号i2c_config_t conf_slave = {.sda_io_num = I2C_SLAVE_SDA_IO, // 设置 I2C 从设备的数据线(SDA)的 GPIO 号码.sda_pullup_en = GPIO_PULLUP_ENABLE, // 启用 I2C 从设备的数据线(SDA)的上拉电阻.scl_io_num = I2C_SLAVE_SCL_IO, // 设置 I2C 从设备的时钟线(SCL)的 GPIO 号码.scl_pullup_en = GPIO_PULLUP_ENABLE, // 启用 I2C 从设备的时钟线(SCL)的上拉电阻.mode = I2C_MODE_SLAVE, // 设置 I2C 模式为从设备模式.slave.addr_10bit_en = 0, // 设置 I2C 从设备的地址模式为 7 位模式.slave.slave_addr = ESP_SLAVE_ADDR, // 设置 I2C 从设备的地址.slave.maximum_speed = I2C_SLAVE_MAX_SPEED, // 设置 I2C 从设备的最大时钟速度.clk_flags = 0, // 可选项,可以使用 I2C_SCLK_SRC_FLAG_* 标志来选择 I2C 源时钟
};
2. 安装驱动程序- 激活一个 I2C 控制器的驱动,该控制器可为主机也可为从机
i2c_driver_install(i2c_master_port, conf.mode, 0, 0, 0);
3. 根据是为主机还是从机配置驱动程序,选择合适的项目
安装 I2C 驱动程序后, ESP32-S3 即可与其他 I2C 设备通信。
ESP32-S3 的 I2C 控制器在主机模式下负责与 I2C 从机设备建立通信,并发送命令让从机响应,如进行测量并将结果发给主机。
为优化通信流程,驱动程序提供一个名为 命令链接的容器,该容器应填充一系列命令,然后传递给 I2C 控制器执行。
🚨主机模式下通信 - 发起通信(主机模式)
下面的示例展示如何为 I2C 主机构建命令链接,从而向从机发送 n 个字节。建议对照上方👆IIC 的基本的读写通讯过程来理解
下面介绍如何为 “主机写入数据” 设置命令链接及其内部内容:
1️⃣ 使用 i2c_cmd_link_create()
创建一个命令链接。
然后,将一系列待发送给从机的数据填充命令链接:
-
启动位 -
i2c_master_start()
-
从机地址 -
i2c_master_write_byte()
。提供单字节地址作为调用此函数的实参。 -
数据 - 一个或多个字节的数据作
i2c_master_write()
的实参。 -
停止位 -
i2c_master_stop()
函数
i2c_master_write_byte()
和i2c_master_write()
都有额外的实参,规定主机是否应确认其有无接受到 ACK 位。
2️⃣通过调用 i2c_master_cmd_begin()
来触发 I2C 控制器执行命令链接。一旦开始执行,就不能再修改命令链接。
3️⃣命令发送后,通过调用i2c_cmd_link_delete()
释放命令链接使用的资源。
// 创建一个 I2C 命令链
i2c_cmd_handle_t cmd = i2c_cmd_link_create();// 添加开始信号到 I2C 命令链
i2c_master_start(cmd);// 添加从设备地址和写入方向到 I2C 命令链
i2c_master_write_byte(cmd, ( slave_address << 1 ) | WRITE_BIT, ACK_CHECK_EN);// 添加数据到 I2C 命令链
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);// 添加停止信号到 I2C 命令链
i2c_master_stop(cmd);// 开始执行 I2C 命令链
esp_err_t ret = i2c_master_cmd_begin(i2c_master_port, cmd, 1000 / portTICK_RATE_MS);// 删除 I2C 命令链
i2c_cmd_link_delete(cmd);
if (ret == ESP_OK) {// 通信成功
} else {// 通信失败,处理错误
}
主机读取数据
下面的示例展示如何为 I2C 主机构建命令链接,以便从从机读取 n 个字节。
✨如果是在读取数据时,在上图的步骤 4 中,不是用i2c_master_write...
,而是用 i2c_master_read_byte()
和/或 i2c_master_read()
填充命令链接。同样,步骤 5 中配置最后一次的读取,以便主机不提供ACK 位。
🚨从机模式下通信 - 响应主机消息(从机模式)
在 ESP32-S3 作为 I2C 的从设备时,它会响应主设备的消息。以下是从设备模式下通信的基本步骤及其对应的 ESP-IDF API:
-
监听开始信号和地址:从设备监听 I2C 总线,等待主设备发出开始信号和它的地址。这个过程是自动的,你不需要编写任何代码来实现。
-
发送应答:如果从设备检测到了它的地址,它会在 SDA 线上产生一个低电平,表示它已经准备好接收或发送数据。这个过程也是自动的。
-
接收/发送数据:从设备可以接收来自主设备的数据,或者发送数据到主设备。每接收/发送一个字节后,从设备都会发送一个应答。你可以使用
i2c_slave_read_buffer
函数来从 I2C 总线读取数据,或者使用i2c_slave_write_buffer
函数来向 I2C 总线写入数据。
// 读取数据
uint8_t data[DATA_LENGTH];
int size = i2c_slave_read_buffer(I2C_SLAVE_NUM, data, DATA_LENGTH, 1000 / portTICK_RATE_MS);// 写入数据
uint8_t data[DATA_LENGTH] = {0x01, 0x02, 0x03, 0x04, 0x05};
int size = i2c_slave_write_buffer(I2C_SLAVE_NUM, data, DATA_LENGTH, 1000 / portTICK_RATE_MS);
- 监听停止信号:从设备监听 I2C 总线,等待主设备发出停止信号。这个过程是自动的,你不需要编写任何代码来实现。
✨大多数 I2C 驱动程序的函数在成功完成时会返回 ESP_OK
,或在失败时会返回特定的错误代码。
4. 删除驱动程序- 在通信结束时释放 I2C 驱动程序所使用的资源
当使用 i2c_driver_install()
建立 I2C 通信,一段时间后不再需要 I2C 通信时,可以通过调用 i2c_driver_delete()
来移除驱动程序以释放分配的资源。
#include "driver/i2c.h"// 定义 I2C 主设备的配置参数
#define I2C_MASTER_SCL_IO 26 // GPIO号码用于I2C主设备的时钟
#define I2C_MASTER_SDA_IO 25 // GPIO号码用于I2C主设备的数据
#define I2C_MASTER_NUM I2C_NUM_0 // I2C端口号码用于主设备
#define I2C_MASTER_FREQ_HZ 100000 // I2C主设备的时钟频率
#define WRITE_BIT I2C_MASTER_WRITE // I2C主设备写入
#define READ_BIT I2C_MASTER_READ // I2C主设备读取
#define ACK_CHECK_EN 0x1 // I2C主设备将检查从设备的应答// 初始化 I2C 主设备
void i2c_master_init()
{int i2c_master_port = I2C_MASTER_NUM; // 定义 I2C 主设备的端口号i2c_config_t conf; // 定义 I2C 配置结构体conf.mode = I2C_MODE_MASTER; // 设置 I2C 模式为主设备模式conf.sda_io_num = I2C_MASTER_SDA_IO; // 设置 I2C 主设备的数据线 GPIO 号码conf.sda_pullup_en = GPIO_PULLUP_ENABLE; // 启用 I2C 主设备的数据线上拉电阻conf.scl_io_num = I2C_MASTER_SCL_IO; // 设置 I2C 主设备的时钟线 GPIO 号码conf.scl_pullup_en = GPIO_PULLUP_ENABLE; // 启用 I2C 主设备的时钟线上拉电阻conf.master.clk_speed = I2C_MASTER_FREQ_HZ; // 设置 I2C 主设备的时钟频率i2c_param_config(i2c_master_port, &conf); // 配置 I2C 主设备的参数i2c_driver_install(i2c_master_port, conf.mode, 0, 0, 0); // 安装 I2C 主设备的驱动程序
}// 主程序
void app_main()
{i2c_master_init(); // 初始化 I2C 主设备// 在这里添加你的I2C通信代码
}
这个程序首先定义了一些 I2C 的配置参数,然后在 i2c_master_init
函数中初始化了 I2C 接口。在 app_main
函数中,我们调用了 i2c_master_init
函数来初始化 I2C 接口,然后你可以在这里添加你的 I2C 通信代码。
参考资料
乐鑫官方文档I2C 驱动程序
正点原子ESP32-S3教程
微软AI Copilot