Reference#

pw_i2c: Cross-platform I2C API with interactive debugging

Overview#

pw::i2c::Address#

class Address#

A helper class that represents I2C addresses.

An address instance remembers whether it was constructed as a seven-bit or ten-bit address. This attribute can be used by Initiators to determine the i2c addressing style to transmit.

Note: Per the above, a ten-bit constructed instance may still have an an address of seven or fewer bits.

#include "pw_i2c/address.h"

constexpr pw::i2c::Address kAddress1 = pw::i2c::Address::SevenBit<0x42>();
uint8_t raw_address_1 = kAddress1.GetSevenBit();

const pw::i2c::Address kAddress2<0x200>();  // 10-bit
uint16_t raw_address_2 = kAddress2.GetAddress();
// Note: kAddress2.GetSevenBit() would fail an assertion here.

Public Functions

explicit Address(uint16_t address)#

Creates a pw::i2c::Address instance from a 7 or 10 bit address.

If the address argument is 7-bits or less, a 7-bit address is constructed equivalent to Address::SevenBit(address);

If the address argument is 8, 9 or 10 bits, a ten-bit address is constructed equivalent to Address::TenBit(address);

The type of address construced will affect how the i2c address is transmitted on the bus. You should always use 7-bit addresses unless you are certain you have a host and device that support 10-bit addresses.

constexpr pw::i2c::Address kAddress(0x200);

Note

This function is deprecated. You should almost certainly use either Address::SevenBit<0x1>() for addresses known at compile time, or Address::SevenBit(0x1) for addresses known at run-time.

Parameters:

address[in] An address no larger than 10-bits as an unsigned integer. This method does a runtime assertion to ensure that address is 10 bits or less.

Returns:

A pw::i2c::Address instance.

uint8_t GetSevenBit() const#

Gets the 7-bit address that was provided when this instance was created.

This method does a runtime assertion to ensure that the address was constructed in 7-bit mode.

Returns:

A 7-bit address as an unsigned integer.

inline uint16_t GetTenBit() const#

Gets the 10-bit address that was provided when this instance was created.

Returns:

A 10-bit address as an unsigned integer.

inline uint16_t GetAddress() const#

Gets the raw address that was provided when this Address was created.

Use IsTenBit() to know whether the address should be interpreted as a 7-bit or 10-bit address.

Returns:

A an address as an unsigned integer.

inline constexpr bool IsTenBit() const#

Getter for whether this object represents a Ten bit address. Note: The address itself may still be fewer than 10 bits.

Returns:

true if the address represents a 10-bit address.

Public Static Functions

template<uint16_t kAddress>
static inline constexpr Address TenBit()#

Creates a pw::i2c::Address instance for an address that’s 10 bits or less.

This constant expression does a compile-time assertion to ensure that the provided address is 10 bits or less.

constexpr pw::i2c::Address kAddress = pw::i2c::Address::TenBit<0x200>();
Returns:

A pw::i2c::Address instance.

static inline Address TenBit(uint16_t address)#

Creates a pw::i2c::Address instance for an address that’s 10 bits or less.

This constructor does a run-time check to ensure that the provided address is 10 bits or less.

pw::i2c::Address kAddress = pw::i2c::Address::TenBit(0x200);
Returns:

A pw::i2c::Address instance.

template<uint8_t kAddress>
static inline constexpr Address SevenBit()#

Creates a pw::i2c::Address instance for an address that’s 7 bits or less.

This constant expression does a compile-time assertion to ensure that the provided address is 7 bits or less.

constexpr pw::i2c::Address kAddress =
    pw::i2c::Address::SevenBit<0x42>();
Returns:

A pw::i2c::Address instance.

static inline Address SevenBit(uint16_t address)#

Creates a pw::i2c::Address instance for an address that’s 7 bits or less.

This constructor does a run-time check to ensure that the provided address is 7 bits or less.

pw::i2c::Address kAddress = pw::i2c::Address::SevenBit(0x42);
Returns:

A pw::i2c::Address instance.

Friends

friend bool operator==(const Address &a1, const Address &a2)#

Operator for testing equality of two Address objects.

Returns:

true if the two Address objects are the same.

pw::i2c::Message#

class Message#

A struct that represents I2C read and write messages.

Individual messages can be accumulated into a span and transmitted in one atomic i2c transaction using an Initiator implementation.

#include "pw_i2c/message.h"

constexpr auto kAddress = pw::i2c::Address::SevenBit<0x42>()
constexpr chrono::SystemClock::duration kTimeout =
    std::chrono::duration_cast<chrono::SystemClock::duration>(100ms);
const std::array<std::byte, 2> tx_buffer = {std::byte{0xCD},
                                            std::byte{0xEF}};
std::array<std::byte, 2> rx_buffer;
Vector<Message, 2> messages;
if (!tx_buffer.empty()) {
  messages.push_back(Message::WriteMessage(kAddress, tx_buffer));
}
if (!rx_buffer.empty()) {
  messages.push_back(Message::ReadMessage(kAddress, rx_buffer));
}
initiator.TransferFor(messages, kTimeout);

Public Functions

inline bool IsRead() const#

Getter for whether this object represents a read operation.

Returns:

true if the message represents a read operation.

inline bool IsTenBit() const#

Getter for whether this object represents an operation addressed with a ten-bit address. When true, communicate on the wire using the i2c 10-bit addressing protocol.

Returns:

true if the message represents a 10-bit addressed operation.

inline bool IsWriteContinuation() const#

Getter for whether this object represents a continued write.

Returns:

true if the message represents a continued write.

inline Address GetAddress() const#

Getter for the address component.

Returns:

the Address passed into one of the constructors or factories.

inline ByteSpan GetMutableData() const#

Getter for the data component.

This method is only valid for Read messages and will runtime ASSERT on other messages.

Returns:

the mutable variant of the data passed into one of the constructors or factories.

inline ConstByteSpan GetData() const#

Getter for the data component.

Returns:

the data passed into one of the constructors or factories.

Public Static Functions

static inline constexpr Message WriteMessage(Address address, ConstByteSpan data)#

Creates a pw::i2c::Message instance for an i2c write message.

This Message can be passed to Initiator::TransferFor().

constexpr Message kMessage = Message::WriteMessage(
    pw::i2c::Address::SevenBit<0x42>(),
    data
);
Returns:

A pw::i2c::Message instance.

static inline constexpr Message WriteMessageContinuation(ConstByteSpan data)#

Creates a pw::i2c::Message instance for an i2c write message without a start condition sent on the bus. Chaining one or more of these messages after a regular Write message allows the client to send non-contiguous blocks of memory as one single write message to the i2c target.

Note: This message must follow another write message.

Note: No addresses is needed and no address will be transmitted. The data should immediately follow the data from the previous write message.

This Message can be passed to Initiator::TransferFor().

constexpr Message kMessage = Message::WriteMessageContinuation(data);
Returns:

A pw::i2c::Message instance.

static inline constexpr Message ReadMessage(Address address, ByteSpan data)#

Creates a pw::i2c::Message instance for an i2c read message.

This Message can be passed to Initiator::TransferFor().

constexpr Message kMessage = Message::ReadMessage(
    pw::i2c::Address::SevenBit<0x42>(),
    data
);
Returns:

A pw::i2c::Message instance.

pw::i2c::Initiator#

class Initiator#

The common, base driver interface for initiating thread-safe transactions with devices on an I2C bus. Other documentation may call this style of interface an I2C “master”, “central”, or “controller”.

pw::i2c::Initiator isn’t required to support 10-bit addressing. If only 7-bit addressing is supported, pw::i2c::Initiator fails a runtime assertion if given a Message with the kTenBitAddress flag, or when given an address that is out of 7-bit address range.

The implementer of TransferFor() (or DoWriteReadFor()) is responsible for ensuring thread safety and enabling functionality such as initialization, configuration, enabling and disabling, unsticking SDA, and detecting device address registration collisions.

Furthermore, devices may require specific sequences of transactions, and application logic must provide the synchronization to execute these sequences correctly.

Note

pw::i2c::Initiator uses internal synchronization, so it’s safe to initiate transactions from multiple threads. Each call to this class will be executed in a single bus transaction using repeated starts.

Subclassed by pw::i2c::GmockInitiator, pw::i2c::LinuxInitiator, pw::i2c::MockInitiator, pw::i2c::MockMessageInitiator

Public Types

enum class Feature : int#

Defined set of supported i2c features.

Values:

enumerator kStandard#
enumerator kTenBit#
enumerator kNoStart#

Public Functions

inline constexpr Initiator()#

Construct Initiator with default features.

Note: this constructor enables kTenBit because the older implementation enabled it by default. Most users will not need kTenBit enabled.

inline explicit constexpr Initiator(Feature supported_features)#

Construct Initiator with defined set of features.

Supported features are defined in the Feature enum.

Note: to support only the required features, you should specify Initiator(Initiator::Feature::kStandard).

Initiator i2c(Initiator::Feature::kStandard);
Initiator i2c(Initiator::Feature::kTenBit |
              Initiator::Feature::kNoStart);
Status WriteReadFor(Address device_address, ConstByteSpan tx_buffer, ByteSpan rx_buffer, chrono::SystemClock::duration timeout)#

Writes bytes to an I2C device and then reads bytes from that same device as one atomic I2C transaction.

The signal on the bus for the atomic transaction should look like this:

START + I2C_ADDRESS + WRITE(0) + TX_BUFFER_BYTES +
START + I2C_ADDRESS + READ(1) + RX_BUFFER_BYTES + STOP
Parameters:
  • device_address[in] The address of the I2C device.

  • tx_buffer[in] The transmit buffer.

  • rx_buffer[out] The receive buffer.

  • timeout[in] The maximum duration to block waiting for both exclusive bus access and the completion of the I2C transaction.

Pre:

The provided address must be supported by the initiator. I.e. don’t use a 10-bit address if the initiator only supports 7-bit addresses. This method fails a runtime assertion if this precondition isn’t met.

Returns:

Code

Description

OK

The transaction or transactions succeeded.

DEADLINE_EXCEEDED

Was unable to acquire exclusive initiator access and complete the I2C transaction in time.

UNAVAILABLE

A NACK condition occurred, meaning the addressed device didn’t respond or was unable to process the request.

FAILED_PRECONDITION

The interface isn’t initialized or enabled.

UNIMPLEMENTED

The interface doesn’t support the necessary i2c features or combination of i2c messages.

inline Status TransferFor(span<const Message> messages, chrono::SystemClock::duration timeout)#

Performs multiple arbitrary reads and writes to an I2C device as one atomic transaction. Each part of the transaction is referred to as a “message”.

For a series of 0…N messages, the signal on the bus for the atomic transaction of two messages should look like this:

START + #0.I2C_ADDRESS + #0.WRITE/READ(0/1) + #0.BYTES +
START + #1.I2C_ADDRESS + #1.WRITE/READ(0/1) + #1.BYTES +
...
START + #N.I2C_ADDRESS + #N.WRITE/READ(0/1) + #N.BYTES + STOP

For each Message msg in messages:

If msg.GetAddress().IsTenBit() is true:

  • The implementation should transmit that message using the 10-bit addressing scheme defined in the i2c spec.

  • The implementation should CHECK or return an error if 10-bit addressing is unsupported.

If msg.GetAddress().IsWriteContinuation() is true:

  • The implementation should transmit this message without a start condition or address.

  • The implementation should CHECK or return an error if the hardware or initiator does not support this feature.

Parameters:
  • messages[in] An array of pw::i2c::Message objects to transmit as one i2c bus transaction.

  • timeout[in] The maximum duration to block waiting for both exclusive bus access and the completion of the bus transaction.

Pre:

The provided addresses of each message must be supported by the initiator, Don’t use a 10-bit address if the initiator only supports 7-bit addresses. This method fails a runtime assertion if this precondition isn’t met.

Returns:

Code

Description

OK

The transaction succeeded.

INVALID_ARGUMENT

The arguments can never be valid. For example, a WriteContinuation without a preceding Write message.

DEADLINE_EXCEEDED

Was unable to acquire exclusive initiator access and complete the I2C transaction in time.

UNAVAILABLE

A NACK condition occurred, meaning the addressed device didn’t respond or was unable to process the request.

FAILED_PRECONDITION

The interface isn’t initialized or enabled.

UNIMPLEMENTED

The interface doesn’t support the necessary i2c features or combination of i2c messages.

inline Status WriteReadFor(
Address device_address,
const void *tx_buffer,
size_t tx_size_bytes,
void *rx_buffer,
size_t rx_size_bytes,
chrono::SystemClock::duration timeout,
)#

A variation of pw::i2c::Initiator::WriteReadFor that accepts explicit sizes for the amount of bytes to write to the device and read from the device.

inline Status WriteFor(Address device_address, ConstByteSpan tx_buffer, chrono::SystemClock::duration timeout)#

Write bytes to the I2C device.

The signal on the bus should look like this:

START + I2C_ADDRESS + WRITE(0) + TX_BUFFER_BYTES + STOP
Parameters:
  • device_address[in] The address of the I2C device.

  • tx_buffer[in] The transmit buffer.

  • timeout[in] The maximum duration to block waiting for both exclusive bus access and the completion of the I2C transaction.

Pre:

The provided address must be supported by the initiator. I.e. don’t use a 10-bit address if the initiator only supports 7-bit addresses. This method fails a runtime assertion if this precondition isn’t met.

Returns:

Code

Description

OK

The transaction succeeded.

DEADLINE_EXCEEDED

Was unable to acquire exclusive initiator access and complete the I2C transaction in time.

UNAVAILABLE

A NACK condition occurred, meaning the addressed device didn’t respond or was unable to process the request.

FAILED_PRECONDITION

The interface isn’t initialized or enabled.

UNIMPLEMENTED

The interface doesn’t support the necessary i2c features or combination of i2c messages.

inline Status WriteFor(
Address device_address,
const void *tx_buffer,
size_t tx_size_bytes,
chrono::SystemClock::duration timeout,
)#

A variation of pw::i2c::Initiator::WriteFor that accepts an explicit size for the amount of bytes to write to the device.

inline Status ReadFor(Address device_address, ByteSpan rx_buffer, chrono::SystemClock::duration timeout)#

Reads bytes from an I2C device.

The signal on the bus should look like this:

START + I2C_ADDRESS + READ(1) + RX_BUFFER_BYTES + STOP
Parameters:
  • device_address[in] The address of the I2C device.

  • rx_buffer[out] The receive buffer.

  • timeout[in] The maximum duration to block waiting for both exclusive bus access and the completion of the I2C transaction.

Pre:

The provided address must be supported by the initiator. I.e. don’t use a 10-bit address if the initiator only supports 7-bit addresses. This method fails a runtime assertion if this precondition isn’t met.

Returns:

Code

Description

OK

The transaction succeeded.

DEADLINE_EXCEEDED

Was unable to acquire exclusive initiator access and complete the I2C transaction in time.

UNAVAILABLE

A NACK condition occurred, meaning the addressed device didn’t respond or was unable to process the request.

FAILED_PRECONDITION

The interface isn’t initialized or enabled.

UNIMPLEMENTED

The interface doesn’t support the necessary i2c features or combination of i2c messages.

inline Status ReadFor(Address device_address, void *rx_buffer, size_t rx_size_bytes, chrono::SystemClock::duration timeout)#

A variation of pw::i2c::Initiator::ReadFor that accepts an explicit size for the amount of bytes to read from the device.

inline Status ProbeDeviceFor(Address device_address, chrono::SystemClock::duration timeout)#

Probes the device for an I2C ACK after only writing the address. This is done by attempting to read a single byte from the specified device.

Warning

This method is not compatible with all devices. For example, some I2C devices require a device_address in W mode before they can ack the device_address in R mode. In this case, use WriteReadFor or TransferFor to read a register with known value.

Parameters:
  • device_address[in] The address of the I2C device.

  • timeout[in] The maximum duration to block waiting for both exclusive bus access and the completion of the I2C transaction.

Pre:

The provided address must be supported by the initiator. I.e. don’t use a 10-bit address if the initiator only supports 7-bit addresses. This method fails a runtime assertion if this precondition isn’t met.

Returns:

Code

Description

OK

The transaction succeeded.

DEADLINE_EXCEEDED

Was unable to acquire exclusive initiator access and complete the I2C transaction in time.

UNAVAILABLE

A NACK condition occurred, meaning the addressed device didn’t respond or was unable to process the request.

FAILED_PRECONDITION

The interface isn’t initialized or enabled.

UNIMPLEMENTED

The interface doesn’t support the necessary i2c features or combination of i2c messages.

pw::i2c::Device#

class Device#

The common interface for generic I2C devices. Reads and writes arbitrary chunks of data over an I2C bus to an I2C device. This class contains pw::i2c::Address and wraps the pw::i2c::Initiator API. Only works with devices that have a single device address.

pw::i2c::Device is intended to represent ownership of a specific responder. Individual transactions are atomic but there’s no synchronization for sequences of transactions. Therefore, shared access should be faciliated with higher-level application abstractions. To help enforce this, pw::i2c::Device instances are only movable and not copyable.

Subclassed by pw::i2c::RegisterDevice

Public Functions

inline constexpr Device(Initiator &initiator, Address device_address)#

Creates a pw::i2c::Device instance.

The address for the I2C device is set in this constructor and can’t be modified later.

Parameters:
  • initiator[in] A reference to a pw::i2c::Initiator instance.

  • device_address[in] The address of the I2C device.

Returns:

A pw::i2c::Device instance.

inline Status TransferFor(span<const Message> messages, chrono::SystemClock::duration timeout)#

Wraps pw::i2c::Initiator::TransferFor.

inline Status WriteReadFor(ConstByteSpan tx_buffer, ByteSpan rx_buffer, chrono::SystemClock::duration timeout)#

Wraps pw::i2c::Initiator::WriteReadFor.

inline Status WriteReadFor(
const void *tx_buffer,
size_t tx_size_bytes,
void *rx_buffer,
size_t rx_size_bytes,
chrono::SystemClock::duration timeout,
)#

Wraps the variation of pw::i2c::Initiator::WriteReadFor that accepts explicit sizes for the amount of bytes to write to the device and read from the device.

inline Status WriteFor(ConstByteSpan tx_buffer, chrono::SystemClock::duration timeout)#

Wraps pw::i2c::Initiator::WriteFor.

inline Status WriteFor(const void *tx_buffer, size_t tx_size_bytes, chrono::SystemClock::duration timeout)#

Wraps the variation of pw::i2c::Initiator::WriteFor that accepts an explicit size for the amount of bytes to write to the device.

inline Status ReadFor(ByteSpan rx_buffer, chrono::SystemClock::duration timeout)#

Wraps pw::i2c::Initiator::ReadFor.

inline Status ReadFor(void *rx_buffer, size_t rx_size_bytes, chrono::SystemClock::duration timeout)#

Wraps the variation of pw::i2c::Initiator::ReadFor that accepts an explicit size for the amount of bytes to read from the device.

inline Status ProbeFor(chrono::SystemClock::duration timeout)#

Wraps pw::i2c::Initiator::ProbeDeviceFor.

pw::i2c::RegisterDevice#

See Configure and read an I2C device’s registers for example usage of pw::i2c::RegisterDevice.

class RegisterDevice : public pw::i2c::Device#

The common interface for I2C register devices. Contains methods to help read and write the device’s registers.

Warning

This interface assumes that you know how to consult your device’s datasheet to determine correct address sizes, data sizes, endianness, etc.

Public Functions

inline constexpr RegisterDevice(
Initiator &initiator,
Address address,
endian register_address_order,
endian data_order,
RegisterAddressSize register_address_size,
)#

This constructor specifies the endianness of the register address and data separately. If your register address and data have the same endianness and you’d like to specify them both with a single argument, see the other pw::i2c::RegisterDevice constructor.

Parameters:
  • initiator[in] A pw::i2c::Initiator instance for the bus that the device is on.

  • address[in] The address of the I2C device.

  • register_address_order[in] The endianness of the register address.

  • data_order[in] The endianness of the data.

  • register_address_size[in] The size of the register address.

inline constexpr RegisterDevice(Initiator &initiator, Address address, endian order, RegisterAddressSize register_address_size)#

This constructor specifies the endianness of the register address and data with a single argument. If your register address and data have different endianness, use the other pw::i2c::RegisterDevice constructor.

Parameters:
  • initiator[in] A pw::i2c::Initiator instance for the bus that the device is on.

  • address[in] The address of the I2C device.

  • order[in] The endianness of both the register address and register data.

  • register_address_size[in] The size of the register address.

inline Status WriteRegisters(
uint32_t register_address,
ConstByteSpan register_data,
ByteSpan buffer,
chrono::SystemClock::duration timeout,
)#

Writes data to multiple contiguous registers starting at a specific register. This method is byte-addressable.

register_address and register_data use the endianness that was provided when this pw::i2c::RegisterDevice instance was constructed.

Parameters:
  • register_address[in] The register address to begin writing at.

  • register_data[in] The data to write. Endianness is taken into account if the data is 2 or 4 bytes.

  • buffer[in] A buffer for constructing the write data. The size of this buffer must be at least as large as the size of register_address plus the size of register_data.

  • timeout[in] The maximum duration to block waiting for both exclusive bus access and the completion of the I2C transaction.

Pre:

This method assumes that you’ve verified that your device supports bulk writes and that register_data is a correct size for your device.

Returns:

Code

Description

OK

The bulk write was successful.

DEADLINE_EXCEEDED

Unable to acquire exclusive bus access and complete the transaction in time.

FAILED_PRECONDITION

The interface is not initialized or enabled.

INTERNAL

An issue occurred while building register_data.

INVALID_ARGUMENT

register_address is larger than the 10-bit address space.

OUT_OF_RANGE

The size of buffer is less than the size of register_address plus the size of register_data.

UNAVAILABLE

The device took too long to respond to the NACK.

inline Status WriteRegisters8(
uint32_t register_address,
span<const uint8_t> register_data,
ByteSpan buffer,
chrono::SystemClock::duration timeout,
)#

Variant of pw::i2c::RegisterDevice::WriteRegisters() that requires register_data to be exactly 8 bits.

inline Status WriteRegisters16(
uint32_t register_address,
span<const uint16_t> register_data,
ByteSpan buffer,
chrono::SystemClock::duration timeout,
)#

Variant of pw::i2c::RegisterDevice::WriteRegisters() that requires register_data to be exactly 16 bits.

inline Status WriteRegisters32(
uint32_t register_address,
span<const uint32_t> register_data,
ByteSpan buffer,
chrono::SystemClock::duration timeout,
)#

Variant of pw::i2c::RegisterDevice::WriteRegisters() that requires register_data to be exactly 32 bits.

Status ReadRegisters(uint32_t register_address, ByteSpan return_data, chrono::SystemClock::duration timeout)#

Reads data from multiple contiguous registers starting from a specific offset or register. This method is byte-addressable.

register_address and return_data use the endianness that was provided when this pw::i2c::RegisterDevice instance was constructed.

Parameters:
  • register_address[in] The register address to begin reading at.

  • return_data[out] The area to read the data into. The amount of data that will be read is equal to the size of this span. Endianness is taken into account if this span is 2 or 4 bytes.

  • timeout[in] The maximum duration to block waiting for both exclusive bus access and the completion of the I2C transaction.

Pre:

This method assumes that you’ve verified that your device supports bulk reads and that return_data is a correct size for your device.

Returns:

Code

Description

OK

The bulk read was successful.

DEADLINE_EXCEEDED

Unable to acquire exclusive bus access and complete the transaction in time.

FAILED_PRECONDITION

The interface is not initialized or enabled.

INTERNAL

An issue occurred while building return_data.

INVALID_ARGUMENT

register_address is larger than the 10-bit address space.

UNAVAILABLE

The device took too long to respond to the NACK.

inline Status ReadRegisters8(uint32_t register_address, span<uint8_t> return_data, chrono::SystemClock::duration timeout)#

Variant of pw::i2c::RegisterDevice::ReadRegisters() that requires return_data to be exactly 8 bits.

inline Status ReadRegisters16(uint32_t register_address, span<uint16_t> return_data, chrono::SystemClock::duration timeout)#

Variant of pw::i2c::RegisterDevice::ReadRegisters() that requires return_data to be exactly 16 bits.

inline Status ReadRegisters32(uint32_t register_address, span<uint32_t> return_data, chrono::SystemClock::duration timeout)#

Variant of pw::i2c::RegisterDevice::ReadRegisters() that requires return_data to be exactly 32 bits.

inline Status WriteRegister(uint32_t register_address, std::byte register_data, chrono::SystemClock::duration timeout)#

Sends a register address to write to and then writes to that address.

register_address and register_data use the endianness that was provided when this pw::i2c::RegisterDevice instance was constructed.

Parameters:
  • register_address[in] The register address to write to.

  • register_data[in] The data that should be written at the address. The maximum allowed size is 4 bytes.

  • timeout[in] The maximum duration to block waiting for both exclusive bus access and the completion of the I2C transaction.

Pre:

This method assumes that you’ve verified that register_data is a correct size for your device.

Returns:

Code

Description

OK

The write was successful.

DEADLINE_EXCEEDED

Unable to acquire exclusive bus access and complete the transaction in time.

FAILED_PRECONDITION

The interface is not initialized or enabled.

INTERNAL

An issue occurred while writing the data.

INVALID_ARGUMENT

register_address is larger than the 10-bit address space.

UNAVAILABLE

The device took too long to respond to the NACK.

inline Status WriteRegister8(uint32_t register_address, uint8_t register_data, chrono::SystemClock::duration timeout)#

Variant of pw::i2c::RegisterDevice::WriteRegister() that writes exactly 8 bits.

inline Status WriteRegister16(uint32_t register_address, uint16_t register_data, chrono::SystemClock::duration timeout)#

Variant of pw::i2c::RegisterDevice::WriteRegister() that writes exactly 16 bits.

inline Status WriteRegister32(uint32_t register_address, uint32_t register_data, chrono::SystemClock::duration timeout)#

Variant of pw::i2c::RegisterDevice::WriteRegister() that writes exactly 32 bits.

inline Result<std::byte> ReadRegister(uint32_t register_address, chrono::SystemClock::duration timeout)#

Sends a register address to read from and then reads from that address.

register_address and the return data use the endianness that was provided when this pw::i2c::RegisterDevice instance was constructed.

Parameters:
  • register_address[in] The register address to read.

  • timeout[in] The maximum duration to block waiting for both exclusive bus access and the completion of the I2C transaction.

Pre:

This method assumes that you’ve verified that the return data size is a correct size for your device.

Returns:

Code

Description

OK

Returns the register data.

DEADLINE_EXCEEDED

Unable to acquire exclusive bus access and complete the transaction in time.

FAILED_PRECONDITION

The interface is not initialized or enabled.

INTERNAL

An issue occurred while building the return data.

INVALID_ARGUMENT

register_address is larger than the 10-bit address space.

UNAVAILABLE

The device took too long to respond to the NACK.

inline Result<uint8_t> ReadRegister8(uint32_t register_address, chrono::SystemClock::duration timeout)#

Variant of pw::i2c::RegisterDevice::ReadRegister() that returns exactly 8 bits.

inline Result<uint16_t> ReadRegister16(uint32_t register_address, chrono::SystemClock::duration timeout)#

Variant of pw::i2c::RegisterDevice::ReadRegister() that returns exactly 16 bits.

inline Result<uint32_t> ReadRegister32(uint32_t register_address, chrono::SystemClock::duration timeout)#

Variant of pw::i2c::RegisterDevice::ReadRegister() that returns exactly 32 bits.

pw::i2c::I2cService#

class I2cService : public pw_rpc::pwpb::I2c::Service<I2cService>#

RPC service for performing I2C transactions.

Public Types

using InitiatorSelector = pw::Function<Initiator*(size_t pos)>#

A callback that returns a pw::i2c::Initiator instance for the given bus index position or nullptr if the position is not valid for this I2C device.

Public Functions

inline explicit I2cService(InitiatorSelector &&initiator_selector)#

Creates an I2cService instance.

void I2cWrite(
const pwpb::I2cWriteRequest::Message &request,
pw::rpc::PwpbUnaryResponder<pwpb::I2cWriteResponse::Message> &responder,
)#

Writes a message to the specified I2C device register.

void I2cRead(
const pwpb::I2cReadRequest::Message &request,
pw::rpc::PwpbUnaryResponder<pwpb::I2cReadResponse::Message> &responder,
)#

Reads a message from the specified I2C device register.

pw::i2c::MockMessageInitiator#

class MockMessageInitiator : public pw::i2c::Initiator#

A generic mocked backend for pw::i2c::Initiator that’s specifically designed to make it easier to develop I2C device drivers. pw::i2c::MessageMockInitiator compares actual I2C transactions against expected transactions. The expected transactions are represented as a list of pw::i2c::MockMessageTransaction instances that are passed as arguments in the pw::i2c::MessageMockInitiator constructor. Each consecutive call to pw::i2c::MockMessageInitiator iterates to the next expected transaction. pw::i2c::MockMessageInitiator::Finalize() indicates whether the actual transactions matched the expected transactions.

pw::i2c::MessageMockInitiator is intended to be used within GoogleTest tests. See pw_unit_test .

#include <chrono>

#include "pw_bytes/array.h"
#include "pw_i2c/address.h"
#include "pw_i2c/initiator_message_mock.h"
#include "pw_i2c/message.h"
#include "pw_result/result.h"
#include "pw_unit_test/framework.h"

using namespace std::chrono_literals;

namespace {

TEST(I2CTestSuite, I2CReadRegisterTestCase) {
  constexpr Address kTestDeviceAddress = Address::SevenBit<0x3F>();
  constexpr auto kExpectedWrite1 = bytes::Array<0x42>();
  constexpr auto kExpectedRead1 = bytes::Array<1, 2>();

  auto expected_transactions = MakeExpectedTransactionArray({
      MockMessageTransaction(
          OkStatus(),
          MakeExpectedMessageArray({
              MockWriteMessage(OkStatus(), kTestDeviceAddress,
                               kExpectedWrite1),
              MockReadMessage(OkStatus(), kTestDeviceAddress,
                               kExpectedRead1),
          }),
          kI2cTransactionTimeout),
  });
  MockMessageInitiator initiator(expected_transactions);
  std::array<std::byte, kExpectedRead1.size()> read1;
  EXPECT_EQ(initiator.WriteReadFor(kTestDeviceAddress, kExpectedWrite1,
            read1, kI2cTransactionTimeout), OkStatus());
  EXPECT_TRUE(pw::containers::Equal(read1, kExpectedRead1));
  EXPECT_EQ(initiator.Finalize(), OkStatus());
}

}

Public Functions

inline Status Finalize() const#

Indicates whether the actual I2C transactions matched the expected transactions. Should be called at the end of the test.

Returns:

Code

Description

OK

The actual transactions matched the expected transactions.

OUT_OF_RANGE

The mocked set of transactions hasn’t been exhausted.

~MockMessageInitiator() override#

Runs pw::i2c::MessageMockInitiator::Finalize() regardless of whether it was already optionally finalized.

pw::i2c::MockMessageTransaction#

class MockMessageTransaction#

Represents a list of MockMessages that make up one i2c transaction. An i2c transaction can consist of any arbitrary combination of i2c read and write messages that are transmitted sequentially and without releasing the bus with an i2c Stop condition.

Public Functions

inline std::optional<chrono::SystemClock::duration> timeout() const#

Gets the minimum duration to wait for a blocking I2C transaction.

pw::i2c::MockMessage#

class MockMessage#

Base class for creating expected individual Messages that make up a MockMessageTransaction instance. For read-only, write-only, or probe messages, improve code readability by using one of the following helpers instead:

  • pw::i2c::MockReadMessage

  • pw::i2c::MockWriteMessage

  • pw::i2c::MockProbeMessage

Public Functions

inline constexpr MockMessage(Status expected_return_value, Address device_address)#

Alternative constructor for creating probe transactions.

inline Status return_value() const#

Gets the expected return value for the transaction.

inline Address address() const#

Gets the I2C address that the I2C transaction is targeting.

inline ConstByteSpan data_buffer() const#

Gets the buffer that is virtually read.

pw::i2c::MockReadMessage#

constexpr MockMessage pw::i2c::MockReadMessage(Status expected_return_value, Address address, ConstByteSpan data_buffer)#

pw::i2c::MockWriteMessage#

constexpr MockMessage pw::i2c::MockWriteMessage(Status expected_return_value, Address address, ConstByteSpan data_buffer)#

pw::i2c::MockProbeMessage#

constexpr MockMessage pw::i2c::MockProbeMessage(Status expected_return_value, Address address)#

pw::i2c::GmockInitiator#

class GmockInitiator : public pw::i2c::Initiator#

Provides the MOCK_METHOD implementation for gMock compatibility.