ESP32_IDF学习(7)--I2C

本文详细介绍了ESP32如何配置和使用I2C通信,包括驱动程序的配置、主机和从机模式的通信、中断处理、用户自定义配置以及错误处理。内容涵盖I2C的基础知识、ESP32的两个I2C控制器功能、驱动程序的安装和删除,以及主机和从机模式下的数据读写操作。此外,还讨论了时钟源配置和中断服务注册,以及如何处理通信错误和资源释放。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

I2C 是一种串行同步半双工通信协议,总线上可以同时挂载多个主机和从机。I2C 总线由串行数据线 (SDA) 和串行时钟线 (SCL) 线构成。这些线都需要上拉电阻。

I2C 具有简单且制造成本低廉等优点,主要用于低速外围设备的短距离通信(一英尺以内)。

ESP32 有两个 I2C 控制器(也称为端口),负责处理在 I2C 两根总线上的通信。每个控制器都可以设置为主机或从机。例如,可以同时让一个控制器用作主机,另一个用作从机。

驱动程序的功能

I2C 驱动程序管理在 I2C 总线上设备的通信,该驱动程序具备以下功能:

  • 在主机模式下读写字节
  • 支持从机模式
  • 读取并写入寄存器,然后由主机读取/写入

使用驱动程序

以下部分将指导您完成 I2C 驱动程序配置和工作的基本步骤:

  1. 配置驱动程序 - 设置初始化参数(如主机模式或从机模式,SDA 和 SCL 使用的 GPIO 管脚,时钟速度等)

  2. 安装驱动程序 - 激活一个 I2C 控制器的驱动,该控制器可为主机也可为从机

  3. 根据是为主机还是从机配置驱动程序,选择合适的项目

    • 主机模式下通信 - 发起通信(主机模式)
    • 从机模式下通信 - 响应主机消息(从机模式)
  4. 中断处理 - 配置和 I2C 中断服务

  5. 用户自定义配置 - 调整默认的 I2C 通信参数(如时序、位序等)

  6. 错误处理 - 如何识别和处理驱动程序配置和通信错误

  7. 删除驱动程序 - 在通信结束时释放 I2C 驱动程序所使用的资源

配置驱动程序

建立 I2C 通信第一步是配置驱动程序,这需要设置 i2c_config_t 结构中的几个参数:

  • 设置 I2C 工作模式 - 从i2c_mode_t中选择主机模式或从机模式

  • 设置 通信管脚

    • 指定 SDA 和 SCL 信号使用的 GPIO 管脚
    • 是否启用ESP32的内部上拉电阻
  • (仅限主机模式)设置 I2C 时钟速度

  • (仅限从机模式)设置以下内容:

    • 是否应启用 10 位寻址模式
    • 定义 从机地址

然后,初始化给定 I2C 端口的配置,请使用端口号和i2c_config_t 作为函数调用参数来调用 i2c_param_config() 函数。

配置示例(主机):

int i2c_master_port = 0;
i2c_config_t conf = {
    .mode = I2C_MODE_MASTER,
    .sda_io_num = I2C_MASTER_SDA_IO,         // select GPIO specific to your project
    .sda_pullup_en = GPIO_PULLUP_ENABLE,
    .scl_io_num = I2C_MASTER_SCL_IO,         // select GPIO specific to your project
    .scl_pullup_en = GPIO_PULLUP_ENABLE,
    .master.clk_speed = I2C_MASTER_FREQ_HZ,  // select frequency specific to your project
    // .clk_flags = 0,          /*!< Optional, you can use I2C_SCLK_SRC_FLAG_* flags to choose i2c source clock here. */
};

配置示例(从机):

int i2c_slave_port = I2C_SLAVE_NUM;
i2c_config_t conf_slave = {
    .sda_io_num = I2C_SLAVE_SDA_IO,          // select GPIO specific to your project
    .sda_pullup_en = GPIO_PULLUP_ENABLE,
    .scl_io_num = I2C_SLAVE_SCL_IO,          // select GPIO specific to your project
    .scl_pullup_en = GPIO_PULLUP_ENABLE,
    .mode = I2C_MODE_SLAVE,
    .slave.addr_10bit_en = 0,
    .slave.slave_addr = ESP_SLAVE_ADDR,      // address of your project
};

在此阶段,i2c_param_config 还将其他 I2C 配置参数设置为 I2C 总线协议规范中定义的默认值。有关默认值及修改默认值的详细信息,请参考用户自定义配置。

源时钟配置

增加了 时钟源分配器,用于支持不同的时钟源。时钟分配器将选择一个满足所有频率和能力要求的时钟源(如i2c_config_t::clk_flag 中的要求)。

i2c_config_t::clk_flags 为 0 时,时钟分配器将仅根据所需频率进行选择。如果不需要诸如 APB 之类的特殊功能,则可以将时钟分配器配置为仅根据所需频率选择源时钟。为此,请将 i2c_config_t::clk_flags设置为 0。有关时钟特性,请参见下表。

如果时钟不满足请求的功能,则该时钟不是有效的选项,即,请求的功能中的任何位(clk_flags)在时钟的功能中均为 0。

时钟名称时钟频率SCL的最大频率时钟功能
APB时钟80MHZ4MHZ/

i2c_config_t::clk_flags的解释如下:

  1. I2C_SCLK_SRC_FLAG_AWARE_DFS:当 APB 时钟改变时,时钟的波特率不会改变。
  2. I2C_SCLK_SRC_FLAG_LIGHT_SLEEP:支持轻度睡眠模式,APB 时钟则不支持。
  3. ESP32 可能不支持某些标志,请在使用前阅读技术参考手册。

在主机模式下,SCL 的时钟频率不应大于上表中提到的 SCL 的最大频率。

安装驱动程序

配置好 I2C 驱动程序后,使用以下参数调用函数 i2c_driver_install 安装驱动程序:

  • 端口号,从i2c_port_t 中二选一
  • 主机或从机模式,从i2c_mode_t 中选择
  • (仅限从机模式)分配用于在从机模式下发送和接收数据的缓存区大小。I2C 是一个以主机为中心的总线,数据只能根据主机的请求从从机传输到主机。因此,从机通常有一个发送缓存区,供从应用程序写入数据使用。数据保留在发送缓存区中,由主机自行读取。
  • 用于分配中断的标志

主机模式下通信

安装 I2C 驱动程序后, ESP32 即可与其他 I2C 设备通信。

ESP32 的 I2C 控制器在主机模式下负责与 I2C 从机设备建立通信,并发送命令让从机响应,如进行测量并将结果发给主机。

为优化通信流程,驱动程序提供一个名为 “命令链接” 的容器,该容器应填充一系列命令,然后传递给 I2C 控制器执行

主机写入数据

下面的示例展示如何为 I2C 主机构建命令链接,从而向从机发送 n 个字节。

下面介绍如何为 “主机写入数据” 设置命令链接及其内部内容:

  1. 使用i2c_cmd_link_create 创建一个命令链接。

    然后,将一系列待发送给从机的数据填充命令链接:

    • 启动位 - i2c_master_start
    • 从机地址 - i2c_master_write_byte。提供单字节地址作为调用此函数的实参。
    • 数据 - 一个或多个字节的数据作为 i2c_master_write 的实参。
    • 停止位 -i2c_master_stop

    函数i2c_master_write_bytei2c_master_write 都有额外的实参,规定主机是否应确认其有无接受到 ACK 位。

  2. 通过调用i2c_master_cmd_begin 来触发 I2C 控制器执行命令链接。一旦开始执行,就不能再修改命令链接。

  3. 命令发送后,通过调用 i2c_cmd_link_delete 释放命令链接使用的资源。

主机读取数据

下面的示例展示如何为 I2C 主机构建命令链接,以便从从机读取 n 个字节。

在读取数据时,在上图的步骤 4 中,不是用 i2c_master_write...,而是用i2c_master_read_byte 和/或 i2c_master_read 填充命令链接。同样,在步骤 5 中配置最后一次的读取,以便主机不提供 ACK 位。

指示写入或读取数据

发送从机地址后(请参考上图中第 3 步),主机可以写入或从从机读取数据。

主机实际执行的操作信息存储在从机地址的最低有效位中。

因此,为了将数据写入从机,主机发送的命令链接应包含地址 (ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE,如下所示:

i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE, ACK_EN);

同理,指示从从机读取数据的命令链接如下所示:

i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | I2C_MASTER_READ, ACK_EN);

从机模式下通信

安装 I2C 驱动程序后, ESP32 即可与其他 I2C 设备通信。

API 为从机提供以下功能:

  • i2c_slave_read_buffer

    当主机将数据写入从机时,从机将自动将其存储在接收缓存区中。从机应用程序可自行调用函数 i2c_slave_read_buffer。如果接收缓存区中没有数据,此函数还具有一个参数用于指定阻塞时间。这将允许从机应用程序在指定的超时设定内等待数据到达缓存区。

  • i2c_slave_write_buffer

    发送缓存区是用于存储从机要以 FIFO 顺序发送给主机的所有数据。在主机请求接收前,这些数据一直存储在发送缓存区。函数 i2c_slave_write_buffer 有一个参数,用于指定发送缓存区已满时的块时间。这将允许从机应用程序在指定的超时设定内等待发送缓存区中足够的可用空间。

中断处理

安装驱动程序时,默认情况下会安装中断处理程序。但是,您可以通过调用函数 i2c_isr_register 来注册自己的而不是默认的中断处理程序。在运行自己的中断处理程序时,可以参考ESP32技术参考手册,以获取有关 I2C 控制器触发的中断描述。

调用函数i2c_isr_free 删除中断处理程序。

用户自定义配置

如本节末尾所述配置驱动程序,函数 i2c_param_config() 在初始化 I2C 端口的驱动程序配置时,也会将几个I2C 通信参数设置为I2C 总线协议规范规定的默认值。 其他一些相关参数已在 I2C 控制器的寄存器中预先配置。

通过调用下表中提供的专用函数,可以将所有这些参数更改为用户自定义值。请注意,时序值是在 APB 时钟周期中定义。APB 的频率在 I2C_APB_CLK_FREQ 中指定。

要更改的参数函数
SCL 脉冲周期的高电平和低电平i2c_set_period()
在产生 启动 信号期间使用的 SCL 和 SDA 信号时序i2c_set_start_timing()
在产生 停止 信号期间使用的 SCL 和 SDA 信号时序i2c_set_stop_timing()
从机采样以及主机切换时,SCL 和 SDA 信号之间的时序关系i2c_set_data_timing()
I2C 超时i2c_set_timeout()
优先发送/接收最高有效位 (LSB) 或最低有效位 (MSB),可在i2c_trans_mode_t定义的模式中选择i2c_set_data_mode()

上述每个函数都有一个 get 对应项来检查当前设置的值。例如,调用 i2c_get_timeout 来检查 I2C 超时值。

要检查在驱动程序配置过程中设置的参数默认值,请参考文件 driver/i2c.c 并查找带有后缀 _DEFAULT 的定义。

通过函数i2c_set_pin 可以为 SDA 和 SCL 信号选择不同的管脚并改变上拉配置。如果要修改已经输入的值,请使用函数 i2c_param_config

ESP32 的内部上拉电阻范围为几万欧姆,因此在大多数情况下,它们本身不足以用作 I2C 上拉电阻。建议用户使用阻值在I2C 总线协议规范规定范围内的上拉电阻。

错误处理

大多数 I2C 驱动程序的函数在成功完成时会返回 ESP_OK ,或在失败时会返回特定的错误代码。实时检查返回的值并进行错误处理是一种好习惯。驱动程序也会打印日志消息,其中包含错误说明,例如检查输入配置的正确性。有关详细信息,请参考文件 driver/i2c.c 并用后缀 _ERR_STR 查找定义。

使用专用中断来捕获通信故障。例如,如果从机将数据发送回主机耗费太长时间,会触发 I2C_TIME_OUT_INT 中断。详细信息请参考 i2c-api-interrupt-handling

如果出现通信失败,可以分别为发送和接收缓存区调用 i2c_reset_tx_fifoi2c_reset_rx_fifo 来重置内部硬件缓存区。

删除驱动程序

_ERR_STR 查找定义。

使用专用中断来捕获通信故障。例如,如果从机将数据发送回主机耗费太长时间,会触发 I2C_TIME_OUT_INT 中断。详细信息请参考 i2c-api-interrupt-handling

如果出现通信失败,可以分别为发送和接收缓存区调用 i2c_reset_tx_fifoi2c_reset_rx_fifo 来重置内部硬件缓存区。

删除驱动程序

如果使用 i2c_driver_install 建立 I2C 通信,一段时间后不再需要 I2C 通信,则可以通过调用 i2c_driver_delete 来移除驱动程序以释放分配的资源。

ESP32-IDF开发环境下进行I2C总线设备地址扫描的实例,我们可以使用ESP-IDF提供的API函数来实现。 首先,我们需要在代码中包含头文件"driver/i2c.h"来获取I2C相关函数的声明。 接下来,我们需要初始化I2C总线。可以使用函数"i2c_config_t"来定义I2C总线的配置参数,包括总线号、SCL引脚、SDA引脚、时钟频率等。然后,我们可以调用函数"i2c_param_config"进行参数配置,并通过函数"i2c_driver_install"来安装I2C驱动程序。 一旦I2C总线初始化完成,我们就可以开始扫描I2C设备的地址了。我们可以使用函数"i2c_scan"来实现扫描。该函数接受一个包含所有扫描地址的数组作为参数。 下面是一个示例代码: ``` #include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/i2c.h" #define I2C_MASTER_NUM I2C_NUM_0 // I2C总线号 #define I2C_MASTER_SCL_IO 19 // SCL引脚 #define I2C_MASTER_SDA_IO 18 // SDA引脚 #define I2C_MASTER_FREQ_HZ 100000 // I2C总线时钟频率 void i2c_scan_task(void *arg) { 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, I2C_MODE_MASTER, 0, 0, 0); uint8_t scan_addr[128]; i2c_scan(I2C_MASTER_NUM, scan_addr); printf("I2C devices found:\n"); for (int i = 0; i < 128; i++) { if (scan_addr[i] != 0) { printf("- Address: 0x%.2X\n", scan_addr[i]); } } vTaskDelete(NULL); } void app_main() { xTaskCreate(i2c_scan_task, "i2c_scan_task", 2048, NULL, 10, NULL); } ``` 以上代码实现了一个名为"i2c_scan_task"的任务,它首先配置了I2C总线的参数,然后安装I2C驱动程序。接着,它创建了一个包含128个元素的数组,用于存储扫描到的I2C设备地址。最后,它遍历该数组并打印出非零的地址,即已扫描到的I2C设备地址。 通过运行以上代码,我们就可以在终端看到已连接到I2C总线上的设备地址列表。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值