Merge branch 'docs/i2c_ng_docs_v5.2' into 'release/v5.2'
docs(I2C): Add new programming guide for new I2C driver (backport v5.2) See merge request espressif/esp-idf!26994
@ -930,13 +930,13 @@ esp_err_t i2c_del_master_bus(i2c_master_bus_handle_t bus_handle)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t i2c_master_bus_reset(i2c_master_bus_handle_t handle)
|
esp_err_t i2c_master_bus_reset(i2c_master_bus_handle_t bus_handle)
|
||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE((handle != NULL), ESP_ERR_INVALID_ARG, TAG, "This bus is not initialized");
|
ESP_RETURN_ON_FALSE((bus_handle != NULL), ESP_ERR_INVALID_ARG, TAG, "This bus is not initialized");
|
||||||
// Reset I2C master bus
|
// Reset I2C master bus
|
||||||
ESP_RETURN_ON_ERROR(s_i2c_hw_fsm_reset(handle), TAG, "I2C master bus reset failed");
|
ESP_RETURN_ON_ERROR(s_i2c_hw_fsm_reset(bus_handle), TAG, "I2C master bus reset failed");
|
||||||
// Reset I2C status state
|
// Reset I2C status state
|
||||||
atomic_store(&handle->status, I2C_STATUS_IDLE);
|
atomic_store(&bus_handle->status, I2C_STATUS_IDLE);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1019,24 +1019,24 @@ esp_err_t i2c_master_receive(i2c_master_dev_handle_t i2c_dev, uint8_t *read_buff
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t i2c_master_probe(i2c_master_bus_handle_t i2c_master, uint16_t address, int xfer_timeout_ms)
|
esp_err_t i2c_master_probe(i2c_master_bus_handle_t bus_handle, uint16_t address, int xfer_timeout_ms)
|
||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE(i2c_master != NULL, ESP_ERR_INVALID_ARG, TAG, "i2c handle not initialized");
|
ESP_RETURN_ON_FALSE(bus_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "i2c handle not initialized");
|
||||||
TickType_t ticks_to_wait = (xfer_timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(xfer_timeout_ms);
|
TickType_t ticks_to_wait = (xfer_timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(xfer_timeout_ms);
|
||||||
if (xSemaphoreTake(i2c_master->bus_lock_mux, ticks_to_wait) != pdTRUE) {
|
if (xSemaphoreTake(bus_handle->bus_lock_mux, ticks_to_wait) != pdTRUE) {
|
||||||
return ESP_ERR_TIMEOUT;
|
return ESP_ERR_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_master->cmd_idx = 0;
|
bus_handle->cmd_idx = 0;
|
||||||
i2c_master->trans_idx = 0;
|
bus_handle->trans_idx = 0;
|
||||||
i2c_master->trans_done = false;
|
bus_handle->trans_done = false;
|
||||||
i2c_hal_context_t *hal = &i2c_master->base->hal;
|
i2c_hal_context_t *hal = &bus_handle->base->hal;
|
||||||
i2c_operation_t i2c_ops[] = {
|
i2c_operation_t i2c_ops[] = {
|
||||||
{.hw_cmd = I2C_TRANS_START_COMMAND},
|
{.hw_cmd = I2C_TRANS_START_COMMAND},
|
||||||
{.hw_cmd = I2C_TRANS_STOP_COMMAND},
|
{.hw_cmd = I2C_TRANS_STOP_COMMAND},
|
||||||
};
|
};
|
||||||
|
|
||||||
i2c_master->i2c_trans = (i2c_transaction_t) {
|
bus_handle->i2c_trans = (i2c_transaction_t) {
|
||||||
.device_address = address,
|
.device_address = address,
|
||||||
.ops = i2c_ops,
|
.ops = i2c_ops,
|
||||||
.cmd_count = DIM(i2c_ops),
|
.cmd_count = DIM(i2c_ops),
|
||||||
@ -1044,18 +1044,18 @@ esp_err_t i2c_master_probe(i2c_master_bus_handle_t i2c_master, uint16_t address,
|
|||||||
|
|
||||||
// I2C probe does not have i2c device module. So set the clock parameter independently
|
// I2C probe does not have i2c device module. So set the clock parameter independently
|
||||||
// This will not influence device transaction.
|
// This will not influence device transaction.
|
||||||
i2c_hal_set_bus_timing(hal, 100000, i2c_master->base->clk_src, i2c_master->base->clk_src_freq_hz);
|
i2c_hal_set_bus_timing(hal, 100000, bus_handle->base->clk_src, bus_handle->base->clk_src_freq_hz);
|
||||||
i2c_ll_master_set_fractional_divider(hal->dev, 0, 0);
|
i2c_ll_master_set_fractional_divider(hal->dev, 0, 0);
|
||||||
i2c_ll_update(hal->dev);
|
i2c_ll_update(hal->dev);
|
||||||
|
|
||||||
s_i2c_send_commands(i2c_master, ticks_to_wait);
|
s_i2c_send_commands(bus_handle, ticks_to_wait);
|
||||||
if (i2c_master->status == I2C_STATUS_ACK_ERROR) {
|
if (bus_handle->status == I2C_STATUS_ACK_ERROR) {
|
||||||
// Reset the status to done, in order not influence next time transaction.
|
// Reset the status to done, in order not influence next time transaction.
|
||||||
i2c_master->status = I2C_STATUS_DONE;
|
bus_handle->status = I2C_STATUS_DONE;
|
||||||
xSemaphoreGive(i2c_master->bus_lock_mux);
|
xSemaphoreGive(bus_handle->bus_lock_mux);
|
||||||
return ESP_ERR_NOT_FOUND;
|
return ESP_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
xSemaphoreGive(i2c_master->bus_lock_mux);
|
xSemaphoreGive(bus_handle->bus_lock_mux);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1082,19 +1082,19 @@ esp_err_t i2c_master_register_event_callbacks(i2c_master_dev_handle_t i2c_dev, c
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t i2c_master_wait_all_done(i2c_master_bus_handle_t i2c_master, int timeout_ms)
|
esp_err_t i2c_master_bus_wait_all_done(i2c_master_bus_handle_t bus_handle, int timeout_ms)
|
||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE(i2c_master, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
TickType_t wait_ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
TickType_t wait_ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||||
i2c_transaction_t t;
|
i2c_transaction_t t;
|
||||||
|
|
||||||
size_t cnt = i2c_master->num_trans_inflight;
|
size_t cnt = bus_handle->num_trans_inflight;
|
||||||
for (size_t i = 0; i < cnt; i++) {
|
for (size_t i = 0; i < cnt; i++) {
|
||||||
ESP_RETURN_ON_FALSE(xQueueReceive(i2c_master->trans_queues[I2C_TRANS_QUEUE_COMPLETE], &t, wait_ticks) == pdTRUE,
|
ESP_RETURN_ON_FALSE(xQueueReceive(bus_handle->trans_queues[I2C_TRANS_QUEUE_COMPLETE], &t, wait_ticks) == pdTRUE,
|
||||||
ESP_ERR_TIMEOUT, TAG, "flush timeout");
|
ESP_ERR_TIMEOUT, TAG, "flush timeout");
|
||||||
ESP_RETURN_ON_FALSE(xQueueSend(i2c_master->trans_queues[I2C_TRANS_QUEUE_READY], &t, 0) == pdTRUE,
|
ESP_RETURN_ON_FALSE(xQueueSend(bus_handle->trans_queues[I2C_TRANS_QUEUE_READY], &t, 0) == pdTRUE,
|
||||||
ESP_ERR_INVALID_STATE, TAG, "ready queue full");
|
ESP_ERR_INVALID_STATE, TAG, "ready queue full");
|
||||||
i2c_master->num_trans_inflight--;
|
bus_handle->num_trans_inflight--;
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ typedef struct {
|
|||||||
size_t trans_queue_depth; /*!< Depth of internal transfer queue, increase this value can support more transfers pending in the background, only valid in asynchronous transaction. (Typically max_device_num * per_transaction)*/
|
size_t trans_queue_depth; /*!< Depth of internal transfer queue, increase this value can support more transfers pending in the background, only valid in asynchronous transaction. (Typically max_device_num * per_transaction)*/
|
||||||
struct {
|
struct {
|
||||||
uint32_t enable_internal_pullup:1; /*!< Enable internal pullups. Note: This is not strong enough to pullup buses under high-speed frequency. Recommend proper external pull-up if possible */
|
uint32_t enable_internal_pullup:1; /*!< Enable internal pullups. Note: This is not strong enough to pullup buses under high-speed frequency. Recommend proper external pull-up if possible */
|
||||||
} flags;
|
} flags; /*!< I2C master config flags */
|
||||||
} i2c_master_bus_config_t;
|
} i2c_master_bus_config_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,13 +157,15 @@ esp_err_t i2c_master_receive(i2c_master_dev_handle_t i2c_dev, uint8_t *read_buff
|
|||||||
/**
|
/**
|
||||||
* @brief Probe I2C address, if address is correct and ACK is received, this function will return ESP_OK.
|
* @brief Probe I2C address, if address is correct and ACK is received, this function will return ESP_OK.
|
||||||
*
|
*
|
||||||
* @param[in] i2c_dev I2C master device handle that created by `i2c_master_bus_add_device`.
|
* @param[in] bus_handle I2C master device handle that created by `i2c_master_bus_add_device`.
|
||||||
|
* @param[in] address I2C device address that you want to probe.
|
||||||
* @param[in] xfer_timeout_ms Wait timeout, in ms. Note: -1 means wait forever (Not recommended in this function).
|
* @param[in] xfer_timeout_ms Wait timeout, in ms. Note: -1 means wait forever (Not recommended in this function).
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: I2C device probe successfully
|
* - ESP_OK: I2C device probe successfully
|
||||||
|
* - ESP_ERR_NOT_FOUND: I2C probe failed, doesn't find the device with specific address you gave.
|
||||||
* - ESP_ERR_TIMEOUT: Operation timeout(larger than xfer_timeout_ms) because the bus is busy or hardware crash.
|
* - ESP_ERR_TIMEOUT: Operation timeout(larger than xfer_timeout_ms) because the bus is busy or hardware crash.
|
||||||
*/
|
*/
|
||||||
esp_err_t i2c_master_probe(i2c_master_bus_handle_t i2c_master, uint16_t address, int xfer_timeout_ms);
|
esp_err_t i2c_master_probe(i2c_master_bus_handle_t bus_handle, uint16_t address, int xfer_timeout_ms);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Register I2C transaction callbacks for a master device
|
* @brief Register I2C transaction callbacks for a master device
|
||||||
@ -186,18 +188,18 @@ esp_err_t i2c_master_register_event_callbacks(i2c_master_dev_handle_t i2c_dev, c
|
|||||||
/**
|
/**
|
||||||
* @brief Reset the I2C master bus.
|
* @brief Reset the I2C master bus.
|
||||||
*
|
*
|
||||||
* @param handle I2C bus handle.
|
* @param bus_handle I2C bus handle.
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: Reset succeed.
|
* - ESP_OK: Reset succeed.
|
||||||
* - ESP_ERR_INVALID_ARG: I2C master bus handle is not initialized.
|
* - ESP_ERR_INVALID_ARG: I2C master bus handle is not initialized.
|
||||||
* - Otherwise: Reset failed.
|
* - Otherwise: Reset failed.
|
||||||
*/
|
*/
|
||||||
esp_err_t i2c_master_bus_reset(i2c_master_bus_handle_t handle);
|
esp_err_t i2c_master_bus_reset(i2c_master_bus_handle_t bus_handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Wait for all pending I2C transactions done
|
* @brief Wait for all pending I2C transactions done
|
||||||
*
|
*
|
||||||
* @param[in] i2c_master I2C bus handle
|
* @param[in] bus_handle I2C bus handle
|
||||||
* @param[in] timeout_ms Wait timeout, in ms. Specially, -1 means to wait forever.
|
* @param[in] timeout_ms Wait timeout, in ms. Specially, -1 means to wait forever.
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: Flush transactions successfully
|
* - ESP_OK: Flush transactions successfully
|
||||||
@ -205,7 +207,7 @@ esp_err_t i2c_master_bus_reset(i2c_master_bus_handle_t handle);
|
|||||||
* - ESP_ERR_TIMEOUT: Flush transactions failed because of timeout
|
* - ESP_ERR_TIMEOUT: Flush transactions failed because of timeout
|
||||||
* - ESP_FAIL: Flush transactions failed because of other error
|
* - ESP_FAIL: Flush transactions failed because of other error
|
||||||
*/
|
*/
|
||||||
esp_err_t i2c_master_wait_all_done(i2c_master_bus_handle_t i2c_master, int timeout_ms);
|
esp_err_t i2c_master_bus_wait_all_done(i2c_master_bus_handle_t bus_handle, int timeout_ms);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ typedef struct {
|
|||||||
#if SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH
|
#if SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH
|
||||||
uint32_t slave_unmatch_en:1; /*!< Can trigger unmatch interrupt when slave address does not match what master sends*/
|
uint32_t slave_unmatch_en:1; /*!< Can trigger unmatch interrupt when slave address does not match what master sends*/
|
||||||
#endif
|
#endif
|
||||||
} flags;
|
} flags; /*!< I2C slave config flags */
|
||||||
} i2c_slave_config_t;
|
} i2c_slave_config_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,7 +78,7 @@ typedef bool (*i2c_master_callback_t)(i2c_master_dev_handle_t i2c_dev, const i2c
|
|||||||
* @brief Event structure used in I2C slave
|
* @brief Event structure used in I2C slave
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t *buffer;
|
uint8_t *buffer; /**< Pointer for buffer received in callback. */
|
||||||
} i2c_slave_rx_done_event_data_t;
|
} i2c_slave_rx_done_event_data_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,7 +98,7 @@ typedef bool (*i2c_slave_received_callback_t)(i2c_slave_dev_handle_t i2c_slave,
|
|||||||
* @brief Stretch cause event structure used in I2C slave
|
* @brief Stretch cause event structure used in I2C slave
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
i2c_slave_stretch_cause_t stretch_cause;
|
i2c_slave_stretch_cause_t stretch_cause; /*!< Stretch cause can be got in callback */
|
||||||
} i2c_slave_stretch_event_data_t;
|
} i2c_slave_stretch_event_data_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
BIN
docs/_static/diagrams/i2c/i2c_code_structure.png
vendored
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
docs/_static/diagrams/i2c/i2c_master_module.png
vendored
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
docs/_static/diagrams/i2c/i2c_master_probe.png
vendored
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
docs/_static/diagrams/i2c/i2c_master_read_slave.png
vendored
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docs/_static/diagrams/i2c/i2c_master_write_read_slave.png
vendored
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
docs/_static/diagrams/i2c/i2c_master_write_slave.png
vendored
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
docs/_static/diagrams/i2c/i2c_slave_read_slave_ram.png
vendored
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
docs/_static/diagrams/i2c/i2c_slave_write_slave_ram.png
vendored
Normal file
After Width: | Height: | Size: 17 KiB |
26
docs/_static/diagrams/i2c/i2c_trans_wave.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"head": {
|
||||||
|
"text": "Standard I2C Transaction Timing Diagram"
|
||||||
|
},
|
||||||
|
"signal": [
|
||||||
|
{
|
||||||
|
"node":"A.....B...................C..D.E...............F...G",
|
||||||
|
"period": 0.5},
|
||||||
|
{
|
||||||
|
"name": "SDA",
|
||||||
|
"wave": "1.0...3...3.|.3...4...5...1..0.6...6.|.6...7...10..1", "data": "A6 . A0 R/W ACK D7 . D0 ACK",
|
||||||
|
"period": 0.5},
|
||||||
|
{
|
||||||
|
"name": "SCL",
|
||||||
|
"wave": "1...0..1.0.1|0.1.0.1.0.1.0......1.0.1|0.1.0.1.0..1..",
|
||||||
|
"period": 0.5}
|
||||||
|
],
|
||||||
|
"config":
|
||||||
|
{
|
||||||
|
"skin": "narrow"
|
||||||
|
},
|
||||||
|
"edge": [
|
||||||
|
"B<->C Write address",
|
||||||
|
"E<->F Write data"
|
||||||
|
]
|
||||||
|
}
|
@ -79,6 +79,9 @@ INPUT = \
|
|||||||
$(PROJECT_PATH)/components/driver/dac/include/driver/dac_cosine.h \
|
$(PROJECT_PATH)/components/driver/dac/include/driver/dac_cosine.h \
|
||||||
$(PROJECT_PATH)/components/driver/dac/include/driver/dac_oneshot.h \
|
$(PROJECT_PATH)/components/driver/dac/include/driver/dac_oneshot.h \
|
||||||
$(PROJECT_PATH)/components/driver/dac/include/driver/dac_types.h \
|
$(PROJECT_PATH)/components/driver/dac/include/driver/dac_types.h \
|
||||||
|
$(PROJECT_PATH)/components/driver/i2c/include/driver/i2c_master.h \
|
||||||
|
$(PROJECT_PATH)/components/driver/i2c/include/driver/i2c_slave.h \
|
||||||
|
$(PROJECT_PATH)/components/driver/i2c/include/driver/i2c_types.h \
|
||||||
$(PROJECT_PATH)/components/driver/gpio/include/driver/dedic_gpio.h \
|
$(PROJECT_PATH)/components/driver/gpio/include/driver/dedic_gpio.h \
|
||||||
$(PROJECT_PATH)/components/driver/gpio/include/driver/gpio.h \
|
$(PROJECT_PATH)/components/driver/gpio/include/driver/gpio.h \
|
||||||
$(PROJECT_PATH)/components/driver/gpio/include/driver/gpio_etm.h \
|
$(PROJECT_PATH)/components/driver/gpio/include/driver/gpio_etm.h \
|
||||||
@ -88,7 +91,6 @@ INPUT = \
|
|||||||
$(PROJECT_PATH)/components/driver/gptimer/include/driver/gptimer.h \
|
$(PROJECT_PATH)/components/driver/gptimer/include/driver/gptimer.h \
|
||||||
$(PROJECT_PATH)/components/driver/gptimer/include/driver/gptimer_etm.h \
|
$(PROJECT_PATH)/components/driver/gptimer/include/driver/gptimer_etm.h \
|
||||||
$(PROJECT_PATH)/components/driver/gptimer/include/driver/gptimer_types.h \
|
$(PROJECT_PATH)/components/driver/gptimer/include/driver/gptimer_types.h \
|
||||||
$(PROJECT_PATH)/components/driver/i2c/include/driver/i2c.h \
|
|
||||||
$(PROJECT_PATH)/components/driver/i2s/include/driver/i2s_common.h \
|
$(PROJECT_PATH)/components/driver/i2s/include/driver/i2s_common.h \
|
||||||
$(PROJECT_PATH)/components/driver/i2s/include/driver/i2s_pdm.h \
|
$(PROJECT_PATH)/components/driver/i2s/include/driver/i2s_pdm.h \
|
||||||
$(PROJECT_PATH)/components/driver/i2s/include/driver/i2s_std.h \
|
$(PROJECT_PATH)/components/driver/i2s/include/driver/i2s_std.h \
|
||||||
|
@ -1,408 +1,548 @@
|
|||||||
Inter-Integrated Circuit (I2C)
|
Inter-Integrated Circuit (I2C)
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
:link_to_translation:`zh_CN:[中文]`
|
Introduction
|
||||||
|
|
||||||
Overview
|
|
||||||
--------
|
|
||||||
|
|
||||||
I2C is a serial, synchronous, half-duplex communication protocol that allows co-existence of multiple masters and slaves on the same bus. The I2C bus consists of two lines: serial data line (SDA) and serial clock (SCL). Both lines require pull-up resistors.
|
|
||||||
|
|
||||||
With such advantages as simplicity and low manufacturing cost, I2C is mostly used for communication of low-speed peripheral devices over short distances (within one foot).
|
|
||||||
|
|
||||||
{IDF_TARGET_NAME} has {IDF_TARGET_SOC_I2C_NUM} I2C controller (also referred to as port), responsible for handling communications on the I2C bus. A single I2C controller can operate as master or slave.
|
|
||||||
|
|
||||||
Driver Features
|
|
||||||
---------------
|
|
||||||
|
|
||||||
I2C driver governs communications of devices over the I2C bus. The driver supports the following features:
|
|
||||||
|
|
||||||
- Reading and writing bytes in Master mode
|
|
||||||
|
|
||||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
- Slave mode
|
|
||||||
|
|
||||||
- Reading and writing to registers which are in turn read/written by the master
|
|
||||||
|
|
||||||
|
|
||||||
Driver Usage
|
|
||||||
------------
|
------------
|
||||||
|
|
||||||
{IDF_TARGET_I2C_ROLE:default="master or slave", esp32c2="master"}
|
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.
|
||||||
|
|
||||||
The following sections describe typical steps of configuring and operating the I2C driver:
|
{IDF_TARGET_NAME} has {IDF_TARGET_SOC_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.
|
||||||
|
|
||||||
1. :ref:`i2c-api-configure-driver` - set the initialization parameters ({IDF_TARGET_I2C_ROLE} mode, GPIO pins for SDA and SCL, clock speed, etc.)
|
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.
|
||||||
2. :ref:`i2c-api-install-driver`- activate the driver on one of the two I2C controllers as a {IDF_TARGET_I2C_ROLE}
|
|
||||||
3. Depending on whether you configure the driver for a {IDF_TARGET_I2C_ROLE}, choose the appropriate item
|
|
||||||
|
|
||||||
a) :ref:`i2c-api-master-mode` - handle communications (master)
|
.. warning::
|
||||||
|
|
||||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
The clock frequency of SCL in master mode should not be larger than 400 KHz
|
||||||
|
|
||||||
b) :ref:`i2c-api-slave-mode` - respond to messages from the master (slave)
|
|
||||||
|
|
||||||
4. :ref:`i2c-api-interrupt-handling` - configure and service I2C interrupts
|
|
||||||
5. :ref:`i2c-api-customized-configuration` - adjust default I2C communication parameters (timings, bit order, etc.)
|
|
||||||
6. :ref:`i2c-api-error-handling` - how to recognize and handle driver configuration and communication errors
|
|
||||||
7. :ref:`i2c-api-delete-driver`- release resources used by the I2C driver when communication ends
|
|
||||||
|
|
||||||
|
|
||||||
.. _i2c-api-configure-driver:
|
|
||||||
|
|
||||||
Configuration
|
|
||||||
^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
To establish I2C communication, start by configuring the driver. This is done by setting the parameters of the structure :cpp:type:`i2c_config_t`:
|
|
||||||
|
|
||||||
- Set I2C **mode of operation** - {IDF_TARGET_I2C_ROLE} from :cpp:type:`i2c_mode_t`
|
|
||||||
- Configure **communication pins**
|
|
||||||
|
|
||||||
- Assign GPIO pins for SDA and SCL signals
|
|
||||||
- Set whether to enable {IDF_TARGET_NAME}'s internal pull-ups
|
|
||||||
|
|
||||||
- (Master only) Set I2C **clock speed**
|
|
||||||
|
|
||||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
- (Slave only) Configure the following
|
|
||||||
|
|
||||||
* Whether to enable **10 bit address mode**
|
|
||||||
* Define **slave address**
|
|
||||||
|
|
||||||
After that, initialize the configuration for a given I2C port. For this, call the function :cpp:func:`i2c_param_config` and pass to it the port number and the structure :cpp:type:`i2c_config_t`.
|
|
||||||
|
|
||||||
Configuration example (master):
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
int i2c_master_port = 0;
|
|
||||||
i2c_config_t conf = {
|
|
||||||
.mode = I2C_MODE_MASTER,
|
|
||||||
.sda_io_num = I2C_MASTER_SDA_IO, // select SDA GPIO specific to your project
|
|
||||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
|
||||||
.scl_io_num = I2C_MASTER_SCL_IO, // select SCL GPIO specific to your project
|
|
||||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
|
||||||
.master.clk_speed = I2C_MASTER_FREQ_HZ, // select frequency specific to your project
|
|
||||||
.clk_flags = 0, // optional; you can use I2C_SCLK_SRC_FLAG_* flags to choose i2c source clock here
|
|
||||||
};
|
|
||||||
|
|
||||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
Configuration example (slave):
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
int i2c_slave_port = I2C_SLAVE_NUM;
|
|
||||||
i2c_config_t conf_slave = {
|
|
||||||
.sda_io_num = I2C_SLAVE_SDA_IO, // select SDA GPIO specific to your project
|
|
||||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
|
||||||
.scl_io_num = I2C_SLAVE_SCL_IO, // select SCL GPIO specific to your project
|
|
||||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
|
||||||
.mode = I2C_MODE_SLAVE,
|
|
||||||
.slave.addr_10bit_en = 0,
|
|
||||||
.slave.slave_addr = ESP_SLAVE_ADDR, // slave address of your project
|
|
||||||
.slave.maximum_speed = I2C_SLAVE_MAX_SPEED // expected maximum clock speed
|
|
||||||
.clk_flags = 0, // optional; you can use I2C_SCLK_SRC_FLAG_* flags to choose I2C source clock here
|
|
||||||
};
|
|
||||||
|
|
||||||
At this stage, :cpp:func:`i2c_param_config` also sets a few other I2C configuration parameters to default values that are defined by the I2C specification. For more details on the values and how to modify them, see :ref:`i2c-api-customized-configuration`.
|
|
||||||
|
|
||||||
Source Clock Configuration
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
**Clock sources allocator** is added for supporting different clock sources. The clock allocator will choose one clock source that meets all the requirements of frequency and capability (as requested in :cpp:member:`i2c_config_t::clk_flags`).
|
|
||||||
|
|
||||||
When :cpp:member:`i2c_config_t::clk_flags` is 0, the clock allocator will select only according to the desired frequency. If no special capabilities are needed, such as APB, you can configure the clock allocator to select the source clock only according to the desired frequency. For this, set :cpp:member:`i2c_config_t::clk_flags` to 0. For clock characteristics, see the table below.
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
A clock is not a valid option, if it does not meet the requested capabilities, i.e., any bit of requested capabilities (clk_flags) is 0 in the clock's capabilities.
|
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.
|
||||||
|
|
||||||
.. only:: esp32
|
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.
|
||||||
|
|
||||||
.. list-table:: Characteristics of {IDF_TARGET_NAME} clock sources
|
|
||||||
:widths: 5 5 50 20
|
|
||||||
:header-rows: 1
|
|
||||||
|
|
||||||
* - Clock name
|
|
||||||
- Clock frequency
|
|
||||||
- MAX freq for SCL
|
|
||||||
- Clock capabilities
|
|
||||||
* - APB clock
|
|
||||||
- 80 MHz
|
|
||||||
- 4 MHz
|
|
||||||
- /
|
|
||||||
|
|
||||||
.. only:: esp32s2
|
|
||||||
|
|
||||||
.. list-table:: Characteristics of {IDF_TARGET_NAME} clock sources
|
|
||||||
:widths: 5 5 50 100
|
|
||||||
:header-rows: 1
|
|
||||||
|
|
||||||
* - Clock name
|
|
||||||
- Clock frequency
|
|
||||||
- MAX freq for SCL
|
|
||||||
- Clock capabilities
|
|
||||||
* - APB clock
|
|
||||||
- 80 MHz
|
|
||||||
- 4 MHz
|
|
||||||
- /
|
|
||||||
* - REF_TICK
|
|
||||||
- 1 MHz
|
|
||||||
- 50 KHz
|
|
||||||
- :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`, :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP`
|
|
||||||
|
|
||||||
Explanations for :cpp:member:`i2c_config_t::clk_flags` are as follows:
|
|
||||||
1. :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`: Clock's baud rate will not change while APB clock is changing.
|
|
||||||
2. :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP`: It supports Light-sleep mode, which APB clock cannot do.
|
|
||||||
|
|
||||||
.. only:: esp32s3
|
|
||||||
|
|
||||||
.. list-table:: Characteristics of {IDF_TARGET_NAME} clock sources
|
|
||||||
:widths: 5 5 50 20
|
|
||||||
:header-rows: 1
|
|
||||||
|
|
||||||
* - Clock name
|
|
||||||
- Clock frequency
|
|
||||||
- MAX freq for SCL
|
|
||||||
- Clock capabilities
|
|
||||||
* - XTAL clock
|
|
||||||
- 40 MHz
|
|
||||||
- 2 MHz
|
|
||||||
- /
|
|
||||||
* - RTC clock
|
|
||||||
- 20 MHz
|
|
||||||
- 1 MHz
|
|
||||||
- :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`, :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP`
|
|
||||||
|
|
||||||
.. only:: esp32c3
|
|
||||||
|
|
||||||
.. list-table:: Characteristics of {IDF_TARGET_NAME} clock sources
|
|
||||||
:widths: 5 5 50 100
|
|
||||||
:header-rows: 1
|
|
||||||
|
|
||||||
* - Clock name
|
|
||||||
- Clock frequency
|
|
||||||
- MAX freq for SCL
|
|
||||||
- Clock capabilities
|
|
||||||
* - XTAL clock
|
|
||||||
- 40 MHz
|
|
||||||
- 2 MHz
|
|
||||||
- /
|
|
||||||
* - RTC clock
|
|
||||||
- 20 MHz
|
|
||||||
- 1 MHz
|
|
||||||
- :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`, :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP`
|
|
||||||
|
|
||||||
Explanations for :cpp:member:`i2c_config_t::clk_flags` are as follows:
|
|
||||||
|
|
||||||
1. :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`: Clock's baud rate will not change while APB clock is changing.
|
|
||||||
2. :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP`: It supports Light-sleep mode, which APB clock cannot do.
|
|
||||||
3. Some flags may not be supported on {IDF_TARGET_NAME}, reading technical reference manual before using it.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The clock frequency of SCL in master mode should not be lager than max frequency for SCL mentioned in the table above.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The clock frequency of SCL is influenced by the pull-up resistors and wire capacitance (or might slave capacitance) together. Therefore, users need to choose correct pull-up resistors by themselves to make the frequency accurate. It is recommended by I2C protocol that the pull-up resistors commonly range from 1KOhms to 10KOhms, but different frequencies need different resistors.
|
|
||||||
|
|
||||||
Generally speaking, the higher frequency is selected, the smaller resistor should be used (but not less than 1KOhms). This is because high resistor declines the current, which will lengthen the rising time and reduce the frequency. Usually, range 2KOhms to 5KOhms is what we recommend, but users also might need to make some adjustment depends on their reality.
|
|
||||||
|
|
||||||
.. _i2c-api-install-driver:
|
|
||||||
|
|
||||||
Install Driver
|
|
||||||
^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
After the I2C driver is configured, install it by calling the function :cpp:func:`i2c_driver_install` with the following parameters:
|
|
||||||
|
|
||||||
- Port number, one of the two port numbers from :cpp:type:`i2c_port_t`
|
|
||||||
- {IDF_TARGET_I2C_ROLE}, selected from :cpp:type:`i2c_mode_t`
|
|
||||||
|
|
||||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
- (Slave only) Size of buffers to allocate for sending and receiving data. As I2C is a master-centric bus, data can only go from the slave to the master at the master's request. Therefore, the slave usually has a send buffer where the slave application writes data. The data remains in the send buffer to be read by the master at the master's own discretion.
|
|
||||||
|
|
||||||
- Flags for allocating the interrupt (see ESP_INTR_FLAG_* values in :component_file:`esp_hw_support/include/esp_intr_alloc.h`)
|
|
||||||
|
|
||||||
.. _i2c-api-master-mode:
|
|
||||||
|
|
||||||
Communication as Master
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
After installing the I2C driver, {IDF_TARGET_NAME} is ready to communicate with other I2C devices.
|
|
||||||
|
|
||||||
{IDF_TARGET_NAME}'s I2C controller operating as master is responsible for establishing communication with I2C slave devices and sending commands to trigger a slave to action, for example, to take a measurement and send the readings back to the master.
|
|
||||||
|
|
||||||
For better process organization, the driver provides a container, called a "command link", that should be populated with a sequence of commands and then passed to the I2C controller for execution.
|
|
||||||
|
|
||||||
|
|
||||||
Master Write
|
I2C Clock Configuration
|
||||||
""""""""""""
|
-----------------------
|
||||||
|
|
||||||
The example below shows how to build a command link for an I2C master to send *n* bytes to a slave.
|
.. list::
|
||||||
|
|
||||||
|
- :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_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.
|
||||||
|
|
||||||
.. blockdiag:: ../../../_static/diagrams/i2c-command-link-master-write-blockdiag.diag
|
I2C File Structure
|
||||||
:scale: 100
|
------------------
|
||||||
:caption: I2C command link - master write example
|
|
||||||
|
.. figure:: ../../../_static/diagrams/i2c/i2c_code_structure.png
|
||||||
:align: center
|
:align: center
|
||||||
|
:alt: I2C file structure
|
||||||
|
|
||||||
|
I2C file structure
|
||||||
|
|
||||||
The following describes how a command link for a "master write" is set up and what comes inside:
|
**Public headers that need to be included in the I2C application**
|
||||||
|
|
||||||
1. Create a command link with :cpp:func:`i2c_cmd_link_create`.
|
- ``i2c.h``: The header file of legacy I2C APIs (for apps using legacy driver).
|
||||||
|
- ``i2c_master.h``: The header file that provides standard communication mode specific APIs (for apps using new driver with master mode).
|
||||||
Then, populate it with the series of data to be sent to the slave:
|
- ``i2c_slave.h``: The header file that provides standard communication mode specific APIs (for apps using new driver with slave mode).
|
||||||
|
|
||||||
a) **Start bit** - :cpp:func:`i2c_master_start`
|
|
||||||
b) **Slave address** - :cpp:func:`i2c_master_write_byte`. The single byte address is provided as an argument of this function call.
|
|
||||||
c) **Data** - One or more bytes as an argument of :cpp:func:`i2c_master_write`
|
|
||||||
d) **Stop bit** - :cpp:func:`i2c_master_stop`
|
|
||||||
|
|
||||||
Both functions :cpp:func:`i2c_master_write_byte` and :cpp:func:`i2c_master_write` have an additional argument specifying whether the master should ensure that it has received the ACK bit.
|
|
||||||
|
|
||||||
2. Trigger the execution of the command link by I2C controller by calling :cpp:func:`i2c_master_cmd_begin`. Once the execution is triggered, the command link cannot be modified.
|
|
||||||
3. After the commands are transmitted, release the resources used by the command link by calling :cpp:func:`i2c_cmd_link_delete`.
|
|
||||||
|
|
||||||
|
|
||||||
Master Read
|
|
||||||
"""""""""""
|
|
||||||
|
|
||||||
The example below shows how to build a command link for an I2C master to read *n* bytes from a slave.
|
|
||||||
|
|
||||||
.. blockdiag:: ../../../_static/diagrams/i2c-command-link-master-read-blockdiag.diag
|
|
||||||
:scale: 100
|
|
||||||
:caption: I2C command link - master read example
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
|
|
||||||
Compared to writing data, the command link is populated in Step 4 not with ``i2c_master_write...`` functions but with :cpp:func:`i2c_master_read_byte` and/or :cpp:func:`i2c_master_read`. Also, the last read in Step 5 is configured so that the master does not provide the ACK bit.
|
|
||||||
|
|
||||||
|
|
||||||
Indicating Write or Read
|
|
||||||
""""""""""""""""""""""""
|
|
||||||
|
|
||||||
After sending a slave address (see Step 3 on both diagrams above), the master either writes or reads from the slave.
|
|
||||||
|
|
||||||
The information on what the master actually does is hidden in the least significant bit of the slave's address.
|
|
||||||
|
|
||||||
For this reason, the command link sent by the master to write data to the slave contains the address ``(ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE`` and looks as follows:
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE, ACK_EN);
|
|
||||||
|
|
||||||
Likewise, the command link to read from the slave looks as follows:
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | I2C_MASTER_READ, ACK_EN);
|
|
||||||
|
|
||||||
|
|
||||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
.. _i2c-api-slave-mode:
|
|
||||||
|
|
||||||
Communication as Slave
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
After installing the I2C driver, {IDF_TARGET_NAME} is ready to communicate with other I2C devices.
|
|
||||||
|
|
||||||
The API provides the following functions for slaves
|
|
||||||
|
|
||||||
- :cpp:func:`i2c_slave_read_buffer`
|
|
||||||
|
|
||||||
Whenever the master writes data to the slave, the slave automatically stores it in the receive buffer. This allows the slave application to call the function :cpp:func:`i2c_slave_read_buffer` at its own discretion. This function also has a parameter to specify block time if no data is in the receive buffer. This allows the slave application to wait with a specified timeout for data to arrive to the buffer.
|
|
||||||
|
|
||||||
- :cpp:func:`i2c_slave_write_buffer`
|
|
||||||
|
|
||||||
The send buffer is used to store all the data that the slave wants to send to the master in FIFO order. The data stays there until the master requests for it. The function :cpp:func:`i2c_slave_write_buffer` has a parameter to specify block time if the send buffer is full. This allows the slave application to wait with a specified timeout for the adequate amount of space to become available in the send buffer.
|
|
||||||
|
|
||||||
A code example showing how to use these functions can be found in :example:`peripherals/i2c`.
|
|
||||||
|
|
||||||
.. _i2c-api-interrupt-handling:
|
|
||||||
|
|
||||||
.. only:: not SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
.. _i2c-api-interrupt-handling:
|
|
||||||
|
|
||||||
Interrupt Handling
|
|
||||||
^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
During driver installation, an interrupt handler is installed by default.
|
|
||||||
|
|
||||||
.. _i2c-api-customized-configuration:
|
|
||||||
|
|
||||||
Customized Configuration
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
As mentioned at the end of Section :ref:`i2c-api-configure-driver`, when the function :cpp:func:`i2c_param_config` initializes the driver configuration for an I2C port, it also sets several I2C communication parameters to default values defined in the I2C specification. Some other related parameters are pre-configured in registers of the I2C controller.
|
|
||||||
|
|
||||||
All these parameters can be changed to user-defined values by calling dedicated functions given in the table below. Please note that the timing values are defined in APB clock cycles.
|
|
||||||
|
|
||||||
.. list-table:: Other Configurable I2C Communication Parameters
|
|
||||||
:widths: 65 35
|
|
||||||
:header-rows: 1
|
|
||||||
|
|
||||||
* - Parameters to Change
|
|
||||||
- Function
|
|
||||||
* - High time and low time for SCL pulses
|
|
||||||
- :cpp:func:`i2c_set_period`
|
|
||||||
* - SCL and SDA signal timing used during generation of **start** signals
|
|
||||||
- :cpp:func:`i2c_set_start_timing`
|
|
||||||
* - SCL and SDA signal timing used during generation of **stop** signals
|
|
||||||
- :cpp:func:`i2c_set_stop_timing`
|
|
||||||
* - Timing relationship between SCL and SDA signals when slave samples, as well as when master toggles
|
|
||||||
- :cpp:func:`i2c_set_data_timing`
|
|
||||||
* - I2C timeout
|
|
||||||
- :cpp:func:`i2c_set_timeout`
|
|
||||||
* - Choice between transmitting / receiving the LSB or MSB first, choose one of the modes defined in :cpp:type:`i2c_trans_mode_t`
|
|
||||||
- :cpp:func:`i2c_set_data_mode`
|
|
||||||
|
|
||||||
|
|
||||||
Each of the above functions has a *_get_* counterpart to check the currently set value. For example, to check the I2C timeout value, call :cpp:func:`i2c_get_timeout`.
|
|
||||||
|
|
||||||
To check the default parameter values which are set during the driver configuration process, please refer to the file :component_file:`driver/i2c/i2c.c` and look for defines with the suffix ``_DEFAULT``.
|
|
||||||
|
|
||||||
You can also select different pins for SDA and SCL signals and alter the configuration of pull-ups with the function :cpp:func:`i2c_set_pin`. If you want to modify already entered values, use the function :cpp:func:`i2c_param_config`.
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
{IDF_TARGET_NAME}'s internal pull-ups are in the range of tens of kOhm, which is, in most cases, insufficient for use as I2C pull-ups. Users are advised to use external pull-ups with values described in the I2C specification. For help with calculating the resistor values see `TI Application Note <https://www.ti.com/lit/an/slva689/slva689.pdf>`_
|
The legacy driver can't coexist with the new driver. Include ``i2c.h`` to use the legacy driver or the other two headers to use the new driver. Please keep in mind that the legacy driver is now deprecated and will be removed in future.
|
||||||
|
|
||||||
|
**Public headers that have been included in the headers above**
|
||||||
|
|
||||||
.. _i2c-api-error-handling:
|
- ``i2c_types_legacy.h``: The legacy public types that only used in the legacy driver.
|
||||||
|
- ``i2c_types.h``: The header file that provides public types.
|
||||||
|
|
||||||
Error Handling
|
Functional Overview
|
||||||
^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
The majority of I2C driver functions either return ``ESP_OK`` on successful completion or a specific error code on failure. It is a good practice to always check the returned values and implement error handling. The driver also prints out log messages that contain error details, e.g., when checking the validity of entered configuration. For details please refer to the file :component_file:`driver/i2c/i2c.c` and look for defines with the suffix ``_ERR_STR``.
|
|
||||||
|
|
||||||
Use dedicated interrupts to capture communication failures. For instance, if a slave stretches the clock for too long while preparing the data to send back to master, the interrupt ``I2C_TIME_OUT_INT`` will be triggered. For detailed information, see :ref:`i2c-api-interrupt-handling`.
|
|
||||||
|
|
||||||
In case of a communication failure, you can reset the internal hardware buffers by calling the functions :cpp:func:`i2c_reset_tx_fifo` and :cpp:func:`i2c_reset_rx_fifo` for the send and receive buffers respectively.
|
|
||||||
|
|
||||||
|
|
||||||
.. _i2c-api-delete-driver:
|
|
||||||
|
|
||||||
Delete Driver
|
|
||||||
^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
When the I2C communication is established with the function :cpp:func:`i2c_driver_install` and is not required for some substantial amount of time, the driver may be deinitialized to release allocated resources by calling :cpp:func:`i2c_driver_delete`.
|
|
||||||
|
|
||||||
Before calling :cpp:func:`i2c_driver_delete` to remove i2c driver, please make sure that all threads have stopped using the driver in any way, because this function does not guarantee thread safety.
|
|
||||||
|
|
||||||
Application Example
|
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
I2C examples: :example:`peripherals/i2c`.
|
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
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Both I2C master bus and I2C slave bus, when supported, are represented by :cpp:type:`i2c_bus_handle_t` in the driver. The available ports are managed in a resource pool that allocates a free port on request.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.. figure:: ../../../_static/diagrams/i2c/i2c_master_module.png
|
||||||
|
:align: center
|
||||||
|
:alt: I2C master bus-device module
|
||||||
|
|
||||||
|
I2C master bus-device module
|
||||||
|
|
||||||
|
I2C master bus requires the configuration that specified by :cpp:type:`i2c_master_bus_config_t`:
|
||||||
|
|
||||||
|
- :cpp:member:`i2c_master_bus_config_t::i2c_port` sets the I2C port used by the controller.
|
||||||
|
- :cpp:member:`i2c_master_bus_config_t::sda_io_num` sets the GPIO number for the serial data bus (SDA).
|
||||||
|
- :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.
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.. 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));
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Install I2C slave device
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
I2C slave requires the configuration that 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_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 broadcase. 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.. 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 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:
|
||||||
|
|
||||||
|
.. wavedrom:: /../_static/diagrams/i2c/i2c_trans_wave.json
|
||||||
|
|
||||||
|
I2C Master Write
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
After installing I2C master bus successfully, you can simply call :cpp:func:`i2c_master_transmit` to write data to the slave device. The principle of this function can be explained by following chart.
|
||||||
|
|
||||||
|
In order to organize the process, the driver uses a command link, that should be populated with a sequence of commands and then passed to I2C controller for execution.
|
||||||
|
|
||||||
|
.. figure:: ../../../_static/diagrams/i2c/i2c_master_write_slave.png
|
||||||
|
:align: center
|
||||||
|
:alt: I2C master write to slave
|
||||||
|
|
||||||
|
I2C master write to slave
|
||||||
|
|
||||||
|
Simple example for writing data to slave:
|
||||||
|
|
||||||
|
.. 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 Master Read
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
After installing I2C master bus successfully, you can simply call :cpp:func:`i2c_master_receive` to read data from the slave device. The principle of this function can be explained by following chart.
|
||||||
|
|
||||||
|
.. figure:: ../../../_static/diagrams/i2c/i2c_master_read_slave.png
|
||||||
|
:align: center
|
||||||
|
:alt: I2C master read from slave
|
||||||
|
|
||||||
|
I2C master read from slave
|
||||||
|
|
||||||
|
Simple example for reading data from slave:
|
||||||
|
|
||||||
|
.. 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 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.
|
||||||
|
|
||||||
|
.. figure:: ../../../_static/diagrams/i2c/i2c_master_write_read_slave.png
|
||||||
|
:align: center
|
||||||
|
:alt: I2C master write to slave and read from slave
|
||||||
|
|
||||||
|
I2C master write to slave and read from slave
|
||||||
|
|
||||||
|
Simple example for writing and reading from slave:
|
||||||
|
|
||||||
|
.. 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(i2c_bus_handle, buf, sizeof(buf), buffer, 2, -1));
|
||||||
|
|
||||||
|
I2C Master Probe
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
I2C driver can use :cpp:func:`i2c_master_probe` to detect whether the specific device has been connected on I2C bus. If this function return ``ESP_OK``, that means the device has been detected.
|
||||||
|
|
||||||
|
.. figure:: ../../../_static/diagrams/i2c/i2c_master_probe.png
|
||||||
|
:align: center
|
||||||
|
:alt: I2C master probe
|
||||||
|
|
||||||
|
I2C master probe
|
||||||
|
|
||||||
|
Simple example for probing an I2C device:
|
||||||
|
|
||||||
|
.. 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 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.
|
||||||
|
|
||||||
|
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 = 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
|
||||||
|
.slave_addr = 0x58, // slave address
|
||||||
|
};
|
||||||
|
|
||||||
|
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 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.
|
||||||
|
|
||||||
|
.. 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, 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.
|
||||||
|
|
||||||
|
.. 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 write 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 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.
|
||||||
|
|
||||||
|
.. 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));
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.. important::
|
||||||
|
|
||||||
|
I2C master asynchronous transaction is still an experimental feature. (The issue is 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.
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.. only:: SOC_I2C_SUPPORT_REF_TICK
|
||||||
|
|
||||||
|
If the controller clock source is selected to :cpp:enumerator:`I2C_CLK_SRC_REF_TICK`, then the driver won't install power management lock for it, which is more suitable for a low power application as long as the source clock can still provide sufficient resolution.
|
||||||
|
|
||||||
|
.. only:: SOC_I2C_SUPPORT_XTAL
|
||||||
|
|
||||||
|
If the controller clock source is selected to :cpp:enumerator:`I2C_CLK_SRC_XTAL`, then the driver won't install power management lock for it, which is more suitable for a low power application as long as the source clock can still provide sufficient resolution.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
API Reference
|
API Reference
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
.. include-build-file:: inc/i2c.inc
|
.. include-build-file:: inc/i2c_master.inc
|
||||||
.. include-build-file:: inc/i2c_types.inc
|
|
||||||
|
.. only:: SOC_I2C_SUPPORT_SLAVE
|
||||||
|
|
||||||
|
.. include-build-file:: inc/i2c_slave.inc
|
||||||
|
|
||||||
|
.. include-build-file:: inc/components/driver/i2c/include/driver/i2c_types.inc
|
||||||
|
.. include-build-file:: inc/components/hal/include/hal/i2c_types.inc
|
@ -9,3 +9,34 @@ Peripherals
|
|||||||
----
|
----
|
||||||
|
|
||||||
- :c:macro:`UART_FIFO_LEN` is deprecated. Please use :c:macro:`UART_HW_FIFO_LEN` instead.
|
- :c:macro:`UART_FIFO_LEN` is deprecated. Please use :c:macro:`UART_HW_FIFO_LEN` instead.
|
||||||
|
|
||||||
|
.. only:: SOC_I2C_SUPPORTED
|
||||||
|
|
||||||
|
I2C
|
||||||
|
---
|
||||||
|
|
||||||
|
I2C driver has been redesigned (see :doc:`I2C API Reference <../../../api-reference/peripherals/i2c>`), which aims to unify the interface and extend the usage of I2C peripheral. Although it is recommended to use the new driver APIs, the legacy driver is still available in the previous include path ``driver/i2c.h``.
|
||||||
|
|
||||||
|
The major breaking changes in concept and usage are listed as follows:
|
||||||
|
|
||||||
|
Major Changes in Concepts
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- ``i2c_config_t`` which was used to configure the I2C bus, but it doesn't really tell whether to configure master or slave. So in the new design, master and slave initialization are separate, user can call ``i2c_master_bus_config_t`` or ``i2c_slave_config_t``.
|
||||||
|
- ``i2c_mode_t`` which was used to tell whether I2C controller works in slave mode or master mode. This enumerator has been deprecated. In the new driver, users don'tneed to manually set the mode anymore since master and slave APIs are different.
|
||||||
|
- ``i2c_rw_t`` which was used to tell whether I2C master controller is performing a `write` or a `read` operation. This is now deprecated.
|
||||||
|
- ``i2c_addr_mode_t`` was renamed to ``i2c_addr_bit_len_t``.
|
||||||
|
- In the legacy driver, operations needed to be chained with a command list (dynamically or statically created). The new driver now handles this internally, making the operations more size and space efficient.
|
||||||
|
- Capability flags like ``I2C_SCLK_SRC_FLAG_FOR_NOMAL`` are used to select clock source in the legacy driver. In the new driver, users can select clock source directly.
|
||||||
|
|
||||||
|
Major Changes in Usage
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- I2C bus initialization is done in two parts: first, initialization of the bus with :cpp:func:`i2c_new_master_bus`, then, initialization of the I2C device with :cpp:func:`i2c_master_bus_add_device`.
|
||||||
|
- ``i2c_reset_tx_fifo`` and ``i2c_reset_rx_fifo`` have been removed, since it is never required to reset the fifo by users. Whole bus can still be reset by calling ``i2c_master_bus_reset``.
|
||||||
|
- ``i2c_cmd_link_xxx`` functions have been removed, user doesn't need to use link to link commands on its own.
|
||||||
|
- ``i2c_master_write_to_device`` has been renamed to ``i2c_master_transmit``.
|
||||||
|
- ``i2c_master_read_from_device`` has been renamed to ``i2c_master_receive``.
|
||||||
|
- ``i2c_master_write_read_device`` has been renamed to ``i2c_master_transmit_receive``
|
||||||
|
- ``i2c_slave_write_buffer`` has been renamed to ``i2c_slave_transmit``
|
||||||
|
- ``i2c_slave_read_buffer`` has been renamed to ``i2c_slave_receive``
|
||||||
|
@ -1,408 +1 @@
|
|||||||
I2C 驱动程序
|
.. include:: ../../../en/api-reference/peripherals/i2c.rst
|
||||||
===============
|
|
||||||
|
|
||||||
:link_to_translation:`en:[English]`
|
|
||||||
|
|
||||||
概述
|
|
||||||
---------
|
|
||||||
|
|
||||||
I2C 是一种串行同步半双工通信协议,总线上可以同时挂载多个主机和从机。I2C 总线由串行数据线 (SDA) 和串行时钟线 (SCL) 线构成。这些线都需要上拉电阻。
|
|
||||||
|
|
||||||
I2C 具有简单且制造成本低廉等优点,主要用于低速外围设备的短距离通信(一英尺以内)。
|
|
||||||
|
|
||||||
{IDF_TARGET_NAME} 有 {IDF_TARGET_SOC_I2C_NUM} 个 I2C 控制器(也称为端口),负责处理在 I2C 总线上的通信。每个控制器都可以设置为主机或从机。
|
|
||||||
|
|
||||||
驱动程序的功能
|
|
||||||
---------------
|
|
||||||
|
|
||||||
I2C 驱动程序管理在 I2C 总线上设备的通信,该驱动程序具备以下功能:
|
|
||||||
|
|
||||||
- 在主机模式下读写字节
|
|
||||||
|
|
||||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
- 支持从机模式
|
|
||||||
|
|
||||||
- 读取并写入寄存器,然后由主机读取/写入
|
|
||||||
|
|
||||||
|
|
||||||
使用驱动程序
|
|
||||||
---------------
|
|
||||||
|
|
||||||
{IDF_TARGET_I2C_ROLE:default="主机或从机", esp32c2="主机"}
|
|
||||||
|
|
||||||
I2C 驱动程序配置和工作的基本步骤如下:
|
|
||||||
|
|
||||||
1. :ref:`i2c-api-configure-driver` - 设置初始化参数(如主机模式或从机模式,SDA 和 SCL 使用的 GPIO 管脚,时钟速度等)
|
|
||||||
2. :ref:`i2c-api-install-driver`- 激活一个 I2C 控制器的驱动,该控制器可为主机也可为从机
|
|
||||||
3. 根据是为主机还是从机配置驱动程序,选择合适的项目
|
|
||||||
|
|
||||||
a) :ref:`i2c-api-master-mode` - 发起通信(主机模式)
|
|
||||||
|
|
||||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
b) :ref:`i2c-api-slave-mode` - 响应主机消息(从机模式)
|
|
||||||
|
|
||||||
4. :ref:`i2c-api-interrupt-handling` - 配置 I2C 中断服务
|
|
||||||
5. :ref:`i2c-api-customized-configuration` - 调整默认的 I2C 通信参数(如时序、位序等)
|
|
||||||
6. :ref:`i2c-api-error-handling` - 如何识别和处理驱动程序配置和通信错误
|
|
||||||
7. :ref:`i2c-api-delete-driver`- 在通信结束时释放 I2C 驱动程序所使用的资源
|
|
||||||
|
|
||||||
|
|
||||||
.. _i2c-api-configure-driver:
|
|
||||||
|
|
||||||
配置驱动程序
|
|
||||||
^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
建立 I2C 通信第一步是配置驱动程序,这需要设置 :cpp:type:`i2c_config_t` 结构中的几个参数:
|
|
||||||
|
|
||||||
- 设置 I2C **工作模式** - 从 :cpp:type:`i2c_mode_t` 中选择主机模式或从机模式
|
|
||||||
- 设置 **通信管脚**
|
|
||||||
|
|
||||||
- 指定 SDA 和 SCL 信号使用的 GPIO 管脚
|
|
||||||
- 是否启用 {IDF_TARGET_NAME} 的内部上拉电阻
|
|
||||||
|
|
||||||
- (仅限主机模式)设置 I2C **时钟速度**
|
|
||||||
|
|
||||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
- (仅限从机模式)设置以下内容:
|
|
||||||
|
|
||||||
* 是否应启用 **10 位寻址模式**
|
|
||||||
* 定义 **从机地址**
|
|
||||||
|
|
||||||
然后,初始化给定 I2C 端口的配置,请使用端口号和 :cpp:type:`i2c_config_t` 作为函数调用参数来调用 :cpp:func:`i2c_param_config` 函数。
|
|
||||||
|
|
||||||
配置示例(主机):
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
int i2c_master_port = 0;
|
|
||||||
i2c_config_t conf = {
|
|
||||||
.mode = I2C_MODE_MASTER,
|
|
||||||
.sda_io_num = I2C_MASTER_SDA_IO, // 配置 SDA 的 GPIO
|
|
||||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
|
||||||
.scl_io_num = I2C_MASTER_SCL_IO, // 配置 SCL 的 GPIO
|
|
||||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
|
||||||
.master.clk_speed = I2C_MASTER_FREQ_HZ, // 为项目选择频率
|
|
||||||
.clk_flags = 0, // 可选项,可以使用 I2C_SCLK_SRC_FLAG_* 标志来选择 I2C 源时钟
|
|
||||||
};
|
|
||||||
|
|
||||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
配置示例(从机):
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
int i2c_slave_port = I2C_SLAVE_NUM;
|
|
||||||
i2c_config_t conf_slave = {
|
|
||||||
.sda_io_num = I2C_SLAVE_SDA_IO, // 配置 SDA 的 GPIO
|
|
||||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
|
||||||
.scl_io_num = I2C_SLAVE_SCL_IO, // 配置 SCL 的 GPIO
|
|
||||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
|
||||||
.mode = I2C_MODE_SLAVE,
|
|
||||||
.slave.addr_10bit_en = 0,
|
|
||||||
.slave.slave_addr = ESP_SLAVE_ADDR, // 项目从机地址
|
|
||||||
.slave.maximum_speed = I2C_SLAVE_MAX_SPEED // 预期的最大时钟速度
|
|
||||||
.clk_flags = 0, // 可选项,可以使用 I2C_SCLK_SRC_FLAG_* 标志来选择 I2C 源时钟
|
|
||||||
};
|
|
||||||
|
|
||||||
在此阶段,:cpp:func:`i2c_param_config` 还将其他 I2C 配置参数设置为 I2C 总线协议规范中定义的默认值。有关默认值及修改默认值的详细信息,请参考 :ref:`i2c-api-customized-configuration`。
|
|
||||||
|
|
||||||
源时钟配置
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
增加了 **时钟源分配器**,用于支持不同的时钟源。时钟分配器将选择一个满足所有频率和能力要求的时钟源(如 :cpp:member:`i2c_config_t::clk_flags` 中的要求)。
|
|
||||||
|
|
||||||
当 :cpp:member:`i2c_config_t::clk_flags` 为 0 时,时钟分配器将仅根据所需频率进行选择。如果不需要诸如 APB 之类的特殊功能,则可以将时钟分配器配置为仅根据所需频率选择源时钟。为此,请将 :cpp:member:`i2c_config_t::clk_flags` 设置为 0。有关时钟特性,请参见下表。
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
如果时钟不满足请求的功能,则该时钟不是有效的选项,即,请求的功能中的任何位(clk_flags)在时钟的功能中均为 0。
|
|
||||||
|
|
||||||
.. only:: esp32
|
|
||||||
|
|
||||||
.. list-table:: {IDF_TARGET_NAME} 时钟源特性
|
|
||||||
:widths: 5 5 50 20
|
|
||||||
:header-rows: 1
|
|
||||||
|
|
||||||
* - 时钟名称
|
|
||||||
- 时钟频率
|
|
||||||
- SCL 的最大频率
|
|
||||||
- 时钟功能
|
|
||||||
* - APB 时钟
|
|
||||||
- 80 MHz
|
|
||||||
- 4 MHz
|
|
||||||
- /
|
|
||||||
|
|
||||||
.. only:: esp32s2
|
|
||||||
|
|
||||||
.. list-table:: {IDF_TARGET_NAME} 时钟源特性
|
|
||||||
:widths: 5 5 50 100
|
|
||||||
:header-rows: 1
|
|
||||||
|
|
||||||
* - 时钟名称
|
|
||||||
- 时钟频率
|
|
||||||
- SCL 的最大频率
|
|
||||||
- 时钟功能
|
|
||||||
* - APB 时钟
|
|
||||||
- 80 MHz
|
|
||||||
- 4 MHz
|
|
||||||
- /
|
|
||||||
* - REF_TICK
|
|
||||||
- 1 MHz
|
|
||||||
- 50 KHz
|
|
||||||
- :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`, :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP`
|
|
||||||
|
|
||||||
对 :cpp:member:`i2c_config_t::clk_flags` 的解释如下:
|
|
||||||
1. :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`:当 APB 时钟改变时,时钟的波特率不会改变。
|
|
||||||
2. :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP`:支持轻度睡眠模式,APB 时钟则不支持。
|
|
||||||
|
|
||||||
.. only:: esp32s3
|
|
||||||
|
|
||||||
.. list-table:: {IDF_TARGET_NAME} 时钟源特性
|
|
||||||
:widths: 5 5 50 20
|
|
||||||
:header-rows: 1
|
|
||||||
|
|
||||||
* - 时钟名称
|
|
||||||
- 时钟频率
|
|
||||||
- SCL 的最大频率
|
|
||||||
- 时钟功能
|
|
||||||
* - XTAL 时钟
|
|
||||||
- 40 MHz
|
|
||||||
- 2 MHz
|
|
||||||
- /
|
|
||||||
* - RTC 时钟
|
|
||||||
- 20 MHz
|
|
||||||
- 1 MHz
|
|
||||||
- :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`, :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP`
|
|
||||||
|
|
||||||
.. only:: esp32c3
|
|
||||||
|
|
||||||
.. list-table:: {IDF_TARGET_NAME} 时钟源特性
|
|
||||||
:widths: 5 5 50 100
|
|
||||||
:header-rows: 1
|
|
||||||
|
|
||||||
* - 时钟名称
|
|
||||||
- 时钟频率
|
|
||||||
- SCL 的最大频率
|
|
||||||
- 时钟功能
|
|
||||||
* - XTAL 时钟
|
|
||||||
- 40 MHz
|
|
||||||
- 2 MHz
|
|
||||||
- /
|
|
||||||
* - RTC 时钟
|
|
||||||
- 20 MHz
|
|
||||||
- 1 MHz
|
|
||||||
- :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`, :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP`
|
|
||||||
|
|
||||||
对 :cpp:member:`i2c_config_t::clk_flags` 的解释如下:
|
|
||||||
|
|
||||||
1. :c:macro:`I2C_SCLK_SRC_FLAG_AWARE_DFS`:当 APB 时钟改变时,时钟的波特率不会改变。
|
|
||||||
2. :c:macro:`I2C_SCLK_SRC_FLAG_LIGHT_SLEEP`:支持轻度睡眠模式,APB 时钟则不支持。
|
|
||||||
3. {IDF_TARGET_NAME} 可能不支持某些标志,请在使用前阅读技术参考手册。
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
在主机模式下,SCL 的时钟频率不应大于上表中提到的 SCL 的最大频率。
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
SCL 的时钟频率会被上拉电阻和线上电容(或是从机电容)一起影响。因此,用户需要自己选择合适的上拉电阻去保证 SCL 时钟频率是准确的。尽管 I2C 协议推荐上拉电阻值为 1 K 欧姆到 10 K 欧姆,但是需要根据不同的频率需要选择不同的上拉电阻。
|
|
||||||
|
|
||||||
通常来说,所选择的频率越高,需要的上拉电阻越小(但是不要小于 1 K 欧姆)。这是因为高电阻会减小电流,这会延长上升时间从而使频率变慢。通常我们推荐的上拉阻值范围为 2 K 欧姆到 5 K 欧姆,但是用户可能也需要根据他们的实际情况做出一些调整。
|
|
||||||
|
|
||||||
.. _i2c-api-install-driver:
|
|
||||||
|
|
||||||
安装驱动程序
|
|
||||||
^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
配置好 I2C 驱动程序后,使用以下参数调用函数 :cpp:func:`i2c_driver_install` 安装驱动程序:
|
|
||||||
|
|
||||||
- 端口号,从 :cpp:type:`i2c_port_t` 中二选一
|
|
||||||
- 主机或从机模式,从 :cpp:type:`i2c_mode_t` 中选择
|
|
||||||
|
|
||||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
- (仅限从机模式)分配用于在从机模式下发送和接收数据的缓存区大小。I2C 是一个以主机为中心的总线,数据只能根据主机的请求从从机传输到主机。因此,从机通常有一个发送缓存区,供从应用程序写入数据使用。数据保留在发送缓存区中,由主机自行读取。
|
|
||||||
|
|
||||||
- 用于分配中断的标志(请参考 :component_file:`esp_hw_support/include/esp_intr_alloc.h` 中 ESP_INTR_FLAG_* 值)
|
|
||||||
|
|
||||||
.. _i2c-api-master-mode:
|
|
||||||
|
|
||||||
主机模式下通信
|
|
||||||
^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
安装 I2C 驱动程序后, {IDF_TARGET_NAME} 即可与其他 I2C 设备通信。
|
|
||||||
|
|
||||||
{IDF_TARGET_NAME} 的 I2C 控制器在主机模式下负责与 I2C 从机设备建立通信,并发送命令让从机响应,如进行测量并将结果发给主机。
|
|
||||||
|
|
||||||
为优化通信流程,驱动程序提供一个名为 “命令链接” 的容器,该容器应填充一系列命令,然后传递给 I2C 控制器执行。
|
|
||||||
|
|
||||||
|
|
||||||
主机写入数据
|
|
||||||
"""""""""""""
|
|
||||||
|
|
||||||
下面的示例展示如何为 I2C 主机构建命令链接,从而向从机发送 *n* 个字节。
|
|
||||||
|
|
||||||
.. blockdiag:: ../../../_static/diagrams/i2c-command-link-master-write-blockdiag.diag
|
|
||||||
:scale: 100
|
|
||||||
:caption: I2C command link - master write example
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
|
|
||||||
下面介绍如何为 “主机写入数据” 设置命令链接及其内部内容:
|
|
||||||
|
|
||||||
1. 使用 :cpp:func:`i2c_cmd_link_create` 创建一个命令链接。
|
|
||||||
|
|
||||||
然后,将一系列待发送给从机的数据填充命令链接:
|
|
||||||
|
|
||||||
a) **启动位** - :cpp:func:`i2c_master_start`
|
|
||||||
b) **从机地址** - :cpp:func:`i2c_master_write_byte`。提供单字节地址作为调用此函数的实参。
|
|
||||||
c) **数据** - 一个或多个字节的数据作为 :cpp:func:`i2c_master_write` 的实参。
|
|
||||||
d) **停止位** - :cpp:func:`i2c_master_stop`
|
|
||||||
|
|
||||||
函数 :cpp:func:`i2c_master_write_byte` 和 :cpp:func:`i2c_master_write` 都有额外的实参,规定主机是否应确认其有无接受到 ACK 位。
|
|
||||||
|
|
||||||
2. 通过调用 :cpp:func:`i2c_master_cmd_begin` 来触发 I2C 控制器执行命令链接。一旦开始执行,就不能再修改命令链接。
|
|
||||||
3. 命令发送后,通过调用 :cpp:func:`i2c_cmd_link_delete` 释放命令链接使用的资源。
|
|
||||||
|
|
||||||
|
|
||||||
主机读取数据
|
|
||||||
""""""""""""""
|
|
||||||
|
|
||||||
下面的示例展示如何为 I2C 主机构建命令链接,以便从从机读取 *n* 个字节。
|
|
||||||
|
|
||||||
.. blockdiag:: ../../../_static/diagrams/i2c-command-link-master-read-blockdiag.diag
|
|
||||||
:scale: 100
|
|
||||||
:caption: I2C command link - master read example
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
|
|
||||||
在读取数据时,在上图的步骤 4 中,不是用 ``i2c_master_write...``,而是用 :cpp:func:`i2c_master_read_byte` 和/或 :cpp:func:`i2c_master_read` 填充命令链接。同样,在步骤 5 中配置最后一次的读取,以便主机不提供 ACK 位。
|
|
||||||
|
|
||||||
|
|
||||||
指示写入或读取数据
|
|
||||||
""""""""""""""""""
|
|
||||||
|
|
||||||
发送从机地址后(请参考上图中第 3 步),主机可以写入或从从机读取数据。
|
|
||||||
|
|
||||||
主机实际执行的操作信息存储在从机地址的最低有效位中。
|
|
||||||
|
|
||||||
因此,为了将数据写入从机,主机发送的命令链接应包含地址 ``(ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE``,如下所示:
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE, ACK_EN);
|
|
||||||
|
|
||||||
同理,指示从从机读取数据的命令链接如下所示:
|
|
||||||
|
|
||||||
.. code-block:: c
|
|
||||||
|
|
||||||
i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | I2C_MASTER_READ, ACK_EN);
|
|
||||||
|
|
||||||
|
|
||||||
.. only:: SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
.. _i2c-api-slave-mode:
|
|
||||||
|
|
||||||
从机模式下通信
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
安装 I2C 驱动程序后, {IDF_TARGET_NAME} 即可与其他 I2C 设备通信。
|
|
||||||
|
|
||||||
API 为从机提供以下功能:
|
|
||||||
|
|
||||||
- :cpp:func:`i2c_slave_read_buffer`
|
|
||||||
|
|
||||||
当主机将数据写入从机时,从机将自动将其存储在接收缓存区中。从机应用程序可自行调用函数 :cpp:func:`i2c_slave_read_buffer`。如果接收缓存区中没有数据,此函数还具有一个参数用于指定阻塞时间。这将允许从机应用程序在指定的超时设定内等待数据到达缓存区。
|
|
||||||
|
|
||||||
- :cpp:func:`i2c_slave_write_buffer`
|
|
||||||
|
|
||||||
发送缓存区是用于存储从机要以 FIFO 顺序发送给主机的所有数据。在主机请求接收前,这些数据一直存储在发送缓存区。函数 :cpp:func:`i2c_slave_write_buffer` 有一个参数,用于指定发送缓存区已满时的块时间。这将允许从机应用程序在指定的超时设定内等待发送缓存区中足够的可用空间。
|
|
||||||
|
|
||||||
在 :example:`peripherals/i2c` 中可找到介绍如何使用这些功能的代码示例。
|
|
||||||
|
|
||||||
.. _i2c-api-interrupt-handling:
|
|
||||||
|
|
||||||
.. only:: not SOC_I2C_SUPPORT_SLAVE
|
|
||||||
|
|
||||||
.. _i2c-api-interrupt-handling:
|
|
||||||
|
|
||||||
中断处理
|
|
||||||
^^^^^^^^^^^
|
|
||||||
|
|
||||||
安装驱动程序时,默认情况下会安装中断处理程序。
|
|
||||||
|
|
||||||
.. _i2c-api-customized-configuration:
|
|
||||||
|
|
||||||
用户自定义配置
|
|
||||||
^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
如本节末尾所述 :ref:`i2c-api-configure-driver`,函数 :cpp:func:`i2c_param_config` 在初始化 I2C 端口的驱动程序配置时,也会将几个 I2C 通信参数设置为 I2C 总线协议规范规定的默认值。其他一些相关参数已在 I2C 控制器的寄存器中预先配置。
|
|
||||||
|
|
||||||
通过调用下表中提供的专用函数,可以将所有这些参数更改为用户自定义值。请注意,时序值是在 APB 时钟周期中定义。
|
|
||||||
|
|
||||||
.. list-table:: 其他可配置的 I2C 通信参数
|
|
||||||
:widths: 65 35
|
|
||||||
:header-rows: 1
|
|
||||||
|
|
||||||
* - 要更改的参数
|
|
||||||
- 函数
|
|
||||||
* - SCL 脉冲周期的高电平和低电平
|
|
||||||
- :cpp:func:`i2c_set_period`
|
|
||||||
* - 在产生 **启动** 信号期间使用的 SCL 和 SDA 信号时序
|
|
||||||
- :cpp:func:`i2c_set_start_timing`
|
|
||||||
* - 在产生 **停止** 信号期间使用的 SCL 和 SDA 信号时序
|
|
||||||
- :cpp:func:`i2c_set_stop_timing`
|
|
||||||
* - 从机采样以及主机切换时,SCL 和 SDA 信号之间的时序关系
|
|
||||||
- :cpp:func:`i2c_set_data_timing`
|
|
||||||
* - I2C 超时
|
|
||||||
- :cpp:func:`i2c_set_timeout`
|
|
||||||
* - 优先发送/接收最高有效位 (LSB) 或最低有效位 (MSB),可在 :cpp:type:`i2c_trans_mode_t` 定义的模式中选择
|
|
||||||
- :cpp:func:`i2c_set_data_mode`
|
|
||||||
|
|
||||||
|
|
||||||
上述每个函数都有一个 *_get_* 对应项来检查当前设置的值。例如,调用 :cpp:func:`i2c_get_timeout` 来检查 I2C 超时值。
|
|
||||||
|
|
||||||
要检查在驱动程序配置过程中设置的参数默认值,请参考文件 :component_file:`driver/i2c/i2c.c` 并查找带有后缀 ``_DEFAULT`` 的定义。
|
|
||||||
|
|
||||||
通过函数 :cpp:func:`i2c_set_pin` 可以为 SDA 和 SCL 信号选择不同的管脚并改变上拉配置。如果要修改已经输入的值,请使用函数 :cpp:func:`i2c_param_config`。
|
|
||||||
|
|
||||||
.. 注解 ::
|
|
||||||
|
|
||||||
{IDF_TARGET_NAME} 的内部上拉电阻范围为几万欧姆,因此在大多数情况下,它们本身不足以用作 I2C 上拉电阻。建议用户使用阻值在 I2C 总线协议规范规定范围内的上拉电阻。计算阻值的具体方法,可参考 `TI 应用说明 <https://www.ti.com/lit/an/slva689/slva689.pdf>`_
|
|
||||||
|
|
||||||
|
|
||||||
.. _i2c-api-error-handling:
|
|
||||||
|
|
||||||
错误处理
|
|
||||||
^^^^^^^^^^
|
|
||||||
|
|
||||||
大多数 I2C 驱动程序的函数在成功完成时会返回 ``ESP_OK`` ,或在失败时会返回特定的错误代码。实时检查返回的值并进行错误处理是一种好习惯。驱动程序也会打印日志消息,其中包含错误说明,例如检查输入配置的正确性。有关详细信息,请参考文件 :component_file:`driver/i2c/i2c.c` 并用后缀 ``_ERR_STR`` 查找定义。
|
|
||||||
|
|
||||||
使用专用中断来捕获通信故障。例如,如果从机将数据发送回主机耗费太长时间,会触发 ``I2C_TIME_OUT_INT`` 中断。详细信息请参考 :ref:`i2c-api-interrupt-handling`。
|
|
||||||
|
|
||||||
如果出现通信失败,可以分别为发送和接收缓存区调用 :cpp:func:`i2c_reset_tx_fifo` 和 :cpp:func:`i2c_reset_rx_fifo` 来重置内部硬件缓存区。
|
|
||||||
|
|
||||||
|
|
||||||
.. _i2c-api-delete-driver:
|
|
||||||
|
|
||||||
删除驱动程序
|
|
||||||
^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
当使用 :cpp:func:`i2c_driver_install` 建立 I2C 通信,一段时间后不再需要 I2C 通信时,可以通过调用 :cpp:func:`i2c_driver_delete` 来移除驱动程序以释放分配的资源。
|
|
||||||
|
|
||||||
由于函数 :cpp:func:`i2c_driver_delete` 无法保证线程安全性,请在调用该函数移除驱动程序前务必确保所有的线程都已停止使用驱动程序。
|
|
||||||
|
|
||||||
应用示例
|
|
||||||
----------
|
|
||||||
|
|
||||||
I2C 主机和从机示例::example:`peripherals/i2c`。
|
|
||||||
|
|
||||||
|
|
||||||
API 参考
|
|
||||||
----------
|
|
||||||
|
|
||||||
.. include-build-file:: inc/i2c.inc
|
|
||||||
.. include-build-file:: inc/i2c_types.inc
|
|
||||||
|
@ -9,3 +9,34 @@
|
|||||||
----
|
----
|
||||||
|
|
||||||
- :c:macro:`UART_FIFO_LEN` 已弃用,更新为 :c:macro:`UART_HW_FIFO_LEN`。
|
- :c:macro:`UART_FIFO_LEN` 已弃用,更新为 :c:macro:`UART_HW_FIFO_LEN`。
|
||||||
|
|
||||||
|
.. only:: SOC_I2C_SUPPORTED
|
||||||
|
|
||||||
|
I2C
|
||||||
|
---
|
||||||
|
|
||||||
|
I2C 驱动已经被重新设计在 :doc:`I2C <../../../api-reference/peripherals/i2c>`, 以统一接口并扩展 I2C 外设的使用。尽管我们推荐使用新驱动的接口, 但用户仍然可以通过包含路径 ``driver/i2c.h`` 来使用老驱动。
|
||||||
|
|
||||||
|
主要的概念上和用法上的改变如下所示:
|
||||||
|
|
||||||
|
主要概念更新
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- 用于初始化整个 I2C 总线的结构体 ``i2c_config_t`` 已经被移除. 在新驱动中, 从机和主机是分开的. 用户可以独立调用 :cpp:type:`i2c_master_bus_config_t` 和 :cpp:type:`i2c_slave_config_t`。
|
||||||
|
- ``i2c_mode_t`` 用来判断 I2C 控制器是工作在从模式还是主模式。该枚举器已被弃用。在新驱动程序中,用户无需自行设置,驱动程序会妥善处理。
|
||||||
|
- ``i2c_rw_t`` 用来判断 I2C 主控制器是在进行 "写 "还是 "读"。现在,它已被弃用。
|
||||||
|
- ``i2c_addr_mode_t`` 被重命名为 ``i2c_addr_bit_len_t``.
|
||||||
|
- 在老驱动中,你需要将 I2C 命令通过 ``I2C_LINK_RECOMMENDED_SIZE``, ``i2c_cmd_link_create_static``, 等链接成链表. 在新驱动中,你不需要这么做,你只需要调用相关的接口函数即可。
|
||||||
|
- 在老驱动中,选择时钟通过例如 ``I2C_SCLK_SRC_FLAG_FOR_NOMAL`` 的时钟标志位. 在新驱动中,你可以直接选择时钟源。
|
||||||
|
|
||||||
|
主要用法更新
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- I2C 总线初始化通过两部分完成. 第一步通过 :cpp:func:`i2c_new_master_bus` 初始化 I2C 总线, 然后初始化 I2C 设备通过 :cpp:func:`i2c_master_bus_add_device`。
|
||||||
|
- ``i2c_reset_tx_fifo`` 和 ``i2c_reset_rx_fifo`` 已被删除,因为用户不太需要重置 fifo。但可以通过 ``i2c_master_bus_reset`` 重置整个总线。
|
||||||
|
- 删除了 ``i2c_cmd_link_xxx`` 函数,用户不需要自己使用 link 来链接命令。
|
||||||
|
- ``i2c_master_write_to_device`` 更名为 ``i2c_master_transmit``。
|
||||||
|
- ``i2c_master_read_from_device`` 已更名为 ``i2c_master_receive``。
|
||||||
|
- ``i2c_master_write_read_device`` 已更名为 ``i2c_master_transmit_receive``。
|
||||||
|
- ``i2c_slave_write_buffer`` 重命名为 ``i2c_slave_transmit``。
|
||||||
|
- ``i2c_slave_read_buffer`` 已更名为 ``i2c_slave_receive``。
|
||||||
|
@ -37,6 +37,7 @@ void app_main(void)
|
|||||||
.i2c_port = PORT_NUMBER,
|
.i2c_port = PORT_NUMBER,
|
||||||
.scl_io_num = SCL_IO_PIN,
|
.scl_io_num = SCL_IO_PIN,
|
||||||
.sda_io_num = SDA_IO_PIN,
|
.sda_io_num = SDA_IO_PIN,
|
||||||
|
.glitch_ignore_cnt = 7,
|
||||||
};
|
};
|
||||||
i2c_master_bus_handle_t bus_handle;
|
i2c_master_bus_handle_t bus_handle;
|
||||||
|
|
||||||
|