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