Module atsamd_hal::sercom::i2c
source · [−]Expand description
Use the SERCOM peripheral for I2C communications
Configuring an I2C peripheral occurs in three steps. First, you must create
a set of Pads
for use by the peripheral. Next, you assemble pieces into
a Config
struct. After configuring the peripheral, you then enable
it, yielding a functional I2c
struct.
Transactions are performed using the i2c
traits from embedded HAL.
Pads
A Sercom
uses two Pin
s as peripheral Pad
s, but only
certain Pin
combinations are acceptable. In particular, all Pin
s
must be mapped to the same Sercom
, and SDA is always Pad0
, while SCL
is always Pad1
(see the datasheet). This HAL makes it impossible to use
invalid Pin
/Pad
combinations, and the Pads
struct is responsible
for enforcing these constraints.
A Pads
type takes three or four type parameters, depending on the chip.
The first type always specifies the Sercom
. On SAMx5x chips, the second
type specifies the IoSet
. The remaining two, SDA
and SCL
represent the
SDA and SCL pads respectively. A Pad
is just a Pin
configured in the
correct PinMode
that implements IsPad
. The
bsp_pins!
macro can be used to define convenient type
aliases for Pad
types.
use atsamd_hal::gpio::{PA08, PA09, AlternateC};
use atsamd_hal::sercom::{Sercom0, i2c};
use atsamd_hal::typelevel::NoneT;
// SAMx5x-specific imports
use atsamd_hal::sercom::pad::IoSet1;
type Sda = Pin<PA08, AlternateC>;
type Scl = Pin<PA09, AlternateC>;
// SAMD11/SAMD21 version
type Pads = i2c::Pads<Sercom0, Sda, Scl>;
// SAMx5x version
type Pads = i2c::Pads<Sercom0, IoSet1, Sda, Scl>;
Alternatively, you can use the PadsFromIds
alias to define a set of
Pads
in terms of PinId
s instead of Pin
s. This is useful when you
don’t have Pin
aliases pre-defined.
use atsamd_hal::gpio::{PA08, PA09};
use atsamd_hal::sercom::{Sercom0, i2c};
type Pads = i2c::PadsFromIds<Sercom0, PA08, PA09>;
Instances of Pads
are created using the new
method.
On SAMD21 and SAMx5x chips, new
method automatically convert
each pin to the correct PinMode
. But for SAMD11 chips, users must
manually convert each pin before calling the builder methods. This is a
consequence of inherent ambiguities in the SAMD11 SERCOM pad definitions.
Specifically, the same PinId
can correspond to two different PadNum
s
for the same Sercom
.
use atsamd_hal::pac::Peripherals;
use atsamd_hal::gpio::Pins;
use atsamd_hal::sercom::{Sercom0, i2c};
let mut peripherals = Peripherals::take().unwrap();
let pins = Pins::new(peripherals.PORT);
let pads = i2c::Pads::<Sercom0>::new(pins.pa08, pins.pa09);
Config
Next, create a Config
struct, which represents the I2C peripheral in
its disabled state. A Config
is specified with one type parameters, the
Pads
type.
Upon creation, the Config
takes ownership of both the Pads
struct
and the PAC Sercom
struct. It takes a reference to the PM, so that it
can enable the APB clock, and it takes a frequency to indicate the GCLK
configuration. Users are responsible for correctly configuring the GCLK.
use atsamd_hal::gpio::{PA08, PA09};
use atsamd_hal::sercom::{Sercom0, i2c};
type Pads = i2c::PadsFromIds<Sercom0, PA08, PA09>;
type Config = i2c::Config<Pads>;
let pm = peripherals.PM;
let sercom = peripherals.SERCOM0;
// Configure GCLK for 10 MHz
let freq = 10.mhz();
let config = i2c::Config::new(&pm, sercom, pads, freq);
The Config
struct can configure the peripheral in one of two ways:
- A set of methods is provided to use in a builder pattern: for example
baud
,run_in_standby
, etc. These methods takeself
and returnSelf
. - A set of methods is provided to use as setters: for example
set_baud
,set_run_in_standby
, etc. These methods take&mut self
and return nothing.
In any case, the peripheral setup ends with a call to enable
, which
consumes the Config
and returns an enabled I2c
peripheral.
let i2c = i2c::Config::new(&pm, sercom, pads, freq)
.baud(1.mhz())
.enable();
Alternatively,
let i2c = i2c::Config::new(&mclk, sercom, pads, freq);
i2c.set_baud(1.mhz());
let i2c = i2c.enable();
Reading the current configuration
It is possible to read the current configuration by using the getter methods
provided: for example get_baud
,
get_run_in_standby
, etc.
I2c
I2c
structs can only be created from a Config
. They have one type
parameter, representing the underlying Config
.
Only the I2c
struct can actually perform
transactions. To do so, use the embedded HAL traits, like
i2c::WriteRead
, i2c::Read
and i2c::Write
.
use embedded_hal::blocking::i2c::Write;
i2c.write(0x54, 0x0fe)
Reading the current configuration
The AsRef<Config<P>>
trait is implemented for I2c<Config<P>>
.
This means you can use the get_
methods implemented for Config
, since
they take an &self
argument.
// Assume i2c is a I2c<C<P>>
let baud = i2c.as_ref().get_baud();
Reconfiguring
The reconfigure
method gives out an &mut Config
reference, which can
then use the set_*
methods.
use atsamd_hal::sercom::i2c::I2c;
use atsamd_hal::time::*;
// Assume config is a valid Duplex I2C Config struct
let i2c = config.enable();
// Send/receive data...
// Reconfigure I2C peripheral
i2c.reconfigure(|c| c.set_run_in_standby(false));
// Disable I2C peripheral
let config = i2c.disable();
Non-supported features
- Slave mode is not supported at this time.
- High-speed mode is not supported.
- 4-wire mode is not supported.
- 32-bit extension mode is not supported (SAMx5x). If you need to transfer
slices, consider using the DMA methods instead. The
dma
Cargo feature must be enabled.
Structs
A configurable, disabled I2C peripheral
Interrupt bitflags for I2C transactions
Abstraction over a I2C peripheral, allowing to perform I2C transactions.
Status flags for I2C transactions
Enums
Type representing the current bus state
Errors available for I2C transactions
Inactive timeout configuration
Traits
Type Definitions
Word size for an I2C message