How Do I Free Up the Data Bus when the I2C Target Device Does Not Respond?

Traffic jam that represents blocked data when the data bus is not released

Photo by SD Pictures

Question from the Customer:

I have a question about return errors.  I am using the Aardvark I2C/SPI Host Adapter with Aardvark Software API with a target I2C device. The test setup: sometimes the target device is not available. In that case, a call to aa_i2c_write results in NACK, which is expected. Because the AA_I2C_NO_STOP flag is used, the Aardvark adapter responds to NACK with the call to free the bus, aa_i2c_free_bus().

However, after freeing the bus, the next call of aa_i2c_write() often results with the error -103, AA_I2C_WRITE_ERROR.

I assume there’s an error in our command usage, and there seems to be no option to reset the Aardvark adapter. Can you help us determine the cause of the AA_I2C_WRITE_ERROR, and how to resolve it?

Response from Technical Support:

Thanks for your question! We will go over why errors may occur with write and read commands, and go over the details of using a write-read command, aa_i2c_write_read, which we recommend for your setup.

How Write-Read Works

The aa_i2c_write_read API call writes a stream of bytes to the I2C slave device, which is followed by a read from the same slave device. This command is used when the master device sends data and then waits for the target (slave) device to return a value that is related to the data sent.

The aa_i2c_write_read() returns either a status code or an error code.

  • A non-negative value represents a status code and is constructed as (read_status << 8) | (write_status)
  • A negative value represents an error. To see details about errors, enable logging with the command aa_log.

When the Bus is Not Released

The aa_i2c_write_read call is a combination of two API calls: aa_i2c_write_ext and aa_i2c_read_ext. Because two calls are used, aa_i2c_write_read() will not release the bus for a long time. Here are instances that cause the bus to not be released:

  • If the first step (aa_i2c_write_ext) fails, the bus is not released. This is equivalent to i2c_write(NO_STOP) followed by i2c_read(). In this case, because aa_i2c_write(NO_STOP) failed, the bus is held.
  • If the first step (aa_i2c_write_ext) completes with a non-zero status code, the Aardvark adapter will not execute the read phase of the operation.
  • If either the write or read command fails, an error code (not a non-zero status code) is returned. Because aa_i2c_write_read executes two commands, preference is given to write errors. In this case, there are two options for releasing the bus: call aa_i2c_free_bus() or aa_i2c_write(NO_FLAGS).

Details About Read-Write Call

Here are details about the aa_i2c_write_read call:
int aa_i2c_write_read (Aardvark   aardvark,

aa_u16                      slave_addr,
AardvarkI2cFlags   flags,
aa_u16                      out_num_bytes,
const aa_u08 *      out_data,
aa_u16 *                  num_written,
aa_u16                     in_num_bytes,
aa_u08 *                  in_data,
aa_u16 *                  num_read);


  • Aardvark: The handle of the Aardvark host adapter.
  • slave_addr: The slave from which to read.
  • flags: Special operations. For details, go to API Documentation.
  • out_num_bytes: The number of bytes to write. The maximum is 65,535
  • out_data: The pointer to the data to write.
  • num_written: The number of bytes that were written.
  • in_num_bytes: The number of bytes to read. The maximum is 65,535.
  • in_data: The pointer to the data to read.
  • num_read: The number of bytes that were read.

Return Values

The aa_i2c_write_read command combines the I2C status code from both write and read operations.
Here is an example of reading five bytes from the register address 0x11 :

  • flags: You can use AA_I2C_NO_FLAGS if you do not have special requirements listed under Notes on Status Codes.
  • out_num_bytes: The address width of the register address. In this example, the width is 1: one byte.
  • out_data: The register address (0x11).
  • num_written: This is updated by the APIs about the total bytes of register address that were sent. This parameter is used to verify if the register address was correctly sent on the bus.
  • in_num_bytes: The number of bytes to read. In this example, five bytes.
  • in_data pointer: The data read from the register location is stored in the pointer specified location.
  • num_read: This is the total number of bytes read from the register address. This parameter is used to verify if the function was successful in reading the desired number of bytes.

We hope this answers your questions. Additional resources that you may find helpful include the following:

If you want more information, feel free to contact us with your questions, or request a demo that applies to your application