Skip to content

Commit 32adbe7

Browse files
committed
docs(i2c_slave): Update i2c slave programming guide for new changes
1 parent 170d44b commit 32adbe7

File tree

4 files changed

+569
-255
lines changed

4 files changed

+569
-255
lines changed

docs/en/api-reference/peripherals/i2c.rst

Lines changed: 81 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ Typically, an I2C slave device has a 7-bit address or 10-bit address. {IDF_TARGE
3636

3737
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.
3838

39+
.. toctree::
40+
:hidden:
41+
42+
i2c_slave_v1
43+
44+
.. note::
45+
46+
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.
3947

4048
I2C Clock Configuration
4149
-----------------------
@@ -218,27 +226,26 @@ I2C slave requires the configuration specified by :cpp:type:`i2c_slave_config_t`
218226
- :cpp:member:`i2c_slave_config_t::sda_io_num` sets the GPIO number for serial data bus (SDA).
219227
- :cpp:member:`i2c_slave_config_t::scl_io_num` sets the GPIO number for serial clock bus (SCL).
220228
- :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.
221-
- :cpp:member:`i2c_slave_config_t::send_buf_depth` sets the sending buffer length.
222-
- :cpp:member:`i2c_slave_config_t::slave_addr` sets the slave address.
223-
- :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.
224-
- :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.
225-
: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.
226-
: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.
227-
: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.
228-
: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.
229+
- :cpp:member:`i2c_slave_config_t::send_buf_depth` sets the sending software buffer length.
230+
- :cpp:member:`i2c_slave_config_t::receive_buf_depth` sets the receiving software buffer length.
231+
- :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.
232+
- :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.
233+
- :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.
234+
: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.
235+
- :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.
229236

230237
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.
231238

232239
.. code:: c
233240
234241
i2c_slave_config_t i2c_slv_config = {
235-
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
242+
.i2c_port = I2C_SLAVE_NUM,
236243
.clk_source = I2C_CLK_SRC_DEFAULT,
237-
.i2c_port = TEST_I2C_PORT,
238-
.send_buf_depth = 256,
239244
.scl_io_num = I2C_SLAVE_SCL_IO,
240245
.sda_io_num = I2C_SLAVE_SDA_IO,
241-
.slave_addr = 0x58,
246+
.slave_addr = ESP_SLAVE_ADDR,
247+
.send_buf_depth = 100,
248+
.receive_buf_depth = 100,
242249
};
243250
244251
i2c_slave_dev_handle_t slave_handle;
@@ -437,142 +444,72 @@ I2C Slave Controller
437444

438445
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.
439446

447+
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.
448+
440449
I2C Slave Write
441450
~~~~~~~~~~~~~~~
442451

443-
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.
452+
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.
444453

445-
Simple example for writing data to FIFO:
454+
Simple example for transmitting data:
446455

447456
.. code:: c
448457
449-
uint8_t *data_wr = (uint8_t *) malloc(DATA_LENGTH);
458+
// Prepare a callback function
459+
static bool i2c_slave_request_cb(i2c_slave_dev_handle_t i2c_slave, const i2c_slave_request_event_data_t *evt_data, void *arg)
460+
{
461+
i2c_slave_event_t evt = I2C_SLAVE_EVT_TX;
462+
BaseType_t xTaskWoken = 0;
463+
xQueueSendFromISR(context->event_queue, &evt, &xTaskWoken);
464+
return xTaskWoken;
465+
}
450466
451-
i2c_slave_config_t i2c_slv_config = {
452-
.addr_bit_len = I2C_ADDR_BIT_LEN_7, // 7-bit address
453-
.clk_source = I2C_CLK_SRC_DEFAULT, // set the clock source
454-
.i2c_port = TEST_I2C_PORT, // set I2C port number
455-
.send_buf_depth = 256, // set TX buffer length
456-
.scl_io_num = I2C_SLAVE_SCL_IO, // SCL GPIO number
457-
.sda_io_num = I2C_SLAVE_SDA_IO, // SDA GPIO number
458-
.slave_addr = 0x58, // slave address
467+
// Register callback in a task
468+
i2c_slave_event_callbacks_t cbs = {
469+
.on_request = i2c_slave_request_cb,
459470
};
471+
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(context.handle, &cbs, &context));
460472
461-
i2c_slave_dev_handle_t slave_handle;
462-
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
463-
for (int i = 0; i < DATA_LENGTH; i++) {
464-
data_wr[i] = i;
473+
// Waiting for request event and send data in a task
474+
static void i2c_slave_task(void *arg)
475+
{
476+
uint8_t buffer_size = 64;
477+
uint32_t write_len;
478+
uint8_t *data_buffer;
479+
480+
while (true) {
481+
i2c_slave_event_t evt;
482+
if (xQueueReceive(context->event_queue, &evt, 10) == pdTRUE) {
483+
ESP_ERROR_CHECK(i2c_slave_write(handle, data_buffer, buffer_size, &write_len, 1000));
484+
}
485+
}
486+
vTaskDelete(NULL);
465487
}
466488
467-
ESP_ERROR_CHECK(i2c_slave_transmit(slave_handle, data_wr, DATA_LENGTH, 10000));
468-
469489
I2C Slave Read
470490
~~~~~~~~~~~~~~
471491

472-
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.
492+
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.
493+
494+
Simple example for receiving data:
473495

474496
.. code:: c
475497
476-
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)
498+
// Prepare a callback function
499+
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)
477500
{
478-
BaseType_t high_task_wakeup = pdFALSE;
479-
QueueHandle_t receive_queue = (QueueHandle_t)user_data;
480-
xQueueSendFromISR(receive_queue, edata, &high_task_wakeup);
481-
return high_task_wakeup == pdTRUE;
501+
i2c_slave_event_t evt = I2C_SLAVE_EVT_RX;
502+
BaseType_t xTaskWoken = 0;
503+
// You can get data and length via i2c_slave_rx_done_event_data_t
504+
xQueueSendFromISR(context->event_queue, &evt, &xTaskWoken);
505+
return xTaskWoken;
482506
}
483507
484-
uint8_t *data_rd = (uint8_t *) malloc(DATA_LENGTH);
485-
uint32_t size_rd = 0;
486-
487-
i2c_slave_config_t i2c_slv_config = {
488-
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
489-
.clk_source = I2C_CLK_SRC_DEFAULT,
490-
.i2c_port = TEST_I2C_PORT,
491-
.send_buf_depth = 256,
492-
.scl_io_num = I2C_SLAVE_SCL_IO,
493-
.sda_io_num = I2C_SLAVE_SDA_IO,
494-
.slave_addr = 0x58,
495-
};
496-
497-
i2c_slave_dev_handle_t slave_handle;
498-
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
499-
500-
s_receive_queue = xQueueCreate(1, sizeof(i2c_slave_rx_done_event_data_t));
508+
// Register callback in a task
501509
i2c_slave_event_callbacks_t cbs = {
502-
.on_recv_done = i2c_slave_rx_done_callback,
510+
.on_receive = i2c_slave_receive_cb,
503511
};
504-
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(slave_handle, &cbs, s_receive_queue));
505-
506-
i2c_slave_rx_done_event_data_t rx_data;
507-
ESP_ERROR_CHECK(i2c_slave_receive(slave_handle, data_rd, DATA_LENGTH));
508-
xQueueReceive(s_receive_queue, &rx_data, pdMS_TO_TICKS(10000));
509-
// Receive done.
510-
511-
.. only:: SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS
512-
513-
Put Data In I2C Slave RAM
514-
~~~~~~~~~~~~~~~~~~~~~~~~~
515-
516-
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.
517-
518-
.. figure:: ../../../_static/diagrams/i2c/i2c_slave_write_slave_ram.png
519-
:align: center
520-
:alt: Put data in I2C slave RAM
521-
522-
Put data in I2C slave RAM
523-
524-
.. code:: c
525-
526-
uint8_t data_rd[DATA_LENGTH_RAM] = {0};
527-
528-
i2c_slave_config_t i2c_slv_config = {
529-
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
530-
.clk_source = I2C_CLK_SRC_DEFAULT,
531-
.i2c_port = TEST_I2C_PORT,
532-
.send_buf_depth = 256,
533-
.scl_io_num = I2C_SLAVE_SCL_IO,
534-
.sda_io_num = I2C_SLAVE_SDA_IO,
535-
.slave_addr = 0x58,
536-
.flags.access_ram_en = true,
537-
};
538-
539-
// Master writes to slave.
540-
541-
i2c_slave_dev_handle_t slave_handle;
542-
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
543-
ESP_ERROR_CHECK(i2c_slave_read_ram(slave_handle, 0x5, data_rd, DATA_LENGTH_RAM));
544-
ESP_ERROR_CHECK(i2c_del_slave_device(slave_handle));
545-
546-
Get Data From I2C Slave RAM
547-
~~~~~~~~~~~~~~~~~~~~~~~~~~~
548-
549-
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.
550-
551-
.. figure:: ../../../_static/diagrams/i2c/i2c_slave_read_slave_ram.png
552-
:align: center
553-
:alt: Get data from I2C slave RAM
554-
555-
Get data from I2C slave RAM
556-
557-
.. code:: c
558-
559-
uint8_t data_wr[DATA_LENGTH_RAM] = {0};
560-
561-
i2c_slave_config_t i2c_slv_config = {
562-
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
563-
.clk_source = I2C_CLK_SRC_DEFAULT,
564-
.i2c_port = TEST_I2C_PORT,
565-
.send_buf_depth = 256,
566-
.scl_io_num = I2C_SLAVE_SCL_IO,
567-
.sda_io_num = I2C_SLAVE_SDA_IO,
568-
.slave_addr = 0x58,
569-
.flags.access_ram_en = true,
570-
};
571-
572-
i2c_slave_dev_handle_t slave_handle;
573-
ESP_ERROR_CHECK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
574-
ESP_ERROR_CHECK(i2c_slave_write_ram(slave_handle, 0x2, data_wr, DATA_LENGTH_RAM));
575-
ESP_ERROR_CHECK(i2c_del_slave_device(slave_handle));
512+
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(context.handle, &cbs, &context));
576513
577514
Register Event Callbacks
578515
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -601,8 +538,8 @@ I2C slave event callbacks are listed in the :cpp:type:`i2c_slave_event_callbacks
601538

602539
.. list::
603540

604-
- :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`.
605-
: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`.
541+
- :cpp:member:`i2c_slave_event_callbacks_t::on_request` sets a callback function for request event.
542+
- :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`.
606543

607544
Power Management
608545
^^^^^^^^^^^^^^^^
@@ -637,13 +574,28 @@ This will allow the interrupt to run while the cache is disabled but will come a
637574
Thread Safety
638575
^^^^^^^^^^^^^
639576

640-
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.
577+
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.
578+
579+
I2C master operation functions are also guaranteed to be thread safe by bus operation semaphore.
580+
581+
- :cpp:func:`i2c_master_transmit`
582+
- :cpp:func:`i2c_master_multi_buffer_transmit`
583+
- :cpp:func:`i2c_master_transmit_receive`
584+
- :cpp:func:`i2c_master_receive`
585+
- :cpp:func:`i2c_master_probe`
586+
587+
I2C slave operation functions are also guaranteed to be thread safe by bus operation semaphore.
588+
589+
- :cpp:func:`i2c_slave_write`
590+
591+
Other functions are not guaranteed to be thread-safe. Thus, you should avoid calling them in different tasks without mutex protection.
641592

642593
Kconfig Options
643594
^^^^^^^^^^^^^^^
644595

645596
- :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.
646597
- :ref:`CONFIG_I2C_ENABLE_DEBUG_LOG` is used to enable the debug log at the cost of increased firmware binary size.
598+
- :ref:`CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2` is used to enable the I2C slave driver v2.0.
647599

648600
Application Examples
649601
--------------------
@@ -652,6 +604,7 @@ Application Examples
652604

653605
- :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.
654606

607+
- :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.
655608

656609
API Reference
657610
-------------

0 commit comments

Comments
 (0)