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 Pins as peripheral Pads, but only
certain Pin combinations are acceptable. In particular, all Pins
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 PinIds instead of Pins. 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 PadNums
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 takeselfand returnSelf. - A set of methods is provided to use as setters: for example
set_baud,set_run_in_standby, etc. These methods take&mut selfand 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
dmaCargo feature must be enabled.