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.