文章目录
- 前言
- 一、I2C驱动使用的步骤
- 二、I2C的使用
- 2.1 配置驱动程序
- 2.2 安装驱动程序
- 2.3 主机写入数据
- 写入数据的过程
- 接收数据的过程
- 总结
前言
ESP32是一款强大的微控制器,广泛应用于物联网(IoT)和嵌入式系统开发。它具备丰富的硬件接口,其中之一是I2C(Inter-Integrated Circuit),这是一种用于短距离通信的串行通信协议。I2C接口常用于连接各种外部传感器、存储器和其他外设,使得ESP32能够轻松实现与外部设备的数据交换。
本文将介绍如何在ESP32 IDF中使用I2C接口,提供一个简单而实用的示例代码。通过本文,您将了解如何初始化I2C主机、进行数据写入以及连接和配置I2C从设备的关键步骤。
一、I2C驱动使用的步骤
以下部分将指导您完成 I2C 驱动程序配置和工作的基本步骤:
-
配置驱动程序 - 设置初始化参数(如主机模式或从机模式,SDA 和 SCL 使用的 GPIO 管脚,时钟速度等)
-
安装驱动程序- 激活一个 I2C 控制器的驱动,该控制器可为主机也可为从机
-
根据是为主机还是从机配置驱动程序,选择合适的项目
-
主机模式下通信 - 发起通信(主机模式)
-
从机模式下通信 - 响应主机消息(从机模式)
-
-
中断处理 - 配置和 I2C 中断服务
-
用户自定义配置 - 调整默认的 I2C 通信参数(如时序、位序等)
-
错误处理 - 如何识别和处理驱动程序配置和通信错误
-
删除驱动程序- 在通信结束时释放 I2C 驱动程序所使用的资源
二、I2C的使用
2.1 配置驱动程序
如果你需要使用I2C,你需要这个结构体:i2c_config_t
,他的结构体定义如下:
typedef struct{
i2c_mode_t mode; /*!< I2C mode */
int sda_io_num; /*!< GPIO number for I2C sda signal */
int scl_io_num; /*!< GPIO number for I2C scl signal */
bool sda_pullup_en; /*!< Internal GPIO pull mode for I2C sda signal*/
bool scl_pullup_en; /*!< Internal GPIO pull mode for I2C scl signal*/
union {
struct {
uint32_t clk_speed; /*!< I2C clock frequency for master mode, (no higher than 1MHz for now) */
} master; /*!< I2C master config */
struct {
uint8_t addr_10bit_en; /*!< I2C 10bit address mode enable for slave mode */
uint16_t slave_addr; /*!< I2C address for slave mode */
} slave; /*!< I2C slave config */
};
uint32_t clk_flags; /*!< Bitwise of ``I2C_SCLK_SRC_FLAG_**FOR_DFS**`` for clk source choice*/
} i2c_config_t;
mode:
用途:设置I2C接口的工作模式,可以是主机模式(I2C_MODE_MASTER)或从机模式(I2C_MODE_SLAVE)。
主机模式(Master Mode)
:
在主机模式下,ESP32充当I2C总线的主设备,负责启动和控制通信。主机模式用于与一个或多个I2C从设备进行通信。主机发送起始信号、地址、数据和停止信号,控制整个通信过程。
从机模式(Slave Mode)
:
在从机模式下,ESP32作为I2C总线上的从设备,等待主机发起通信。ESP32从设备接收来自主机的地址和数据,并根据主机的命令执行相应的操作。从机模式使ESP32能够与其他I2C主机设备通信,实现更复杂的系统互联。
我们使用主机模式即可
sda_io_num:
用途:指定I2C总线的数据线(SDA)连接的GPIO引脚的编号。
scl_io_num:
用途:指定I2C总线的时钟线(SCL)连接的GPIO引脚的编号。
sda_pullup_en:
用途:控制SDA引脚的内部上拉电阻是否启用。
scl_pullup_en:
用途:控制SCL引脚的内部上拉电阻是否启用。
master(union内部的结构体):
用途:当mode为主机模式时,用于配置主机模式下的具体参数。
clk_speed:设置I2C主机模式下的时钟频率,即通信速率。
slave(union内部的结构体):
用途:当mode为从机模式时,用于配置从机模式下的具体参数。
addr_10bit_en:启用或禁用I2C从机模式下的10位地址模式。
slave_addr:设置I2C从机模式下的从机地址。
clk_flags:
用途:通过使用位掩码(bitwise flags)来指定时钟源的选择,具体取值可以是I2C_SCLK_SRC_FLAG_CORE或I2C_SCLK_SRC_FLAG_DFS。
接下来,我们需要为I2C结构体进行初始化,使用这个函数:
esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf)
i2c_num:
意义:指定要配置的I2C总线编号,即I2C控制器的索引。
取值:I2C_NUM_0 或 I2C_NUM_1,表示要配置的是I2C0或I2C1。
i2c_conf:
意义:指向一个 i2c_config_t 类型的结构体,用于配置I2C总线的参数。
取值:通常情况下,可以通过填充 i2c_config_t 结构体的各个成员来指定具体的配置参数。前面已经介绍过 i2c_config_t 结构体的成员及其作用。
2.2 安装驱动程序
我们可以使用下面这个函数来安装驱动程序:
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)
i2c_num:
含义:指定要安装和初始化的I2C总线编号,即I2C控制器的索引。
取值:I2C_NUM_0 或 I2C_NUM_1,表示要初始化的是I2C0或I2C1。
mode:
含义:指定I2C总线的工作模式。
取值:可以是 I2C_MODE_MASTER(主机模式)或 I2C_MODE_SLAVE(从机模式)。
slv_rx_buf_len:
含义:指定从机模式下接收缓冲区的大小。
取值:正整数,表示从机模式下接收缓冲区的长度,单位是字节。如果不使用从机模式,则可以将其设置为0。
slv_tx_buf_len:
含义:指定从机模式下发送缓冲区的大小。
取值:正整数,表示从机模式下发送缓冲区的长度,单位是字节。如果不使用从机模式,则可以将其设置为0。
intr_alloc_flags:
含义:指定中断分配的标志位。
取值:可以使用ESP_INTR_FLAG_*系列宏来设置,用于指定中断服务程序的优先级、CPU核心等信息。通常可以选择 ESP_INTR_FLAG_IRAM 或 ESP_INTR_FLAG_SHARED。
如果不使用,写0即可
2.3 主机写入数据
写入数据的过程
主机发送数据有如下的过程:
-
使用
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 位。 -
通过调用
i2c_master_cmd_begin()
来触发 I2C 控制器执行命令链接。一旦开始执行,就不能再修改命令链接。 -
命令发送后,通过调用
i2c_cmd_link_delete()
释放命令链接使用的资源。
我们就可以写出下面这个函数
// 发送数据到I2C从设备
static esp_err_t i2c_master_send(uint8_t *data, size_t size) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (I2C_SLAVE_ADDR << 1) | I2C_MASTER_WRITE, true);
i2c_master_write(cmd, data, size, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(1000));
i2c_cmd_link_delete(cmd);
return ret;
}
接收数据的过程
在读取数据时,在上图的步骤 4 中,不是用 i2c_master_write…,而是用 i2c_master_read_byte() 和/或 i2c_master_read() 填充命令链接。同样,在步骤 5 中配置最后一次的读取,以便主机不提供 ACK 位。
我们可以写出下面这个代码:
// 从I2C从设备接收数据
static esp_err_t i2c_master_receive(uint8_t *data, size_t size) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (I2C_SLAVE_ADDR << 1) | I2C_MASTER_READ, true);
if (size > 1) {
i2c_master_read(cmd, data, size - 1, I2C_MASTER_ACK);
}
i2c_master_read_byte(cmd, data + size - 1, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(1000));
i2c_cmd_link_delete(cmd);
return ret;
}
我们实现完这几个函数就可以去实现我们的I2C操作了。如果大家需要详细的代码可以进入我的资源
找到ESP32-IDF I2C代码
总结
通过本文,我们深入了解了ESP32 IDF中I2C接口的基本使用方法。首先,我们学习了如何初始化I2C主机,包括配置GPIO引脚和设置时钟频率等关键步骤。接着,我们展示了一个简单的示例代码,演示了如何使用ESP32与I2C从设备通信,向其写入数据。
这个示例代码不仅展示了ESP32 IDF中I2C API的基本用法,还为开发者提供了一个起点,可根据实际需求进行定制和扩展。通过理解I2C接口的基本原理和ESP32 IDF中的相关API,开发者可以更轻松地与各种I2C设备进行集成,从而实现更复杂的嵌入式系统和物联网应用。
总体而言,ESP32 IDF为开发者提供了丰富而强大的工具,使得利用ESP32的I2C接口进行通信变得简单而高效。这为物联网和嵌入式系统领域的开发者们带来了更多可能性,同时也推动着这一领域的不断创新和发展。