docs: Provide translation for I2C

pull/14033/head
shenmengjing 2024-05-14 14:12:31 +08:00 zatwierdzone przez BOT
rodzic b3b234f3e5
commit 27860aa505
2 zmienionych plików z 707 dodań i 60 usunięć

Wyświetl plik

@ -1,30 +1,40 @@
Inter-Integrated Circuit (I2C)
==============================
:link_to_translation:`zh_CN:[中文]`
Introduction
------------
I2C is a serial, synchronous, multi-device, half-duplex communication protocol that allows co-existence of multiple masters and slaves on the same bus. I2C uses two bidirectional open-drain lines: serial data line (SDA) and serial clock line (SCL), pulled up by resistors.
{IDF_TARGET_NAME} has {IDF_TARGET_SOC_HP_I2C_NUM} I2C controller (also called port), responsible for handling communication on the I2C bus. A single I2C controller can be a master or a slave.
{IDF_TARGET_NAME} has {IDF_TARGET_SOC_HP_I2C_NUM} I2C controller (also called port), responsible for handling communication on the I2C bus.
.. only:: not esp32c2
A single I2C controller can be a master or a slave.
.. only:: esp32c2
The I2C controller can only be a master.
.. only:: SOC_LP_I2C_SUPPORTED
Additionally, the {IDF_TARGET_NAME} chip has 1 low-power (LP) I2C controller. It is the cut-down version of regular I2C. Usually, the LP I2C controller only support basic I2C functionality with a much smaller RAM size, and does not support slave mode. For a full list of difference between HP I2C and LP I2C, please refer to the *{IDF_TARGET_NAME} Technical Reference Manual* > *I2C Controller (I2C)* > *Features* [`PDF <{IDF_TARGET_TRM_EN_URL}#i2c>`__].
You can use LP I2C peripheral when HP I2C is not sufficient for users' usage. But please note again the LP I2C does not support all HP I2C functions. Please read docs before you use it.
You can use LP I2C peripheral when HP I2C is not sufficient for users' usage. But please note again the LP I2C does not support all HP I2C functions. Please read documentation before you use it.
Typically, an I2C slave device has a 7-bit address or 10-bit address. {IDF_TARGET_NAME} supports both I2C Standard-mode (Sm) and Fast-mode (Fm) which can go up to 100KHz and 400KHz respectively.
Typically, an I2C slave device has a 7-bit address or 10-bit address. {IDF_TARGET_NAME} supports both I2C Standard-mode (Sm) and Fast-mode (Fm) which can go up to 100 kHz and 400 kHz respectively.
.. warning::
The clock frequency of SCL in master mode should not be larger than 400 KHz
The clock frequency of SCL in master mode should not be larger than 400 kHz.
.. note::
The frequency of SCL is influenced by both the pull-up resistor and the wire capacitance. Therefore, users are strongly recommended to choose appropriate pull-up resistors to make the frequency accurate. The recommended value for pull-up resistors usually ranges from 1K Ohms to 10K Ohms.
The frequency of SCL is influenced by both the pull-up resistor and the wire capacitance. Therefore, it is strongly recommended to choose appropriate pull-up resistors to make the frequency accurate. The recommended value for pull-up resistors usually ranges from 1 kΩ to 10 kΩ.
Keep in mind that the higher the frequency, the smaller the pull-up resistor should be (but not less than 1 KOhms). Indeed, large resistors will decline the current, which will increase the clock switching time and reduce the frequency. We usually recommend a range of 2 KOhms to 5 KOhms, but users may also need to make some adjustments depending on their current draw requirements.
Keep in mind that the higher the frequency, the smaller the pull-up resistor should be (but not less than 1 KOhms). Indeed, large resistors will decline the current, which will increase the clock switching time and reduce the frequency. A range of 2 KOhms to 5 KOhms is recommended, but adjustments may also be necessary depending on their current draw requirements.
I2C Clock Configuration
@ -34,9 +44,9 @@ I2C Clock Configuration
- :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_DEFAULT`: Default I2C source clock.
:SOC_I2C_SUPPORT_XTAL: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_XTAL`: External crystal for I2C clock source.
:SOC_I2C_SUPPORT_RTC: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_RC_FAST`: Internal 20MHz rc oscillator for I2C clock source.
:SOC_I2C_SUPPORT_RTC: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_RC_FAST`: Internal 20 MHz RC oscillator for I2C clock source.
:SOC_I2C_SUPPORT_APB: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_APB`: APB clock as I2C clock source.
:SOC_I2C_SUPPORT_REF_TICK: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_REF_TICK`: 1MHZ clock.
:SOC_I2C_SUPPORT_REF_TICK: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_REF_TICK`: 1 MHZ clock.
I2C File Structure
------------------
@ -59,7 +69,7 @@ I2C File Structure
**Public headers that have been included in the headers above**
- ``i2c_types_legacy.h``: The legacy public types that only used in the legacy driver.
- ``i2c_types_legacy.h``: The legacy public types that are only used in the legacy driver.
- ``i2c_types.h``: The header file that provides public types.
Functional Overview
@ -67,13 +77,13 @@ Functional Overview
The I2C driver offers following services:
- `Resource Allocation <#resource-allocation>`__ - covers how to allocate I2C bus with properly set of configurations. It also covers how to recycle the resources when they finished working.
- `I2C Master Controller <#i2c_master_controller>`__ - covers behavior of I2C master controller. Introduce data transmit, data receive, and data transmit and receive.
- `I2C Slave Controller <#i2c_slave_controller>`__ - covers behavior of I2C slave controller. Involve data transmit and data receive.
- `Power Management <#power-management>`__ - describes how different source clock will affect power consumption.
- `IRAM Safe <#iram-safe>`__ - describes tips on how to make the I2C interrupt work better along with a disabled cache.
- `Thread Safety <#thread-safety>`__ - lists which APIs are guaranteed to be thread safe by the driver.
- `Kconfig Options <#kconfig-options>`__ - lists the supported Kconfig options that can bring different effects to the driver.
- `Resource Allocation <#resource-allocation>`__ - covers how to allocate I2C bus with properly set of configurations. It also covers how to recycle the resources when they finished working.
- `I2C Master Controller <#i2c_master_controller>`__ - covers behavior of I2C master controller. Introduce data transmit, data receive, and data transmit and receive.
- `I2C Slave Controller <#i2c_slave_controller>`__ - covers behavior of I2C slave controller. Involve data transmit and data receive.
- `Power Management <#power-management>`__ - describes how different source clock will affect power consumption.
- `IRAM Safe <#iram-safe>`__ - describes tips on how to make the I2C interrupt work better along with a disabled cache.
- `Thread Safety <#thread-safety>`__ - lists which APIs are guaranteed to be thread safe by the driver.
- `Kconfig Options <#kconfig-options>`__ - lists the supported Kconfig options that can bring different effects to the driver.
Resource Allocation
^^^^^^^^^^^^^^^^^^^
@ -83,7 +93,7 @@ Both I2C master bus and I2C slave bus, when supported, are represented by :cpp:t
Install I2C master bus and device
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The I2C master is designed based on bus-device model. So :cpp:type:`i2c_master_bus_config_t` and :cpp:type:`i2c_device_config_t` are required separately to allocate the I2C master bus instance and I2C device instance.
The I2C master bus is designed based on bus-device model. So :cpp:type:`i2c_master_bus_config_t` and :cpp:type:`i2c_device_config_t` are required separately to allocate the I2C master bus instance and I2C device instance.
.. figure:: ../../../_static/diagrams/i2c/i2c_master_module.png
:align: center
@ -98,22 +108,22 @@ I2C master bus requires the configuration that specified by :cpp:type:`i2c_maste
- :cpp:member:`i2c_master_bus_config_t::scl_io_num` sets the GPIO number for the serial clock bus (SCL).
- :cpp:member:`i2c_master_bus_config_t::clk_source` selects the source clock for I2C bus. The available clocks are listed in :cpp:type:`i2c_clock_source_t`. For the effect on power consumption of different clock source, please refer to `Power Management <#power-management>`__ section.
- :cpp:member:`i2c_master_bus_config_t::glitch_ignore_cnt` sets the glitch period of master bus, if the glitch period on the line is less than this value, it can be filtered out, typically value is 7.
- :cpp:member:`i2c_master_bus_config_t::intr_priority` Set the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1,2 or 3), otherwise use the priority indicated by :cpp:member:`i2c_master_bus_config_t::intr_priority` Please use the number form (1,2,3) , not the bitmask form ((1<<1),(1<<2),(1<<3)).
- :cpp:member:`i2c_master_bus_config_t::trans_queue_depth` Depth of internal transfer queue. Only valid in asynchronous transaction.
- :cpp:member:`i2c_master_bus_config_t::enable_internal_pullup` Enable internal pullups. Note: This is not strong enough to pullup buses under high-speed frequency. A suitable external pullup is recommended.
- :cpp:member:`i2c_master_bus_config_t::intr_priority` sets the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1, 2 or 3), otherwise use the priority indicated by :cpp:member:`i2c_master_bus_config_t::intr_priority`. Please use the number form (1, 2, 3) , not the bitmask form ((1<<1), (1<<2), (1<<3)).
- :cpp:member:`i2c_master_bus_config_t::trans_queue_depth` sets the depth of internal transfer queue. Only valid in asynchronous transaction.
- :cpp:member:`i2c_master_bus_config_t::enable_internal_pullup` enables internal pullups. Note: This is not strong enough to pullup buses under high-speed frequency. A suitable external pullup is recommended.
If the configurations in :cpp:type:`i2c_master_bus_config_t` is specified, users can call :cpp:func:`i2c_new_master_bus` to allocate and initialize an I2C master bus. This function will return an I2C bus handle if it runs correctly. Specifically, when there are no more I2C port available, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error.
If the configurations in :cpp:type:`i2c_master_bus_config_t` is specified, then :cpp:func:`i2c_new_master_bus` can be called to allocate and initialize an I2C master bus. This function will return an I2C bus handle if it runs correctly. Specifically, when there are no more I2C port available, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error.
I2C master device requires the configuration that specified by :cpp:type:`i2c_device_config_t`:
- :cpp:member:`i2c_device_config_t::dev_addr_length` configure the address bit length of the slave device. User can choose from enumerator :cpp:enumerator:`I2C_ADDR_BIT_LEN_7` or :cpp:enumerator:`I2C_ADDR_BIT_LEN_10` (if supported).
- :cpp:member:`i2c_device_config_t::device_address` I2C device raw address. Please parse the device address to this member directly. For example, the device address is 0x28, then parse 0x28 to :cpp:member:`i2c_device_config_t::device_address`, don't carry a write/read bit.
- :cpp:member:`i2c_device_config_t::scl_speed_hz` set the scl line frequency of this device.
- :cpp:member:`i2c_device_config_t::scl_wait_us`. SCL await time (in us). Usually this value should not be very small because slave stretch will happen in pretty long time. (It's possible even stretch for 12ms). Set 0 means use default reg value.
- :cpp:member:`i2c_device_config_t::dev_addr_length` configure the address bit length of the slave device. It can be chosen from enumerator :cpp:enumerator:`I2C_ADDR_BIT_LEN_7` or :cpp:enumerator:`I2C_ADDR_BIT_LEN_10` (if supported).
- :cpp:member:`i2c_device_config_t::device_address` sets the I2C device raw address. Please parse the device address to this member directly. For example, the device address is 0x28, then parse 0x28 to :cpp:member:`i2c_device_config_t::device_address`, don't carry a write or read bit.
- :cpp:member:`i2c_device_config_t::scl_speed_hz` sets the SCL line frequency of this device.
- :cpp:member:`i2c_device_config_t::scl_wait_us` sets the SCL await time (in μs). Usually this value should not be very small because slave stretch will happen in pretty long time (It's possible even stretch for 12 ms). Set ``0`` means use default register value.
Once the :cpp:type:`i2c_device_config_t` structure is populated with mandatory parameters, users can call :cpp:func:`i2c_master_bus_add_device` to allocate an I2C device instance and mounted to the master bus then. This function will return an I2C device handle if it runs correctly. Specifically, when the I2C bus is not initialized properly, calling this function will result in a :c:macro:`ESP_ERR_INVALID_ARG` error.
Once the :cpp:type:`i2c_device_config_t` structure is populated with mandatory parameters, :cpp:func:`i2c_master_bus_add_device` can be called to allocate an I2C device instance and mounted to the master bus then. This function will return an I2C device handle if it runs correctly. Specifically, when the I2C bus is not initialized properly, calling this function will result in a :c:macro:`ESP_ERR_INVALID_ARG` error.
.. code:: c
@ -145,14 +155,14 @@ Once the :cpp:type:`i2c_device_config_t` structure is populated with mandatory p
Install I2C master bus with LP I2C Peripheral
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Install I2C master bus with LP I2C peripheral is almost as same as how HP I2C peripheral is installed. However, there are still some difference user should take focus on including IOs, clock sources, i2c port number, etc. Following code will show you how to install I2C master bus with LP_I2C.
Install I2C master bus with LP I2C peripheral is almost as same as how HP I2C peripheral is installed. However, there are still some difference should be taken focus on, including IOs, clock sources, I2C port number, etc. Following code will show how to install I2C master bus with LP_I2C.
.. code:: c
#include "driver/i2c_master.h"
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = LP_I2C_SCLK_DEFAULT, // clock source for LP i2c, might different from HP I2C
.clk_source = LP_I2C_SCLK_DEFAULT, // clock source for LP I2C, might different from HP I2C
.i2c_port = LP_I2C_NUM_0, // Assign to LP I2C port
.scl_io_num = 7, // SCL IO number. Please refer to technical reference manual
.sda_io_num = 6, // SDA IO number. Please refer to technical reference manual
@ -175,7 +185,7 @@ Once the :cpp:type:`i2c_device_config_t` structure is populated with mandatory p
Uninstall I2C master bus and device
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If a previously installed I2C bus or device is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`i2c_master_bus_rm_device` or :cpp:func:`i2c_del_master_bus`, so that to release the underlying hardware.
If a previously installed I2C bus or device is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`i2c_master_bus_rm_device` or :cpp:func:`i2c_del_master_bus`, so as to release the underlying hardware.
Install I2C slave device
~~~~~~~~~~~~~~~~~~~~~~~~
@ -189,15 +199,15 @@ I2C slave requires the configuration that specified by :cpp:type:`i2c_slave_conf
- :cpp:member:`i2c_slave_config_t::scl_io_num` sets the GPIO number for serial clock bus (SCL).
- :cpp:member:`i2c_slave_config_t::clk_source` selects the source clock for I2C bus. The available clocks are listed in :cpp:type:`i2c_clock_source_t`. For the effect on power consumption of different clock source, please refer to `Power Management <#power-management>`__ section.
- :cpp:member:`i2c_slave_config_t::send_buf_depth` sets the sending buffer length.
- :cpp:member:`i2c_slave_config_t::slave_addr` sets the slave address
- :cpp:member:`i2c_master_bus_config_t::intr_priority` Set the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1,2 or 3), otherwise use the priority indicated by :cpp:member:`i2c_master_bus_config_t::intr_priority` Please use the number form (1,2,3) , not the bitmask form ((1<<1),(1<<2),(1<<3)). Please pay attention that once the interrupt priority is set, it cannot be changed until :cpp:func:`i2c_del_master_bus` is called.
- :cpp:member:`i2c_slave_config_t::addr_bit_len` sets true if you need the slave to have a 10-bit address.
:SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE: - :cpp:member:`i2c_slave_config_t::stretch_en` Set true if you want the slave controller stretch works, please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#i2c>`__] to learn how I2C stretch works.
:SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE: - :cpp:member:`i2c_slave_config_t::broadcast_en` Set true to enable the slave broadcast. When the slave receives the general call address 0x00 from the master and the R/W bit followed is 0, it responds to the master regardless of its own address.
:SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS: - :cpp:member:`i2c_slave_config_t::access_ram_en` Set true to enable the non-fifo mode. Thus the I2C data fifo can be used as RAM, and double addressing will be synchronised opened.
:SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH: - :cpp:member:`i2c_slave_config_t::slave_unmatch_en` Set true to enable the slave unmatch interrupt. If master send command address cannot match the slave address, and unmatch interrupt will be triggered.
- :cpp:member:`i2c_slave_config_t::slave_addr` sets the slave address.
- :cpp:member:`i2c_master_bus_config_t::intr_priority` sets the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1, 2 or 3), otherwise use the priority indicated by :cpp:member:`i2c_master_bus_config_t::intr_priority`. Please use the number form (1, 2, 3), not the bitmask form ((1<<1), (1<<2), (1<<3)). Please pay attention that once the interrupt priority is set, it cannot be changed until :cpp:func:`i2c_del_master_bus` is called.
- :cpp:member:`i2c_slave_config_t::addr_bit_len`. Set this variable to ``I2C_ADDR_BIT_LEN_10`` if the slave should have a 10-bit address.
:SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE: - :cpp:member:`i2c_slave_config_t::stretch_en`. Set this variable to true, then the slave controller stretch will work. Please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#i2c>`__] to learn how I2C stretch works.
:SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE: - :cpp:member:`i2c_slave_config_t::broadcast_en`. Set this to true to enable the slave broadcast. When the slave receives the general call address 0x00 from the master and the R/W bit followed is 0, it responds to the master regardless of its own address.
:SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS: - :cpp:member:`i2c_slave_config_t::access_ram_en`. Set this to true to enable the non-FIFO mode. Thus the I2C data FIFO can be used as RAM, and double addressing will be synchronised opened.
:SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH: - :cpp:member:`i2c_slave_config_t::slave_unmatch_en`. Set this to true to enable the slave unmatch interrupt. If the command address sent by master can't match the slave address, then unmatch interrupt will be triggered.
Once the :cpp:type:`i2c_slave_config_t` structure is populated with mandatory parameters, users can call :cpp:func:`i2c_new_slave_device` to allocate and initialize an I2C master bus. This function will return an I2C bus handle if it runs correctly. Specifically, when there are no more I2C port available, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error.
Once the :cpp:type:`i2c_slave_config_t` structure is populated with mandatory parameters, :cpp:func:`i2c_new_slave_device` can be called to allocate and initialize an I2C master bus. This function will return an I2C bus handle if it runs correctly. Specifically, when there are no more I2C port available, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error.
.. code:: c
@ -223,7 +233,7 @@ If a previously installed I2C bus is no longer needed, it's recommended to recyc
I2C Master Controller
^^^^^^^^^^^^^^^^^^^^^
After installing the i2c master driver by :cpp:func:`i2c_new_master_bus`, {IDF_TARGET_NAME} is ready to communicate with other I2C devices. I2C APIs allow the standard transactions. Like the wave as follows:
After installing the I2C master driver by :cpp:func:`i2c_new_master_bus`, {IDF_TARGET_NAME} is ready to communicate with other I2C devices. I2C APIs allow the standard transactions. Like the wave as follows:
.. wavedrom:: /../_static/diagrams/i2c/i2c_trans_wave.json
@ -268,7 +278,7 @@ Simple example for writing data to slave:
ESP_ERROR_CHECK(i2c_master_transmit(dev_handle, data_wr, DATA_LENGTH, -1));
I2C master write also supports transmit multi buffer in one transaction. Take following transaction as a simple example:
I2C master write also supports transmit multi-buffer in one transaction. Take following transaction as a simple example:
.. code:: c
@ -344,7 +354,7 @@ Simple example for reading data from slave:
I2C Master Write and Read
~~~~~~~~~~~~~~~~~~~~~~~~~
Some I2C device needs write configurations before reading data from it, therefore, an interface called :cpp:func:`i2c_master_transmit_receive` can help. The principle of this function can be explained by following chart.
Some I2C device needs write configurations before reading data from it. Therefore, an interface called :cpp:func:`i2c_master_transmit_receive` can help. The principle of this function can be explained by following chart.
.. figure:: ../../../_static/diagrams/i2c/i2c_master_write_read_slave.png
:align: center
@ -405,7 +415,7 @@ Simple example for probing an I2C device:
I2C Slave Controller
^^^^^^^^^^^^^^^^^^^^
After installing the i2c slave driver by :cpp:func:`i2c_new_slave_device`, {IDF_TARGET_NAME} is ready to communicate with other I2C master as a slave.
After installing the I2C slave driver by :cpp:func:`i2c_new_slave_device`, {IDF_TARGET_NAME} is ready to communicate with other I2C masters as a slave.
I2C Slave Write
~~~~~~~~~~~~~~~
@ -422,9 +432,9 @@ Simple example for writing data to FIFO:
.addr_bit_len = I2C_ADDR_BIT_LEN_7, // 7-bit address
.clk_source = I2C_CLK_SRC_DEFAULT, // set the clock source
.i2c_port = 0, // set I2C port number
.send_buf_depth = 256, // set tx buffer length
.scl_io_num = 2, // SCL gpio number
.sda_io_num = 1, // SDA gpio number
.send_buf_depth = 256, // set TX buffer length
.scl_io_num = 2, // SCL GPIO number
.sda_io_num = 1, // SDA GPIO number
.slave_addr = 0x58, // slave address
};
@ -439,7 +449,7 @@ Simple example for writing data to FIFO:
I2C Slave Read
~~~~~~~~~~~~~~
Whenever the master writes data to the slave, the slave will automatically store data in the receive buffer. This allows the slave application to call the function :cpp:func:`i2c_slave_receive` as its own discretion. As :cpp:func:`i2c_slave_receive` is designed as a non-blocking interface. So the user needs to register callback :cpp:func:`i2c_slave_register_event_callbacks` to know when the receive has finished.
Whenever the master writes data to the slave, the slave will automatically store data in the receive buffer. This allows the slave application to call the function :cpp:func:`i2c_slave_receive` as its own discretion. As :cpp:func:`i2c_slave_receive` is designed as a non-blocking interface, users need to register callback :cpp:func:`i2c_slave_register_event_callbacks` to know when the receive has finished.
.. code:: c
@ -483,7 +493,7 @@ Whenever the master writes data to the slave, the slave will automatically store
Put Data In I2C Slave RAM
~~~~~~~~~~~~~~~~~~~~~~~~~
I2C slave fifo mentioned above can be used as RAM, which means user can access the RAM directly via address fields. For example, writing data to the 3rd ram block with following graph. Before using this, please note that :cpp:member:`i2c_slave_config_t::access_ram_en` needs to be set to true.
I2C slave FIFO mentioned above can be used as RAM, which means user can access the RAM directly via address fields. For example, write data to the third RAM block with following graph. Before using this, please note that :cpp:member:`i2c_slave_config_t::access_ram_en` needs to be set to true.
.. figure:: ../../../_static/diagrams/i2c/i2c_slave_write_slave_ram.png
:align: center
@ -506,7 +516,7 @@ Whenever the master writes data to the slave, the slave will automatically store
.flags.access_ram_en = true,
};
// Master write to slave.
// Master writes to slave.
i2c_slave_dev_handle_t slave_handle;
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
@ -516,7 +526,7 @@ Whenever the master writes data to the slave, the slave will automatically store
Get Data From I2C Slave RAM
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Data can be stored in the RAM with a specific offset by the slave controller, and the master can read this data directly via the RAM address. For example, if the data is stored in 3rd ram block, master can read this data by following graph. Before using this, please note that :cpp:member:`i2c_slave_config_t::access_ram_en` needs to be set to true.
Data can be stored in the RAM with a specific offset by the slave controller, and the master can read this data directly via the RAM address. For example, if the data is stored in the third RAM block, master can read this data by the following graph. Before using this, please note that :cpp:member:`i2c_slave_config_t::access_ram_en` needs to be set to true.
.. figure:: ../../../_static/diagrams/i2c/i2c_slave_read_slave_ram.png
:align: center
@ -550,22 +560,22 @@ Register Event Callbacks
I2C master callbacks
~~~~~~~~~~~~~~~~~~~~
When an I2C master bus triggers an interrupt, a specific event will be generated and notify the CPU. If you have some functions that need to be called when those events occurred, you can hook your functions to the ISR (Interrupt Service Routine) by calling :cpp:func:`i2c_master_register_event_callbacks`. Since the registered callback functions are called in the interrupt context, user should ensure the callback function doesn't attempt to block (e.g. by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function). The callback functions are required to return a boolean value, to tell the ISR whether a high priority task is woke up by it.
When an I2C master bus triggers an interrupt, a specific event will be generated and notify the CPU. If you have some functions that need to be called when those events occurred, you can hook your functions to the ISR (Interrupt Service Routine) by calling :cpp:func:`i2c_master_register_event_callbacks`. Since the registered callback functions are called in the interrupt context, users should ensure the callback function doesn't attempt to block (e.g. by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from the function). The callback functions are required to return a boolean value, to tell the ISR whether a high priority task is woken up by it.
I2C master event callbacks are listed in the :cpp:type:`i2c_master_event_callbacks_t`.
Although I2C is a synchronous communication protocol, we also support asynchronous behavior by registering above callback. In this way, I2C APIs will be non-blocking interface. But note that on the same bus, only one device can adopt asynchronous operation.
Although I2C is a synchronous communication protocol, asynchronous behavior is supported by registering above callbacks. In this way, I2C APIs will be non-blocking interface. But note that on the same bus, only one device can adopt asynchronous operation.
.. important::
I2C master asynchronous transaction is still an experimental feature. (The issue is when asynchronous transaction is very large, it will cause memory problem.)
I2C master asynchronous transaction is still an experimental feature (The issue is that when asynchronous transaction is very large, it will cause memory problem).
- :cpp:member:`i2c_master_event_callbacks_t::on_recv_done` sets a callback function for master "transaction-done" event. The function prototype is declared in :cpp:type:`i2c_master_callback_t`.
I2C slave callbacks
~~~~~~~~~~~~~~~~~~~
When an I2C slave bus triggers an interrupt, a specific event will be generated and notify the CPU. If you have some function that needs to be called when those events occurred, you can hook your function to the ISR (Interrupt Service Routine) by calling :cpp:func:`i2c_slave_register_event_callbacks`. Since the registered callback functions are called in the interrupt context, user should ensure the callback function doesn't attempt to block (e.g. by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function). The callback function has a boolean return value, to tell the caller whether a high priority task is woke up by it.
When an I2C slave bus triggers an interrupt, a specific event will be generated and notify the CPU. If you have some function that needs to be called when those events occurred, you can hook your function to the ISR (Interrupt Service Routine) by calling :cpp:func:`i2c_slave_register_event_callbacks`. Since the registered callback functions are called in the interrupt context, users should ensure the callback function doesn't attempt to block (e.g. by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from the function). The callback function has a boolean return value, to tell the caller whether a high priority task is woken up by it.
I2C slave event callbacks are listed in the :cpp:type:`i2c_slave_event_callbacks_t`.
@ -579,9 +589,9 @@ Power Management
.. only:: SOC_I2C_SUPPORT_APB
When the power management is enabled (i.e. :ref:`CONFIG_PM_ENABLE` is on), the system will adjust or stop the source clock of I2C fifo before going into light sleep, thus potentially changing the I2C signals and leading to transmitting or receiving invalid data.
When the power management is enabled (i.e. :ref:`CONFIG_PM_ENABLE` is on), the system will adjust or stop the source clock of I2C FIFO before going into Light-sleep mode, thus potentially changing the I2C signals and leading to transmitting or receiving invalid data.
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. Whenever user creates an I2C bus that has selected :cpp:enumerator:`I2C_CLK_SRC_APB` as the clock source, the driver will guarantee that the power management lock is acquired when I2C operations begin and release the lock automatically when I2C operations finish.
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. Whenever user creates an I2C bus that has selected :cpp:enumerator:`I2C_CLK_SRC_APB` as the clock source, the driver will guarantee that the power management lock is acquired when I2C operations begin and the lock will be released automatically when I2C operations finish.
.. only:: SOC_I2C_SUPPORT_REF_TICK
@ -594,20 +604,20 @@ Power Management
IRAM Safe
^^^^^^^^^
By default, the I2C interrupt will be deferred when the Cache is disabled for reasons like writing/erasing Flash. Thus the event callback functions will not get executed in time, which is not expected in a real-time application.
By default, the I2C interrupt will be deferred when the cache is disabled for reasons like writing or erasing flash. Thus the event callback functions will not get executed in time, which is not expected in a real-time application.
There's a Kconfig option :ref:`CONFIG_I2C_ISR_IRAM_SAFE` that will:
1. Enable the interrupt being serviced even when cache is disabled
2. Place all functions that used by the ISR into IRAM
3. Place driver object into DRAM (in case it's mapped to PSRAM by accident)
1. Enable the interrupt being serviced even when cache is disabled.
2. Place all functions that used by the ISR into IRAM.
3. Place driver object into DRAM (in case it's mapped to PSRAM by accident).
This will allow the interrupt to run while the cache is disabled but will come at the cost of increased IRAM consumption.
Thread Safety
^^^^^^^^^^^^^
The factory function :cpp:func:`i2c_new_master_bus` and :cpp:func:`i2c_new_slave_device` are guaranteed to be thread safe by the driver, which means, user can call them from different RTOS tasks without protection by extra locks. Other public I2C APIs are not thread safe. which means the user should avoid calling them from multiple tasks, if user strongly needs to call them in multiple tasks, please add extra lock.
The factory function :cpp:func:`i2c_new_master_bus` and :cpp:func:`i2c_new_slave_device` are guaranteed to be thread safe by the driver, which means that the functions can be called from different RTOS tasks without protection by extra locks. Other public I2C APIs are not thread safe, which means the user should avoid calling them from multiple tasks, if it is necessary to call them in multiple tasks, please add extra locks.
Kconfig Options
^^^^^^^^^^^^^^^

Wyświetl plik

@ -1 +1,638 @@
.. include:: ../../../en/api-reference/peripherals/i2c.rst
集成电路总线 (I2C)
==================
:link_to_translation:`en:[English]`
简介
----
I2C 是一种串行同步半双工通信协议总线上可以同时挂载多个主机和从机。I2C 使用两条双向开漏线:串行数据线 (SDA) 和串行时钟线 (SCL),通过电阻上拉。
{IDF_TARGET_NAME} 有 {IDF_TARGET_SOC_HP_I2C_NUM} 个 I2C 控制器(也被称为端口),负责处理 I2C 总线上的通信。
.. only:: not esp32c2
单个 I2C 控制器既可以是主机也可以是从机。
.. only:: esp32c2
单个 I2C 控制器只能是主机。
.. only:: SOC_LP_I2C_SUPPORTED
此外,{IDF_TARGET_NAME} 芯片还有 1 个低功耗 (LP) I2C 控制器,是常规 I2C 的简化版本。通常LP I2C 控制器的 RAM 较小,仅支持基本的 I2C 功能,不支持从机模式。有关 HP I2C 和 LP I2C 的所有差异,请参阅 *{IDF_TARGET_NAME} 技术参考手册* > *I2C 控制器 (I2C)* > *特性* [`PDF <{IDF_TARGET_TRM_EN_URL}#i2c>`__]。
当 HP I2C 不足以满足需求时,可以使用 LP I2C 外设。但请注意LP I2C 不支持某些 HP I2C 功能,在使用 LP I2C 前,请阅读相关文档。
通常I2C 从机设备具有 7 位地址或 10 位地址。{IDF_TARGET_NAME} 支持 I2C 标准模式 (Sm) 和快速模式 (Fm),可分别达到 100 kHz 和 400 kHz。
.. warning::
主机模式的 SCL 时钟频率不应大于 400 kHz。
.. note::
SCL 的频率受上拉电阻及导线电容的影响。因此,强烈建议选择适当的上拉电阻,确保频率准确。推荐的上拉电阻值通常在 1 kΩ 到 10 kΩ 之间。
请注意SCL 的频率越高,上拉电阻应该越小(但不能小于 1 kΩ。较大的电阻会降低电流增加时钟切换时间并降低频率。通常推荐 2 kΩ 到 5 kΩ 左右的电阻,也可根据电流需求进行一定调整。
I2C 时钟配置
------------
.. list::
- :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_DEFAULT`:默认的 I2C 时钟源。
:SOC_I2C_SUPPORT_XTAL: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_XTAL`:以外部晶振作为 I2C 时钟源。
:SOC_I2C_SUPPORT_RTC: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_RC_FAST`:以内部 20 MHz RC 振荡器作为 I2C 时钟源。
:SOC_I2C_SUPPORT_APB: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_APB`:以 APB 时钟作为 I2C 时钟源。
:SOC_I2C_SUPPORT_REF_TICK: - :cpp:enumerator:`i2c_clock_source_t::I2C_CLK_SRC_REF_TICK`1 MHZ 时钟。
I2C 文件结构
------------
.. figure:: ../../../_static/diagrams/i2c/i2c_code_structure.png
:align: center
:alt: I2C 文件结构
I2C 文件结构
**需要包含在 I2C 应用程序中的公共头文件**
- ``i2c.h``:遗留 I2C API 的头文件(用于使用旧驱动程序的应用)。
- ``i2c_master.h``:提供标准通信模式下特定 API 的头文件(用于使用主机模式的新驱动程序的应用)。
- ``i2c_slave.h``:提供标准通信模式下特定 API 的头文件(用于使从机模式的新驱动程序的应用)。
.. note::
旧驱动程序与新驱动程序无法共存。包含 ``i2c.h`` 头文件可使用旧驱动程序,或包含 ``i2c_master.h````i2c_slave.h`` 来使用新驱动程序。请注意,现已弃用旧驱动程序,之后将移除。
**上述头文件中包含的公共头文件**
- ``i2c_types_legacy.h``:仅在旧驱动程序中使用的旧公共类型。
- ``i2c_types.h``:提供公共类型的头文件。
功能概述
--------
I2C 驱动程序提供以下服务:
- `资源分配 <#resource-allocation>`__ - 包括如何使用正确的配置来分配 I2C 总线,以及如何在完成工作后回收资源。
- `I2C 主机控制器 <#i2c_master_controller>`__ - 包括 I2C 主机控制器的行为,介绍了数据发送、数据接收和数据的双向传输。
- `I2C 从机控制器 <#i2c_slave_controller>`__ - 包括 I2C 从机控制器的行为,涉及数据发送和数据接收。
- `电源管理 <#power-management>`__ - 描述了不同时钟源对功耗的影响。
- `IRAM 安全 <#iram-safe>`__ - 描述了如何在 cache 被禁用时正常运行 I2C 中断。
- `线程安全 <#thread-safety>`__ - 列出了驱动程序中线程安全的 API。
- `Kconfig 选项 <#kconfig-options>`__ - 列出了支持的 Kconfig 选项,这些选项可以对驱动程序产生不同影响。
资源分配
^^^^^^^^^
若系统支持 I2C 主机总线和 I2C 从机总线,则由驱动程序中的 :cpp:type:`i2c_bus_handle_t` 来表示。资源池管理可用的端口,并在有请求时分配空闲端口。
安装 I2C 主机总线和设备
~~~~~~~~~~~~~~~~~~~~~~~
I2C 主机总线是基于总线-设备模型设计的,因此需要分别使用 :cpp:type:`i2c_master_bus_config_t`:cpp:type:`i2c_device_config_t` 来分配 I2C 主机总线实例和 I2C 设备实例。
.. figure:: ../../../_static/diagrams/i2c/i2c_master_module.png
:align: center
:alt: I2C 主机总线-设备模块
I2C 主机总线-设备模块
I2C 主机总线需要 :cpp:type:`i2c_master_bus_config_t` 指定的配置:
- :cpp:member:`i2c_master_bus_config_t::i2c_port` 设置控制器使用的 I2C 端口。
- :cpp:member:`i2c_master_bus_config_t::sda_io_num` 设置串行数据总线 (SDA) 的 GPIO 编号。
- :cpp:member:`i2c_master_bus_config_t::scl_io_num` 设置串行时钟总线 (SCL) 的 GPIO 编号。
- :cpp:member:`i2c_master_bus_config_t::clk_source` 选择 I2C 总线的时钟源。可用时钟列表见 :cpp:type:`i2c_clock_source_t`。有关不同时钟源对功耗的影响,请参阅 `电源管理 <#power-management>`__ 部分。
- :cpp:member:`i2c_master_bus_config_t::glitch_ignore_cnt` 设置主机总线的毛刺周期。如果线上的毛刺周期小于设置的值(通常设为 7则可以被滤除。
- :cpp:member:`i2c_master_bus_config_t::intr_priority` 设置中断的优先级。如果设置为 ``0``,则驱动程序将使用低或中优先级的中断(优先级可设为 1、2 或 3 中的一个),若未设置,则将使用 :cpp:member:`i2c_master_bus_config_t::intr_priority` 指示的优先级。请使用数字形式1、2、3不要用位掩码形式(1<<1)、(1<<2)、(1<<3))。
- :cpp:member:`i2c_master_bus_config_t::trans_queue_depth` 设置内部传输队列的深度,但仅在异步传输中有效。
- :cpp:member:`i2c_master_bus_config_t::enable_internal_pullup` 启用内部上拉电阻。注意:该设置无法在高速频率下拉高总线,此时建议使用合适的外部上拉电阻。
如果在 :cpp:type:`i2c_master_bus_config_t` 中指定了配置,则可调用 :cpp:func:`i2c_new_master_bus` 来分配和初始化 I2C 主机总线。如果函数运行正确,则将返回一个 I2C 总线句柄。若没有可用的 I2C 端口,此函数将返回 :c:macro:`ESP_ERR_NOT_FOUND` 错误。
I2C 主机设备需要 :cpp:type:`i2c_device_config_t` 指定的配置:
- :cpp:member:`i2c_device_config_t::dev_addr_length` 配置从机设备的地址位长度,可从枚举 :cpp:enumerator:`I2C_ADDR_BIT_LEN_7`:cpp:enumerator:`I2C_ADDR_BIT_LEN_10` (如果支持)中进行选择。
- :cpp:member:`i2c_device_config_t::device_address` 设置 I2C 设备原始地址,请直接将设备地址解析到此成员。例如,若设备地址为 0x28则将 0x28 解析到 :cpp:member:`i2c_device_config_t::device_address`,不要带写入或读取位。
- :cpp:member:`i2c_device_config_t::scl_speed_hz` 设置此设备的 SCL 线频率。
- :cpp:member:`i2c_device_config_t::scl_wait_us` 设置 SCL 等待时间(以微秒为单位)。通常此值较大,因为从机延伸时间会很长(甚至可能延伸到 12 ms。设置为 ``0`` 表示使用默认的寄存器值。
一旦填充好 :cpp:type:`i2c_device_config_t` 结构体的必要参数,就可调用 :cpp:func:`i2c_master_bus_add_device` 来分配 I2C 设备实例,并将设备挂载到主机总线上。如果函数运行正确,则将返回一个 I2C 设备句柄。若未正确初始化 I2C 总线,此函数将返回 :c:macro:`ESP_ERR_INVALID_ARG` 错误。
.. code:: c
#include "driver/i2c_master.h"
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = TEST_I2C_PORT,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_io_num = I2C_MASTER_SDA_IO,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
i2c_master_bus_handle_t bus_handle;
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
i2c_device_config_t dev_cfg = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = 0x58,
.scl_speed_hz = 100000,
};
i2c_master_dev_handle_t dev_handle;
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));
.. only:: SOC_LP_I2C_SUPPORTED
使用 LP I2C 外设来安装 I2C 主机总线
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
使用 LP I2C 外设来安装 I2C 主机总线的流程与安装 HP I2C 外设几乎相同,但仍有一些区别,包括 IO、时钟源及 I2C 端口编号等。以下代码展示了如何使用 LP_I2C 来安装 I2C 主机总线。
.. code:: c
#include "driver/i2c_master.h"
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = LP_I2C_SCLK_DEFAULT, // LP I2C 的时钟源,可能与 HP I2C 不同
.i2c_port = LP_I2C_NUM_0, // 分配给 LP I2C 端口
.scl_io_num = 7, // SCL IO 编号。请参阅技术参考手册
.sda_io_num = 6, // SDA IO 编号号。请参阅技术参考手册
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
i2c_master_bus_handle_t bus_handle;
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
i2c_device_config_t dev_cfg = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = 0x58,
.scl_speed_hz = 100000,
};
i2c_master_dev_handle_t dev_handle;
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));
卸载 I2C 主机总线和设备
~~~~~~~~~~~~~~~~~~~~~~~~
如果不再需要之前安装的 I2C 总线或设备,建议调用 :cpp:func:`i2c_master_bus_rm_device`:cpp:func:`i2c_del_master_bus` 来回收资源,以释放底层硬件。
安装 I2C 从机设备
~~~~~~~~~~~~~~~~~~~
I2C 从机设备需要 :cpp:type:`i2c_slave_config_t` 指定的配置:
.. list::
- :cpp:member:`i2c_slave_config_t::i2c_port` 设置控制器使用的 I2C 端口。
- :cpp:member:`i2c_slave_config_t::sda_io_num` 设置串行数据总线 (SDA) 的 GPIO 编号。
- :cpp:member:`i2c_slave_config_t::scl_io_num` 设置串行时钟总线 (SCL) 的 GPIO 编号。
- :cpp:member:`i2c_slave_config_t::clk_source` 选择 I2C 总线的时钟源。可用时钟列表见 :cpp:type:`i2c_clock_source_t`。有关不同时钟源对功耗的影响,请参阅 `电源管理 <#power-management>`__
- :cpp:member:`i2c_slave_config_t::send_buf_depth` 设置发送 buffer 的长度。
- :cpp:member:`i2c_slave_config_t::slave_addr` 设置从机地址。
- :cpp:member:`i2c_master_bus_config_t::intr_priority` 设置中断的优先级。如果设置为 ``0`` ,则驱动程序将使用低或中优先级的中断(优先级可设为 1、2 或 3 中的一个),若未设置,则将使用 :cpp:member:`i2c_master_bus_config_t::intr_priority` 指示的优先级。请使用数字形式1、2、3不要用位掩码形式(1<<1)、(1<<2)、(1<<3))。请注意,中断优先级一旦设置完成,在调用 :cpp:func:`i2c_del_master_bus` 之前都无法更改。
- :cpp:member:`i2c_slave_config_t::addr_bit_len`。如果需要从机设备具有 10 位地址,则将该成员变量设为 ``I2C_ADDR_BIT_LEN_10``
:SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE: - :cpp:member:`i2c_slave_config_t::stretch_en`。如果要启用从机控制器拉伸功能,请将该成员变量设为 true。有关 I2C 拉伸的工作原理,请参阅 [`TRM <{IDF_TARGET_TRM_EN_URL}#i2c>`__]。
:SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE: - :cpp:member:`i2c_slave_config_t::broadcast_en`。如果要启用从机广播,请将该成员变量设为 true。当从机设备接收到来自主机设备的通用调用地址 0x00且后面的读写位为 0 时,无论从机设备自身地址如何,都会响应主机设备。
:SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS: - :cpp:member:`i2c_slave_config_t::access_ram_en`。如果要启用 non-FIFO 模式,请将该成员变量设为 true则 I2C 数据 FIFO 可用作 RAM并将同步打开双地址。
:SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH: - :cpp:member:`i2c_slave_config_t::slave_unmatch_en`。将该成员变量设为 true将启用从机设备不匹配中断。如果主机设备发送的命令地址与从机设备地址不匹配则会触发不匹配中断。
一旦填充好 :cpp:type:`i2c_slave_config_t` 结构体的必要参数,就可调用 :cpp:func:`i2c_new_slave_device` 来分配和初始化 I2C 主机总线。如果函数运行正确,则将返回一个 I2C 总线句柄。若没有可用的 I2C 端口,此函数将返回 :c:macro:`ESP_ERR_NOT_FOUND` 错误。
.. code:: c
i2c_slave_config_t i2c_slv_config = {
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = TEST_I2C_PORT,
.send_buf_depth = 256,
.scl_io_num = I2C_SLAVE_SCL_IO,
.sda_io_num = I2C_SLAVE_SDA_IO,
.slave_addr = 0x58,
};
i2c_slave_dev_handle_t slave_handle;
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
卸载 I2C 从机设备
~~~~~~~~~~~~~~~~~~
如果不再需要之前安装的 I2C 总线,建议调用 :cpp:func:`i2c_del_slave_device` 来回收资源,以释放底层硬件。
I2C 主机控制器
^^^^^^^^^^^^^^^
通过调用 :cpp:func:`i2c_new_master_bus` 安装好 I2C 主机控制器驱动程序后,{IDF_TARGET_NAME} 就可以与其他 I2C 设备进行通信了。I2C API 允许标准事务,如下图所示:
.. wavedrom:: /../_static/diagrams/i2c/i2c_trans_wave.json
I2C 主机写入
~~~~~~~~~~~~~~
在成功安装 I2C 主机总线之后,可以通过调用 :cpp:func:`i2c_master_transmit` 来向从机设备写入数据。下图解释了该函数的原理。
简单来说,驱动程序用一系列命令填充了一个命令链,并将该命令链传递给 I2C 控制器执行。
.. figure:: ../../../_static/diagrams/i2c/i2c_master_write_slave.png
:align: center
:alt: I2C 主机向从机设备写入数据
I2C 主机向从机设备写入数据
将数据写入从机设备的简单示例:
.. code:: c
#define DATA_LENGTH 100
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_PORT_NUM_0,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_io_num = I2C_MASTER_SDA_IO,
.glitch_ignore_cnt = 7,
};
i2c_master_bus_handle_t bus_handle;
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
i2c_device_config_t dev_cfg = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = 0x58,
.scl_speed_hz = 100000,
};
i2c_master_dev_handle_t dev_handle;
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));
ESP_ERROR_CHECK(i2c_master_transmit(dev_handle, data_wr, DATA_LENGTH, -1));
I2C 主机写入操作还支持在单次传输事务中传输多个 buffer。如下所示
.. code:: c
uint8_t control_phase_byte = 0;
size_t control_phase_size = 0;
if (/*condition*/) {
control_phase_byte = 1;
control_phase_size = 1;
}
uint8_t *cmd_buffer = NULL;
size_t cmd_buffer_size = 0;
if (/*condition*/) {
uint8_t cmds[4] = {BYTESHIFT(lcd_cmd, 3), BYTESHIFT(lcd_cmd, 2), BYTESHIFT(lcd_cmd, 1), BYTESHIFT(lcd_cmd, 0)};
cmd_buffer = cmds;
cmd_buffer_size = 4;
}
uint8_t *lcd_buffer = NULL;
size_t lcd_buffer_size = 0;
if (buffer) {
lcd_buffer = (uint8_t*)buffer;
lcd_buffer_size = buffer_size;
}
i2c_master_transmit_multi_buffer_info_t lcd_i2c_buffer[3] = {
{.write_buffer = &control_phase_byte, .buffer_size = control_phase_size},
{.write_buffer = cmd_buffer, .buffer_size = cmd_buffer_size},
{.write_buffer = lcd_buffer, .buffer_size = lcd_buffer_size},
};
i2c_master_multi_buffer_transmit(handle, lcd_i2c_buffer, sizeof(lcd_i2c_buffer) / sizeof(i2c_master_transmit_multi_buffer_info_t), -1);
I2C 主机读取
~~~~~~~~~~~~~~
在成功安装 I2C 主机总线后,可以通过调用 :cpp:func:`i2c_master_receive` 从从机设备读取数据。下图解释了该函数的原理。
.. figure:: ../../../_static/diagrams/i2c/i2c_master_read_slave.png
:align: center
:alt: I2C 主机从从机设备读取数据
I2C 主机从从机设备读取数据
从从机设备读取数据的简单示例:
.. code:: c
#define DATA_LENGTH 100
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_PORT_NUM_0,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_io_num = I2C_MASTER_SDA_IO,
.glitch_ignore_cnt = 7,
};
i2c_master_bus_handle_t bus_handle;
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
i2c_device_config_t dev_cfg = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = 0x58,
.scl_speed_hz = 100000,
};
i2c_master_dev_handle_t dev_handle;
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));
i2c_master_receive(dev_handle, data_rd, DATA_LENGTH, -1);
I2C 主机写入和读取
~~~~~~~~~~~~~~~~~~~
从一些 I2C 设备中读取数据之前需要进行写入配置,可通过 :cpp:func:`i2c_master_transmit_receive` 接口进行配置。下图解释了该函数的原理。
.. figure:: ../../../_static/diagrams/i2c/i2c_master_write_read_slave.png
:align: center
:alt: I2C 主机向从机设备写入并从从机设备读取数据
I2C 主机向从机设备写入并从从机设备读取数据
向从机设备写入并从从机设备读取数据的简单示例:
.. code:: c
i2c_device_config_t dev_cfg = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = 0x58,
.scl_speed_hz = 100000,
};
i2c_master_dev_handle_t dev_handle;
ESP_ERROR_CHECK(i2c_master_bus_add_device(I2C_PORT_NUM_0, &dev_cfg, &dev_handle));
uint8_t buf[20] = {0x20};
uint8_t buffer[2];
ESP_ERROR_CHECK(i2c_master_transmit_receive(dev_handle, buf, sizeof(buf), buffer, 2, -1));
I2C 主机探测
~~~~~~~~~~~~
I2C 驱动程序可以使用 :cpp:func:`i2c_master_probe` 来检测设备是否已经连接到 I2C 总线上。如果该函数返回 ``ESP_OK``,则表示该设备已经被检测到。
.. important::
在调用该函数时,必须将上拉电阻连接到 SCL 和 SDA 管脚。如果在正确解析 `xfer_timeout_ms` 时收到 `ESP_ERR_TIMEOUT`,则应检查上拉电阻。若暂无合适的电阻,也可将 `flags.enable_internal_pullup` 设为 true。
.. figure:: ../../../_static/diagrams/i2c/i2c_master_probe.png
:align: center
:alt: I2C 主机探测
I2C 主机探测
探测 I2C 设备的简单示例:
.. code:: c
i2c_master_bus_config_t i2c_mst_config_1 = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = TEST_I2C_PORT,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_io_num = I2C_MASTER_SDA_IO,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
i2c_master_bus_handle_t bus_handle;
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config_1, &bus_handle));
ESP_ERROR_CHECK(i2c_master_probe(bus_handle, 0x22, -1));
ESP_ERROR_CHECK(i2c_del_master_bus(bus_handle));
I2C 从机控制器
^^^^^^^^^^^^^^
通过调用 :cpp:func:`i2c_new_slave_device` 安装好 I2C 从机驱动程序后,{IDF_TARGET_NAME} 就可以作为从机与其他 I2C 主机进行通信了。
I2C 从机写入
~~~~~~~~~~~~~~~
I2C 从机的发送 buffer 可作为 FIFO 来存储要发送的数据。在主机请求这些数据前,它们会一直排队。可通过调用 :cpp:func:`i2c_slave_transmit` 来传输数据。
将数据写入 FIFO 的简单示例:
.. code:: c
uint8_t *data_wr = (uint8_t *) malloc(DATA_LENGTH);
i2c_slave_config_t i2c_slv_config = {
.addr_bit_len = I2C_ADDR_BIT_LEN_7, // 7 位地址
.clk_source = I2C_CLK_SRC_DEFAULT, // 设置时钟源
.i2c_port = 0, // 设置 I2C 端口编号
.send_buf_depth = 256, // 设置 TX buffer 长度
.scl_io_num = 2, // SCL 管脚编号
.sda_io_num = 1, // SDA 管脚编号
.slave_addr = 0x58, // 从机地址
};
i2c_bus_handle_t i2c_bus_handle;
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &i2c_bus_handle));
for (int i = 0; i < DATA_LENGTH; i++) {
data_wr[i] = i;
}
ESP_ERROR_CHECK(i2c_slave_transmit(i2c_bus_handle, data_wr, DATA_LENGTH, 10000));
I2C 从机读取
~~~~~~~~~~~~~
每当主机将数据写入从机,从机都会自动将数据存储在接收 buffer 中,从而使从机应用程序能自由调用 :cpp:func:`i2c_slave_receive`:cpp:func:`i2c_slave_receive` 为非阻塞接口,因此要想知道接收是否完成,需注册回调函数 :cpp:func:`i2c_slave_register_event_callbacks`
.. code:: c
static IRAM_ATTR bool i2c_slave_rx_done_callback(i2c_slave_dev_handle_t channel, const i2c_slave_rx_done_event_data_t *edata, void *user_data)
{
BaseType_t high_task_wakeup = pdFALSE;
QueueHandle_t receive_queue = (QueueHandle_t)user_data;
xQueueSendFromISR(receive_queue, edata, &high_task_wakeup);
return high_task_wakeup == pdTRUE;
}
uint8_t *data_rd = (uint8_t *) malloc(DATA_LENGTH);
uint32_t size_rd = 0;
i2c_slave_config_t i2c_slv_config = {
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = TEST_I2C_PORT,
.send_buf_depth = 256,
.scl_io_num = I2C_SLAVE_SCL_IO,
.sda_io_num = I2C_SLAVE_SDA_IO,
.slave_addr = 0x58,
};
i2c_slave_dev_handle_t slave_handle;
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
s_receive_queue = xQueueCreate(1, sizeof(i2c_slave_rx_done_event_data_t));
i2c_slave_event_callbacks_t cbs = {
.on_recv_done = i2c_slave_rx_done_callback,
};
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(slave_handle, &cbs, s_receive_queue));
i2c_slave_rx_done_event_data_t rx_data;
ESP_ERROR_CHECK(i2c_slave_receive(slave_handle, data_rd, DATA_LENGTH));
xQueueReceive(s_receive_queue, &rx_data, pdMS_TO_TICKS(10000));
// 接收完成。
.. only:: SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS
将数据放入 I2C 从机 RAM 中
~~~~~~~~~~~~~~~~~~~~~~~~~~~
如上所述I2C 从机 FIFO 可被用作 RAM即可以通过地址字段直接访问 RAM。例如可参照下图将数据写入第三个 RAM 块。请注意,在进行操作前需要先将 :cpp:member:`i2c_slave_config_t::access_ram_en` 设为 true。
.. figure:: ../../../_static/diagrams/i2c/i2c_slave_write_slave_ram.png
:align: center
:alt: 将数据放入 I2C 从机 RAM 中
将数据放入 I2C 从机 RAM 中
.. code:: c
uint8_t data_rd[DATA_LENGTH_RAM] = {0};
i2c_slave_config_t i2c_slv_config = {
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = TEST_I2C_PORT,
.send_buf_depth = 256,
.scl_io_num = I2C_SLAVE_SCL_IO,
.sda_io_num = I2C_SLAVE_SDA_IO,
.slave_addr = 0x58,
.flags.access_ram_en = true,
};
// 主机将数据写入从机。
i2c_slave_dev_handle_t slave_handle;
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
ESP_ERROR_CHECK(i2c_slave_read_ram(slave_handle, 0x5, data_rd, DATA_LENGTH_RAM));
ESP_ERROR_CHECK(i2c_del_slave_device(slave_handle));
从 I2C 从机 RAM 中获取数据
~~~~~~~~~~~~~~~~~~~~~~~~~~~
数据可被存储在相对从机一定偏移量的 RAM 中,且主机可直接通过 RAM 地址读取这些数据。例如,如果数据被存储在第三个 RAM 块中,则主机可参照下图读取这些数据。请注意,在操作前需要先将 :cpp:member:`i2c_slave_config_t::access_ram_en` 设为 true。
.. figure:: ../../../_static/diagrams/i2c/i2c_slave_read_slave_ram.png
:align: center
:alt: 从 I2C 从机 RAM 中获取数据
从 I2C 从机 RAM 中获取数据
.. code:: c
uint8_t data_wr[DATA_LENGTH_RAM] = {0};
i2c_slave_config_t i2c_slv_config = {
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = TEST_I2C_PORT,
.send_buf_depth = 256,
.scl_io_num = I2C_SLAVE_SCL_IO,
.sda_io_num = I2C_SLAVE_SDA_IO,
.slave_addr = 0x58,
.flags.access_ram_en = true,
};
i2c_slave_dev_handle_t slave_handle;
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
ESP_ERROR_CHECK(i2c_slave_write_ram(slave_handle, 0x2, data_wr, DATA_LENGTH_RAM));
ESP_ERROR_CHECK(i2c_del_slave_device(slave_handle));
注册事件回调函数
^^^^^^^^^^^^^^^^^
I2C 主机回调
~~~~~~~~~~~~~
当 I2C 主机总线触发中断时,将生成特定事件并通知 CPU。如果需要在发生这些事件时调用某些函数可通过 :cpp:func:`i2c_master_register_event_callbacks` 将这些函数挂接到中断服务程序 (ISR) 上。由于注册的回调函数是在中断上下文中被调用的,所以应确保这些函数不会阻塞(例如,确保仅从函数内部调用带有 ``ISR`` 后缀的 FreeRTOS API。回调函数需要返回一个布尔值告诉 ISR 是否唤醒了高优先级任务。
I2C 主机事件回调函数列表见 :cpp:type:`i2c_master_event_callbacks_t`
虽然 I2C 是一种同步通信协议,但也支持通过注册上述回调函数来实现异步行为,此时 I2C API 将成为非阻塞接口。但请注意,在同一总线上只有一个设备可以采用异步操作。
.. important::
I2C 主机异步传输仍然是一个实验性功能(问题在于当异步传输量过大时,会导致内存异常。)
- :cpp:member:`i2c_master_event_callbacks_t::on_recv_done` 可设置用于主机“传输完成”事件的回调函数。该函数原型在 :cpp:type:`i2c_master_callback_t` 中声明。
I2C 从机回调
~~~~~~~~~~~~~
当 I2C 从机总线触发中断时,将生成特定事件并通知 CPU。如果需要在发生这些事件时调用某些函数可通过 :cpp:func:`i2c_slave_register_event_callbacks` 将这些函数挂接到中断服务程序 (ISR) 上。由于注册的回调函数是在中断上下文中被调用的,所以应确保这些函数不会导致延迟(例如,确保仅从函数中调用带有 ``ISR`` 后缀的 FreeRTOS API。回调函数需要返回一个布尔值告诉调用者是否唤醒了高优先级任务。
I2C 从机事件回调函数列表见 :cpp:type:`i2c_slave_event_callbacks_t`
.. list::
- :cpp:member:`i2c_slave_event_callbacks_t::on_recv_done` 可设置用于“接收完成”事件的回调函数。该函数原型在 :cpp:type:`i2c_slave_received_callback_t` 中声明。
:SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE: - :cpp:member:`i2c_slave_event_callbacks_t::on_stretch_occur` 可设置用于“时钟拉伸”事件的回调函数。该函数原型在 :cpp:type:`i2c_slave_stretch_callback_t` 中声明。
电源管理
^^^^^^^^^^
.. only:: SOC_I2C_SUPPORT_APB
启用电源管理(即打开 :ref:`CONFIG_PM_ENABLE`),系统会在进入 Light-sleep 模式前调整或暂停 I2C FIFO 的时钟源,这可能会导致 I2C 信号改变,传输或接收到无效数据。
但驱动程序可以通过获取 :cpp:enumerator:`ESP_PM_APB_FREQ_MAX` 类型的电源管理锁来防止系统改变 APB 频率。每当用户创建一个以 :cpp:enumerator:`I2C_CLK_SRC_APB` 为时钟源的 I2C 总线,驱动程序将在开始 I2C 操作时获取电源管理锁,并在结束 I2C 操作时自动释放锁。
.. only:: SOC_I2C_SUPPORT_REF_TICK
如果控制器以 :cpp:enumerator:`I2C_CLK_SRC_REF_TICK` 为时钟源,则驱动程序不会为其安装电源管理锁,因为对于低功耗应用,只要时钟源能够提供足够的精度即可。
.. only:: SOC_I2C_SUPPORT_XTAL
如果控制器以 :cpp:enumerator:`I2C_CLK_SRC_XTAL` 为时钟源,则驱动程序不会为其安装电源管理锁,因为对于低功耗应用,只要时钟源能够提供足够的分辨率即可。
IRAM 安全
^^^^^^^^^
默认情况下,若 cache 因写入或擦除 flash 等原因而被禁用时,将推迟 I2C 中断。此时事件回调函数将无法按时执行,会影响实时应用的系统响应。
Kconfig 选项 :ref:`CONFIG_I2C_ISR_IRAM_SAFE` 能够做到以下几点:
1. 即使 cache 被禁用I2C 中断依旧正常运行。
2. 将 ISR 使用的所有函数放入 IRAM 中。
3. 将驱动程序对象放入 DRAM 中(以防它被意外映射到 PSRAM 中)。
启用以上选项,即使 cache 被禁用I2C 中断依旧正常运行,但会增加 IRAM 的消耗。
线程安全
^^^^^^^^^^^^^
工厂函数 :cpp:func:`i2c_new_master_bus`:cpp:func:`i2c_new_slave_device` 由驱动程序保证其线程安全,不需要额外的锁保护,也可从不同的 RTOS 任务中调用这些函数。应避免从多个任务中调用其他非线程安全的公共 I2C API若确实需要调用请添加额外的锁。
Kconfig 选项
^^^^^^^^^^^^^^^
- :ref:`CONFIG_I2C_ISR_IRAM_SAFE` 将在 cache 被禁用时控制默认的 ISR 处理程序正常工作,详情请参阅 `IRAM 安全 <#iram-safe>`__
- :ref:`CONFIG_I2C_ENABLE_DEBUG_LOG` 可启用调试日志,但会增加固件二进制文件大小。
API 参考
--------
.. include-build-file:: inc/i2c_master.inc
.. only:: SOC_I2C_SUPPORT_SLAVE
.. include-build-file:: inc/i2c_slave.inc
.. include-build-file:: inc/components/esp_driver_i2c/include/driver/i2c_types.inc
.. include-build-file:: inc/components/hal/include/hal/i2c_types.inc