Quickstart and guides#
pw_i2c: Cross-platform I2C API with interactive debugging
Quickstart#
Depend on the pw_i2c
API and an appropriate implementation of the
API like pw_i2c_linux
. See Implementations for the
list of existing implementations and to learn how to create your own.
cc_library(
# ...
deps = [
# ...
"@pigweed//pw_i2c:address",
"@pigweed//pw_i2c:device",
# ...
] + select({
"@platforms//os:linux": [
"@pigweed//pw_i2c_linux:initiator",
],
"//conditions:default": [
# Fake example of a custom implementation.
"//lib/pw_i2c_my_device:initiator",
],
}),
)
Note
This assumes that your Bazel WORKSPACE
has a repository named
@pigweed
that points to the upstream Pigweed repository.
If creating your own implementation, depend on the virtual interface:
cc_library(
name = "initiator",
srcs = ["initiator.cc"],
hdrs = ["initiator.h"],
deps = ["@pigweed//pw_i2c:initiator"],
)
Write some C++ code to interact with an I2C device:
#include "pw_i2c_rp2040/initiator.h"
#include "hardware/i2c.h"
constexpr pw::i2c::Rp2040Initiator::Config ki2cConfig{
.clock_frequency_hz = 400'000,
.sda_pin = 8,
.scl_pin = 9,
};
pw::i2c::Rp2040Initiator i2c_bus(ki2cConfig, i2c0);
// Calls these Pico SDK functions:
// * gpio_set_function(8, GPIO_FUNC_I2C)
// * gpio_set_function(9, GPIO_FUNC_I2C)
i2c_bus.Enable();
Guides#
Mock I2C transactions#
See the example in pw::i2c::MockInitiator
.
Configure and read an I2C device’s registers#
#include <chrono>
#include <cstddef>
#include <cstdint>
#include "pw_bytes/bit.h"
#include "pw_i2c/address.h"
#include "pw_i2c/register_device.h"
#include "pw_log/log.h"
#include "pw_status/status.h"
using ::pw::Status;
using namespace std::chrono_literals;
// Search for `pi4ioe5v6416` in the Kudzu codebase to see real usage of
// pw::i2c::RegisterDevice
namespace pw::pi4ioe5v6416 {
namespace {
constexpr pw::i2c::Address kAddress = pw::i2c::Address::SevenBit<0x20>();
enum Register : uint32_t {
InputPort0 = 0x0,
ConfigPort0 = 0x6,
PullUpDownEnablePort0 = 0x46,
PullUpDownSelectionPort0 = 0x48,
};
} // namespace
// This particular example instantiates `pw::i2c::RegisterDevice`
// as part of a higher-level general "device" interface.
// See //lib/pi4ioe5v6416/public/pi4ioe5v6416/device.h in Kudzu.
Device::Device(pw::i2c::Initiator& initiator)
: initiator_(initiator),
register_device_(initiator,
kAddress,
endian::little,
pw::i2c::RegisterAddressSize::k1Byte) {}
Status Device::Enable() {
// Set port 0 as inputs for buttons (1=input)
device_.WriteRegister8(Register::ConfigPort0,
0xff,
pw::chrono::SystemClock::for_at_least(10ms));
// Select pullup resistors for button input (1=pullup)
device_.WriteRegister8(Register::PullUpDownSelectionPort0,
0xff,
pw::chrono::SystemClock::for_at_least(10ms));
// Enable pullup/down resistors for button input (1=enable)
device_.WriteRegister8(Register::PullUpDownEnablePort0,
0xff,
pw::chrono::SystemClock::for_at_least(10ms));
return OkStatus();
}
pw::Result<uint8_t> Device::ReadPort0() {
return device_.ReadRegister8(Register::InputPort0,
pw::chrono::SystemClock::for_at_least(10ms));
}
} // namespace pw::pi4ioe5v6416
The code example above was adapted from Kudzu. See the following
files for real pw::i2c::RegisterDevice
usage:
Access an I2C device’s registers over RPC#
pw::i2c::I2cService
enables accessing an I2C device’s registers
over RPC.
Using pw_console, invoke the service to perform an I2C read:
# Read register `0x0e` from the device at `0x22`.
device.rpcs.pw.i2c.I2c.I2cRead(
bus_index=0,
target_address=0x22,
register_address=b'\x0e',
read_size=1
)
For responders that support 4-byte register width, you can specify the register address like this:
device.rpcs.pw.i2c.I2c.I2cRead(
bus_index=0,
target_address=<address>,
register_address=b'\x00\x00\x00\x00',
read_size=4
)
To perform an I2C write:
device.rpcs.pw.i2c.I2c.I2cWrite(
bus_index=0,
target_address=0x22,
register_address=b'\x0e',
value=b'\xbc'
)
Multi-byte writes can also be specified with the bytes fields for
register_address
and value
.
I2C responders that require multi-byte access may expect a specific endianness. The order of bytes specified in the bytes field will match the order of bytes sent or received on the bus. The maximum supported value for multi-byte access is 4 bytes.