Expand description
Use the SERCOM peripheral for UART communications
Configuring an UART 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 Uart
struct.
Transactions are performed using the serial
traits
from embedded HAL.
Pads
A Sercom
can use up to four 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
(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 five type parameters. The first specifies the Sercom
,
while the remaining four, DI
, DO
, CK
and SS
, represent the Data In,
Data Out, Sclk and SS pads respectively. Each of the remaining type
parameters is an OptionalPad
and defaults to NoneT
. Aliases defining
the pad types can be provided by the bsp_pins!
macro.
use atsamd_hal::gpio::v2::{PA08, PA09, AlternateC};
use atsamd_hal::sercom::v2::{Sercom0, uart};
use atsamd_hal::typelevel::NoneT;
type Rx = Pin<PA08, AlternateC>;
type Tx = Pin<PA09, AlternateC>;
type Pads = uart::Pads<Sercom0, Rx, Tx>;
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::v2::{PA08, PA09};
use atsamd_hal::sercom::v2::{Sercom0, uart};
use atsamd_hal::typelevel::NoneT;
type Pads = uart::PadsFromIds<Sercom0, PA09, PA09>;
Instaces of Pads
are created using the builder pattern. Start by
creating an empty set of Pads
using Default
. Then pass each
respective Pin
using the corresponding methods. Both v1::Pin
and
v2::Pin
types are accepted here.
On SAMD21 and SAMx5x chips, the builder methods 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::v2::Pins;
use atsamd_hal::sercom::v2::{Sercom0, uart};
let mut peripherals = Peripherals::take().unwrap();
let pins = Pins::new(peripherals.PORT);
let pads = uart::Pads::<Sercom0>::default()
.rx(pins.pa09)
.tx(pins.pa08);
To be accepted as ValidPads
, a set of Pads
must do two things:
- Specify a type for at least one of
RX
orTX
- Satisfy the
RxpoTxpo
trait (SAMD11/SAMD21), or theRxpo
andTxpo
traits (SAMx5x)
Config
Next, create a Config
struct, which represents the UART peripheral in
its disabled state. A Config
is specified with two type parameters: the
Pads
type; and a CharSize
, which defaults to EightBit
.
use atsamd_hal::gpio::v2::{PA08, PA09};
use atsamd_hal::sercom::v2::{Sercom0, uart};
use atsamd_hal::sercom::v2::uart::{NineBit};
use atsamd_hal::typelevel::NoneT;
type Pads = uart::PadsFromIds<Sercom0, PA08, PA09>;
type Config = uart::Config<Pads, NineBit>;
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::time::U32Ext;
let pm = peripherals.PM;
let sercom = peripherals.SERCOM0;
// Configure GCLK for 10 MHz
let freq = 10.mhz();
let config = uart::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
,stop_bits
, etc. These methods takeself
and returnSelf
. - A set of methods is provided to use as setters: for example
set_baud
,set_stop_bits
, 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 Uart
peripheral.
use atsamd_hal::sercom::v2::uart::{StopBits, NineBit, BitOrder, BaudMode, Oversampling};
let uart = uart::Config::new(&mclk, sercom, pads, freq)
.baud(1.mhz(), BaudMode::Arithmetic(Oversampling::Bits16))
.char_size::<NineBit>()
.bit_order(BitOrder::LsbFirst)
.stop_bits(StopBits::TwoBits)
.enable();
Alternatively,
use atsamd_hal::sercom::v2::uart::{StopBits, NineBit, BitOrder, BaudMode, Oversampling};
let uart = uart::Config::new(&mclk, sercom, pads, freq);
uart.set_baud(1.mhz(), BaudMode::Arithmetic(Oversampling::Bits16));
uart.set_char_size::<NineBit>();
uart.set_bit_order(BitOrder::LsbFirst);
uart.set_stop_bits(StopBits::TwoBits);
let uart = uart.enable();
To be accepted as a ValidConfig
, the Config
must have at least one
of Rx
or Tx
pads.
CharSize
The UART peripheral can be configured to use different character sizes. By
default, a Config
is configured with an EightBit
character size.
This can be changed through the char_size
method.
Changing the character normally also changes the Config
’s type.
Alternatively, you can also use a DynCharSize
through the
dyn_char_size
method. This enables you to
dynamically change the character size on the fly through the
set_dyn_char_size
method when calling
reconfigure
.
Reading the current configuration
It is possible to read the current configuration by using the getter methods
provided: for example get_baud
,
get_stop_bits
, etc.
Uart
and capabilities
Uart
structs can only be created from a Config
. They have two type
parameters: the first one represents the underlying Config
, while the
second represents the Uart
’s capabilities. The second type parameter can
be one of:
Rx
orRxDuplex
: Can perform receive transactionsTx
orTxDuplex
: Can perform transmit transactionsDuplex
: UART configured as duplex that can perform receive and transmit transactions. Additionally, thesplit
method can be called to return aUart<C, RxDuplex>, Uart<C, TxDuplex>)
tuple. See the Splitting section for more information.
The nature of the underlying Pads
contained inside Config
determines
the type returned by a call to enable
. If the pads only have a TX
pin
specified, then enable
will return a Uart<C, Tx>
. Similarly, If the
pads only have a RX
pin specified, then enable
will return a Uart<C, Rx>
. Finally, if both RX
and TX
pins are specified, then enable
will return a Uart<C, Duplex>
, which can be further split into a Uart<C, RxDuplex>
and a Uart<C, TxDuplex>
.
use atsamd_hal::gpio::v2::{PA08, PA09};
use atsamd_hal::sercom::v2::{Sercom0, uart};
use atsamd_hal::sercom::v2::uart::NineBit;
use atsamd_hal::typelevel::NoneT;
// Assuming SAMD21 or SAMx5x
type Pads = uart::PadsFromIds<Sercom0, PA08, NoneT, PA09>;
type Config = uart::Config<Pads, NineBit>;
type UartRx = uart::Uart<Config, RxDuplex>;
type UartTx = uart::UartTx<Config, RxDuples>;
Only the Uart
struct can actually perform
transactions. To do so, use the embedded HAL traits, like
serial::Read
and serial::Write
.
use nb::block;
use embedded_hal::serial::Write;
block!(uart_tx.write(0x0fe));
UART flow control (CTS/RTS)
This module supports CTS and RTS pins.
The RTS
pin is a fully hardware-controlled output pin that gets deasserted
when:
- The USART receiver is disabled;
- The USART’s RX buffer is full.
The CTS
pin is an input pin that provides an interrupt when a change
(rising or falling edge) is detected on the corresponding Pad. This
interrupt, CTSIC
, can be enabled with the
enable_ctsic
method only when the corresponding
Config
has a CTS
pad specified. The
disable_ctsic
and
clear_ctsic
methods are also available under the same
conditions. This application note
provides more information about UART hardware flow control.
Splitting
A Uart<C, Duplex>
can be split into its RxDuplex
and TxDuplex
constituents:
use atsamd_hal::sercom::v2::uart::Uart;
// Assume uart is a Uart<C, Duplex>
let (rx, tx) = uart.split();
Joining
When a Uart<C, Duplex>
has been split into its RxDuplex
and
TxDuplex
parts, these parts can be joined back into a Uart<C, Duplex>
by calling the join
function for Uart<C, Duplex>
. It takes a Uart<C, RxDuplex>
and a Uart<C, TxDuplex>
and moves them into a full Duplex
Uart
.
use atsamd_hal::sercom::v2::uart::Uart;
// Assume rx is a Uart<C, RxDuplex> and tx is a Uart<C, TxDuplex>
let uart = Uart::join(rx, tx);
// uart is now a Uart<C, Duplex>
The AsMut<Uart<C, Duplex>>
trait is also implemented for (&mut Uart<C, RxDuplex>, &mut Uart<C, TxDuplex>)
. This is useful if you need an &mut Uart<C, Duplex>
but you only have a pair of &mut Uart<C, RxDuplex>
and
&mut Uart<C, TxDuplex>
. This can be leveraged to use the reconfigure
method when all you have is a pair of mutable references to the RxDuplex
and TxDuplex
halves.
use atsamd_hal::sercom::v2::uart::Uart;
use atsamd_hal::time::*;
// Assume rx is a Uart<C, RxDuplex> and tx is a Uart<C, TxDuplex>
// Reconfigure peripheral from mutable references to RxDuplex
// and TxDuplex halves
(&mut rx, &mut tx).as_mut().reconfigure(|c| c.set_run_in_standby(false));
Reading the current configuration
The AsRef<Config<P, C>>
trait is implemented for Uart<Config<P, C>, D>
.
This means you can use the get_
methods implemented for Config
, since
they take an &self
argument.
// Assume uart is a Uart<C, D>
let (baud, baud_mode) = uart.as_ref().get_baud();
Disabling and reconfiguring
Some methods, such as disable
and reconfigure
, need to operate on
all parts of a UART at once. In practice, this means that these methods
operate on the type that was returned by enable
. This can be Uart<C, Rx>
, Uart<C, Tx>
, or Uart<C, Duplex>
, depending on how the
peripheral was configured.
The reconfigure
method gives out an &mut Config
reference, which can
then use the set_*
methods.
use atsamd_hal::sercom::v2::uart::Uart;
use atsamd_hal::time::*;
// Assume config is a valid Duplex UART Config struct
let (rx, tx)= config.enable().split();
// Send/receive data with tx/rx halves...
// If the UART peripheral is configured in Duplex mode,
// the two constituting halves need to be joined back into
// a Uart<C, Duplex> before calling disable()
let uart = Uart::join(rx, tx);
// Reconfigure UART peripheral
uart.reconfigure(|c| c.set_run_in_standby(false));
// Disable UART peripheral
let config = uart.disable();
Non-supported advanced features
- Synchronous mode (USART) is not supported
- LIN mode is not supported (SAMx5x)
- 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.
Modules
Structs
A configurable, disabled UART peripheral
Interrupt bit flags for UART Rx transactions
Status flags for UART Rx transactions
Abstraction over a UART peripheral, allowing to perform UART transactions.
The second type parameter, D
, denotes what the struct’s Capability
is.
Enums
Baudrate calculation in asynchronous mode
Bit order of a UART frame
enum
version of CharSize
Marker type representing a UART that has both transmit and receive capability
Dynamic CharSize
that can be changed on the fly
Errors available for UART transactions
Baudrate oversampling values
Parity setting of a UART frame
Marker type representing a UART that can only receive
Number of stop bits in a UART frame
Marker type representing a UART that can only transmit
Constants
Interrupt flags available for Duplex transactions
Status flags available for Duplex transactions
Interrupt flags available for RX transactions
Status flags available for RX transactions
Interrupt flags available for TX transactions
Traits
Type-level enum
representing the capabilities of a UART peripheral
Type-level enum
representing the UART character size
Type-level enum
indicating a CharSize
that is not dynamic
Type-level function to recover the OptionalPad
types from a generic set
of Pads
Type-level enum representing a UART that can receive
Type-level enum representing a UART that has transmit or receive capability, but not both
Type-level enum representing a UART that can transmit
Marker trait for valid UART Config
urations
Type Definitions
Size of the SERCOM’s DATA
register