From 28eeb92f8eec5b8268d2a74ad0561670d14e189a Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Tue, 11 Nov 2025 04:02:35 -0800 Subject: [PATCH] Add I2C Transmit and Receive function (#25637) * feat: adds a transmit and receive i2c method * fix: address the i2c transmit and receive length on u16 * Add AVR/LUFA implementation Didn't add a progmem version, since that would only apply to receive. Figured it wasn't worth it, but can add. * Rearrange order of functions * Add docs * Fix doc gen error * Fix lint issues * fix more lint issues --- docs/drivers/i2c.md | 25 +++++++++++++++++++++++++ drivers/i2c_master.h | 15 +++++++++++++++ platforms/avr/drivers/i2c_master.c | 26 ++++++++++++++++++++++++++ platforms/chibios/drivers/i2c_master.c | 6 ++++++ 4 files changed, 72 insertions(+) diff --git a/docs/drivers/i2c.md b/docs/drivers/i2c.md index ad74d0e481..75823c682b 100644 --- a/docs/drivers/i2c.md +++ b/docs/drivers/i2c.md @@ -221,6 +221,31 @@ Receive multiple bytes from the selected I2C device. --- +### `i2c_status_t i2c_transmit_and_receive(uint8_t address, const uint8_t* tx_data, uint16_t tx_length, uint8_t* rx_data, uint16_t rx_length, uint16_t timeout)` {#api-i2c-transmit-and-receive} + +Send and receive multiple bytes from the selected I2C device. + +#### Arguments {#api-i2c-transmit-and-receive-arguments} + + - `uint8_t address` + The 7-bit I2C address of the device. + - `const uint8_t* tx_data` + A pointer to the data to transmit. + - `uint16_t tx_length` + The number of bytes to write. Take care not to overrun the length of `tx_data`. + - `uint8_t* rx_data` + A pointer to a buffer to read into. + - `uint16_t rx_length` + The number of bytes to read. Take care not to overrun the length of `data`. + - `uint16_t timeout` + The time in milliseconds to wait for a response from the target device. + +#### Return Value {#api-i2c-transmit-and-receive-return} + +`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. + +--- + ### `i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register} Write to a register with an 8-bit address on the I2C device. diff --git a/drivers/i2c_master.h b/drivers/i2c_master.h index dbe1cd42fa..5f6094bc81 100644 --- a/drivers/i2c_master.h +++ b/drivers/i2c_master.h @@ -72,6 +72,21 @@ i2c_status_t i2c_transmit_P(uint8_t address, const uint8_t* data, uint16_t lengt */ i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); +/** + * \brief Send multiple bytes and then receive multiple bytes from the selected I2C device. + * + * \param address The 7-bit I2C address of the device. + * \param tx_data A pointer to the data to transmit. + * \param tx_length The number of bytes to write. Take care not to overrun the length of `tx_data`. + * \param rx_data A pointer to a buffer to read into. + * \param rx_length The number of bytes to read. Take care not to overrun the length of `rx_data`. + * \param timeout The time in milliseconds to wait for a response from the target device. + * + * \return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. + */ + +i2c_status_t i2c_transmit_and_receive(uint8_t address, const uint8_t* tx_data, uint16_t tx_length, uint8_t* rx_data, uint16_t rx_length, uint16_t timeout); + /** * \brief Write to a register with an 8-bit address on the I2C device. * diff --git a/platforms/avr/drivers/i2c_master.c b/platforms/avr/drivers/i2c_master.c index 9136f4a7b9..e5caa995cf 100644 --- a/platforms/avr/drivers/i2c_master.c +++ b/platforms/avr/drivers/i2c_master.c @@ -207,6 +207,32 @@ i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16 return (status < 0) ? status : I2C_STATUS_SUCCESS; } +i2c_status_t i2c_transmit_and_receive(uint8_t address, const uint8_t* tx_data, uint16_t tx_length, uint8_t* rx_data, uint16_t rx_length, uint16_t timeout) { + i2c_status_t status = i2c_start(address | I2C_ACTION_WRITE, timeout); + + for (uint16_t i = 0; i < tx_length && status >= 0; i++) { + status = i2c_write(tx_data[i], timeout); + } + + for (uint16_t i = 0; i < (rx_length - 1) && status >= 0; i++) { + status = i2c_read_ack(timeout); + if (status >= 0) { + rx_data[i] = status; + } + } + + if (status >= 0) { + status = i2c_read_nack(timeout); + if (status >= 0) { + rx_data[(rx_length - 1)] = status; + } + } + + i2c_stop(); + + return status; +} + i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { i2c_status_t status = i2c_start(devaddr | 0x00, timeout); if (status >= 0) { diff --git a/platforms/chibios/drivers/i2c_master.c b/platforms/chibios/drivers/i2c_master.c index 20850859b5..a44051b82a 100644 --- a/platforms/chibios/drivers/i2c_master.c +++ b/platforms/chibios/drivers/i2c_master.c @@ -160,6 +160,12 @@ i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16 return i2c_epilogue(status); } +i2c_status_t i2c_transmit_and_receive(uint8_t address, const uint8_t* tx_data, uint16_t tx_length, uint8_t* rx_data, uint16_t rx_length, uint16_t timeout) { + i2cStart(&I2C_DRIVER, &i2cconfig); + msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (address >> 1), tx_data, tx_length, rx_data, rx_length, TIME_MS2I(timeout)); + return i2c_epilogue(status); +} + i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { i2cStart(&I2C_DRIVER, &i2cconfig);