1I2C 驱动程序 2=============== 3 4:link_to_translation:`en:[English]` 5 6概述 7--------- 8 9I2C 是一种串行同步半双工通信协议,总线上可以同时挂载多个主机和从机。I2C 总线由串行数据线 (SDA) 和串行时钟线 (SCL) 线构成。这些线都需要上拉电阻。 10 11I2C 具有简单且制造成本低廉等优点,主要用于低速外围设备的短距离通信(一英尺以内)。 12 13.. only:: esp32c3 14 15 {IDF_TARGET_NAME} 只有一个 I2C 控制器(也称为端口),负责处理在 I2C 总线上的通信。每个控制器都可以设置为主机或从机。 16 17.. only:: not esp32c3 18 19 {IDF_TARGET_NAME} 有两个 I2C 控制器(也称为端口),负责处理在 I2C 两根总线上的通信。每个控制器都可以设置为主机或从机。例如,可以同时让一个控制器用作主机,另一个用作从机。 20 21驱动程序的功能 22--------------- 23 24I2C 驱动程序管理在 I2C 总线上设备的通信,该驱动程序具备以下功能: 25 26- 在主机模式下读写字节 27- 支持从机模式 28- 读取并写入寄存器,然后由主机读取/写入 29 30 31使用驱动程序 32--------------- 33 34以下部分将指导您完成 I2C 驱动程序配置和工作的基本步骤: 35 361. :ref:`i2c-api-configure-driver` - 设置初始化参数(如主机模式或从机模式,SDA 和 SCL 使用的 GPIO 管脚,时钟速度等) 372. :ref:`i2c-api-install-driver`- 激活一个 I2C 控制器的驱动,该控制器可为主机也可为从机 383. 根据是为主机还是从机配置驱动程序,选择合适的项目 39 40 a) :ref:`i2c-api-master-mode` - 发起通信(主机模式) 41 b) :ref:`i2c-api-slave-mode` - 响应主机消息(从机模式) 42 434. :ref:`i2c-api-interrupt-handling` - 配置 I2C 中断服务 445. :ref:`i2c-api-customized-configuration` - 调整默认的 I2C 通信参数(如时序、位序等) 456. :ref:`i2c-api-error-handling` - 如何识别和处理驱动程序配置和通信错误 467. :ref:`i2c-api-delete-driver`- 在通信结束时释放 I2C 驱动程序所使用的资源 47 48 49.. _i2c-api-configure-driver: 50 51配置驱动程序 52^^^^^^^^^^^^^ 53 54建立 I2C 通信第一步是配置驱动程序,这需要设置 :cpp:type:`i2c_config_t` 结构中的几个参数: 55 56- 设置 I2C **工作模式** - 从 :cpp:type:`i2c_mode_t` 中选择主机模式或从机模式 57- 设置 **通信管脚** 58 59 - 指定 SDA 和 SCL 信号使用的 GPIO 管脚 60 - 是否启用 {IDF_TARGET_NAME} 的内部上拉电阻 61 62- (仅限主机模式)设置 I2C **时钟速度** 63- (仅限从机模式)设置以下内容: 64 65 * 是否应启用 **10 位寻址模式** 66 * 定义 **从机地址** 67 68然后,初始化给定 I2C 端口的配置,请使用端口号和 :cpp:type:`i2c_config_t` 作为函数调用参数来调用 :cpp:func:`i2c_param_config` 函数。 69 70配置示例(主机): 71 72.. code-block:: c 73 74 int i2c_master_port = 0; 75 i2c_config_t conf = { 76 .mode = I2C_MODE_MASTER, 77 .sda_io_num = I2C_MASTER_SDA_IO, // select GPIO specific to your project 78 .sda_pullup_en = GPIO_PULLUP_ENABLE, 79 .scl_io_num = I2C_MASTER_SCL_IO, // select GPIO specific to your project 80 .scl_pullup_en = GPIO_PULLUP_ENABLE, 81 .master.clk_speed = I2C_MASTER_FREQ_HZ, // select frequency specific to your project 82 // .clk_flags = 0, /*!< Optional, you can use I2C_SCLK_SRC_FLAG_* flags to choose i2c source clock here. */ 83 }; 84 85配置示例(从机): 86 87.. code-block:: c 88 89 int i2c_slave_port = I2C_SLAVE_NUM; 90 i2c_config_t conf_slave = { 91 .sda_io_num = I2C_SLAVE_SDA_IO, // select GPIO specific to your project 92 .sda_pullup_en = GPIO_PULLUP_ENABLE, 93 .scl_io_num = I2C_SLAVE_SCL_IO, // select GPIO specific to your project 94 .scl_pullup_en = GPIO_PULLUP_ENABLE, 95 .mode = I2C_MODE_SLAVE, 96 .slave.addr_10bit_en = 0, 97 .slave.slave_addr = ESP_SLAVE_ADDR, // address of your project 98 }; 99 100在此阶段,:cpp:func:`i2c_param_config` 还将其他 I2C 配置参数设置为 I2C 总线协议规范中定义的默认值。有关默认值及修改默认值的详细信息,请参考 :ref:`i2c-api-customized-configuration`。 101 102源时钟配置 103^^^^^^^^^^^^^^^^^^^^^^^^^^ 104 105增加了 **时钟源分配器**,用于支持不同的时钟源。时钟分配器将选择一个满足所有频率和能力要求的时钟源(如 :cpp:member:`i2c_config_t::clk_flags` 中的要求)。 106 107当 :cpp:member:`i2c_config_t::clk_flags` 为 0 时,时钟分配器将仅根据所需频率进行选择。如果不需要诸如 APB 之类的特殊功能,则可以将时钟分配器配置为仅根据所需频率选择源时钟。为此,请将 :cpp:member:`i2c_config_t::clk_flags` 设置为 0。有关时钟特性,请参见下表。 108 109.. note:: 110 111 如果时钟不满足请求的功能,则该时钟不是有效的选项,即,请求的功能中的任何位(clk_flags)在时钟的功能中均为 0。 112 113.. only:: esp32 114 115 .. list-table:: {IDF_TARGET_NAME} 时钟源特性 116 :widths: 5 5 50 20 117 :header-rows: 1 118 119 * - 时钟名称 120 - 时钟频率 121 - SCL 的最大频率 122 - 时钟功能 123 * - APB 时钟 124 - 80 MHz 125 - 4 MHz 126 - / 127 128.. only:: esp32s2 129 130 .. list-table:: {IDF_TARGET_NAME} 时钟源特性 131 :widths: 5 5 50 100 132 :header-rows: 1 133 134 * - 时钟名称 135 - 时钟频率 136 - SCL 的最大频率 137 - 时钟功能 138 * - APB 时钟 139 - 80 MHz 140 - 4 MHz 141 - / 142 * - REF_TICK 143 - 1 MHz 144 - 50 KHz 145 - :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`, :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP` 146 147 对 :cpp:member:`i2c_config_t::clk_flags` 的解释如下: 148 1. :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`:当 APB 时钟改变时,时钟的波特率不会改变。 149 2. :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP`:支持轻度睡眠模式,APB 时钟则不支持。 150 151.. only:: esp32s3 152 153 .. list-table:: {IDF_TARGET_NAME} 时钟源特性 154 :widths: 5 5 50 20 155 :header-rows: 1 156 157 * - 时钟名称 158 - 时钟频率 159 - SCL 的最大频率 160 - 时钟功能 161 * - XTAL 时钟 162 - 40 MHz 163 - 2 MHz 164 - / 165 * - RTC 时钟 166 - 20 MHz 167 - 1 MHz 168 - :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`, :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP` 169 170.. only:: esp32c3 171 172 .. list-table:: {IDF_TARGET_NAME} 时钟源特性 173 :widths: 5 5 50 100 174 :header-rows: 1 175 176 * - 时钟名称 177 - 时钟频率 178 - SCL 的最大频率 179 - 时钟功能 180 * - XTAL 时钟 181 - 40 MHz 182 - 2 MHz 183 - / 184 * - RTC 时钟 185 - 20 MHz 186 - 1 MHz 187 - :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`, :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP` 188 189对 :cpp:member:`i2c_config_t::clk_flags` 的解释如下: 190 1911. :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`:当 APB 时钟改变时,时钟的波特率不会改变。 1922. :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP`:支持轻度睡眠模式,APB 时钟则不支持。 1933. {IDF_TARGET_NAME} 可能不支持某些标志,请在使用前阅读技术参考手册。 194 195.. note:: 196 197 在主机模式下,SCL 的时钟频率不应大于上表中提到的 SCL 的最大频率。 198 199.. _i2c-api-install-driver: 200 201安装驱动程序 202^^^^^^^^^^^^^^ 203 204配置好 I2C 驱动程序后,使用以下参数调用函数 :cpp:func:`i2c_driver_install` 安装驱动程序: 205 206- 端口号,从 :cpp:type:`i2c_port_t` 中二选一 207- 主机或从机模式,从 :cpp:type:`i2c_mode_t` 中选择 208- (仅限从机模式)分配用于在从机模式下发送和接收数据的缓存区大小。I2C 是一个以主机为中心的总线,数据只能根据主机的请求从从机传输到主机。因此,从机通常有一个发送缓存区,供从应用程序写入数据使用。数据保留在发送缓存区中,由主机自行读取。 209- 用于分配中断的标志(请参考 :component_file:`esp_hw_support/include/esp_intr_alloc.h` 中 ESP_INTR_FLAG_* 值) 210 211.. _i2c-api-master-mode: 212 213主机模式下通信 214^^^^^^^^^^^^^^^^^^ 215 216安装 I2C 驱动程序后, {IDF_TARGET_NAME} 即可与其他 I2C 设备通信。 217 218{IDF_TARGET_NAME} 的 I2C 控制器在主机模式下负责与 I2C 从机设备建立通信,并发送命令让从机响应,如进行测量并将结果发给主机。 219 220为优化通信流程,驱动程序提供一个名为 “命令链接” 的容器,该容器应填充一系列命令,然后传递给 I2C 控制器执行。 221 222 223主机写入数据 224""""""""""""" 225 226下面的示例展示如何为 I2C 主机构建命令链接,从而向从机发送 *n* 个字节。 227 228.. blockdiag:: ../../../_static/diagrams/i2c-command-link-master-write-blockdiag.diag 229 :scale: 100 230 :caption: I2C command link - master write example 231 :align: center 232 233 234下面介绍如何为 “主机写入数据” 设置命令链接及其内部内容: 235 2361. 使用 :cpp:func:`i2c_cmd_link_create` 创建一个命令链接。 237 238 然后,将一系列待发送给从机的数据填充命令链接: 239 240 a) **启动位** - :cpp:func:`i2c_master_start` 241 b) **从机地址** - :cpp:func:`i2c_master_write_byte`。提供单字节地址作为调用此函数的实参。 242 c) **数据** - 一个或多个字节的数据作为 :cpp:func:`i2c_master_write` 的实参。 243 d) **停止位** - :cpp:func:`i2c_master_stop` 244 245 函数 :cpp:func:`i2c_master_write_byte` 和 :cpp:func:`i2c_master_write` 都有额外的实参,规定主机是否应确认其有无接受到 ACK 位。 246 2472. 通过调用 :cpp:func:`i2c_master_cmd_begin` 来触发 I2C 控制器执行命令链接。一旦开始执行,就不能再修改命令链接。 2483. 命令发送后,通过调用 :cpp:func:`i2c_cmd_link_delete` 释放命令链接使用的资源。 249 250 251主机读取数据 252"""""""""""""" 253 254下面的示例展示如何为 I2C 主机构建命令链接,以便从从机读取 *n* 个字节。 255 256.. blockdiag:: ../../../_static/diagrams/i2c-command-link-master-read-blockdiag.diag 257 :scale: 100 258 :caption: I2C command link - master read example 259 :align: center 260 261 262在读取数据时,在上图的步骤 4 中,不是用 ``i2c_master_write...``,而是用 :cpp:func:`i2c_master_read_byte` 和/或 :cpp:func:`i2c_master_read` 填充命令链接。同样,在步骤 5 中配置最后一次的读取,以便主机不提供 ACK 位。 263 264 265指示写入或读取数据 266"""""""""""""""""" 267 268发送从机地址后(请参考上图中第 3 步),主机可以写入或从从机读取数据。 269 270主机实际执行的操作信息存储在从机地址的最低有效位中。 271 272因此,为了将数据写入从机,主机发送的命令链接应包含地址 ``(ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE``,如下所示: 273 274.. code-block:: c 275 276 i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE, ACK_EN); 277 278同理,指示从从机读取数据的命令链接如下所示: 279 280.. code-block:: c 281 282 i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | I2C_MASTER_READ, ACK_EN); 283 284 285.. _i2c-api-slave-mode: 286 287从机模式下通信 288^^^^^^^^^^^^^^^^^^^^^^ 289 290安装 I2C 驱动程序后, {IDF_TARGET_NAME} 即可与其他 I2C 设备通信。 291 292API 为从机提供以下功能: 293 294- :cpp:func:`i2c_slave_read_buffer` 295 296 当主机将数据写入从机时,从机将自动将其存储在接收缓存区中。从机应用程序可自行调用函数 :cpp:func:`i2c_slave_read_buffer`。如果接收缓存区中没有数据,此函数还具有一个参数用于指定阻塞时间。这将允许从机应用程序在指定的超时设定内等待数据到达缓存区。 297 298- :cpp:func:`i2c_slave_write_buffer` 299 300 发送缓存区是用于存储从机要以 FIFO 顺序发送给主机的所有数据。在主机请求接收前,这些数据一直存储在发送缓存区。函数 :cpp:func:`i2c_slave_write_buffer` 有一个参数,用于指定发送缓存区已满时的块时间。这将允许从机应用程序在指定的超时设定内等待发送缓存区中足够的可用空间。 301 302在 :example:`peripherals/i2c` 中可找到介绍如何使用这些功能的代码示例。 303 304 305.. _i2c-api-interrupt-handling: 306 307中断处理 308^^^^^^^^^^^ 309 310安装驱动程序时,默认情况下会安装中断处理程序。但是,您可以通过调用函数 :cpp:func:`i2c_isr_register` 来注册自己的而不是默认的中断处理程序。在运行自己的中断处理程序时,可以参考 *{IDF_TARGET_NAME} 技术参考手册* > *I2C 控制器 (I2C)* > *中断* [`PDF <{IDF_TARGET_TRM_CN_URL}#i2c>`__],以获取有关 I2C 控制器触发的中断描述。 311 312调用函数 :cpp:func:`i2c_isr_free` 删除中断处理程序。 313 314.. _i2c-api-customized-configuration: 315 316用户自定义配置 317^^^^^^^^^^^^^^^ 318 319如本节末尾所述 :ref:`i2c-api-configure-driver`,函数 :cpp:func:`i2c_param_config` 在初始化 I2C 端口的驱动程序配置时,也会将几个 I2C 通信参数设置为 `I2C 总线协议规范 <https://www.nxp.com/docs/en/user-guide/UM10204.pdf>`_ 规定的默认值。 其他一些相关参数已在 I2C 控制器的寄存器中预先配置。 320 321通过调用下表中提供的专用函数,可以将所有这些参数更改为用户自定义值。请注意,时序值是在 APB 时钟周期中定义。APB 的频率在 :cpp:type:`I2C_APB_CLK_FREQ` 中指定。 322 323.. list-table:: 其他可配置的 I2C 通信参数 324 :widths: 65 35 325 :header-rows: 1 326 327 * - 要更改的参数 328 - 函数 329 * - SCL 脉冲周期的高电平和低电平 330 - :cpp:func:`i2c_set_period` 331 * - 在产生 **启动** 信号期间使用的 SCL 和 SDA 信号时序 332 - :cpp:func:`i2c_set_start_timing` 333 * - 在产生 **停止** 信号期间使用的 SCL 和 SDA 信号时序 334 - :cpp:func:`i2c_set_stop_timing` 335 * - 从机采样以及主机切换时,SCL 和 SDA 信号之间的时序关系 336 - :cpp:func:`i2c_set_data_timing` 337 * - I2C 超时 338 - :cpp:func:`i2c_set_timeout` 339 * - 优先发送/接收最高有效位 (LSB) 或最低有效位 (MSB),可在 :cpp:type:`i2c_trans_mode_t` 定义的模式中选择 340 - :cpp:func:`i2c_set_data_mode` 341 342 343上述每个函数都有一个 *_get_* 对应项来检查当前设置的值。例如,调用 :cpp:func:`i2c_get_timeout` 来检查 I2C 超时值。 344 345要检查在驱动程序配置过程中设置的参数默认值,请参考文件 :component_file:`driver/i2c.c` 并查找带有后缀 ``_DEFAULT`` 的定义。 346 347通过函数 :cpp:func:`i2c_set_pin` 可以为 SDA 和 SCL 信号选择不同的管脚并改变上拉配置。如果要修改已经输入的值,请使用函数 :cpp:func:`i2c_param_config`。 348 349.. 注解 :: 350 351 {IDF_TARGET_NAME} 的内部上拉电阻范围为几万欧姆,因此在大多数情况下,它们本身不足以用作 I2C 上拉电阻。建议用户使用阻值在 `I2C 总线协议规范 <https://www.nxp.com/docs/en/user-guide/UM10204.pdf>`_ 规定范围内的上拉电阻。 352 353 354.. _i2c-api-error-handling: 355 356错误处理 357^^^^^^^^^^ 358 359大多数 I2C 驱动程序的函数在成功完成时会返回 ``ESP_OK`` ,或在失败时会返回特定的错误代码。实时检查返回的值并进行错误处理是一种好习惯。驱动程序也会打印日志消息,其中包含错误说明,例如检查输入配置的正确性。有关详细信息,请参考文件 :component_file:`driver/i2c.c` 并用后缀 ``_ERR_STR`` 查找定义。 360 361使用专用中断来捕获通信故障。例如,如果从机将数据发送回主机耗费太长时间,会触发 ``I2C_TIME_OUT_INT`` 中断。详细信息请参考 :ref:`i2c-api-interrupt-handling`。 362 363如果出现通信失败,可以分别为发送和接收缓存区调用 :cpp:func:`i2c_reset_tx_fifo` 和 :cpp:func:`i2c_reset_rx_fifo` 来重置内部硬件缓存区。 364 365 366.. _i2c-api-delete-driver: 367 368删除驱动程序 369^^^^^^^^^^^^^ 370 371当使用 :cpp:func:`i2c_driver_install` 建立 I2C 通信,一段时间后不再需要 I2C 通信时,可以通过调用 :cpp:func:`i2c_driver_delete` 来移除驱动程序以释放分配的资源。 372 373由于函数 :cpp:func:`i2c_driver_delete` 无法保证线程安全性,请在调用该函数移除驱动程序前务必确保所有的线程都已停止使用驱动程序。 374 375应用示例 376---------- 377 378I2C 主机和从机示例::example:`peripherals/i2c`。 379 380 381API 参考 382---------- 383 384.. include-build-file:: inc/i2c.inc 385.. include-build-file:: inc/i2c_types.inc 386