The I²C driver provides a high-level, easy-to-use API for configuring and operating the I²C hardware block on Synaptics MCUs It supports both Controller (formerly “Master”) and Target (formerly “Slave”) roles, in blocking and non-blocking modes, with full control over timing, addressing, and error handling.
Key Features
- Controller & Target roles
- 7-bit & 10-bit addressing, General-Call support
- Standard (100 kHz), Fast (400 kHz), Fast-Plus (1 MHz)
- Automatic bus-recovery on stuck SDA
- Comprehensive interrupt control in the driver
- Blocking and interrupt-driven transfer APIs with user callbacks
- Detailed status and error reporting, including TX_ABRT source flags
API Organization
- Initialization / Deinitialization
- Role Configuration
- Transfer
- Control Operations
- Status & Error Handling
Typical Usage
- Configure the I2C-related pins using the pinmux driver (e.g., set SDA/SCL to I2C function).
- Call i2c_init() to reset the block and enable its clock.
- Configure timing and addressing via i2c_controller_set_config(), i2c_target_set_config()
- Register a callback for non-blocking transfers.
- Enable the I²C block with i2c_enable().
- Perform transfers via blocking or non-blocking APIs.
- When finished, disable the block via i2c_disable() and call i2c_deinit().
I2C Initialization and Pinmux Configuration
This snippet handles the initialization of I2C modules (Controller 0, Controller 1, and Target) and configures the corresponding pinmux settings based on enabled configuration macros.
I2C Enable
This snippet enables the required I2C controllers and targets after pinmux configuration.
Controller Configuration
This snippet shows how to define a structure to configure the I2C controller.
.ten_bit_addr = false,
.enable_bus_clear = false,
.clear_stuck_sda = false,
.scl_stuck_timeout_ms = 1,
.sda_stuck_timeout_ms = 1
};
Target Configuration
This snippet demonstrates how to define a structure for configuring the I2C target.
.ten_bit_addr = false,
.target_address = I2C_TARGET_ADDRESS,
.general_call_response = false,
.scl_stuck_timeout_ms = 1,
.sda_stuck_timeout_ms = 1
};
Setting Target Address
This snippet demonstrates how to set and retrieve the I2C controller's target address.
LOG_INFO(LOG_MOD_GENERIC, "Controller target address: 0x%02X\n", address);
} else {
LOG_ERROR(LOG_MOD_GENERIC, "Failed to get I2C target address\n");
}
Blocking Controller Write and Read
This snippet shows how to perform blocking write and read operations.
LOG_INFO(LOG_MOD_GENERIC, "I2C controller write blocking started\n");
LOG_INFO(LOG_MOD_GENERIC, "Controller write success with status code:%d\n", ret);
}
else {
LOG_ERROR(LOG_MOD_GENERIC, "Controller write failed with status code:%d\n", ret);
}
LOG_INFO(LOG_MOD_GENERIC, "Blocking read success with status code: %d\n", ret);
for (int i = 0; i < read_config.size; i++) {
LOG_INFO(LOG_MOD_GENERIC, "Read byte [%d]: 0x%02X\n", i, read_config.buf[i]);
if (read_config.buf[i] == target_tx_buf[i]) {
match_count++;
} else {
mismatch_count++;
}
}
}
else {
LOG_ERROR(LOG_MOD_GENERIC, "Blocking read failed with status code: %d\n", ret);
}
Non-Blocking Controller Write and Read
This snippet shows how to perform non-blocking write and read operations.
LOG_INFO(LOG_MOD_GENERIC, "Controller: Sending data...\n");
for (int i = 0; i < 10; ++i) {
{
LOG_INFO(LOG_MOD_GENERIC,"Write is completed\n");
break;
}
vTaskDelay(pdMS_TO_TICKS(10));
}
if (xSemaphoreTake(i2c_controller_semaphore, pdMS_TO_TICKS(SEMAPHORE_TIMEOUT)) == pdTRUE) {
LOG_INFO(LOG_MOD_GENERIC, "Controller semaphore taken!");
} else {
LOG_INFO(LOG_MOD_GENERIC, "Controller semaphore TIMEOUT!");
}
LOG_INFO(LOG_MOD_GENERIC, "Controller: Write completed with event: 0x%X\n",i2c_event_master_type);
} else {
LOG_ERROR(LOG_MOD_GENERIC, "Controller: Write failed. Event: 0x%X\n", i2c_event_master_type);
}
if (xSemaphoreTake(i2c_controller_semaphore, pdMS_TO_TICKS(SEMAPHORE_TIMEOUT)) == pdTRUE) {
LOG_INFO(LOG_MOD_GENERIC, "Controller semaphore taken!");
} else {
LOG_INFO(LOG_MOD_GENERIC, "Controller semaphore TIMEOUT!");
}
LOG_ERROR(LOG_MOD_GENERIC, "Non-Blocking read failed due to NACK/invalid address\n");
LOG_INFO(LOG_MOD_GENERIC, "Non-Blocking read success with event type: %d\n", i2c_event_master_type);
for (int i = 0; i < read_non_block.size; i++) {
LOG_INFO(LOG_MOD_GENERIC, "Read byte [%d]: 0x%02X\n", i, read_non_block.buf[i]);
if (read_non_block.buf[i] == target_tx_buf[i]) {
match_count++;
} else {
mismatch_count++;
}
}
} else {
LOG_INFO(LOG_MOD_GENERIC, "Non-Blocking read with event type: %d\n", i2c_event_master_type);
}
Non-Blocking Target Read
This snippet shows how to perform non-blocking read operation.
LOG_INFO(LOG_MOD_GENERIC,"Target: Reading Starts...\n");
LOG_ERROR(LOG_MOD_GENERIC, "Target: Failed to prep controller write: %d", ret);
vTaskDelete(NULL);
}
Non-Blocking Target Read
This snippet shows how to perform non-blocking write operation.
LOG_ERROR(LOG_MOD_GENERIC, "Target: Failed to prep controller read: %d", ret);
vTaskDelete(NULL);
}
Callback Handler
This callback function is registered with the I2C driver and gets invoked asynchronously when an I2C event occurs. The function handles events such as transmission completion, reception, or error conditions.
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (id == I2C_INST_M) {
i2c_event_master_type = event_mask;
i2c_event_master_flag = 1;
LOG_INFO(LOG_MOD_GENERIC, "[Controller] I2C Event: 0x%X\n", event_mask);
if (xPortIsInsideInterrupt()) {
xSemaphoreGiveFromISR(i2c_controller_semaphore, &xHigherPriorityTaskWoken);
} else {
xSemaphoreGive(i2c_controller_semaphore);
}
} else {
i2c_event_type = event_mask;
i2c_event_flag = 1;
LOG_INFO(LOG_MOD_GENERIC, "[Target] I2C Event: 0x%X\n", event_mask);
if (xPortIsInsideInterrupt()) {
xSemaphoreGiveFromISR(i2c_target_semaphore, &xHigherPriorityTaskWoken);
} else {
xSemaphoreGive(i2c_target_semaphore);
}
}
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
I2C Disable
This snippet disables the required I2C controllers and targets.
- Note
- For complete register listings and detailed descriptions, refer to the Technical Reference Manual of your device.