ESP-IDF 5.4.x I2C Master - SMBus/PMBus block-read
Posted: Wed Apr 30, 2025 2:27 pm
Hi everyone,
I've been trying to figure out how to implement a PMBus/SMBus-compatible block read using the i2c_master_execute_defined_operations API without creating multiple transactions.
The block-read can be either a write-restart-read or read-only transaction.
The first byte returned from the client on the read operation is the block size; it can be up to 32 (SMBus) or 255 (PMBus) bytes long. For PMBus and SMBus there is also an optional PEC byte after the block data, so a total of 33 bytes (SMBus) or 256 bytes (PMBus) may need to be retrieved.
Ideally what I want to do is a read of this byte without a STOP being required or automatically added; many commercial I2C interfaces offer a read-without-stop option to support these types of commands. I attempted to construct a defined-operations list without a STOP condition at the end, however this seems to not work with the latest ESP-IDF on my ESP32-S3 devkit.
Since I am used to base-metal MCU, I thought that a piecewise I2C host implementation would be best (write address, check for ACK, write data, check for ACKS, restart and write address, check for ACK, read block size, check for ACK, read the block data, check for ACK w/a NACK on the last byte). This seems not to be possible.
This block of code, where I attempt to only send the address to check if the client is present, fails without any I2C bytes being transmitted at all. Hence it seems impossible to use the defined-operations list without a STOP.
If I cannot use the defined-operations list without a STOP, then I would need to split the transaction into two; one with a single-byte read to get the block size, then use the returned block size to trigger a second transaction with a fixed size based on the block size (and PEC).
Any suggestions are welcomed.
I've been trying to figure out how to implement a PMBus/SMBus-compatible block read using the i2c_master_execute_defined_operations API without creating multiple transactions.
The block-read can be either a write-restart-read or read-only transaction.
The first byte returned from the client on the read operation is the block size; it can be up to 32 (SMBus) or 255 (PMBus) bytes long. For PMBus and SMBus there is also an optional PEC byte after the block data, so a total of 33 bytes (SMBus) or 256 bytes (PMBus) may need to be retrieved.
Ideally what I want to do is a read of this byte without a STOP being required or automatically added; many commercial I2C interfaces offer a read-without-stop option to support these types of commands. I attempted to construct a defined-operations list without a STOP condition at the end, however this seems to not work with the latest ESP-IDF on my ESP32-S3 devkit.
Since I am used to base-metal MCU, I thought that a piecewise I2C host implementation would be best (write address, check for ACK, write data, check for ACKS, restart and write address, check for ACK, read block size, check for ACK, read the block data, check for ACK w/a NACK on the last byte). This seems not to be possible.
Code: Select all
// first write the address in write mode
i2c_operation_job_t i2c_write_1[] = {
{ .command = I2C_MASTER_CMD_START },
{ .command = I2C_MASTER_CMD_WRITE, .write = { .ack_check = true, .data = (uint8_t *) &u8Addr, .total_bytes = 1 } },
};
// issue the partial command
espError = i2c_master_execute_defined_operations(dev_handle, i2c_write_1, 2, 10);
If I cannot use the defined-operations list without a STOP, then I would need to split the transaction into two; one with a single-byte read to get the block size, then use the returned block size to trigger a second transaction with a fixed size based on the block size (and PEC).
Any suggestions are welcomed.