mirror of
https://github.com/espressif/esp-idf
synced 2025-03-10 01:29:21 -04:00
docs(i2c_slave): Update i2c slave programming guide for new changes
This commit is contained in:
parent
170d44b75d
commit
32adbe72b6
@ -36,6 +36,14 @@ Typically, an I2C slave device has a 7-bit address or 10-bit address. {IDF_TARGE
|
||||
|
||||
Keep in mind that the higher the frequency, the smaller the pull-up resistor should be (but not less than 1 kΩ). Indeed, large resistors will decline the current, which will increase the clock switching time and reduce the frequency. A range of 2 kΩ to 5 kΩ is recommended, but adjustments may also be necessary depending on their current draw requirements.
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
i2c_slave_v1
|
||||
|
||||
.. note::
|
||||
|
||||
We realized that our first version of the I2C slave driver had some problems and was not easy to use, so we have prepared a second version of the I2C slave driver, which solves many of the problems with our current I2C slave and which will be the focus of our maintenance. We encourage and recommend that you use the second version of the I2C slave driver, which you can do by enabling :ref:`CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2`. This document focuses on the content of I2C slave v2.0. If you still want to read programming guide of I2C slave v1.0, please refer to :ref:`i2c-slave-v1`. The I2C slave v1.0 driver will be removed with the IDF v6.0 update.
|
||||
|
||||
I2C Clock Configuration
|
||||
-----------------------
|
||||
@ -218,27 +226,26 @@ I2C slave requires the configuration specified by :cpp:type:`i2c_slave_config_t`
|
||||
- :cpp:member:`i2c_slave_config_t::sda_io_num` sets the GPIO number for serial data bus (SDA).
|
||||
- :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` 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), instead of 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.
|
||||
- :cpp:member:`i2c_slave_config_t::send_buf_depth` sets the sending software buffer length.
|
||||
- :cpp:member:`i2c_slave_config_t::receive_buf_depth` sets the receiving software buffer length.
|
||||
- :cpp:member:`i2c_slave_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_slave_config_t::intr_priority`. Please use the number form (1, 2, 3), instead of 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_slave_device` 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.
|
||||
- :cpp:member:`i2c_slave_config_t::allow_pd` If set, the driver will backup/restore the I2C registers before/after entering/exist sleep mode. By this approach, the system can power off I2C's power domain. This can save power, but at the expense of more RAM being consumed.
|
||||
:SOC_I2C_SLAVE_SUPPORT_BROADCAST: - :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.
|
||||
- :cpp:member:`i2c_slave_config_t::enable_internal_pullup` Set this to enable internal pull-up. Even though, an output pull-up resistance is strongly recommended.
|
||||
|
||||
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
|
||||
|
||||
i2c_slave_config_t i2c_slv_config = {
|
||||
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
|
||||
.i2c_port = I2C_SLAVE_NUM,
|
||||
.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,
|
||||
.slave_addr = ESP_SLAVE_ADDR,
|
||||
.send_buf_depth = 100,
|
||||
.receive_buf_depth = 100,
|
||||
};
|
||||
|
||||
i2c_slave_dev_handle_t slave_handle;
|
||||
@ -437,142 +444,72 @@ 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 masters as a slave.
|
||||
|
||||
The I2C slave is not as subjective as the I2C master which knows when it should send data and when it should receive data. The I2C slave is very passive in most cases, that means the I2C slave's ability to send and receive data is largely dependent on the master's actions. Therefore, we throw two callback functions in the driver that represent read requests and write requests from the I2C master.
|
||||
|
||||
I2C Slave Write
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The send buffer of the I2C slave is used as a FIFO to store the data to be sent. The data will queue up until the master requests them. You can call :cpp:func:`i2c_slave_transmit` to transfer data.
|
||||
You can get I2C slave write event be register :cpp:member:`i2c_slave_event_callbacks_t::on_request` callback, and in a task when get the request event, you can call `i2c_slave_write` to send data.
|
||||
|
||||
Simple example for writing data to FIFO:
|
||||
Simple example for transmitting data:
|
||||
|
||||
.. 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-bit address
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT, // set the clock source
|
||||
.i2c_port = TEST_I2C_PORT, // set I2C port number
|
||||
.send_buf_depth = 256, // set TX buffer length
|
||||
.scl_io_num = I2C_SLAVE_SCL_IO, // SCL GPIO number
|
||||
.sda_io_num = I2C_SLAVE_SDA_IO, // SDA GPIO number
|
||||
.slave_addr = 0x58, // slave address
|
||||
};
|
||||
|
||||
i2c_slave_dev_handle_t slave_handle;
|
||||
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
|
||||
for (int i = 0; i < DATA_LENGTH; i++) {
|
||||
data_wr[i] = i;
|
||||
// Prepare a callback function
|
||||
static bool i2c_slave_request_cb(i2c_slave_dev_handle_t i2c_slave, const i2c_slave_request_event_data_t *evt_data, void *arg)
|
||||
{
|
||||
i2c_slave_event_t evt = I2C_SLAVE_EVT_TX;
|
||||
BaseType_t xTaskWoken = 0;
|
||||
xQueueSendFromISR(context->event_queue, &evt, &xTaskWoken);
|
||||
return xTaskWoken;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(i2c_slave_transmit(slave_handle, data_wr, DATA_LENGTH, 10000));
|
||||
// Register callback in a task
|
||||
i2c_slave_event_callbacks_t cbs = {
|
||||
.on_request = i2c_slave_request_cb,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(context.handle, &cbs, &context));
|
||||
|
||||
// Waiting for request event and send data in a task
|
||||
static void i2c_slave_task(void *arg)
|
||||
{
|
||||
uint8_t buffer_size = 64;
|
||||
uint32_t write_len;
|
||||
uint8_t *data_buffer;
|
||||
|
||||
while (true) {
|
||||
i2c_slave_event_t evt;
|
||||
if (xQueueReceive(context->event_queue, &evt, 10) == pdTRUE) {
|
||||
ESP_ERROR_CHECK(i2c_slave_write(handle, data_buffer, buffer_size, &write_len, 1000));
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
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, users need to register callback :cpp:func:`i2c_slave_register_event_callbacks` to know when the receive has finished.
|
||||
Same as write, you can get I2C slave read event be register :cpp:member:`i2c_slave_event_callbacks_t::on_receive` callback, and in a task when get the request event, you can save the data and do what you want.
|
||||
|
||||
Simple example for receiving data:
|
||||
|
||||
.. 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)
|
||||
// Prepare a callback function
|
||||
static bool i2c_slave_receive_cb(i2c_slave_dev_handle_t i2c_slave, const i2c_slave_rx_done_event_data_t *evt_data, void *arg)
|
||||
{
|
||||
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;
|
||||
i2c_slave_event_t evt = I2C_SLAVE_EVT_RX;
|
||||
BaseType_t xTaskWoken = 0;
|
||||
// You can get data and length via i2c_slave_rx_done_event_data_t
|
||||
xQueueSendFromISR(context->event_queue, &evt, &xTaskWoken);
|
||||
return xTaskWoken;
|
||||
}
|
||||
|
||||
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));
|
||||
// Register callback in a task
|
||||
i2c_slave_event_callbacks_t cbs = {
|
||||
.on_recv_done = i2c_slave_rx_done_callback,
|
||||
.on_receive = i2c_slave_receive_cb,
|
||||
};
|
||||
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));
|
||||
// Receive done.
|
||||
|
||||
.. only:: SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS
|
||||
|
||||
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, 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
|
||||
:alt: Put data in I2C slave RAM
|
||||
|
||||
Put data in I2C slave 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,
|
||||
};
|
||||
|
||||
// Master writes to slave.
|
||||
|
||||
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));
|
||||
|
||||
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 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
|
||||
:alt: Get data from I2C slave RAM
|
||||
|
||||
Get data from I2C slave 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));
|
||||
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(context.handle, &cbs, &context));
|
||||
|
||||
Register Event Callbacks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -601,8 +538,8 @@ I2C slave event callbacks are listed in the :cpp:type:`i2c_slave_event_callbacks
|
||||
|
||||
.. list::
|
||||
|
||||
- :cpp:member:`i2c_slave_event_callbacks_t::on_recv_done` sets a callback function for "receive-done" event. The function prototype is declared in :cpp:type:`i2c_slave_received_callback_t`.
|
||||
:SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE: - :cpp:member:`i2c_slave_event_callbacks_t::on_stretch_occur` sets a callback function for "stretch" cause. The function prototype is declared in :cpp:type:`i2c_slave_stretch_callback_t`.
|
||||
- :cpp:member:`i2c_slave_event_callbacks_t::on_request` sets a callback function for request event.
|
||||
- :cpp:member:`i2c_slave_event_callbacks_t::on_receive` sets a callback function for receive event. The function prototype is declared in :cpp:type:`i2c_slave_received_callback_t`.
|
||||
|
||||
Power Management
|
||||
^^^^^^^^^^^^^^^^
|
||||
@ -637,13 +574,28 @@ This will allow the interrupt to run while the cache is disabled but will come a
|
||||
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 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.
|
||||
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.
|
||||
|
||||
I2C master operation functions are also guaranteed to be thread safe by bus operation semaphore.
|
||||
|
||||
- :cpp:func:`i2c_master_transmit`
|
||||
- :cpp:func:`i2c_master_multi_buffer_transmit`
|
||||
- :cpp:func:`i2c_master_transmit_receive`
|
||||
- :cpp:func:`i2c_master_receive`
|
||||
- :cpp:func:`i2c_master_probe`
|
||||
|
||||
I2C slave operation functions are also guaranteed to be thread safe by bus operation semaphore.
|
||||
|
||||
- :cpp:func:`i2c_slave_write`
|
||||
|
||||
Other functions are not guaranteed to be thread-safe. Thus, you should avoid calling them in different tasks without mutex protection.
|
||||
|
||||
Kconfig Options
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_I2C_ISR_IRAM_SAFE` controls whether the default ISR handler can work when cache is disabled, see also `IRAM Safe <#iram-safe>`__ for more information.
|
||||
- :ref:`CONFIG_I2C_ENABLE_DEBUG_LOG` is used to enable the debug log at the cost of increased firmware binary size.
|
||||
- :ref:`CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2` is used to enable the I2C slave driver v2.0.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
@ -652,6 +604,7 @@ Application Examples
|
||||
|
||||
- :example:`peripherals/i2c/i2c_tools` demonstrates how to use the I2C Tools for developing I2C related applications, providing command-line tools for configuring the I2C bus, scanning for devices, reading and setting registers, and examining registers.
|
||||
|
||||
- :example:`peripherals/i2c/i2c_slave_network_sensor` demonstrates how to use the I2C slave for developing I2C related applications, providing how I2C slave can behave as a network sensor, and use event callbacks to receive and send data.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
202
docs/en/api-reference/peripherals/i2c_slave_v1.rst
Normal file
202
docs/en/api-reference/peripherals/i2c_slave_v1.rst
Normal file
@ -0,0 +1,202 @@
|
||||
.. _i2c-slave-v1:
|
||||
|
||||
I2C Slave v1.0
|
||||
==============
|
||||
|
||||
.. warning::
|
||||
|
||||
This I2C slave driver version 1 will be removed when idf v6.0 update. We suggest you use I2C slave version 2 via :ref:`CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2`.
|
||||
|
||||
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.
|
||||
|
||||
Install I2C slave device
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
I2C slave requires the configuration specified by :cpp:type:`i2c_slave_config_t`:
|
||||
|
||||
.. list::
|
||||
|
||||
- :cpp:member:`i2c_slave_config_t::i2c_port` sets the I2C port used by the controller.
|
||||
- :cpp:member:`i2c_slave_config_t::sda_io_num` sets the GPIO number for serial data bus (SDA).
|
||||
- :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_slave_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_slave_config_t::intr_priority`. Please use the number form (1, 2, 3), instead of 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_slave_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, :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
|
||||
|
||||
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));
|
||||
|
||||
Uninstall I2C slave device
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a previously installed I2C bus is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`i2c_del_slave_device`, so that to release the underlying hardware.
|
||||
|
||||
|
||||
I2C Slave Write
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The send buffer of the I2C slave is used as a FIFO to store the data to be sent. The data will queue up until the master requests them. You can call :cpp:func:`i2c_slave_transmit` to transfer data.
|
||||
|
||||
Simple example for writing data to 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-bit address
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT, // set the clock source
|
||||
.i2c_port = TEST_I2C_PORT, // set I2C port number
|
||||
.send_buf_depth = 256, // set TX buffer length
|
||||
.scl_io_num = I2C_SLAVE_SCL_IO, // SCL GPIO number
|
||||
.sda_io_num = I2C_SLAVE_SDA_IO, // SDA GPIO number
|
||||
.slave_addr = 0x58, // slave address
|
||||
};
|
||||
|
||||
i2c_slave_dev_handle_t slave_handle;
|
||||
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
|
||||
for (int i = 0; i < DATA_LENGTH; i++) {
|
||||
data_wr[i] = i;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(i2c_slave_transmit(slave_handle, data_wr, DATA_LENGTH, 10000));
|
||||
|
||||
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, users need to register callback :cpp:func:`i2c_slave_register_event_callbacks` to know when the receive has finished.
|
||||
|
||||
.. 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));
|
||||
// Receive done.
|
||||
|
||||
.. only:: SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS
|
||||
|
||||
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, 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
|
||||
:alt: Put data in I2C slave RAM
|
||||
|
||||
Put data in I2C slave 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,
|
||||
};
|
||||
|
||||
// Master writes to slave.
|
||||
|
||||
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));
|
||||
|
||||
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 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
|
||||
:alt: Get data from I2C slave RAM
|
||||
|
||||
Get data from I2C slave 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 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, 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`.
|
||||
|
||||
.. list::
|
||||
|
||||
- :cpp:member:`i2c_slave_event_callbacks_t::on_recv_done` sets a callback function for "receive-done" event. The function prototype is declared in :cpp:type:`i2c_slave_received_callback_t`.
|
||||
:SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE: - :cpp:member:`i2c_slave_event_callbacks_t::on_stretch_occur` sets a callback function for "stretch" cause. The function prototype is declared in :cpp:type:`i2c_slave_stretch_callback_t`.
|
@ -36,6 +36,15 @@ I2C 是一种串行同步半双工通信协议,总线上可以同时挂载多
|
||||
|
||||
请注意,SCL 的频率越高,上拉电阻应该越小(但不能小于 1 kΩ)。较大的电阻会降低电流,增加时钟切换时间并降低频率。通常推荐 2 kΩ 到 5 kΩ 左右的电阻,也可根据电流需求进行一定调整。
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
i2c_slave_v1
|
||||
|
||||
.. note::
|
||||
|
||||
我们发现 :ref:`i2c-slave-v1` 存在一些问题,且使用体验不够友好。为此,我们推出了 I2C 从机驱动 v2.0,此版本不仅解决了现有问题,还将成为我们未来的主要维护版本。我们建议并鼓励你使用 I2C 从机驱动 v2.0,你可以通过配置选项 :ref:`CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2` 启用该功能。本文档主要介绍 I2C 从机驱动 v2.0 的功能。如果你想使用 I2C 从机驱动 v1.0,请参考 :ref:`i2c-slave-v1`。I2C 从机驱动 v1.0 将在 ESP-IDF v6.0 中移除。
|
||||
|
||||
|
||||
I2C 时钟配置
|
||||
------------
|
||||
@ -218,27 +227,26 @@ I2C 从机设备需要 :cpp:type:`i2c_slave_config_t` 指定的配置:
|
||||
- :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::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:member:`i2c_slave_config_t::intr_priority` 设置中断的优先级。如果设置为 ``0`` ,则驱动程序将使用低或中优先级的中断(优先级可设为 1、2 或 3 中的一个),若未设置,则将使用 :cpp:member:`i2c_slave_config_t::intr_priority` 指示的优先级。请使用数字形式(1、2、3),不要用位掩码形式((1<<1)、(1<<2)、(1<<3))。请注意,中断优先级一旦设置完成,在调用 :cpp:func:`i2c_del_slave_device` 之前都无法更改。
|
||||
- :cpp:member:`i2c_slave_config_t::addr_bit_len` 如果需要从机设备具有 10 位地址,则将该成员变量设为 ``I2C_ADDR_BIT_LEN_10``。
|
||||
- :cpp:member:`i2c_slave_config_t::allow_pd` 配置驱动程序是否允许系统在睡眠模式下关闭外设电源。在进入睡眠之前,系统将备份 I2C 寄存器上下文,当系统退出睡眠模式时,这些上下文将被恢复。关闭外设可以节省更多功耗,但代价是消耗更多内存来保存寄存器上下文。你需要在功耗和内存消耗之间做权衡。此配置选项依赖于特定的硬件功能,如果在不支持的芯片上启用它,你将看到类似 ``not able to power down in light sleep`` 的错误消息。
|
||||
:SOC_I2C_SLAVE_SUPPORT_BROADCAST: - :cpp:member:`i2c_slave_config_t::broadcast_en` 如果要启用从机广播,请将该成员变量设为 true。当从机设备接收到来自主机设备的通用调用地址 0x00,且后面的读写位为 0 时,无论从机设备自身地址如何,都会响应主机设备。
|
||||
- :cpp:member:`i2c_slave_config_t::enable_internal_pullup` 置 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,
|
||||
.i2c_port = I2C_SLAVE_NUM,
|
||||
.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,
|
||||
.slave_addr = ESP_SLAVE_ADDR,
|
||||
.send_buf_depth = 100,
|
||||
.receive_buf_depth = 100,
|
||||
};
|
||||
|
||||
i2c_slave_dev_handle_t slave_handle;
|
||||
@ -435,144 +443,73 @@ I2C 驱动程序可以使用 :cpp:func:`i2c_master_probe` 来检测设备是否
|
||||
I2C 从机控制器
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
通过调用 :cpp:func:`i2c_new_slave_device` 安装好 I2C 从机驱动程序后,{IDF_TARGET_NAME} 就可以作为从机与其他 I2C 主机进行通信了。
|
||||
I2C 从机不像 I2C 主机那样主观,主机知道自己何时应该发送数据,何时应该接收数据。在绝大多数情况下,I2C 从机是非常被动的,这意味着 I2C 从机发送和接收数据的能力在很大程度上取决于主机的操作。因此,我们在驱动程序中抛出了两个回调函数,分别代表 I2C 主机的读取请求和写入请求。
|
||||
|
||||
I2C 从机写入
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
I2C 从机的发送 buffer 可作为 FIFO 来存储要发送的数据。在主机请求这些数据前,它们会一直排队。可通过调用 :cpp:func:`i2c_slave_transmit` 来传输数据。
|
||||
你可以通过注册 :cpp:member:`i2c_slave_event_callbacks_t::on_request` 回调来获取 I2C 从机写事件,并在获取请求事件的任务中调用 `i2c_slave_write` 来发送数据。
|
||||
|
||||
将数据写入 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 = TEST_I2C_PORT, // 设置 I2C 端口编号
|
||||
.send_buf_depth = 256, // 设置 TX buffer 长度
|
||||
.scl_io_num = I2C_SLAVE_SCL_IO, // SCL 管脚编号
|
||||
.sda_io_num = I2C_SLAVE_SDA_IO, // SDA 管脚编号
|
||||
.slave_addr = 0x58, // 从机地址
|
||||
};
|
||||
|
||||
i2c_slave_dev_handle_t slave_handle;
|
||||
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
|
||||
for (int i = 0; i < DATA_LENGTH; i++) {
|
||||
data_wr[i] = i;
|
||||
// Prepare a callback function
|
||||
static bool i2c_slave_request_cb(i2c_slave_dev_handle_t i2c_slave, const i2c_slave_request_event_data_t *evt_data, void *arg)
|
||||
{
|
||||
i2c_slave_event_t evt = I2C_SLAVE_EVT_TX;
|
||||
BaseType_t xTaskWoken = 0;
|
||||
xQueueSendFromISR(context->event_queue, &evt, &xTaskWoken);
|
||||
return xTaskWoken;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(i2c_slave_transmit(slave_handle, data_wr, DATA_LENGTH, 10000));
|
||||
// Register callback in a task
|
||||
i2c_slave_event_callbacks_t cbs = {
|
||||
.on_request = i2c_slave_request_cb,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(context.handle, &cbs, &context));
|
||||
|
||||
// Waiting for request event and send data in a task
|
||||
static void i2c_slave_task(void *arg)
|
||||
{
|
||||
uint8_t buffer_size = 64;
|
||||
uint32_t write_len;
|
||||
uint8_t *data_buffer;
|
||||
|
||||
while (true) {
|
||||
i2c_slave_event_t evt;
|
||||
if (xQueueReceive(context->event_queue, &evt, 10) == pdTRUE) {
|
||||
ESP_ERROR_CHECK(i2c_slave_write(handle, data_buffer, buffer_size, &write_len, 1000));
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
I2C 从机读取
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
每当主机将数据写入从机,从机都会自动将数据存储在接收 buffer 中,从而使从机应用程序能自由调用 :cpp:func:`i2c_slave_receive`。:cpp:func:`i2c_slave_receive` 为非阻塞接口,因此要想知道接收是否完成,需注册回调函数 :cpp:func:`i2c_slave_register_event_callbacks`。
|
||||
与写入一样,您可以通过注册 :cpp:member:`i2c_slave_event_callbacks_t::on_receive` 回调来获取 I2C 从机读取事件,在任务中获取请求事件时,您可以保存数据并做您想做的事情。
|
||||
|
||||
接收数据的简单示例
|
||||
|
||||
.. 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)
|
||||
// Prepare a callback function
|
||||
static bool i2c_slave_receive_cb(i2c_slave_dev_handle_t i2c_slave, const i2c_slave_rx_done_event_data_t *evt_data, void *arg)
|
||||
{
|
||||
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;
|
||||
i2c_slave_event_t evt = I2C_SLAVE_EVT_RX;
|
||||
BaseType_t xTaskWoken = 0;
|
||||
// You can get data and length via i2c_slave_rx_done_event_data_t
|
||||
xQueueSendFromISR(context->event_queue, &evt, &xTaskWoken);
|
||||
return xTaskWoken;
|
||||
}
|
||||
|
||||
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));
|
||||
// Register callback in a task
|
||||
i2c_slave_event_callbacks_t cbs = {
|
||||
.on_recv_done = i2c_slave_rx_done_callback,
|
||||
.on_receive = i2c_slave_receive_cb,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(slave_handle, &cbs, s_receive_queue));
|
||||
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(context.handle, &cbs, &context));
|
||||
|
||||
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));
|
||||
|
||||
注册事件回调函数
|
||||
^^^^^^^^^^^^^^^^^
|
||||
@ -601,8 +538,8 @@ 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` 中声明。
|
||||
- :cpp:member:`i2c_slave_event_callbacks_t::on_request` 为请求事件设置回调函数。
|
||||
- :cpp:member:`i2c_slave_event_callbacks_t::on_receive` 为 receive 事件设置回调函数。函数原型在 :cpp:type:`i2c_slave_received_callback_t` 中声明。
|
||||
|
||||
电源管理
|
||||
^^^^^^^^^^
|
||||
@ -637,13 +574,28 @@ Kconfig 选项 :ref:`CONFIG_I2C_ISR_IRAM_SAFE` 能够做到以下几点:
|
||||
线程安全
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
工厂函数 :cpp:func:`i2c_new_master_bus` 和 :cpp:func:`i2c_new_slave_device` 由驱动程序保证其线程安全,不需要额外的锁保护,也可从不同的 RTOS 任务中调用这些函数。应避免从多个任务中调用其他非线程安全的公共 I2C API,若确实需要调用,请添加额外的锁。
|
||||
工厂函数 :cpp:func:`i2c_new_master_bus` 和 :cpp:func:`i2c_new_slave_device` 由驱动程序保证线程安全,这意味着可以从不同的 RTOS 任务调用这些函数,而无需额外的锁保护。
|
||||
|
||||
I2C 主机操作函数也通过总线操作信号保证线程安全。
|
||||
|
||||
- :cpp:func:`i2c_master_transmit`.
|
||||
- :cpp:func:`i2c_master_multi_buffer_transmit`.
|
||||
- :cpp:func:`i2c_master_transmit_receive`.
|
||||
- :cpp:func:`i2c_master_receive`.
|
||||
- :cpp:func:`i2c_master_probe`.
|
||||
|
||||
I2C 从机操作函数也通过总线操作信号保证线程安全。
|
||||
|
||||
- :cpp:func:`i2c_slave_write`.
|
||||
|
||||
其他函数不保证线程安全。因此,应避免在没有互斥保护的不同任务中调用这些函数。
|
||||
|
||||
Kconfig 选项
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_I2C_ISR_IRAM_SAFE` 将在 cache 被禁用时控制默认的 ISR 处理程序正常工作,详情请参阅 `IRAM 安全 <#iram-safe>`__。
|
||||
- :ref:`CONFIG_I2C_ENABLE_DEBUG_LOG` 可启用调试日志,但会增加固件二进制文件大小。
|
||||
- :ref:`CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2` 用于启用 I2C 从机驱动 v2.0 程序。
|
||||
|
||||
应用示例
|
||||
--------
|
||||
@ -652,6 +604,7 @@ Kconfig 选项
|
||||
|
||||
- :example:`peripherals/i2c/i2c_tools` 演示了如何使用 I2C 工具开发 I2C 相关的应用程序,提供了用于配置 I2C 总线、扫描设备、读取、设置和检查寄存器的命令行工具。
|
||||
|
||||
- :example:`peripherals/i2c/i2c_slave_network_sensor` 演示如何使用 I2C 从机开发 I2C 相关应用程序,提供 I2C 从机如何充当网络传感器,以及如何使用事件回调接收和发送数据。
|
||||
|
||||
API 参考
|
||||
--------
|
||||
|
206
docs/zh_CN/api-reference/peripherals/i2c_slave_v1.rst
Normal file
206
docs/zh_CN/api-reference/peripherals/i2c_slave_v1.rst
Normal file
@ -0,0 +1,206 @@
|
||||
.. _i2c-slave-v1:
|
||||
|
||||
I2C Slave v1.0
|
||||
=================
|
||||
|
||||
.. warning::
|
||||
|
||||
该 I2C 从机驱动程序 v1.0 将在 ESP-IDF v6.0 中删除。我们建议你通过 :ref:`CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2` 使用 I2C 驱动程序 v2.0。
|
||||
|
||||
通过 :cpp:func:`i2c_new_slave_device` 安装 I2C 从机驱动程序后,{IDF_TARGET_NAME} 就可以作为从站与其他 I2C Master 通信了。
|
||||
|
||||
安装 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_slave_config_t::intr_priority` 设置中断的优先级。如果设置为 ``0`` ,则驱动程序将使用低或中优先级的中断(优先级可设为 1、2 或 3 中的一个),若未设置,则将使用 :cpp:member:`i2c_slave_config_t::intr_priority` 指示的优先级。请使用数字形式(1、2、3),不要用位掩码形式((1<<1)、(1<<2)、(1<<3))。请注意,中断优先级一旦设置完成,在调用 :cpp:func:`i2c_del_slave_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_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 = TEST_I2C_PORT, // 设置 I2C 端口编号
|
||||
.send_buf_depth = 256, // 设置 TX buffer 长度
|
||||
.scl_io_num = I2C_SLAVE_SCL_IO, // SCL 管脚编号
|
||||
.sda_io_num = I2C_SLAVE_SDA_IO, // SDA 管脚编号
|
||||
.slave_addr = 0x58, // 从机地址
|
||||
};
|
||||
|
||||
i2c_slave_dev_handle_t slave_handle;
|
||||
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
|
||||
for (int i = 0; i < DATA_LENGTH; i++) {
|
||||
data_wr[i] = i;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(i2c_slave_transmit(slave_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_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` 中声明。
|
Loading…
x
Reference in New Issue
Block a user