19-ESP32-S3外设IIC

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 总线有如下特点

  1. 总线由数据线 SDA 和时钟线 SCL 构成的串行总线,数据线用来传输数据,时钟线用来同步数据收发。
  2. 总线上每一个器件都有一个唯一的地址识别,所以我们只需要知道器件的地址,根据时
    序就可以实现微控制器与器件之间的通信。
  3. 数据线 SDA 和时钟线 SCL 都是双向线路,都通过一个电流源或上拉电阻连接到正的电
    压,所以当总线空闲的时候,这两条线路都是高电平。
  4. 总线上数据的传输速率在标准模式下可达 100kbit/s,在快速模式下可达 400kbit/s,在高
    速模式下可达 3.4Mbit/s。
  5. 总线支持设备连接。在使用 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:

  1. 监听开始信号和地址:从设备监听 I2C 总线,等待主设备发出开始信号和它的地址。这个过程是自动的,你不需要编写任何代码来实现。

  2. 发送应答:如果从设备检测到了它的地址,它会在 SDA 线上产生一个低电平,表示它已经准备好接收或发送数据。这个过程也是自动的。

  3. 接收/发送数据:从设备可以接收来自主设备的数据,或者发送数据到主设备。每接收/发送一个字节后,从设备都会发送一个应答。你可以使用 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);
  1. 监听停止信号:从设备监听 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

  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是ESP32-C3的I2C(IIC)代码示例: ```c #include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/i2c.h" #define I2C_MASTER_SCL_IO 17 /*!< gpio number for I2C master clock */ #define I2C_MASTER_SDA_IO 16 /*!< gpio number for I2C master data */ #define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master dev */ #define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ #define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ #define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */ void i2c_master_init() { i2c_config_t conf; conf.mode = I2C_MODE_MASTER; conf.sda_io_num = I2C_MASTER_SDA_IO; conf.sda_pullup_en = GPIO_PULLUP_ENABLE; conf.scl_io_num = I2C_MASTER_SCL_IO; conf.scl_pullup_en = GPIO_PULLUP_ENABLE; conf.master.clk_speed = I2C_MASTER_FREQ_HZ; i2c_param_config(I2C_MASTER_NUM, &conf); i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); } void i2c_master_example_task(void *arg) { uint8_t sensor_addr = 0x68; uint8_t reg_addr = 0x75; uint8_t data; i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (sensor_addr << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, reg_addr, true); i2c_master_start(cmd); i2c_master_write_byte(cmd, (sensor_addr << 1) | I2C_MASTER_READ, true); i2c_master_read_byte(cmd, &data, I2C_MASTER_NACK); i2c_master_stop(cmd); i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS); i2c_cmd_link_delete(cmd); printf("I2C master read: %x\n", data); vTaskDelete(NULL); } void app_main() { i2c_master_init(); xTaskCreate(i2c_master_example_task, "i2c_master_example_task", 2048, NULL, 10, NULL); } ``` 这是一个简单的I2C示例,它从I2C从设备(地址0x68)的寄存器地址0x75中读取一个字节。在实际应用中,您需要根据您的设备规格和需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宁子希

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值